✨ A CTP service providing a WebSocket interface, developed based on the Python CTP API✨
English | 简体中文
- Overview
- Installation and Operation
- Request Example
- Protocol
- Project Structure
- Architecture Description
- Test
- Other Notes
homalos-webctp is a CTP service based on the Python CTP API that provides a WebSocket interface. It aims to provide an interface for the operation and development of futures quantitative trading.
- Current Status: Under Development
-
Python :3.13
-
Tools: UV
-
CTP API:6.7.10
-
Environment Preparation
Install UV lamp; UV lamps are recommended.
-
👈Method 1: System-wide installation. This method is recommended. Other Python projects can also use UV management.
Install on Windows system
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"Installing on a Linux system
curl -LsSf https://astral.sh/uv/install.sh | sh👈Method 2: Install on an existing Python installation
Choose one of the two methods above. If method one is executed, method two will be skipped. The UV (unique visitors) for this installation method can only be used in this Python environment.pip install uv
-
Install Python
If you selected to install UV globally in step 1, you need to perform this step; otherwise, skip it.
uv python install 3.13
👈Tips
This method installs Python globally, isolating it from the Python environment in the project, and does not affect it. -
Cloning project
git clone https://github.com/Homalos/homalos-webctp.git cd homalos-webctp -
Install dependencies
uv sync
Based on the information in pyproject.toml, automatically create a Python virtual environment named .venv in the current project root directory and install all dependencies.
-
Configuration
👈Configuration Reference
📌The configuration example is config.example.yaml. The example uses the front-end address for market data and trading. The default configuration is the SimNow 7x24 environment. For more detailed information on the SimNow environment, please refer to SimNow Official Website and OpenCTP Environment Monitoring. You can change it to other trading environment that supports CTPAPI (official implementation) as needed.
📌 SimNow 7x24 environment:
Front Information BrokerID 9999 Brokerage ID Trade Front 182.254.243.31:40001 A transparent front-end system uses a monitoring center to generate keys. Market Front 182.254.243.31:40011 Transaction phase (service time) Trading days, 16:00 to 09:00 the following day On non-trading days, from 16:00 to 12:00 the following day -
This environment is only for CTP API development enthusiasts and is only provided for users' CTP API testing needs. It does not provide other services such as settlement.
-
Newly registered users will need to wait until the third trading day to use the second environment.
-
The account, funds, and warehouses remain consistent with the previous trading day's environment.
📌 SimNow Non-7x24 environment:
Font Information BrokerID 9999 APPID simnow_client_test AuthCode 0000000000000000(16 zeros) Group 1 Trade Front 182.254.243.31:30001 A transparent front-end system uses a monitoring center to generate keys. Market Front 182.254.243.31:30012 Group 2 Trade Front 182.254.243.31:30002 Market Front 182.254.243.31:30012 Group 3 Trade Front 182.254.243.31:30003 Market Front 182.254.243.31:30013 Transaction phase (service time) It should be consistent with the actual production environment. -
Supports options from the Shanghai Futures Exchange, the National Energy Exchange, the China Financial Futures Exchange, the Guangzhou Futures Exchange, the Zhengzhou Commodity Exchange, and the Dalian Commodity Exchange.
-
After user registration, the default APPID is simnow_client_test, and the authentication code is 0000000000000000 (16 zeros). Terminal authentication is enabled by default, but programmatic users can choose not to enable terminal authentication for access.
-
Trading instruments: All futures instruments traded on the six exchanges, as well as all options instruments traded on the Shanghai Futures Exchange, the Energy Exchange, the China Financial Futures Exchange, and the Guangzhou Futures Exchange, and some options instruments traded on the Zhengzhou Commodity Exchange and the Dalian Commodity Exchange.
-
Account funds: Initial capital of 20 million, supports deposits, up to three times per day.
-
Create your own market data configuration file: config_md.yaml
TdFrontAddress: tcp://182.254.243.31:40001 # Trade Front Address
MdFrontAddress: tcp://182.254.243.31:40011 # Market Front Address
BrokerID: "9999" # Brokerage ID
AuthCode: "0000000000000000" # Authentication code
AppID: simnow_client_test # Application ID
Port: 8080 # the listening port, default 8080
Host: 127.0.0.1 # the bind ip address, default 127.0.0.1
LogLevel: INFO # NOTSET, DEBUG, INFO, WARN, ERROR, CRITICALCreate your own trading configuration file config_td.yaml:
TdFrontAddress: tcp://182.254.243.31:40001 # Trade Front Address
MdFrontAddress: tcp://182.254.243.31:40011 # Market Front Address
BrokerID: "9999" # Brokerage ID
AuthCode: "0000000000000000" # Authentication code
AppID: simnow_client_test # Application ID
Port: 8081 # the listening port, default 8081
Host: 127.0.0.1 # the bind ip address, default 127.0.0.1
LogLevel: INFO # NOTSET, DEBUG, INFO, WARN, ERROR, CRITICAL# Activate the virtual environment in the project root directory. Deactivating it will use the system default Python environment instead of the one required by the project.
.venv\Scripts\activate
# Start trading service
python main.py --config=./config/config_td.yaml --app_type=td
# Start market data service
python main.py --config=./config/config_md.yaml --app_type=md
# Or run the script
start_td_server.bat
start_md_server.batRefactoring Complete! SyncStrategyApi has been successfully refactored with a modular architecture, significantly improving code quality and maintainability.
- ✅ Modular Architecture: Split 2300+ line monolithic file into multiple modules with clear responsibilities
- ✅ Code Reuse: Generic cache manager and event manager eliminate code duplication
- ✅ Plugin System: Extend functionality without modifying core code
- ✅ Backward Compatible: API interface remains unchanged, existing code requires no modifications
- ✅ Comprehensive Testing: Complete unit test and property test coverage
- ✅ Complete Documentation: Detailed module documentation and plugin development guide
src/strategy/
├── sync_api.py (~400 lines) # Main module, provides public interface
└── internal/ # Internal modules
├── data_models.py # Quote and Position data classes
├── cache_manager.py # Generic cache manager
├── event_manager.py # Unified event manager
├── event_loop_thread.py # Background event loop thread
├── plugin.py # Plugin system
├── order_helper.py # Order processing helper functions
└── instrument_helper.py # Instrument info processing helper functions
The plugin system allows you to extend functionality without modifying core code:
from src.strategy.sync_api import SyncStrategyApi, StrategyPlugin
class MyPlugin(StrategyPlugin):
def on_init(self, api):
print("Plugin initialized")
def on_quote(self, quote):
print(f"Received quote: {quote.InstrumentID} @ {quote.LastPrice}")
return quote
api = SyncStrategyApi("user_id", "password")
api.register_plugin(MyPlugin())Plugin Examples:
examples/plugins/logging_plugin.py- Logging pluginexamples/plugins/risk_control_plugin.py- Risk control plugin
Plugin Development Guide: See examples/plugins/README.md
Basic usage (exactly the same as before refactoring):
from src.strategy.sync_api import SyncStrategyApi
# Initialize API (auto-connect and login)
api = SyncStrategyApi(
user_id="your_user_id",
password="your_password",
config_path="config.yaml"
)
# Get quote
quote = api.get_quote("rb2605")
print(f"Last price: {quote.LastPrice}")
# Get position
position = api.get_position("rb2605")
print(f"Long position: {position.pos_long}")
# Open position
result = api.open_close("rb2605", "kaiduo", 1, 3500.0)
if result["success"]:
print(f"Order success: {result['order_ref']}")
# Stop service
api.stop()- Easier to Maintain: Each module under 300 lines with single responsibility
- Easier to Test: Modular test structure with higher test coverage
- Easier to Extend: Plugin system supports feature extension without modifying core code
- Easier to Understand: Clear module boundaries and documentation
For more details, see:
📌 See md_protocol.md、td_protocol.md
The example is based on the SimNow Telecom 1 environment. Data may differ in different environments, and the example data below may not be universally applicable. Adjustments should be made according to your environment.
Market data link: ws://127.0.0.1:8080/md/
Trading link: ws://127.0.0.1:8081/td/
Log in
request
{
"MsgType": "ReqUserLogin",
"ReqUserLogin": {
"UserID": "028742",
"Password": "123456"
}
}response
{
"MsgType": "RspUserLogin",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"IsLast": true,
"RspUserLogin": {
"BrokerID": "",
"CZCETime": "",
"DCETime": "",
"FFEXTime": "",
"FrontID": 0,
"INETime": "",
"LoginTime": "",
"MaxOrderRef": "",
"SessionID": 0,
"SHFETime": "",
"SystemName": "",
"SysVersion": "",
"TradingDay": "20251203",
"UserID": ""
}
}Subscribe to market data
request
{
"MsgType": "SubscribeMarketData",
"InstrumentID": [
"au2602",
"rb2605",
"TA601"
]
}response
{
"MsgType": "RspSubMarketData",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"SpecificInstrument": {
"InstrumentID": "au2602"
}
}{
"MsgType": "RspSubMarketData",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"SpecificInstrument": {
"InstrumentID": "rb2605"
}
}{
"MsgType": "RspSubMarketData",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"IsLast": true,
"SpecificInstrument": {
"InstrumentID": "TA601"
}
}In-depth market response
{
"MsgType": "RtnDepthMarketData",
"DepthMarketData": {
"ActionDay": "20251203",
"AskPrice1": 956.62,
"AskPrice2": 0,
"AskPrice3": 0,
"AskPrice4": 0,
"AskPrice5": 0,
"AskVolume1": 3,
"AskVolume2": 0,
"AskVolume3": 0,
"AskVolume4": 0,
"AskVolume5": 0,
"AveragePrice": 956858.858479762,
"BandingLowerPrice": 0.0,
"BandingUpperPrice": 0.0,
"BidPrice1": 956.6,
"BidPrice2": 0,
"BidPrice3": 0,
"BidPrice4": 0,
"BidPrice5": 0,
"BidVolume1": 9,
"BidVolume2": 0,
"BidVolume3": 0,
"BidVolume4": 0,
"BidVolume5": 0,
"ClosePrice": 0,
"CurrDelta": 1.7976931348623157e+308,
"ExchangeID": "",
"ExchangeInstID": "",
"HighestPrice": 962.1800000000001,
"InstrumentID": "au2602",
"LastPrice": 956.62,
"LowerLimitPrice": 827.32,
"LowestPrice": 948.1800000000001,
"OpenInterest": 199696.0,
"OpenPrice": 958.0,
"PreClosePrice": 958.42,
"PreDelta": 0.0,
"PreOpenInterest": 202038.0,
"PreSettlementPrice": 962.02,
"SettlementPrice": 0,
"TradingDay": "20251203",
"Turnover": 253162846200.0,
"UpdateMillisec": 500,
"UpdateTime": "13:41:23",
"UpperLimitPrice": 1096.7,
"Volume": 264577,
"reserve1": "au2602",
"reserve2": ""
}
}{
"MsgType": "RtnDepthMarketData",
"DepthMarketData": {
"ActionDay": "20251203",
"AskPrice1": 3170.0,
"AskPrice2": 0,
"AskPrice3": 0,
"AskPrice4": 0,
"AskPrice5": 0,
"AskVolume1": 261,
"AskVolume2": 0,
"AskVolume3": 0,
"AskVolume4": 0,
"AskVolume5": 0,
"AveragePrice": 31645.592201667798,
"BandingLowerPrice": 0.0,
"BandingUpperPrice": 0.0,
"BidPrice1": 3169.0,
"BidPrice2": 0,
"BidPrice3": 0,
"BidPrice4": 0,
"BidPrice5": 0,
"BidVolume1": 624,
"BidVolume2": 0,
"BidVolume3": 0,
"BidVolume4": 0,
"BidVolume5": 0,
"ClosePrice": 0,
"CurrDelta": 1.7976931348623157e+308,
"ExchangeID": "",
"ExchangeInstID": "",
"HighestPrice": 3174.0,
"InstrumentID": "rb2605",
"LastPrice": 3170.0,
"LowerLimitPrice": 3010.0,
"LowestPrice": 3154.0,
"OpenInterest": 1288823.0,
"OpenPrice": 3167.0,
"PreClosePrice": 3169.0,
"PreDelta": 0.0,
"PreOpenInterest": 1175559.0,
"PreSettlementPrice": 3169.0,
"SettlementPrice": 0,
"TradingDay": "20251203",
"Turnover": 18507703080.0,
"UpdateMillisec": 500,
"UpdateTime": "13:41:23",
"UpperLimitPrice": 3327.0,
"Volume": 584843,
"reserve1": "rb2605",
"reserve2": ""
}
}{
"MsgType": "RtnDepthMarketData",
"DepthMarketData": {
"ActionDay": "20251203",
"AskPrice1": 4734.0,
"AskPrice2": 0.0,
"AskPrice3": 0.0,
"AskPrice4": 0.0,
"AskPrice5": 0.0,
"AskVolume1": 300,
"AskVolume2": 0,
"AskVolume3": 0,
"AskVolume4": 0,
"AskVolume5": 0,
"AveragePrice": 4734.0,
"BandingLowerPrice": 0.0,
"BandingUpperPrice": 0.0,
"BidPrice1": 4732.0,
"BidPrice2": 0.0,
"BidPrice3": 0.0,
"BidPrice4": 0.0,
"BidPrice5": 0.0,
"BidVolume1": 282,
"BidVolume2": 0,
"BidVolume3": 0,
"BidVolume4": 0,
"BidVolume5": 0,
"ClosePrice": 0,
"CurrDelta": 1.7976931348623157e+308,
"ExchangeID": "",
"ExchangeInstID": "",
"HighestPrice": 4754.0,
"InstrumentID": "TA601",
"LastPrice": 4734.0,
"LowerLimitPrice": 4466.0,
"LowestPrice": 4716.0,
"OpenInterest": 885382.0,
"OpenPrice": 4742.0,
"PreClosePrice": 4752.0,
"PreDelta": 0.0,
"PreOpenInterest": 899833.0,
"PreSettlementPrice": 4752.0,
"SettlementPrice": 4736.0,
"TradingDay": "20251203",
"Turnover": 1930738230.0,
"UpdateMillisec": 500,
"UpdateTime": "13:41:23",
"UpperLimitPrice": 5038.0,
"Volume": 407845,
"reserve1": "TA601",
"reserve2": ""
}
}Cancel subscription market data
request
{
"MsgType": "UnSubscribeMarketData",
"InstrumentID": [
"au2602",
"rb2605",
"TA601"
]
}response
{
"MsgType": "RspUnSubMarketData",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"SpecificInstrument": {
"InstrumentID": "au2602"
}
}{
"MsgType": "RspUnSubMarketData",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"SpecificInstrument": {
"InstrumentID": "rb2605"
}
}{
"MsgType": "RspUnSubMarketData",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "CTP:No Error"
},
"IsLast": true,
"SpecificInstrument": {
"InstrumentID": "TA601"
}
}# request
{
"MsgType": "{method_name}",
"{request_field}": {
"filed1": {value1},
"...": "...",
"fieldn": {valuen}
},
"RequestID": 1
}
# response
{
"MsgType": "{rsp_of_method}",
"RspInfo": {
"ErrorID": 0,
"ErrorMsg": "OK"
},
"IsLast": true,
"RequestID": 1
"{response_filed}": {response_body} # Please refer to the detailed documentation for details.
}👈
ErrorID="-400" ErrorMsg="Incorrect parameters"
ErrorID="-401" ErrorMsg="Not logged in"
ErrorID="-404" ErrorMsg="This method has not yet been implemented."
ErrorID="-1" ErrorMsg="CTP:Request failed"
ErrorID="-2" ErrorMsg="CTP:Unprocessed requests exceed the number of licenses"
ErrorID="-3" ErrorMsg="CTP:The number of requests sent per second exceeded the number of licenses."
ErrorID="0" ErrorMsg="CTP:correct"
ErrorID="1" ErrorMsg="CTP:Not in synchronized state"
ErrorID="2" ErrorMsg="CTP:Inconsistent session information"
ErrorID="3" ErrorMsg="CTP:Invalid login"
ErrorID="4" ErrorMsg="CTP:User inactive"
ErrorID="5" ErrorMsg="CTP:Duplicate logins"
ErrorID="6" ErrorMsg="CTP:Not logged in yet"
ErrorID="7" ErrorMsg="CTP:Not initialized yet"
ErrorID="8" ErrorMsg="CTP:Pre-inactive"
ErrorID="9" ErrorMsg="CTP:No permission required"
ErrorID="10" ErrorMsg="CTP:Change other people's passwords"
ErrorID="11" ErrorMsg="CTP:User not found"
ErrorID="12" ErrorMsg="CTP:The brokerage firm could not be found."
ErrorID="13" ErrorMsg="CTP:Cannot find investors"
ErrorID="14" ErrorMsg="CTP:Original password does not match"
ErrorID="15" ErrorMsg="CTP:The order field is incorrect."
ErrorID="16" ErrorMsg="CTP:Contract not found"Transaction Service Agreement Document
Market Data Service Agreement Document
homalos-webctp/
├── 📁 config/ # Project Configuration
├── 📁 docs/ # Other project documents
├── 📁 libs/ # Third-party libraries, including the original CTP dynamic library
├── 📁 src/ # Core source code
├── 📁 tests/ # test script
├── 📁 CHANGELOG.md # Historical Updates
├── 📁 LICENSE.txt # License file
├── 📁 README.md # Project English Documentation
├── 📁 README_CN.md # Project Chinese Documentation
├── 📁 main.py # Project main entrance
├── 📁 pyproject.toml # Project configuration files, dependencies managed by UV.
├── 📁 start_md_server.bat # Market data service startup script
├── 📁 start_td_server.bat # Trading service startup script
└── 📁 uv.lock # UV file lock, managed by UV- Application Layer (apps/): FastAPI WebSocket endpoint
- Service Layer (services/): Asynchronous/synchronous boundary handling, message routing
- Client Layer (clients/): CTP API Encapsulation
- BaseClient: An abstract base class that provides common client management logic.
- TdClient/MdClient (services): Handling WebSocket messages and interactions with CTP clients
- TdClient/MdClient (clients): Encapsulate CTP API calls
It is recommended to conduct thorough testing in the SimNow simulation environment before connecting to the production environment.
For more detailed information, please refer to the Development Documentation.
- Due to limited resources, only a simple test of the SimNow platform was conducted. Please conduct thorough testing yourself before integrating it into the production environment.
- Users are solely responsible for any consequences arising from real-money trading.
Last updated: 2025-12-10