Skip to content

homalos-webctp is a CTP service that provides a WebSocket interface based on the CTP API.

License

Notifications You must be signed in to change notification settings

Homalos/homalos-webctp

Repository files navigation

✨ A CTP service providing a WebSocket interface, developed based on the Python CTP API✨

Group#1

English | 简体中文

Table of contents

Overview

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

Installation and Operation

Environment Dependencies

  • Python :3.13

  • Tools: UV

  • CTP API:6.7.10

Environment Setup

  1. Environment Preparation

    Install UV lamp; UV lamps are recommended.

  2. 👈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
  3. 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.
  4. Cloning project

    git clone https://github.com/Homalos/homalos-webctp.git
    cd homalos-webctp
  5. 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.

  6. 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.

See SimNow official website

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, CRITICAL

Create 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

Run

# 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.bat

Performance Optimization Features

SyncStrategyApi Modular Refactoring

Refactoring Complete! SyncStrategyApi has been successfully refactored with a modular architecture, significantly improving code quality and maintainability.

Refactoring Achievements

  • 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

Module Structure

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

Plugin System

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 plugin
  • examples/plugins/risk_control_plugin.py - Risk control plugin

Plugin Development Guide: See examples/plugins/README.md

Usage Example

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()

Refactoring Benefits

  1. Easier to Maintain: Each module under 300 lines with single responsibility
  2. Easier to Test: Modular test structure with higher test coverage
  3. Easier to Extend: Plugin system supports feature extension without modifying core code
  4. Easier to Understand: Clear module boundaries and documentation

For more details, see:

Request Example

📌 See md_protocol.mdtd_protocol.md

Partial Examples

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"
    }
}

Protocol

General Protocol Format

# 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.
}

Explanation of Some General Error Codes

👈
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"

Detailed API Documentation

Transaction Service Agreement Document

Market Data Service Agreement Document

Project Structure

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

Architecture Description

Three-Tier Architecture

  1. Application Layer (apps/): FastAPI WebSocket endpoint
  2. Service Layer (services/): Asynchronous/synchronous boundary handling, message routing
  3. Client Layer (clients/): CTP API Encapsulation

Core components

  • 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

Test

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.

Other Notes

  • 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

About

homalos-webctp is a CTP service that provides a WebSocket interface based on the CTP API.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages