调用参考
https://github.com/shizhuolin/PyCTP/blob/master/tests/test_PyCTP.py
具体参数可参考ctp头文件

git clone https://github.com/shizhuolin/PyCTP.git
cd PyCTP
python setup.py build

这是程序化期货交易上期ctp接口版本. 为方便数值计算.将其包装为python版本.
支持python 2.7+, 3.x, windows/linux/gcc/vs2022

绝大部分代码是直接从ctp源码转换而来.

  • char[x] 均用 pybytes代替
  • char用pybyte代替,bool使用pybool或int

调用示范:

# -*- coding: utf-8 -*-
"""
Created on Sat Jul 30 17:19:21 2025

@author: zhuolin
"""
from __future__ import print_function

import sys
import threading
import PyCTP

class ReadonlyModuleWrapper(object):
    def __init__(self, wrapped):
        self._wrapped = wrapped

    def __getattr__(self, key):
        return getattr(self._wrapped, key)

    def __setattr__(self, key, value):
        if key == "_wrapped":
            super(ReadonlyModuleWrapper, self).__setattr__(key, value)
        else:
            raise AttributeError("Module attributes are read-only")

    def __delattr__(self, key):
        raise AttributeError("Module attributes are read-only")

def user_input(prompt=''):
    if sys.version_info[0] < 3:
        # Python 2
        return raw_input(prompt)
    else:
        # Python 3
        return input(prompt)

def bytes2unicode(data, encoding='gb2312', errors='replace'):
    if sys.version_info[0] < 3:
        # Python 2
        return data if isinstance(data, unicode) else data.decode(encoding, errors)
    else:
        # Python 3
        return data if isinstance(data, str) else data.decode(encoding, errors)

def unicode2bytes(data, encoding='gb2312', errors='replace'):
    if sys.version_info[0] < 3:
        # Python 2
        return data.encode(encoding, errors) if isinstance(data, unicode) else data
    else:
        # Python 3
        return data.encode(encoding, errors) if isinstance(data, str) else data

PyCTP = ReadonlyModuleWrapper(PyCTP)

class PyCTP_Market_API(PyCTP.CThostFtdcMdApi):

    TIMEOUT = 30

    __RequestID = 0
    __isLogined = False

    def __IncRequestID(self):
        """ 自增并返回请求ID """
        # self.__RequestID += 1
        return self.__RequestID

    def Connect(self, frontAddr):
        """ 连接前置服务器 """
        self.RegisterSpi(self)
        self.RegisterFront(frontAddr)
        self.Init()
        self.__rsp_Connect_event = threading.Event()
        self.__rsp_Connect_event.clear()
        return 0 if self.__rsp_Connect_event.wait(self.TIMEOUT) else -4

    def Login(self, BrokerID, UserID, Password):
        """ 用户登录请求 """
        reqUserLogin = PyCTP.CThostFtdcReqUserLoginField()
        reqUserLogin.BrokerID = BrokerID
        reqUserLogin.UserID   = UserID
        reqUserLogin.Password = Password
        self.__rsp_Login_event = threading.Event()
        self.__rsp_Login_RequestID = self.__IncRequestID()
        ret = self.ReqUserLogin(reqUserLogin, self.__rsp_Login_RequestID)
        if ret == 0:
            self.__rsp_Login_event.clear()
            if self.__rsp_Login_event.wait(self.TIMEOUT):
                if self.__rsp_Login_RspInfo.ErrorID == 0:
                    self.__isLogined = True
                    self.__BrokerID = BrokerID
                    self.__UserID   = UserID
                    self.__Password = Password
                else:
                    sys.stderr.write(bytes2unicode(self.__rsp_Login_ErrorMsg) + '\n')
                return self.__rsp_Login_RspInfo.ErrorID
            else:
                return -4
        return ret

    def Logout(self):
        """ 登出请求 """
        reqUserLogout = PyCTP.CThostFtdcUserLogoutField(BrokerID = self.__BrokerID
                                                        ,UserID  = self.__UserID)
        self.__rsp_Logout_event = threading.Event()
        self.__rsp_Logout_RequestID = self.__IncRequestID()
        ret = self.ReqUserLogout(reqUserLogout, self.__rsp_Logout_RequestID)
        if ret == 0:
            self.__rsp_Logout_event.clear()
            if self.__rsp_Logout_event.wait(self.TIMEOUT):
                if self.__rsp_Logout_RspInfo.ErrorID == 0:
                    self.__isLogined = False
                return self.__rsp_Logout_RspInfo.ErrorID
            else:
                return -4
        return ret

    def SubMarketData(self, InstrumentID):
        """ 订阅行情 """
        self.__rsp_SubMarketData_results=[]
        self.__rsp_SubMarketData_event=threading.Event()
        self.__rsp_SubMarketData_RequestID=self.__IncRequestID()
        ret = self.SubscribeMarketData(InstrumentID, len(InstrumentID))
        if ret == 0:
            self.__rsp_SubMarketData_event.clear()
            if self.__rsp_SubMarketData_event.wait(self.TIMEOUT):
                if self.__rsp_SubMarketData_RspInfo.ErrorID != 0:
                    return self.__rsp_SubMarketData_RspInfo.ErrorID
                return self.__rsp_SubMarketData_results
            else:
                return -4
        return ret

    def UnSubMarketData(self, InstrumentID):
        """ 退订行情 """
        self.__rsp_UnSubMarketData_results = []
        self.__rsp_UnSubMarketData_event = threading.Event()
        self.__rsp_UnSubMarketData_RequestID = self.__IncRequestID()
        ret = self.UnSubscribeMarketData(InstrumentID, len(InstrumentID))
        if ret == 0:
            self.__rsp_UnSubMarketData_event.clear()
            if self.__rsp_UnSubMarketData_event.wait(self.TIMEOUT):
                if self.__rsp_UnSubMarketData_RspInfo.ErrorID != 0:
                    return self.__rsp_UnSubMarketData_RspInfo.ErrorID
                return self.__rsp_UnSubMarketData_results
            else:
                return -4
        return ret

    def SubForQuoteRsp(self, InstrumentID):
        """ 订阅询价 """
        self.__rsp_SubForQuoteRsp_results = []
        self.__rsp_SubForQuoteRsp_event = threading.Event()
        self.__rsp_SubForQuoteRsp_RequestID = self.__IncRequestID()
        ret = self.SubscribeForQuoteRsp(InstrumentID, len(InstrumentID))
        if ret == 0:
            self.__rsp_SubForQuoteRsp_event.clear()
            if self.__rsp_SubForQuoteRsp_event.wait(self.TIMEOUT):
                if self.__rsp_SubForQuoteRsp_RspInfo.ErrorID != 0:
                    return self.__rsp_SubForQuoteRsp_RspInfo.ErrorID
                return self.__rsp_SubForQuoteRsp_results
            else:
                return -4
        return ret

    def UnSubForQuoteRsp(self, InstrumentID):
        """ 订阅询价 """
        self.__rsp_UnSubForQuoteRsp_results = []
        self.__rsp_UnSubForQuoteRsp_event = threading.Event()
        self.__rsp_UnSubForQuoteRsp_RequestID = self.__IncRequestID()
        ret = self.UnSubscribeForQuoteRsp(InstrumentID, len(InstrumentID))
        if ret == 0:
            self.__rsp_UnSubForQuoteRsp_event.clear()
            if self.__rsp_UnSubForQuoteRsp_event.wait(self.TIMEOUT):
                if self.__rsp_UnSubForQuoteRsp_RspInfo.ErrorID != 0:
                    return self.__rsp_UnSubForQuoteRsp_RspInfo.ErrorID
                return self.__rsp_UnSubForQuoteRsp_results
            else:
                return -4
        return ret

    def OnFrontConnected(self):
        """ 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。 """
        self.__rsp_Connect_event.set()

    def OnFrontDisconnected(self, nReason):
        """ 当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
        nReason 错误原因
        0x1001 网络读失败
        0x1002 网络写失败
        0x2001 接收心跳超时
        0x2002 发送心跳失败
        0x2003 收到错误报文
        """
        sys.stderr.write('前置连接中断: %s' % hex(nReason))

    def OnRspError(self, RspInfo,  RequestID, IsLast):
        """ 错误信息 """
        sys.stderr.write(repr(([RspInfo.ErrorID, bytes2unicode(RspInfo.ErrorMsg)], RequestID, IsLast))+'\n')

    def OnRspUserLogin(self, RspUserLogin, RspInfo, RequestID, IsLast):
        """ 登录请求响应 """
        if RequestID == self.__rsp_Login_RequestID and IsLast:
            self.__BrokerID = RspUserLogin.BrokerID
            self.__UserID = RspUserLogin.UserID
            self.__SystemName = RspUserLogin.SystemName
            self.__TradingDay = RspUserLogin.TradingDay
            self.__DCETime = RspUserLogin.DCETime
            self.__SessionID = RspUserLogin.SessionID
            self.__MaxOrderRef = RspUserLogin.MaxOrderRef
            self.__INETime = RspUserLogin.INETime
            self.__LoginTime = RspUserLogin.LoginTime
            self.__FrontID = RspUserLogin.FrontID
            self.__FFEXTime = RspUserLogin.FFEXTime
            self.__CZCETime = RspUserLogin.CZCETime
            self.__SHFETime = RspUserLogin.SHFETime
            self.__rsp_Login_RspInfo = RspInfo
            self.__rsp_Login_event.set()

    def OnRspUserLogout(self, RspUserLogout, RspInfo, RequestID, IsLast):
        """ 登出请求响应 """
        if RequestID == self.__rsp_Logout_RequestID and IsLast:
            self.__rsp_Logout_RspInfo = RspInfo
            self.__rsp_Logout_event.set()

    def OnRspSubMarketData(self, SpecificInstrument, RspInfo, RequestID, IsLast):
        """ 订阅行情应答 """
        if RequestID == self.__rsp_SubMarketData_RequestID:
            if RspInfo is not None:
                self.__rsp_SubMarketData_RspInfo = RspInfo
            if SpecificInstrument is not None:
                self.__rsp_SubMarketData_results.append(SpecificInstrument)
            if IsLast:
                self.__rsp_SubMarketData_event.set()

    def OnRspUnSubMarketData(self, SpecificInstrument, RspInfo, RequestID, IsLast):
        """ 取消订阅行情应答 """
        if RequestID == self.__rsp_UnSubMarketData_RequestID:
            if RspInfo is not None:
                self.__rsp_UnSubMarketData_RspInfo = RspInfo
            if SpecificInstrument is not None:
                self.__rsp_UnSubMarketData_results.append(SpecificInstrument)
            if IsLast:
                self.__rsp_UnSubMarketData_event.set()
    def OnRspSubForQuoteRsp(self, SpecificInstrument, RspInfo, RequestID, IsLast):
        """ 订阅询价应答 """
        if RequestID == self.__rsp_SubForQuoteRsp_RequestID:
            if RspInfo is not None:
                self.__rsp_SubForQuoteRsp_RspInfo = RspInfo
            if SpecificInstrument is not None:
                self.__rsp_SubForQuoteRsp_results.append(SpecificInstrument)
            if IsLast:
                self.__rsp_SubForQuoteRsp_event.set()

    def OnRspUnSubForQuoteRsp(self, SpecificInstrument, RspInfo, RequestID, IsLast):
        """ 订阅询价应答 """
        if RequestID == self.__rsp_UnSubForQuoteRsp_RequestID:
            if RspInfo is not None:
                self.__rsp_UnSubForQuoteRsp_RspInfo = RspInfo
            if SpecificInstrument is not None:
                self.__rsp_UnSubForQuoteRsp_results.append(SpecificInstrument)
            if IsLast:
                self.__rsp_UnSubForQuoteRsp_event.set()

class PyCTP_Trader_API(PyCTP.CThostFtdcTraderApi):

    TIMEOUT = 30

    __RequestID = 0
    __isLogined = False

    def __IncRequestID(self):
        """ 自增并返回请求ID """
        self.__RequestID += 1
        return self.__RequestID
    
    def __IncOrderRef(self):
        """ 递增报单引用 """
        OrderRef = self.__OrderRef
        self.__OrderRef += 1
        return OrderRef
    
    def setInvestorID(self, InvestorID):
        self.__InvestorID = InvestorID
        return self.__InvestorID

    def Connect(self, frontAddr):
        """ 连接前置服务器 """
        self.RegisterSpi(self)
        self.SubscribePrivateTopic(PyCTP.THOST_TERT_RESUME)
        self.SubscribePublicTopic(PyCTP.THOST_TERT_RESUME)
        self.RegisterFront(frontAddr)
        self.Init()
        self.__rsp_Connect_event = threading.Event()
        self.__rsp_Connect_event.clear()
        return 0 if self.__rsp_Connect_event.wait(self.TIMEOUT) else -4

    def Authenticate(self, BrokerID, UserID, UserProductInfo, AuthCode, AppID):
        """ 客户端认证 """
        pReqAuthenticateField = PyCTP.CThostFtdcReqAuthenticateField()
        pReqAuthenticateField.BrokerID  = BrokerID
        pReqAuthenticateField.UserID    = UserID
        #pReqAuthenticateField.UserProductInfo = UserProductInfo
        pReqAuthenticateField.AuthCode  = AuthCode
        pReqAuthenticateField.AppID     = AppID
        self.__rsp_Authenticate_event = threading.Event()
        self.__rsp_Authenticate_RequestID = self.__IncRequestID()
        self.__rsp_Authenticate_RspInfo = None
        ret = self.ReqAuthenticate(pReqAuthenticateField, self.__rsp_Authenticate_RequestID)
        if ret == 0:
            self.__rsp_Authenticate_event.clear()
            if self.__rsp_Authenticate_event.wait(self.TIMEOUT):
                if self.__rsp_Authenticate_RspInfo and self.__rsp_Authenticate_RspInfo.ErrorID != 0:
                    return self.__rsp_Authenticate_RspInfo.ErrorID
                return bytes2unicode(self.__rsp_Authenticate_RspInfo.ErrorMsg)
            else:
                return -4
        return ret

    def Login(self, BrokerID, UserID, Password):
        """ 用户登录请求 """
        reqUserLogin = PyCTP.CThostFtdcReqUserLoginField()
        reqUserLogin.BrokerID = BrokerID
        reqUserLogin.UserID   = UserID
        reqUserLogin.Password = Password
        reqUserLogin.LoginRemark = b'test'
        self.__rsp_Login_event = threading.Event()
        self.__rsp_Login_RequestID = self.__IncRequestID()
        self.__rsp_Login_RspInfo = None
        ret = self.ReqUserLogin(reqUserLogin, self.__rsp_Login_RequestID)
        if ret == 0:
            self.__rsp_Login_event.clear()
            if self.__rsp_Login_event.wait(self.TIMEOUT):
                if self.__rsp_Login_RspInfo and self.__rsp_Login_RspInfo.ErrorID == 0:
                    self.__isLogined = True
                    self.__Password = Password
                else:
                    sys.stderr.write(bytes2unicode(self.__rsp_Login_RspInfo.ErrorMsg)+'\n')
                return self.__rsp_Login_RspInfo.ErrorID
            else:
                return -4
        return ret

    def Logout(self):
        """ 登出请求 """
        reqUserLogout = PyCTP.CThostFtdcUserLogoutField(BrokerID = self.__BrokerID
                                                        ,UserID  = self.__UserID)
        self.__rsp_Logout_event = threading.Event()
        self.__rsp_Logout_RequestID = self.__IncRequestID()
        self.__rsp_Logout_RspInfo = None
        ret = self.ReqUserLogout(reqUserLogout, self.__rsp_Logout_RequestID)
        if ret == 0:
            self.__rsp_Logout_event.clear()
            if self.__rsp_Logout_event.wait(self.TIMEOUT):
                if self.__rsp_Logout_RspInfo and self.__rsp_Logout_RspInfo.ErrorID == 0:
                    self.__isLogined = False
                return self.__rsp_Logout_RspInfo.ErrorID
            else:
                return -4
        return ret

    def SubUserSystemInfo(self, BrokerID, UserID, AppID):
        """
        ///上报用户终端信息,用于中继服务器操作员登录模式
        ///操作员登录后,可以多次调用该接口上报客户信息
        """
        userSystemInfo = PyCTP.CThostFtdcUserSystemInfoField()
        userSystemInfo.BrokerID     = BrokerID
        userSystemInfo.UserID       = UserID
        systemInfo = PyCTP.CTP_GetSystemInfo()
        Return = systemInfo['Return']
        userSystemInfo.ClientSystemInfo = systemInfo['SystemInfo']
        userSystemInfo.ClientSystemInfoLen = systemInfo['Length']
        userSystemInfo.ClientPublicIP = b'192.168.0.1'
        userSystemInfo.ClientIPPort = 51305
        userSystemInfo.ClientLoginTime = b'20190121'
        userSystemInfo.ClientAppID  = AppID
        ret = self.SubmitUserSystemInfo(userSystemInfo)
        return ret

    def RegUserSystemInfo(self, BrokerID, UserID, AppID):
        """
        ///注册用户终端信息,用于中继服务器多连接模式
        ///需要在终端认证成功后,用户登录前调用该接口
        """
        userSystemInfo = PyCTP.CThostFtdcUserSystemInfoField()
        userSystemInfo.BrokerID     = BrokerID
        userSystemInfo.UserID       = UserID
        systemInfo = PyCTP.CTP_GetSystemInfo()
        Return = systemInfo['Return']
        userSystemInfo.ClientSystemInfo = systemInfo['SystemInfo']
        userSystemInfo.ClientSystemInfoLen = systemInfo['Length']
        userSystemInfo.ClientPublicIP = b'192.168.0.1'
        userSystemInfo.ClientIPPort = 51305
        userSystemInfo.ClientLoginTime = b'20190121'
        userSystemInfo.ClientAppID  = AppID
        ret = self.RegisterUserSystemInfo(userSystemInfo)
        return ret
    
    def SettlementInfoConfirm(self):
        """ 请求确认结算单 """
        confirm = PyCTP.CThostFtdcSettlementInfoConfirmField()
        confirm.BrokerID = self.__BrokerID
        confirm.InvestorID = self.__InvestorID
        self.__rsp_SettlementInfoConfirm_RequestID = self.__IncRequestID()
        self.__rsp_SettlementInfoConfirm_event = threading.Event()
        self.__rsp_SettlementInfoConfirm_RspInfo = None
        self.__rsp_SettlementInfoConfirm_results = []
        ret = self.ReqSettlementInfoConfirm(confirm, self.__rsp_SettlementInfoConfirm_RequestID)
        if ret == 0:
            self.__rsp_SettlementInfoConfirm_event.clear()
            if self.__rsp_SettlementInfoConfirm_event.wait(self.TIMEOUT):
                if self.__rsp_SettlementInfoConfirm_RspInfo and self.__rsp_SettlementInfoConfirm_RspInfo.ErrorID != 0:
                    return self.__rsp_SettlementInfoConfirm_RspInfo.ErrorID
                return self.__rsp_SettlementInfoConfirm_results
            else:
                return -4
        return ret
    
    def QryExchange(self, ExchangeID=b''):
        """ 请求查询交易所 """
        QryExchangeField = PyCTP.CThostFtdcQryExchangeField(ExchangeID=ExchangeID)
        self.__rsp_QryExchange_results = []
        self.__rsp_QryExchange_RequestID = self.__IncRequestID()
        self.__rsp_QryExchange_event = threading.Event()
        self.__rsp_QryExchange_RspInfo = None
        ret = self.ReqQryExchange(QryExchangeField, self.__rsp_QryExchange_RequestID)
        if ret == 0:
            self.__rsp_QryExchange_event.clear()
            if self.__rsp_QryExchange_event.wait(self.TIMEOUT):
                if self.__rsp_QryExchange_RspInfo and self.__rsp_QryExchange_RspInfo.ErrorID != 0:
                    return self.__rsp_QryExchange_RspInfo.ErrorID
                return self.__rsp_QryExchange_results
            else:
                return -4
        return ret

    def QryInvestor(self):
        """ 请求查询投资者 """
        InvestorField = PyCTP.CThostFtdcQryInvestorField(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID)
        self.__rsp_QryInvestor_results = []
        self.__rsp_QryInvestor_RequestID = self.__IncRequestID()
        self.__rsp_QryInvestor_event=threading.Event()
        self.__rsp_QryInvestor_RspInfo = None
        ret = self.ReqQryInvestor(InvestorField, self.__rsp_QryInvestor_RequestID)
        if ret == 0:
            self.__rsp_QryInvestor_event.clear()
            if self.__rsp_QryInvestor_event.wait(self.TIMEOUT):
                if self.__rsp_QryInvestor_RspInfo and self.__rsp_QryInvestor_RspInfo.ErrorID != 0:
                    return self.__rsp_QryInvestor_RspInfo.ErrorID
                return self.__rsp_QryInvestor_results
            else:
                return -4
        return ret

    def QryTradingAccount(self):
        """ 请求查询资金账户 """
        QryTradingAccountField = PyCTP.CThostFtdcQryTradingAccountField(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID)
        self.__rsp_QryTradingAccount_results = []
        self.__rsp_QryTradingAccount_RequestID = self.__IncRequestID()
        self.__rsp_QryTradingAccount_event = threading.Event()
        self.__rsp_QryTradingAccount_RspInfo = None
        ret = self.ReqQryTradingAccount(QryTradingAccountField, self.__rsp_QryTradingAccount_RequestID)
        if ret == 0:
            self.__rsp_QryTradingAccount_event.clear()
            if self.__rsp_QryTradingAccount_event.wait(self.TIMEOUT):
                if self.__rsp_QryTradingAccount_RspInfo and self.__rsp_QryTradingAccount_RspInfo.ErrorID != 0:
                    return self.__rsp_QryTradingAccount_RspInfo.ErrorID
                return self.__rsp_QryTradingAccount_results
            else:
                return -4
        return ret

    def QryInstrument(self, ExchangeID=b'', InstrumentID=b''):
        """ 查询和约 """
        QryInstrument = PyCTP.CThostFtdcQryInstrumentField(ExchangeID = ExchangeID, InstrumentID = InstrumentID)
        self.__rsp_QryInstrument_event = threading.Event()
        self.__rsp_QryInstrument_RequestID = self.__IncRequestID()
        self.__rsp_QryInstrument_results = []
        self.__rsp_QryInstrument_RspInfo = None
        ret = self.ReqQryInstrument(QryInstrument, self.__rsp_QryInstrument_RequestID)
        if ret == 0:
            self.__rsp_QryInstrument_event.clear()
            if self.__rsp_QryInstrument_event.wait(self.TIMEOUT):
                if self.__rsp_QryInstrument_RspInfo and self.__rsp_QryInstrument_RspInfo.ErrorID != 0:
                    return self.__rsp_QryInstrument_RspInfo.ErrorID
                return self.__rsp_QryInstrument_results
            else:
                return -4
        return ret

    def QryDepthMarketData(self, InstrumentID):
        """ 请求查询行情 """
        QryDepthMarketData = PyCTP.CThostFtdcQryDepthMarketDataField()
        QryDepthMarketData.ProductClass = PyCTP.THOST_FTDC_PC_Futures
        QryDepthMarketData.InstrumentID = InstrumentID
        self.__rsp_QryDepthMarketData_results = []
        self.__rsp_QryDepthMarketData_RequestID = self.__IncRequestID()
        self.__rsp_QryDepthMarketData_event = threading.Event()
        self.__rsp_QryDepthMarketData_RspInfo = None
        ret = self.ReqQryDepthMarketData(QryDepthMarketData, self.__rsp_QryDepthMarketData_RequestID)
        if ret == 0:
            self.__rsp_QryDepthMarketData_event.clear()
            if self.__rsp_QryDepthMarketData_event.wait(self.TIMEOUT):
                if self.__rsp_QryDepthMarketData_RspInfo and self.__rsp_QryDepthMarketData_RspInfo.ErrorID != 0:
                    return self.__rsp_QryDepthMarketData_RspInfo.ErrorID
                return self.__rsp_QryDepthMarketData_results
            else:
                return -4
        return ret
    
    def QryInstrumentCommissionRate(self, InstrumentID):
        """ 请求查询合约手续费率 """
        QryInstrumentCommissionRate = PyCTP.CThostFtdcQryInstrumentCommissionRateField()
        QryInstrumentCommissionRate.BrokerID = self.__BrokerID
        QryInstrumentCommissionRate.InvestorID = self.__InvestorID
        QryInstrumentCommissionRate.InstrumentID = InstrumentID
        self.__rsp_QryInstrumentCommissionRate_results =  []
        self.__rsp_QryInstrumentCommissionRate_RequestID = self.__IncRequestID()
        self.__rsp_QryInstrumentCommissionRate_RspInfo = None
        self.__rsp_QryInstrumentCommissionRate_event = threading.Event()
        ret = self.ReqQryInstrumentCommissionRate(QryInstrumentCommissionRate, self.__rsp_QryInstrumentCommissionRate_RequestID)
        if ret == 0:
            self.__rsp_QryInstrumentCommissionRate_event.clear()
            if self.__rsp_QryInstrumentCommissionRate_event.wait(self.TIMEOUT):
                if self.__rsp_QryInstrumentCommissionRate_RspInfo and self.__rsp_QryInstrumentCommissionRate_RspInfo.ErrorID != 0:
                    return self.__rsp_QryInstrumentCommissionRate_RspInfo.ErrorID
                return self.__rsp_QryInstrumentCommissionRate_results
            else:
                return -4
        return ret
    
    def QryInstrumentMarginRate(self, InstrumentID):
        """ 请求查询合约保证金率 """
        QryInstrumentMarginRate = PyCTP.CThostFtdcQryInstrumentMarginRateField()
        QryInstrumentMarginRate.BrokerID = self.__BrokerID
        QryInstrumentMarginRate.InvestorID = self.__InvestorID
        QryInstrumentMarginRate.InstrumentID = InstrumentID
        self.__rsp_QryInstrumentMarginRate_results = []
        self.__rsp_QryInstrumentMarginRate_RequestID = self.__IncRequestID()
        self.__rsp_QryInstrumentMarginRate_RspInfo = None
        self.__rsp_QryInstrumentMarginRate_event = threading.Event()
        ret = self.ReqQryInstrumentMarginRate(QryInstrumentMarginRate, self.__rsp_QryInstrumentMarginRate_RequestID)
        if ret == 0:
            self.__rsp_QryInstrumentMarginRate_event.clear()
            if self.__rsp_QryInstrumentMarginRate_event.wait(self.TIMEOUT):
                if self.__rsp_QryInstrumentMarginRate_RspInfo and self.__rsp_QryInstrumentMarginRate_RspInfo.ErrorID != 0:
                    return self.__rsp_QryInstrumentMarginRate_RspInfo.ErrorID
                return self.__rsp_QryInstrumentMarginRate_results
            else:
                return -4
        return ret
    
    def QryInvestorPosition(self, InstrumentID=b''):
        """ 请求查询投资者持仓 """
        QryInvestorPositionFiel = PyCTP.CThostFtdcQryInvestorPositionField()
        QryInvestorPositionFiel.BrokerID = self.__BrokerID
        QryInvestorPositionFiel.InvestorID = self.__InvestorID
        QryInvestorPositionFiel.InstrumentID = InstrumentID
        self.__rsp_QryInvestorPosition_results = []
        self.__rsp_QryInvestorPosition_RequestID = self.__IncRequestID()
        self.__rsp_QryInvestorPosition_RspInfo = None
        self.__rsp_QryInvestorPosition_event = threading.Event()
        ret = self.ReqQryInvestorPosition(QryInvestorPositionFiel, self.__rsp_QryInvestorPosition_RequestID)
        if ret == 0:
            self.__rsp_QryInvestorPosition_event.clear()
            if self.__rsp_QryInvestorPosition_event.wait(self.TIMEOUT):
                if self.__rsp_QryInvestorPosition_RspInfo and self.__rsp_QryInvestorPosition_RspInfo.ErrorID != 0:
                    return self.__rsp_QryInvestorPosition_RspInfo.ErrorID
                return self.__rsp_QryInvestorPosition_results
            else:
                return -4
        return ret
    
    def QryInvestorPositionDetail(self, InstrumentID=b''):
        """ 请求查询投资者持仓明细 """
        QryInvestorPositionDetailField = PyCTP.CThostFtdcQryInvestorPositionDetailField()
        QryInvestorPositionDetailField.BrokerID = self.__BrokerID
        QryInvestorPositionDetailField.InvestorID = self.__InvestorID
        QryInvestorPositionDetailField.InstrumentID = InstrumentID
        self.__rsp_QryInvestorPositionDetail_results = []
        self.__rsp_QryInvestorPositionDetail_RequestID = self.__IncRequestID()
        self.__rsp_QryInvestorPositionDetail_RspInfo = None
        self.__rsp_QryInvestorPositionDetail_event = threading.Event()
        ret = self.ReqQryInvestorPositionDetail(QryInvestorPositionDetailField, self.__rsp_QryInvestorPositionDetail_RequestID)
        if ret == 0:
            self.__rsp_QryInvestorPositionDetail_event.clear()
            if self.__rsp_QryInvestorPositionDetail_event.wait(self.TIMEOUT):
                if self.__rsp_QryInvestorPositionDetail_RspInfo and self.__rsp_QryInvestorPositionDetail_RspInfo.ErrorID != 0:
                    return self.__rsp_QryInvestorPositionDetail_RspInfo.ErrorID
                return self.__rsp_QryInvestorPositionDetail_results
            else:
                return -4
        return ret
    
    def OrderInsert(self, InstrumentID, Action, Direction, Volume, Price):
        """ 开平仓单申报 """
        InputOrder = PyCTP.CThostFtdcInputOrderField()
        InputOrder.BrokerID = self.__BrokerID                            # 经纪公司代码
        InputOrder.InvestorID = self.__InvestorID                        # 投资者代码
        InputOrder.InstrumentID = InstrumentID                           # 合约代码
        InputOrder.OrderRef = unicode2bytes(str(self.__IncOrderRef()))   # 报单引用
        InputOrder.UserID = self.__UserID                                # 用户代码
        InputOrder.OrderPriceType = PyCTP.THOST_FTDC_OPT_LimitPrice      # 报单价格条件:限价单
        InputOrder.Direction = Direction                                 # 买卖方向
        InputOrder.CombOffsetFlag = Action                               # 组合开平标志
        InputOrder.CombHedgeFlag = PyCTP.THOST_FTDC_HF_Speculation       # 组合投机套保标志:投机
        InputOrder.LimitPrice = Price                                    # 价格
        InputOrder.VolumeTotalOriginal = Volume                          # 数量
        InputOrder.TimeCondition = PyCTP.THOST_FTDC_TC_GFD               # 有效期类型:立即完成,否则撤销
        InputOrder.VolumeCondition = PyCTP.THOST_FTDC_VC_AV              # 成交量类型:任意数量
        InputOrder.MinVolume = Volume                                    # 最小成交量
        InputOrder.ContingentCondition = PyCTP.THOST_FTDC_CC_Immediately # 触发条件:立即
        InputOrder.ForceCloseReason = PyCTP.THOST_FTDC_FCC_NotForceClose # 强平原因:非强平
        InputOrder.IsAutoSuspend = 0                                     # 自动挂起标志
        InputOrder.UserForceClose = 0                                    # 用户强平标志
        self.__rsp_OrderInsert_FrontID = self.__FrontID
        self.__rsp_OrderInsert_SessionID = self.__SessionID
        self.__rsp_OrderInsert_InputOrder = InputOrder
        self.__rsp_OrderInsert_RequestID = self.__IncRequestID()
        self.__rsp_OrderInsert_event = threading.Event()
        ret = self.ReqOrderInsert(InputOrder, self.__rsp_OrderInsert_RequestID)
        # if ret == 0:
        #     self.__rsp_OrderInsert_event.clear()
        #     if self.__rsp_OrderInsert_event.wait(self.TIMEOUT):
        #         if self.__rsp_OrderInsert_RspInfo and self.__rsp_OrderInsert_RspInfo.ErrorID != 0:
        #             sys.stderr.write(bytes2unicode(self.__rsp_OrderInsert_RspInfo.ErrorMsg) + '\n')
        #             return self.__rsp_OrderInsert_RspInfo.ErrorID
        #         return self.__rsp_OrderInsert.copy()
        #     else:
        #         return -4
        return ret
    
    def OnFrontConnected(self):
        """ 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。 """
        self.__rsp_Connect_event.set()

    def OnFrontDisconnected(self, nReason):
        """ 当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
        nReason 错误原因
        0x1001 网络读失败
        0x1002 网络写失败
        0x2001 接收心跳超时
        0x2002 发送心跳失败
        0x2003 收到错误报文
        """
        sys.stderr.write('前置连接中断: %s \n' % hex(nReason))

    def OnRspError(self, RspInfo,  RequestID, IsLast):
        """ 错误信息 """
        sys.stderr.write(repr(([RspInfo.ErrorID, bytes2unicode(RspInfo.ErrorMsg)], RequestID, IsLast))+'\n')

    def OnRspAuthenticate(self, pRspAuthenticateField, RspInfo, RequestID, IsLast):
        """ 请求响应 """
        if RequestID == self.__rsp_Authenticate_RequestID and IsLast:
            self.__rsp_Authenticate_RspInfo = RspInfo
            self.__rsp_Authenticate_event.set()

    def OnRspUserLogin(self, RspUserLogin, RspInfo, RequestID, IsLast):
        """ 登录请求响应 """
        if RequestID == self.__rsp_Login_RequestID and IsLast:
            self.__BrokerID = RspUserLogin.BrokerID
            self.__UserID = RspUserLogin.UserID
            self.__SystemName = RspUserLogin.SystemName
            self.__TradingDay = RspUserLogin.TradingDay
            self.__DCETime = RspUserLogin.DCETime
            self.__SessionID = RspUserLogin.SessionID
            self.__MaxOrderRef = RspUserLogin.MaxOrderRef
            self.__OrderRef = int(bytes2unicode(self.__MaxOrderRef)) # 初始化报单引用
            self.__INETime = RspUserLogin.INETime
            self.__LoginTime = RspUserLogin.LoginTime
            self.__FrontID = RspUserLogin.FrontID
            self.__FFEXTime = RspUserLogin.FFEXTime
            self.__CZCETime = RspUserLogin.CZCETime
            self.__SHFETime = RspUserLogin.SHFETime
            self.__rsp_Login_RspInfo = RspInfo
            self.__rsp_Login_event.set()

    def OnRspUserLogout(self, RspUserLogout, RspInfo, RequestID, IsLast):
        """ 登出请求响应 """
        if RequestID == self.__rsp_Logout_RequestID and IsLast:
            self.__rsp_Logout_RspInfo = RspInfo
            self.__rsp_Logout_event.set()
            
    def OnRspSettlementInfoConfirm(self, SettlementInfoConfirm, RspInfo, RequestID, IsLast):
        """ 投资者结算结果确认响应 """
        if RequestID == self.__rsp_SettlementInfoConfirm_RequestID:
            self.__rsp_SettlementInfoConfirm_results.append( SettlementInfoConfirm )
            if RspInfo:
                self.__rsp_SettlementInfoConfirm_RspInfo = RspInfo
            if IsLast:
                self.__rsp_SettlementInfoConfirm_event.set()
                
    def OnRspQryExchange(self, Exchange, RspInfo, RequestID, IsLast):
        """ 请求查询交易所响应 """
        
        if RequestID == self.__rsp_QryExchange_RequestID:
            if RspInfo is not None:
                self.__rsp_QryExchange_RspInfo = RspInfo
            if Exchange is not None:
                self.__rsp_QryExchange_results.append(Exchange)
            if IsLast:
                self.__rsp_QryExchange_event.set()

    def OnRspQryInvestor(self, Investor, RspInfo, RequestID, IsLast):
        """ 请求查询投资者响应 """
        if RequestID == self.__rsp_QryInvestor_RequestID:
            if RspInfo is not None:
                self.__rsp_QryInvestor_RspInfo = RspInfo
            if Investor is not None:
                self.__rsp_QryInvestor_results.append(Investor)
            if IsLast:
                self.__rsp_QryInvestor_event.set()

    def OnRspQryTradingAccount(self, TradingAccount, RspInfo, RequestID, IsLast):
        """ 请求查询资金账户响应 """
        if RequestID == self.__rsp_QryTradingAccount_RequestID:
            if RspInfo is not None:
                self.__rsp_QryTradingAccount_RspInfo = RspInfo
            if TradingAccount is not None:
                self.__rsp_QryTradingAccount_results.append(TradingAccount)
            if IsLast:
                self.__rsp_QryTradingAccount_event.set()

    def OnRspQryInstrument(self, Instrument, RspInfo, RequestID, IsLast):
        """ 请求查询合约响应 """
        if RequestID == self.__rsp_QryInstrument_RequestID:
            if RspInfo is not None:
                self.__rsp_QryInstrument_RspInfo = RspInfo
            if Instrument is not None:
                self.__rsp_QryInstrument_results.append(Instrument)
            if IsLast:
                self.__rsp_QryInstrument_event.set()

    def OnRspQryDepthMarketData(self, DepthMarketData, RspInfo, RequestID, IsLast):
        """ 请求查询行情响应 """
        if RequestID == self.__rsp_QryDepthMarketData_RequestID:
            if RspInfo is not None:
                self.__rsp_QryDepthMarketData_RspInfo = RspInfo
            if DepthMarketData is not None:
                self.__rsp_QryDepthMarketData_results.append(DepthMarketData)
            if IsLast:
                self.__rsp_QryDepthMarketData_event.set()
                
    def OnRspQryInstrumentCommissionRate(self, InstrumentCommissionRate, RspInfo, RequestID, IsLast):
        """ 请求查询合约手续费率响应 """
        if RequestID == self.__rsp_QryInstrumentCommissionRate_RequestID:
            if RspInfo is not None:
                self.__rsp_QryInstrumentCommissionRate_RspInfo = RspInfo
            if InstrumentCommissionRate is not None:
                self.__rsp_QryInstrumentCommissionRate_results.append(InstrumentCommissionRate)
            if IsLast:
                self.__rsp_QryInstrumentCommissionRate_event.set()
                
    def OnRspQryInstrumentMarginRate(self, InstrumentMarginRate, RspInfo, RequestID, IsLast):
        """ 请求查询合约保证金率响应 """
        if RequestID == self.__rsp_QryInstrumentMarginRate_RequestID:
            if RspInfo is not None:
                self.__rsp_QryInstrumentMarginRate_RspInfo = RspInfo
            if InstrumentMarginRate is not None:
                self.__rsp_QryInstrumentMarginRate_results.append(InstrumentMarginRate)
            if IsLast:
                self.__rsp_QryInstrumentMarginRate_event.set()
    
    def OnRspQryInvestorPosition(self, InvestorPosition, RspInfo, RequestID, IsLast):
        """ 请求查询投资者持仓响应 """
        if RequestID == self.__rsp_QryInvestorPosition_RequestID:
            if RspInfo is not None:
                self.__rsp_QryInvestorPosition_RspInfo = RspInfo
            if InvestorPosition is not None:
                self.__rsp_QryInvestorPosition_results.append(InvestorPosition)
            if IsLast:
                self.__rsp_QryInvestorPosition_event.set()
    
    def OnRspQryInvestorPositionDetail(self, InvestorPositionDetail, RspInfo, RequestID, IsLast):
        """ 请求查询投资者持仓明细响应 """
        if RequestID == self.__rsp_QryInvestorPositionDetail_RequestID:
            if RspInfo is not None:
                self.__rsp_QryInvestorPositionDetail_RspInfo = RspInfo
            if InvestorPositionDetail is not None:
                self.__rsp_QryInvestorPositionDetail_results.append(InvestorPositionDetail)
            if IsLast:
                self.__rsp_QryInvestorPositionDetail_event.set()
    
    def OnRspOrderInsert(self, InputOrder, RspInfo, RequestID, IsLast):
        """ 报单录入请求响应 """
        print('OnRspOrderInsert:', InputOrder)
        #if self.__rsp_OrderInsert_RequestID == RequestID:
        #    if RspInfo and RspInfo.ErrorID != 0:
        #        self.__rsp_OrderInsert_RspInfo = RspInfo
        #        self.__rsp_OrderInsert_event.set()
        
    def OnErrRtnOrderInsert(self, InputOrder, RspInfo):
        """ 报单录入错误回报 """
        print( 'OnErrRtnOrderInsert:', bytes2unicode( RspInfo.ErrorMsg ) )
        
    def OnRtnTrade(self, Trade):
        """ 成交通知 """
        print('OnRtnTrade:', Trade)
        #  {'BrokerID': b'9999'
        # , 'InvestorID': b'245110'
        # , 'reserve1': b'cu2510'
        # , 'OrderRef': b'6'                    ///报单引用
        # , 'UserID': b'245110'
        # , 'ExchangeID': b'SHFE'
        # , 'TradeID': b'      173342'          ///成交编号
        # , 'Direction': b'0'                   ///买卖方向 ///买 #define THOST_FTDC_D_Buy '0' ///卖 #define THOST_FTDC_D_Sell '1'
        # , 'OrderSysID': b'      266549'       ///报单编号
        # , 'ParticipantID': b'9999'            ///会员代码
        # , 'ClientID': b'9999245088'           ///客户代码
        # , 'TradingRole': b'\x00'              ///交易角色 ///代理 #define THOST_FTDC_ER_Broker '1' ///自营 #define THOST_FTDC_ER_Host '2' ///做市商 #define THOST_FTDC_ER_Maker '3'
        # , 'reserve2': b'cu2510'
        # , 'OffsetFlag': b'0'                  ///开平标志 ///开仓 THOST_FTDC_OF_Open '0' ///平仓 THOST_FTDC_OF_Close '1' ///强平 THOST_FTDC_OF_ForceClose '2' ///平今 THOST_FTDC_OF_CloseToday '3' ///平昨 THOST_FTDC_OF_CloseYesterday '4' ///强减 THOST_FTDC_OF_ForceOff '5' ///本地强平 THOST_FTDC_OF_LocalForceClose '6'
        # , 'HedgeFlag': b'1'                   ///投机套保标志  ///投机 THOST_FTDC_HF_Speculation '1' ///套利 THOST_FTDC_HF_Arbitrage '2' ///套保 THOST_FTDC_HF_Hedge '3' ///做市商 THOST_FTDC_HF_MarketMaker '5' ///第一腿投机第二腿套保 THOST_FTDC_HF_SpecHedge '6' ///第一腿套保第二腿投机 THOST_FTDC_HF_HedgeSpec '7'
        # , 'Price': 79380.0
        # , 'Volume': 1
        # , 'TradeDate': b'20250813'
        # , 'TradeTime': b'14:33:46'
        # , 'TradeType': b'\x00'                ///成交类型 THOST_FTDC_TRDT_SplitCombination '#' ///普通成交 THOST_FTDC_TRDT_Common '0' ///期权执行 THOST_FTDC_TRDT_OptionsExecution '1' ///OTC成交 THOST_FTDC_TRDT_OTC '2' ///期转现衍生成交 THOST_FTDC_TRDT_EFPDerived '3' ///组合衍生成交 THOST_FTDC_TRDT_CombinationDerived '4' ///大宗交易成交 THOST_FTDC_TRDT_BlockTrade '5'
        # , 'PriceSource': b'\x00'              ///成交价来源 ///前成交价 THOST_FTDC_PSRC_LastPrice '0' ///买委托价 THOST_FTDC_PSRC_Buy '1' ///卖委托价 THOST_FTDC_PSRC_Sell '2' ///场外成交价 THOST_FTDC_PSRC_OTC '3'
        # , 'TraderID': b'9999xc6'              ///交易所交易员代码
        # , 'OrderLocalID': b'      175031'     ///本地报单编号
        # , 'ClearingPartID': b'9999'           ///结算会员编号
        # , 'BusinessUnit': b''
        # , 'SequenceNo': 192469
        # , 'TradingDay': b'20250813'
        # , 'SettlementID': 1
        # , 'BrokerOrderSeq': 1515922
        # , 'TradeSource': b'0'                 ///成交来源 ///来自交易所普通回报 THOST_FTDC_TSRC_NORMAL '0' ///来自查询 THOST_FTDC_TSRC_QUERY '1'
        # , 'InvestUnitID': b''
        # , 'InstrumentID': b'cu2510'
        # , 'ExchangeInstID': b'cu2510'}    
        
    def OnRtnOrder(self, Order):
        """ 报单通知 """
        print('OnRtnOrder - 报单通知:')
        if Order.InvestorID != self.__InvestorID:
            return
        else:
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_AllTraded:
                print('报单全部成交')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_PartTradedQueueing:
                print('部分成交还在队列中')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_PartTradedNotQueueing:
                print('部分成交不在队列中')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_NoTradeQueueing:
                print('未成交还在队列中')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_NoTradeNotQueueing:
                print('未成交不在队列中')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_Canceled:
                print('撤单')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_Unknown:
                print('未知')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_NotTouched:
                print('尚未触发')
            if Order.OrderStatus == PyCTP.THOST_FTDC_OST_Touched:
                print('已触发')

class PyCTP_Trader(PyCTP_Trader_API):
    def OnRtnInstrumentStatus(self, InstrumentStatus):
        """ 合约交易状态通知 """
        print("合约交易状态通知:", InstrumentStatus)

class PyCTP_Market(PyCTP_Market_API):
    def OnRtnDepthMarketData(self, DepthMarketData):
        """ 深度行情通知 """
        import datetime
        tick = dict(InstrumentID=DepthMarketData.InstrumentID
                    , time=datetime.datetime.strptime(bytes2unicode(DepthMarketData.ActionDay+DepthMarketData.UpdateTime), '%Y%m%d%H:%M:%S').replace(microsecond=DepthMarketData.UpdateMillisec*1000).strftime('%Y-%m-%d %H:%M:%S.%f')
                    , last=DepthMarketData.LastPrice
                    , volume=DepthMarketData.Volume
                    , amount=DepthMarketData.Turnover
                    , position=DepthMarketData.OpenInterest
                    , ask1=DepthMarketData.AskPrice1
                    , bid1=DepthMarketData.BidPrice1
                    , asize1=DepthMarketData.AskVolume1
                    , bsize1=DepthMarketData.BidVolume1)
        print('深度行情通知:', tick)

def __main__():
    import os
    import time
    BrokerID = unicode2bytes(os.getenv('TEST_BROKERID', 'Your BrokerID'))
    UserID = unicode2bytes(os.getenv('TEST_USERID', 'Your UserID'))
    Password = unicode2bytes(os.getenv('TEST_PASSWORD', 'Your Password'))
    AuthCode = b'0000000000000000'
    AppID = b'simnow_client_test'
    ProductInfo = b''
    ExchangeID = b'SHFE'
    InstrumentID = b'cu2509'
    FrontAddr = [{'name':'第一套环境 - 第一组', 'tFrontAddr':b'tcp://182.254.243.31:30001', 'mFrontAddr':b'tcp://182.254.243.31:30011'}
                 ,{'name':'第一套环境 - 第二组', 'tFrontAddr':b'tcp://182.254.243.31:30002', 'mFrontAddr':b'tcp://182.254.243.31:30012'}
                 ,{'name':'第一套环境 - 第三组', 'tFrontAddr':b'tcp://182.254.243.31:30003', 'mFrontAddr':b'tcp://182.254.243.31:30013'}
                 ,{'name':'7*24环境', 'tFrontAddr':b'tcp://182.254.243.31:40001', 'mFrontAddr':b'tcp://182.254.243.31:40011'}]
    current_dir = os.path.dirname(os.path.abspath(__file__))
    tmp_path = os.path.join(current_dir, 'flow/')
    if not os.path.exists(tmp_path):
        os.makedirs(tmp_path)
    flowPath = unicode2bytes(tmp_path)

    print('Python版本:', sys.version)

    while True:
        print('选择环境:')
        for idx, front in enumerate(FrontAddr):
            print('%d. %s' % (idx+1, front['name']))
            print('\t Trade Front:%s \n\t Market Front:%s' % (front['tFrontAddr'], front['mFrontAddr']))
        print('100.退出')
        try: idx = int(user_input())
        except ValueError: continue
        if idx > 0  and idx <= len(FrontAddr):
            tFrontAddr = FrontAddr[idx-1]['tFrontAddr']
            mFrontAddr = FrontAddr[idx-1]['mFrontAddr']
            break
        if idx == 100: return

    while True:
        print('选择连接行情/交易:')
        print('1.连接交易')
        print('2.连接行情')
        print('100.退出')
        try: trade_md = int(user_input())
        except ValueError: continue
        if trade_md == 1:
            trader = PyCTP_Trader.CreateFtdcTraderApi(flowPath)
            print('Api版本:', PyCTP_Trader.GetApiVersion())
            print('采集库版本:', PyCTP.CTP_GetDataCollectApiVersion())
            print('连接前置:', trader.Connect(tFrontAddr))
            print("请确定连接模式:")
            print("1.直连模式")
            print("2.中继服务器操作员模式(一对多模式)")
            print("3.中继服务器非操作员模式(多对多模式)")
            while True:
                try: mode_num = int(user_input())
                except ValueError: continue
                if mode_num == 1:
                    # 1.直连模式
                    print('客户端认证:', trader.Authenticate(BrokerID, UserID, ProductInfo, AuthCode, AppID))
                    print('账号登陆:', trader.Login(BrokerID, UserID, Password))
                    break
                elif mode_num == 2:
                    # 2.中继服务器操作员模式(一对多模式)
                    print('客户端认证:', trader.Authenticate(BrokerID, UserID, ProductInfo, AuthCode, AppID))
                    print('账号登陆:', trader.Login(BrokerID, UserID, Password))
                    print('上报用户终端信息:', trader.SubUserSystemInfo(BrokerID, UserID, AppID))
                    break
                elif mode_num == 3:
                    # 3.中继服务器非操作员模式(多对多模式)
                    print('客户端认证:', trader.Authenticate(BrokerID, UserID, ProductInfo, AuthCode, AppID))
                    print('注册用户终端信息:', trader.RegUserSystemInfo(BrokerID, UserID, AppID) )
                    print('账号登陆:', trader.Login(BrokerID, UserID, Password))
                    break
                else:
                     print('选择的模式有误,请重新输入!')
            print('交易日:', trader.GetTradingDay())
            print('投资者代码:', trader.setInvestorID(UserID))
            while True:
                print('请输入选择的序号')
                print('  1.请求结算单确认')
                print('  2.请求查询交易所')
                print('  3.请求查询投资者')
                print('  4.请求查询资金账户')
                print('  5.请求查询行情')
                print('  6.请求查询合约')
                print('  7.请求查询合约手续费率')
                print('  8.请求查询合约保证金率')
                print('  9.请求查询投资者持仓')
                print(' 10.请求申报开多仓一手')
                print(' 11.请求申报开空仓一手')
                print(' 12.请求申报平多仓一手')
                print(' 13.请求申报平空仓一手')
                print('100.退出程序')
                try: inp = int(user_input())
                except ValueError: continue
                if inp == 1:
                    print('结算单确认:', trader.SettlementInfoConfirm())
                if inp == 2:
                    print('查询交易所:', [(exchange.ExchangeID, bytes2unicode(exchange.ExchangeName), exchange.ExchangeProperty) for exchange in trader.QryExchange()])
                elif inp == 3:
                    print('查询投资者:', trader.QryInvestor())
                elif inp == 4:
                    print('查询账户:', trader.QryTradingAccount())
                elif inp == 5:
                    print('查询行情:')
                    data = trader.QryDepthMarketData(InstrumentID)
                    for d in data:
                        print( d.InstrumentID, d.LastPrice )
                elif inp == 6:
                    print('查询合约:', trader.QryInstrument(ExchangeID, InstrumentID))
                elif inp == 7:
                    print('查询合约手续费率:', trader.QryInstrumentCommissionRate(InstrumentID))
                elif inp == 8:
                    print('查询合约保证金率:', trader.QryInstrumentMarginRate(InstrumentID))
                elif inp == 9:
                    # print('查询投资者持仓:', trader.QryInvestorPosition())
                    # print('查询投资者持仓明细:', trader.QryInvestorPositionDetail())
                    # 持仓多空方向
                    PosiDirection_Dict = {PyCTP.THOST_FTDC_PD_Net: '净', PyCTP.THOST_FTDC_PD_Long:'买', PyCTP.THOST_FTDC_PD_Short: '卖'}
                    table = [['持仓合约', '买卖', '总仓', '持仓盈亏']]
                    for position in trader.QryInvestorPosition():
                        if position.Position:
                            table.append([  bytes2unicode(position.InstrumentID), PosiDirection_Dict[position.PosiDirection], position.Position, position.PositionProfit])
                    for row in table:
                        print('{:<10}{:<10}{:<10}{:<10}'.format(*row))
                elif inp == 10:
                    marketData = trader.QryDepthMarketData(InstrumentID)
                    price = marketData[0].AskPrice1
                    print('申报开多仓一手:', trader.OrderInsert(InstrumentID, Action=PyCTP.THOST_FTDC_OF_Open, Direction=PyCTP.THOST_FTDC_D_Buy, Volume = 1, Price=price))
                elif inp == 11:
                    marketData = trader.QryDepthMarketData(InstrumentID)
                    price = marketData[0].BidPrice1
                    print('申报开空仓一手:', trader.OrderInsert(InstrumentID, Action=PyCTP.THOST_FTDC_OF_Open, Direction=PyCTP.THOST_FTDC_D_Sell, Volume = 1, Price = price))
                elif inp == 12:
                    marketData = trader.QryDepthMarketData(InstrumentID)
                    price = marketData[0].BidPrice1
                    print('申报平多仓一手:', trader.OrderInsert(InstrumentID, Action=PyCTP.THOST_FTDC_OF_CloseToday, Direction=PyCTP.THOST_FTDC_D_Sell, Volume = 1, Price=price))
                elif inp == 13:
                    marketData = trader.QryDepthMarketData(InstrumentID)
                    price = marketData[0].AskPrice1
                    print('申报平空仓一手:', trader.OrderInsert(InstrumentID, Action=PyCTP.THOST_FTDC_OF_CloseToday, Direction=PyCTP.THOST_FTDC_D_Buy, Volume = 1, Price=price))
                elif inp == 100:
                    break
                else:
                    print('请输入正确的序号')
            print('账号登出:', trader.Logout())
            print('删除接口:', trader.Release())
            break
        elif trade_md == 2:
            market = PyCTP_Market.CreateFtdcMdApi(flowPath)
            print('Api版本:', PyCTP_Market.GetApiVersion())
            print('连接前置:', market.Connect(mFrontAddr))
            print('账号登陆:', market.Login(BrokerID, UserID, Password))
            print('交易日:', market.GetTradingDay())
            print('订阅行情:', market.SubMarketData([InstrumentID]))
            #print('订阅询价:', market.SubForQuoteRsp([InstrumentID]))
            user_input('按任意键退出:\n')
            print('退订行情:', market.UnSubMarketData([InstrumentID]))
            #print('退订询价:', market.UnSubForQuoteRsp([InstrumentID]))
            #print('账号登出:', market.Logout())
            print('删除接口:', market.Release())
            break
        elif trade_md == 100:
            break
        else:
            print('请输入正确的序号.')

if __name__ == '__main__':
    __main__()
    user_input('按任意键退出:\n')

Tag:ctp, pyctp, trader, c/c++

85 comments.

  1. peterlink peterlink

    请教大佬,看穿式的版本在哪里能修改自己申请到的AppID?

  2. chaowen li chaowen li

    代码方面的修改:

    之前做CTP都需要调用ReqUserLogin();这个登录请求,现在如果要接入穿透式监管的流程改变了,将ReqUserLogin();替换为ReqAuthenticate(CThostFtdcReqAuthenticateField *pReqAuthenticateField, int nRequestID) 认证请求,然后在认证回调里添加ReqUserLogin();

    调用 pyctp 如何实现这个流程? 谢谢

  3. chaowen li chaowen li

    如何理解 看穿监管?

    调用 CTraderApi::ReqAuthenticate()函数填入 AppID和授权码 进行 终端认证 ,通过 ,通过 终端认证后再调用 CTraderApi::ReqUserLogin()函数 登录。 调用 pyctp 怎么实现?

  4. Andy Sun Andy Sun

    32位的python,在setup里的include & lib 目录还是 v6.3.6_20160606_tradeapi_windows。编译环境是只支持64位的python 吗?

  5. mugugu mugugu

    大神 为什么我在调用api的release是出现了段错误,最为关键的是,同样的操作系统/编译器,分别运行,一个报错,一个正常

  6. 木古古 木古古

    大神 能不能帮我看看这个帖子的问题
    https://bbs.csdn.net/topics/395193839

    1. zhuolin zhuolin

      官方CTP c/c++ 库在linux64bit上release是是存在问题的,无法解决,只能忽略这个错误.

  7. hushaojun hushaojun

    C:\CTP\PyCTP-master>python setup.py install
    running install
    running build
    running build_ext
    building 'PyCTP' extension
    creating build\temp.win-amd64-3.5
    creating build\temp.win-amd64-3.5\Release
    creating build\temp.win-amd64-3.5\Release\src
    ...
    Generating code
    Finished generating code
    running build_scripts
    creating build\scripts-3.5
    copying src\test_PyCTP.py -> build\scripts-3.5
    running install_lib
    copying build\lib.win-amd64-3.5\PyCTP.cp35-win_amd64.pyd -> C:\Program Files\Python35\Lib\site-packages
    running install_scripts
    running install_egg_info
    Removing C:\Program Files\Python35\Lib\site-packages\PyCTP-1.1.0-py3.5.egg-info
    Writing C:\Program Files\Python35\Lib\site-packages\PyCTP-1.1.0-py3.5.egg-info

    编译算是通过啊?

    为啥后面执行测试脚本,一直提示ImportError: DLL load failed: 找不到指定的模块。

    已经把thostmduserapi_se.dll和thosttraderapi_se.dll复制到相同目录去了

  8. Sifan Chen Sifan Chen

    你好,我在按照guidance进行编译,进入VS2013 x86 本机工具命令提示,将地址换到python master这个文件夹(即包含 setup.py 的文件夹),然后输入指令 python setup.py build,但是什么都没有发生,也没有给出任何信息,就直接进入新的一行让我输入指令了,请问是怎么回事呢。我的python版本是3.7.3,请问是否是版本的问题呢?

  9. chen2485 chen2485

    问一下啊,我按照guidance说的进入 VS2013 x86 本机工具命令提示 将地址转到python-master文件夹下,想要跑 python setup.py build 但是一点反应都没有直接就给出了新的一行让我输入指令,也就是说看起来什么都没有发生。请问是什么原因呢?我的python是3.7.3,请问是否会是python版本影响?

  10. Andy Andy

    请问博主,现在交易所更新了6.3.13和6.3.15版本,这两个新版本不再向下兼容,且6月份期货公司升级后老版本全部使用不了,之能使用6.3.15版本, 博主你的版本还停留在6.3.6是不是太老了,目前我用博主的代码成功编译封装了6.3.13和6.3.15版本,都能正常调用,但是不知道博主的代码是否对这两个新版本做了支持?

    且最新的行情端增加了QryMulticastInstrument接口, 这接口函数暂时没什么用, 但是在封装代码中并没有把这个新函数给封装进去。看了下源码,好像自己写下可以把QryMulticastInstrument给封装进去,想请问博主,源码中的src文件夹下的C++源代码是手动写的么?还是通过什么生成的?APIToPyCTP.py可以正常运行,但是不知道是不是得通过这个py文件生成c++的代码,请问c++的代码的如何生成的?

    一直是用博主的代码封装的,非常感谢

    1. zhuolin zhuolin

      已更新到6.3.15穿透版

  11. wowo wowo

    朋友你好!我相信很多人和我一样,能力有限,只能站在巨人的肩膀上做一点策略小开发,所以非常感谢您封装了这么好的接口,使我们这些小白也能有机会研究量化策略。目前我看到github上面有针对看穿式的更新,但不知怎么用。请问你能更新一下相应的demo吗?非常感谢!

  12. daoyuan daoyuan

    您好,十分感谢您共享的代码,CTP API已经更新到了6.3.11_20180109版本,我通过您的代码重新编译后已经无法正常使用,具体出现问题是前置链接无法登陆所以后续也无法正常使用,我通过您共享的 APIToPyCTP.py 转换代码但是似乎没有成功,不太理解转换代码。

  13. DaoYuan DaoYuan

    您好,十分感谢您共享的代码,CTP API已经更新到了6.3.11_20180109版本,我通过您的代码重新编译后已经无法正常使用,具体出现问题是前置链接无法登陆所以后续也无法正常使用,我通过您共享的 APIToPyCTP.py 转换代码但是似乎没有成功

  14. DDl DDl

    您好,windows平台下发送订单出现ValueError: The first arguments is invalid.不知道是什么原因。
    用的是OrderInsert函数,第一个参数是InputOrder
    InputOrder里面的传参正常输入和bytes处理都是一样的错误。
    迫切等待回复,谢谢

    1. zhuolin zhuolin

      必须是python3环境,所有字串均bytes处理.

      1. DDI DDI

        您好,测试过后还是不行,windows、python3.6环境下
        在OrderInsert的传参都进行了bytes(‘xxx’, ‘gb2312’)处理。
        错误还是ValueError: The first arguments is invalid
        方便给个事例或进一步告知原因么?

        1. zhuolin zhuolin

          trader.OrderInsert(b'pp1602', PyCTP.THOST_FTDC_OF_Open, PyCTP.THOST_FTDC_D_Buy, 0, 0.0)

  15. xiaolan xiaolan

    关于行情数据入Oracle数据库的时候,我遇到了两个问题:
    1、由于某些时刻tick数据波动较大,所有推送的数据很快,导致单个tick入库的时间追不上推送时间,达不到数据的时效性。然后我用批量插入:先收集一分钟的tick数据,我用list保存,然后批量插入后立即置list为空,但此时,行情仍在往list里面写,导致某些tick数据有遗漏。请问有没有好的解决办法??
    2、在生成分钟tick数据的时候,我拿到一分钟的tick数据,在这数据中多种合约,每一种合约都需要计算最高、最低、开盘、收盘、数量,这里非常耗费时间,请问楼主大大有什么建议吗??
    跪求回复!!

    1. zhuolin zhuolin

      使用队列, ontick写入队列就返回, 其它的现成处理队列中的数据.

  16. xiaolan xiaolan

    关于行情数据入Oracle数据库的时候,我遇到了两个问题:
    1、由于某些时刻tick数据波动较大,所有推送的数据很快,导致单个tick入库的时间追不上推送时间,达不到数据的时效性。然后我用批量插入:先收集一分钟的tick数据,我用list保存,然后批量插入后立即置list为空,但此时,行情仍在往list里面写,导致某些tick数据有遗漏。请问有没有好的解决办法??
    2、在生成分钟tick数据的时候,我拿到一分钟的tick数据,在这数据中多种合约,每一种合约都需要计算最高、最低、开盘、收盘、数量,这里非常耗费时间,请问楼主大大有什么建议吗??
    跪求回复!!

  17. lihai lihai

    释放是这样写的吗:
    def ReleaseMarket(self):
    """ 删除接口对象本身"""
    self.RegisterSpi(None)
    self.Release()
    self = None
    我用maket.ReleaseMarket()直接死机啦

    1. zhuolin zhuolin

      在linux下不用释放,ctp在linux下有内存段错误,但死机是不会的。

  18. Wahaha Wahaha

    晚上好。 看了一晚CTP文档,参考 PyCTP_Trader.py 里面的 函数 OrderInsert(这个函数是限价单),想写一个 开平仓市价单申报 函数,想不出来,可以指点下吗?万分感谢。

  19. tuxingguo tuxingguo

    行情登陆的时候,为什么无论用户密码怎样输入,都返回一样的结果:
    'BrokerID': b'', 'SHFETime': b'', 'MaxOrderRef': b'', 'FrontID': 0, 'CZCETime': b'', 'UserID': b'',

  20. buyinfosun buyinfosun

    请问一下,使用test.py的时候报dll的错误应该怎么解决呢
    Traceback (most recent call last):
    File "D:/work/stock/pyctp-master/example/test.py", line 4, in
    from ctp.futures import ApiStruct, MdApi
    File "D:\work\stock\pyctp-master\example\ctp\futures__init__.py", line 837, in get
    value = self.fget()
    File "D:\work\stock\pyctp-master\example\ctp\futures__init__.py", line 851, in MdApi
    from ._MdApi import _init, MdApi; _init(ApiStruct)
    ImportError: DLL load failed: %1 不是有效的 Win32 应用程序。

  21. 涂兴国 涂兴国

    行情订阅的时候,报错:('OnRspError', [77, 'CTP:无此功能'], 0, True),为啥呢??

  22. 涂兴国 涂兴国

    行情订阅的时候报错:
    ('OnRspError', [77, 'CTP:无此功能'], 0, True)为啥呀??

  23. tuxingguo tuxingguo

    为什么在test_PyCTP_market登陆,无论输入的userId和password正确与否,登陆都能通过呀??

  24. tuxingguo tuxingguo

    您好,运行脚本test_PyCTP_market.py,登陆输入账号Id和密码之后,返回ret:
    登陆: {'LoginTime': b'', 'SHFETime': b'', 'FrontID': 0, 'UserID': b'', 'FFEXTime': b'', 'SystemName': b'', 'BrokerID': b'', 'SessionID': 0, 'DCETime': b'', 'TradingDay': b'20181112', 'MaxOrderRef': b'', 'CZCETime': b'', 'INETime': b''},其中大部分都是空的,请问是登陆成功了还是失败了??

  25. Tony Tony

    您好, 很感谢您的开源代码, 看到这个函数不太明白, 好像是不用这个去register spi, 而是在 CTP_THOST_FTDC_MD_API_CreateFtdcMdApi 中就注册好了

    PyObject *CTP_THOST_FTDC_MD_API_RegisterSpi(PyObject *self, PyObject *args) { CTP_THOST_FTDC_MD_API *api = (CTP_THOST_FTDC_MD_API *) self; PyObject *tmp = api->pySpi; if (!PyArg_ParseTuple(args, "O", &api->pySpi)) { PyErr_SetString(PyExc_ValueError, "parameter invalid."); return nullptr; } Py_XDECREF(tmp); Py_INCREF(api->pySpi); Py_RETURN_NONE; };

    1. zhuolin zhuolin

      只是为了模拟注册效果,总需要有个回调类.

  26. zhiyi zhiyi

    满屏都是下面的错误
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnQuote'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnQuote'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnQuote'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnQuote'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnQuote'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnQuote'

    1. zhuolin zhuolin

      故意增加的这个错误信息,目的是避免事件遗漏。请建立空OnRtnQuote方法 。

  27. xiong xiong

    你好,我使用python2.7 32位的环境编译成功但运行test_PyCTP报错:
    File "test_PyCTP.py", line 598, in main
    trader = PyCTP_Trader.CreateFtdcTraderApi(b'

    tmp_t

    ')
    ValueError: parameter invalid.
    请问这是什么原因呢?

  28. xiong xiong

    您好,请问运行test_PyCTP.py产生
    trader = File "test_PyCTP.py", line 598, in main
    trader = PyCTP_Trader.CreateFtdcTraderApi(b'

    tmp_t

    ')
    ValueError: parameter invalid.
    报错是什么原因呢?

    1. zhuolin zhuolin

      必须设置已经存在的文件夹路径,注意看参考代码

  29. xiong xiong

    您好,请问运行test_PyCTP.py产生
    trader = File "test_PyCTP.py", line 598, in main
    trader = PyCTP_Trader.CreateFtdcTraderApi(b'

    tmp_t

    ')
    ValueError: parameter invalid.
    报错是什么原因呢?

  30. wlock wlock

    PyObject *PyXTP_PyDict_FromStruct(XTPOB *pOB)
    {
    if(pOB == nullptr) Py_RETURN_NONE;

    PyObject *lbid = PyList_New(10); PyObject *lask = PyList_New(10); PyObject *lbid_qty = PyList_New(10); PyObject *lask_qty = PyList_New(10); for(int i=0; i < 10; i++){ PyList_SetItem(lbid, i, PyFloat_FromDouble(pOB->bid[i])); PyList_SetItem(lask, i, PyFloat_FromDouble(pOB->ask[i])); PyList_SetItem(lbid_qty, i, PyLong_FromLongLong(pOB->bid_qty[i])); PyList_SetItem(lask_qty, i, PyLong_FromLongLong(pOB->ask_qty[i])); } PyObject *ret=Py_BuildValue("{s:i,s:y,s:d,s:L,s:d,s:L,s:O,s:O,s:O,s:O,s:L}" , "exchange_id", pOB->exchange_id , "ticker", pOB->ticker , "last_price", pOB->last_price , "qty", pOB->qty , "turnover", pOB->turnover , "trades_count", pOB->trades_count , "bid", lbid , "ask", lask , "bid_qty", lbid_qty , "ask_qty", lask_qty , "data_time", pOB->data_time ); Py_DECREF(lbid); Py_DECREF(lask); Py_DECREF(lbid_qty); Py_DECREF(lask_qty); return ret;

    }

    另外之前这么写过dict的也不行

    1. zhuolin zhuolin

      python有一个debug模式,在debug模式下,可以直接调试扩展代码。这样更容易发现原因

      1. wlock wlock

        好的,多谢

  31. wlock wlock

    struct OrderBookStruct {
    XTP_EXCHANGE_TYPE exchange_id;
    char ticker[XTP_TICKER_LEN];
    double last_price;
    int64_t qty;
    double turnover;
    int64_t trades_count;
    ///十档申买价
    double bid[10];
    ///十档申卖价
    double ask[10];
    ///十档申买量
    int64_t bid_qty[10];
    ///十档申卖量
    int64_t ask_qty[10];
    /// 时间类
    int64_t data_time;
    } ;

    类似这样的,请教转换函数该怎么写?。。。我先尝试转成list,没有成功;如果是转换成dict怎么写?

    PyObject *PyXTP_PyDict_FromStruct(XTPOB *pOB)
    {
    if(pOB == nullptr) Py_RETURN_NONE;

    PyObject *lbid = PyList_New(10); PyObject *lask = PyList_New(10); PyObject *lbid_qty = PyList_New(10); PyObject *lask_qty = PyList_New(10); for(int i=0; i < 10; i++){ PyList_SetItem(lbid, i, PyFloat_FromDouble(pOB->bid[i])); PyList_SetItem(lask, i, PyFloat_FromDouble(pOB->ask[i])); PyList_SetItem(lbid_qty, i, PyLong_FromLongLong(pOB->bid_qty[i])); PyList_SetItem(lask_qty, i, PyLong_FromLongLong(pOB->ask_qty[i])); } PyObject *ret=Py_BuildValue("[i,y,d,L,d,L,L]" , pOB->exchange_id , pOB->ticker , pOB->last_price , pOB->qty , pOB->turnover , pOB->trades_count , pOB->data_time ); PyList_Append(ret, lbid); PyList_Append(ret, lask); PyList_Append(ret, lbid_qty); PyList_Append(ret, lask_qty); Py_DECREF(lbid); Py_DECREF(lask); Py_DECREF(lbid_qty); Py_DECREF(lask_qty); return ret;

    }

  32. wlock wlock

    请教个扩展的问题,char[x],用pybytes;如果是struct里面有int[x],double[x],用啥做转换啊?

    1. zhuolin zhuolin

      用数组或列表就可以了,在c++中需做一次转换处理. 另外,如是非ctp, 那么python好像提供有直接的c结构类。

  33. imlooker imlooker

    您好!非常感谢提供此CTP的python接口,我想把它用到32位的linux系统上运行,请问是否可以自己更改源代码实现?我安装了VS2015社区版,结果报Unable to find vcvarsall.bat错误,请问那两个libthostmduserapi.so和libthosttraderapi.so是在windows下编译出来的吗?我试过linux下如果没有这两个so文件,不能编译出第三个PyCTP.cpython-34m.so。真的非常感谢提供了这个接口!

    1. zhuolin zhuolin

      不可以应用于32位linux,CTP官方只提供了64位的库

  34. geekmo geekmo

    你好!刚研究PYCTP,有个问题不是很清楚,可以解答一下不?谢谢。

    订阅行情的结果:
    {'InstrumentID': 'rb1810', 'time': datetime.datetime(2018, 6, 8, 15, 13, 24, 500000), 'last': 3806.000000000001, 'volume': 3697188, 'amount': 140560926800.0, 'position': 3311490.0, 'ask1': 3807.0, 'bid1': 3806.000000000001, 'asize1': 339, 'bsize1': 333}
    这些结果能不能找个变量接收。如果能,怎么接收?

    1. zhuolin zhuolin

      设计pyctp的目的就是为了方便python接收和处理数据,这点需要有python基础. 总的来说就是可以使用队列或列表保存

  35. shi shi

    博主,对于其中的请求行情模块,test_PyCTP.py里的def SubMarketData(self, InstrumentID):这个函数里,ret = self.SubscribeMarketData(InstrumentID, len(InstrumentID))应该是这一行触发了请求行情吧,我如何能把在窗口里的滚动的每秒的数据存成一个个字典,供我别的分析啊,SubscribeMarketData这个函数只返回了个int值,我如何把得到的行情数据存起来哈,谢谢

    1. zhuolin zhuolin

      使用python list或queue对象保存数据. 保存行情只能在回调中实现. 这点需要有python基础

  36. jian jian

    请问博主 在win10(64位)Python3.6环境下运行PyCTP_build_20170122 无法导入PyCTP 是什么问题
    Traceback (most recent call last):
    File "test_PyCTP_Market.py", line 12, in
    from PyCTP_Market import PyCTP_Market
    File "C:\Users\Windows\Desktop\PyCTP_build_20170122\PyCTP_Market.py", line 11, in
    import PyCTP
    ImportError: DLL load failed: 找不到指定的模块。

    1. zhuolin zhuolin

      需要先编译得到pyctp.pyd,编译后把编译得到的 pyctp.pyd文件复制到自己项目根目录,同时把下载文件里面带的ctp官方提供的两个windows ctp dll文件也复制到项目根目录

  37. j88r j88r

    能不能共享下.h转换h&cpp的新脚本学习下。
    因为api不停的更新,所以想着也能跟进更新一下pyctp接口,如果API有变化,我也可以尝试修改正则,然后再共享出来:D

    1. zhuolin zhuolin

      github中已共享出此脚本

  38. rookie rookie

    我在做web版的ctp 登录的时候发现 ctp有自动掉线重连功能 导致OnFrontDisconnected这个函数会不断的触发 如何才能停止这个函数的调用呢

    1. zhuolin zhuolin

      无法停止调用,要想看不到显示,把函数体留空就是了.

  39. lg lg

    行情退出登录: 显示错误--('OnRspError', [68, 'CTP:无此功能'], 0, True)

    1. zhuolin zhuolin

      此问题无法解决。原因是官方CTP服务器并不存在退出功能. 客户段这个api只是做个样子.

  40. lg lg

    交易退出登录也有问题:登出: {'UserID': b'112769', 'BrokerID': b'9999'}
    CThostFtdcUserApiImplBase::OnSessionDisconnected[0000000002B03778][-313393151][ 4097]
    其他都很好,就是退出登录行情,及交易都会有问。

    1. zhuolin zhuolin

      此提示信息是官方CTPapi自行产生, 忽视即可. windows上似乎无此信息.

  41. 行情退出登录有问题。 ('OnRspError', [68, 'CTP:无此功能'], 0, True)

    1. zhuolin zhuolin

      此问题无法解决。原因是官方CTP服务器并不存在退出功能. 客户api只是做个样子.

  42. warrock warrock

    求问兄台能不能共享下.h转换h&cpp的新脚本学习下,github的运行结果稍微一点点对不上的。

    1. zhuolin zhuolin

      ctp新版本有变化,需要针对修改一下才行, 其实转换脚本只是一些正则.

  43. lin lin

    请问楼主,查询复数合约怎么写?
    单数合约是InstrumentID= b'cu1802'
    这里试了很多写法,比如想写2,3个合约,都运行失败啊

    1. zhuolin zhuolin

      多个合约用 list传递 例如 InstrumentID= [b’cu1802′, b’cu1803′]

  44. ltw ltw

    请问在 OnFrontDisconnected 函数里的 self.__Inst_Interval() 方法是啥?

    1. zhuolin zhuolin

      最初设想如果掉线,就过段时间自动登陆,这个函数是用来延迟的。不过后发现ctp有自动掉线重连功能,也就注释掉了.

  45. zfw zfw

    非常感谢回复,问题已经解决。代码中的测试部分全部通过,没有问题。但是不知道怎么写开仓,平仓,撤单操作。你的代码中只有限价单(没有市价单、条件单),试了试,可能是传参有问题,限价单也不会用。哈哈哈哈。能不能写个demo。我看上面好多人都在问这个问题。望百忙之中,抽点时间帮助一下我们这样的小白。谢谢!万分感谢!

  46. zfw zfw

    满屏全是
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnInstrumentStatus'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnInstrumentStatus'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnInstrumentStatus'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnInstrumentStatus'
    AttributeError: 'PyCTP_Trader' object has no attribute 'OnRtnInstrumentStatus'

    1. zhuolin zhuolin

      这是默认加的未处理事件提示(此消息是交易所发送的交易和约状态改变通知,比如当前进入竞价,当前进入交易,当前停止交易.....)
      具体事件原因描述可以查看 https://github.com/shizhuolin/PyCTP/blob/master/v6.3.6_20160606_tradeapi64_windows/ThostFtdcTraderApi.h 第242行
      字段含义查看 https://github.com/shizhuolin/PyCTP/blob/master/v6.3.6_20160606_tradeapi64_windows/ThostFtdcUserApiStruct.h 第4534行
      状态描述查看 https://github.com/shizhuolin/PyCTP/blob/master/v6.3.6_20160606_tradeapi64_windows/ThostFtdcUserApiDataType.h 第941-959行
      在 PyCTP_Trader 中增加
      def OnRtnInstrumentStatus(self, status):
      pass
      就可屏蔽此事件.

  47. 请问想要通过CTP进行开仓,平仓,撤单操作,代码应该怎么写啊,您有写好的吗?

  48. 请问要想通过CTP实现开仓,平仓,撤单等操作,代码应该怎么写啊?

    1. zhuolin zhuolin

      参考例子中的python订单操作代码

  49. def QryInstrument(self, ExchangeID=b'', InstrumentID=b''):
    """ 查询和约 """
    QryInstrument = dict(ExchangeID = ExchangeID
    , InstrumentID = InstrumentID)
    self.__rsp_QryInstrument = dict(event = threading.Event()
    , RequestID = self.__IncRequestID()
    , results = []
    , ErrorID = 0)
    ret = self.ReqQryInstrument(QryInstrument, self.__rsp_QryInstrument['RequestID'])
    if ret == 0:
    self.__rsp_QryInstrument['event'].clear()
    if self.__rsp_QryInstrument['event'].wait(self.TIMEOUT):
    if self.__rsp_QryInstrument['ErrorID'] != 0:
    return self.__rsp_QryInstrument['ErrorID']
    return self.__rsp_QryInstrument['results']
    else:
    return -4
    return ret

    我看好几段代码都是这个格式,但是我不太明白,能不能麻烦给解释一下这段代码是什么意思

    1. zhuolin zhuolin

      CTP指令基本都是异步指令. 也就是说发送请求后不等结果返回就会继续执行下一行代码. 这里的目的是事件等待.必须等到查询消息返回结束才会继续执行后续代码. 这么做的目的是为了方便策略编写. 每个def 指令(self,...) 方法都对应有一个 def On指令(self,...)事件处理。

  50. dongfang dongfang

    能不能将编译好的dll也放上

    1. zhuolin zhuolin

      好的,我放一份编译好的上来

  51. 版主你好,看到了pyctp,感觉挺好的,想使用但需要 vs2013编译, 不过本人未装 vs2013,也不熟悉,将否将编译后的dll也放上,方便直接使用

  52. new13 new13

    您好,我运行python setup.py build时提示,查了很多资料都没解决,故此求救,谢谢:
    D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\BIN\amd64\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -I./v6.3.6_20160606_tradeapi64_windows -IC:\Python27\include -IC:\Python27\PC /Tp.\src\stdafx.cpp /Fobuild\temp.win-amd64-2.7\Release\.\src\stdafx.obj
    stdafx.cpp
    D:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\BIN\amd64\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -I./v6.3.6_20160606_tradeapi64_windows -IC:\Python27\include -IC:\Python27\PC /Tp.\src\PyCTP.cpp /Fobuild\temp.win-amd64-2.7\Release\.\src\PyCTP.obj
    PyCTP.cpp
    .\src\PyCTP.cpp(12) : error C2065: “PyModuleDef_HEAD_INIT”: 未声明的标识符
    .\src\PyCTP.cpp(13) : error C2514: “PyModuleDef”: 类没有构造函数
    .\src\PyCTP.cpp(10) : 参见“PyModuleDef”的声明
    .\src\PyCTP.cpp(21) : error C3861: “PyModule_Create”: 找不到标识符
    .\src\PyCTP.cpp(25) : error C2562: “PyInit_PyCTP”:“void”函数返回值
    .\src\PyCTP.cpp(16) : 参见“PyInit_PyCTP”的声明
    .\src\PyCTP.cpp(31) : error C2562: “PyInit_PyCTP”:“void”函数返回值
    .\src\PyCTP.cpp(16) : 参见“PyInit_PyCTP”的声明
    .\src\PyCTP.cpp(40) : error C2562: “PyInit_PyCTP”:“void”函数返回值
    .\src\PyCTP.cpp(16) : 参见“PyInit_PyCTP”的声明
    .\src\PyCTP.cpp(49) : error C2562: “PyInit_PyCTP”:“void”函数返回值
    .\src\PyCTP.cpp(16) : 参见“PyInit_PyCTP”的声明
    error: command 'D:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\BIN\\amd64\\cl.exe' failed with exit status 2

    1. zhuolin zhuolin

      目前pyctp只支持python3 并在python3.4.3下完美测试通过(windows/linux)

  53. valleysong valleysong

    stdafx.h(9): fatal error C1083: 无法打开包括文件: “Python.h”: No such file or directory
    提示这个啥原因啊?

    1. zhuolin zhuolin

      已经更改过代码,重新git. 现在可以直接使用setup.py编译, 自动寻找python.h相关配置

  54. lucas.yuan lucas.yuan

    可否告知您采用的什么封装方法么?

    1. zhuolin zhuolin

      python 官方c/c++扩展封装, 先手工写好部分函数试验效果,可行后做成py脚本批量转换头文件为c/c++文件

Add a new comment.