Python-CTP PyCTP 接口

看穿调用参考 https://github.com/shizhuolin/PyCTP/blob/master/src/test_PyCTP.py#L660
已经编译好文件,内附调用示例.具体参数可参考ctp头文件.
支持python3.4/linux, python3.4/windows
PyCTP_build_20170122

这是程序化期货交易上期ctp接口版本. 为方便数值计算.将其包装为python版本. 支 python3.4.3, win64/32/vs2010/centos7.2/gcc4.8.5上调试和编译
编译需求:vs2010,
https://github.com/shizhuolin/PyCTP

为了省事, 绝大部分代码是直接从ctp源码转换而来.
所有char[x] 均用 pybytes代替, 数据结构用pydict代替,char用pybyte代替,bool使用pybool或int
调用示范:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
from sys import *
from threading import *
from PyCTP import *
 
class CTP_Trader_API(CThostFtdcTraderApi):
 
    _event = Event()
    _BrokerID = None
    _UserID = None
    _InvestorID = None
    _FrontID = None
    _SessionID = None
    _MaxOrderRef = None
 
    def Connect(self, frontAddr):
        self.RegisterSpi(self)
        self.SubscribePrivateTopic(THOST_TERT_RESUME)
        self.SubscribePublicTopic(THOST_TERT_RESUME)
        self.RegisterFront(frontAddr)
        self.Init()
        self._event.clear()
        self._event.wait()
 
    def Login(self, BrokerID, UserID, Password):
        reqUserLogin = {}
        reqUserLogin["BrokerID"] = BrokerID
        reqUserLogin["UserID"] = UserID
        reqUserLogin["Password"] = Password
        self.ReqUserLogin(reqUserLogin, 0)
        self._event.clear()
        self._event.wait()
 
    def setInvestorID(self, InvestorID):
        self._InvestorID = InvestorID
 
    def QrySettlementInfo(self, TradingDay):
        QrySettlementInfoField = {}
        QrySettlementInfoField["BrokerID"] = self._BrokerID
        QrySettlementInfoField["InvestorID"] = self._InvestorID
        QrySettlementInfoField["TradingDay"] = TradingDay
        self.ReqQrySettlementInfo(QrySettlementInfoField, 0)
 
    def OnFrontConnected(self):
        self._event.set()
 
    def OnRspUserLogin(self, RspUserLogin, RspInfo, RequestID, IsLast):
        self._BrokerID = RspUserLogin["BrokerID"]
        self._UserID = RspUserLogin["UserID"]
        self._FrontID = RspUserLogin["FrontID"]
        self._SessionID = RspUserLogin["SessionID"]
        self._MaxOrderRef = RspUserLogin["MaxOrderRef"]
        self._event.set()
 
    def OnRspQrySettlementInfo(self, SettlementInfo, RspInfo, RequestID, IsLast):
        print(SettlementInfo)
 
class CTP_Market_API(CThostFtdcMdApi):
 
    _event = Event()
    _BrokerID = None
    _UserID = None
 
    def Connect(self, frontAddr):
        self.RegisterSpi(self)
        self.RegisterFront(frontAddr)
        self.Init()
        self._event.clear()
        self._event.wait()
 
    def Login(self, BrokerID, UserID, Password):
        reqUserLogin = {}
        reqUserLogin["BrokerID"] = BrokerID
        reqUserLogin["UserID"] = UserID
        reqUserLogin["Password"] = Password
        self.ReqUserLogin(reqUserLogin, 0)
        self._event.clear()
        self._event.wait()
 
    def setInvestorID(self, InvestorID):
        self._InvestorID = InvestorID
 
    def OnFrontConnected(self):
        self._event.set()
 
    def OnRspUserLogin(self, RspUserLogin, RspInfo, RequestID, IsLast):
        self._BrokerID = RspUserLogin["BrokerID"]
        self._UserID = RspUserLogin["UserID"]
        self._event.set()
 
    def OnRspSubMarketData(self, SpecificInstrument, RspInfo, RequestID, bIsLast):
        pass
 
    def OnRtnDepthMarketData(self, data):
        print(repr(data))
 
print(CTP_Trader_API.GetApiVersion())
trader = CTP_Trader_API.CreateFtdcTraderApi(b"_trader_*****_****_")
trader.Connect(b"tcp://180.168.146.187:10000")
trader.Login(b"****", b"*****", bytes(input("password:"), "gb2312"))
print(trader.GetTradingDay())
trader.setInvestorID(b"****")
##trader.QrySettlementInfo(b"")
 
print(CTP_Market_API.GetApiVersion())
market = CTP_Market_API.CreateFtdcMdApi(b"_market_*****_****_")
market.Connect(b"tcp://180.168.146.187:10010")
market.Login(b"*****", b"*****", bytes(input("password:"), "gb2312"))
print(market.GetTradingDay())
market.SubscribeMarketData([b"IF1508", b"IF1509"], 2)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
if __name__ == '__main__':
    import time, os, datetime, PyCTP
    from os.path import join, split, dirname, exists
    from PyCTP_Trader import PyCTP_Trader
    os.makedirs('tmp', exist_ok=True)
    trader = PyCTP_Trader.CreateFtdcTraderApi( join(b'tmp', b'test_t_') )
    time.sleep(1.0)
    print('连接前置服务器:', trader.Connect(b'tcp://******:****'))
    #print('连接前置服务器:', trader.Connect(b'tcp://******:***'))
    time.sleep(1.0)
    print('登陆:', trader.Login(b'****', b'*****', bytes(input('enter password:'), 'gbk')))
    time.sleep(1.0)
    print('设置投资者:', trader.setInvestorID(b'*****'))
    time.sleep(1.0)
    print('查询投资者:')
    Investor = trader.QryInvestor()
    print('经纪公司代码:', Investor['BrokerID'])
    print('投资者代码:', Investor['InvestorID'])
    print('投资者名称:', str(Investor['InvestorName'], 'gbk'))
    print('联系方式:', '电话', str(Investor['Telephone'],'gbk'), '手机', str(Investor['Mobile'], 'gbk'), '地址', str(Investor['Address'], 'gbk'))
    time.sleep(1.0)
    print('查询资金账户:')
    account = trader.QryTradingAccount()
    print('手续费:', account['Commission'])
    print('平仓盈亏:', account['CloseProfit'])
    print('持仓盈亏:', account['PositionProfit'])
    print('可用资金:', account['Available'])
    print('动态权益:', account['Balance'])
    time.sleep(1.0)
    print('查询投资者结算结果:\n', str(trader.QrySettlementInfo()['Content'], 'gbk'))
    time.sleep(1.0)
    print('投资者结算信息确认:', trader.QrySettlementInfoConfirm())
    time.sleep(1.0)
    print('查询交易所与合约:\n')
    exchanges = trader.QryExchange()
    for exchange in exchanges:
        # 交易所
        print(exchange['ExchangeID'], str(exchange['ExchangeName'], 'gbk'))
        time.sleep(1.0)
        # 合约
        Instruments = trader.QryInstrument(ExchangeID=exchange['ExchangeID'])
        for instrument in Instruments:
            time.sleep(1.0)
            # 手续费
            commissionrate = trader.QryInstrumentCommissionRate(instrument['InstrumentID'])[0]
            time.sleep(1.0)
            # 保证金
            marginrate = trader.QryInstrumentMarginRate(instrument['InstrumentID'])[0]
            time.sleep(1.0)
            # 深度行情
            DepthMarketData = trader.QryDepthMarketData(instrument['InstrumentID'])[0]
 
            #print(marginrate)
 
            LongMarginRatio = max(instrument['LongMarginRatio'], marginrate['LongMarginRatioByMoney'])
            ShortMarginRatio = max(instrument['ShortMarginRatio'], marginrate['ShortMarginRatioByMoney'])
            print(instrument['ExchangeID']
            , instrument['InstrumentID']
            , str(instrument['InstrumentName'], 'gbk')
            , '乘数', instrument['VolumeMultiple']
            , '变动', instrument['PriceTick']
            , '开仓手续费', (commissionrate['OpenRatioByMoney'], commissionrate['OpenRatioByVolume'])
            , '平仓手续费', (commissionrate['CloseRatioByMoney'], commissionrate['CloseRatioByVolume'])
            , '平今手续费', (commissionrate['CloseTodayRatioByMoney'], commissionrate['CloseTodayRatioByVolume'])
            , '保证金', ((LongMarginRatio, marginrate['LongMarginRatioByVolume']), (ShortMarginRatio, marginrate['ShortMarginRatioByVolume']))
            )
            uptime = None            
            if DepthMarketData['ActionDay'] != b'' and DepthMarketData['UpdateTime'] != b'':
                uptime=datetime.datetime.strptime(str(DepthMarketData['ActionDay']+DepthMarketData['UpdateTime'], 'gbk'), '%Y%m%d%H:%M:%S').replace(microsecond=DepthMarketData['UpdateMillisec']*1000)            
            a = DepthMarketData['LastPrice'] * instrument['VolumeMultiple'] * (commissionrate['OpenRatioByMoney'] + commissionrate['CloseTodayRatioByMoney']) + commissionrate['OpenRatioByVolume'] + commissionrate['CloseTodayRatioByVolume']
            b = instrument['PriceTick'] * instrument['VolumeMultiple']
            c = a/b
            print(uptime
            , '最新价', DepthMarketData['LastPrice']
            , '数量', DepthMarketData['Volume']
            , '成交金额', DepthMarketData['Turnover']
            , '持仓量', DepthMarketData['OpenInterest']
            , '卖价一', DepthMarketData['AskPrice1']
            , '买价一', DepthMarketData['BidPrice1']
            , '卖量一', DepthMarketData['AskVolume1']
            , '买量一', DepthMarketData['BidVolume1']
            , '成本', c
            )
            break
    time.sleep(1.0)
    print('投资者持仓', trader.QryInvestorPosition())
    time.sleep(1.0)
    print('投资者持仓明细', trader.QryInvestorPositionDetail())
    time.sleep(1.0)
    print('查询报单:')
    orders = trader.QryOrder()
    for order in orders:
        order['StatusMsg'] = str(order['StatusMsg'], 'gbk')
        #break
    print(orders)
    time.sleep(1.0)
    print('查询成交: \n')
    for Trade in trader.QryTrade():
        print(Trade['InstrumentID'], Trade['ExchangeID'], Trade['TradeID'])
        #break
    time.sleep(1.0)
    print('申报开仓:', trader.OrderInsert(b'pp1602', PyCTP.THOST_FTDC_OF_Open, PyCTP.THOST_FTDC_D_Buy, 0, 0.0))
    time.sleep(1.0)
    #print('撤消申报:', trader.OrderActionDelete(order))
    while input('enter q to quit this program:') is not 'q':
        pass
    time.sleep(1.0)
    print('登出:', trader.Logout())
# -*- coding: utf-8 -*-
"""
Created on Sat Jan 23 21:48:07 2016
 
@author: Zhuolin
"""
 
import logging
import threading
import PyCTP
 
logger = logging.getLogger(__name__)
 
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 = bytes('%012d' % self.__OrderRef, 'gbk')
        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_RESTART)
        self.SubscribePublicTopic(PyCTP.THOST_TERT_RESTART)
        self.RegisterFront(frontAddr)
        self.Init()
        self.__rsp_Connect = dict(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 = dict(BrokerID = BrokerID, UserID = UserID, Password = Password)
        self.__rsp_Login = dict(event = threading.Event(), 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['ErrorID'] != 0:
                    logger.error('%s' % str(self.__rsp_Login['ErrorMsg'], encoding='gbk'))
                    return self.__rsp_Login['ErrorID']
                self.__isLogined = True
                self.__Password = Password
                self.__BrokerID = self.__rsp_Login['result']['BrokerID']
                self.__UserID = self.__rsp_Login['result']['UserID']
                self.__SystemName = self.__rsp_Login['result']['SystemName']
                self.__TradingDay = self.__rsp_Login['result']['TradingDay']
                self.__DCETime = self.__rsp_Login['result']['DCETime']
                self.__SessionID = self.__rsp_Login['result']['SessionID'] 
                self.__MaxOrderRef = self.__rsp_Login['result']['MaxOrderRef']
                self.__OrderRef = int(self.__MaxOrderRef) # 初始化报单引用
                self.__INETime = self.__rsp_Login['result']['INETime']
                self.__LoginTime = self.__rsp_Login['result']['LoginTime']
                self.__FrontID = self.__rsp_Login['result']['FrontID']
                self.__FFEXTime = self.__rsp_Login['result']['FFEXTime']
                self.__CZCETime = self.__rsp_Login['result']['CZCETime']
                self.__SHFETime = self.__rsp_Login['result']['SHFETime']
                return self.__rsp_Login['result']
            else:
                return -4
        return ret
 
    def Logout(self):
        """ 登出请求 """
        if not self.__isLogined:
            return 6
        reqUserLogout = dict(BrokerID = self.__BrokerID, UserID = self.__UserID)
        self.__rsp_Logout = dict(event = threading.Event(), 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['ErrorID'] != 0:
                    return self.__rsp_Logout['ErrorID']
                self.__isLogined = False
                return self.__rsp_Logout['result']
            else:
                return -4
        return ret
 
    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
 
    def QryInstrumentMarginRate(self, InstrumentID):
        """ 请求查询合约保证金率 """
        QryInstrumentMarginRate = dict(BrokerID = self.__BrokerID, InvestorID = self.__InvestorID, InstrumentID = InstrumentID)
        self.__rsp_QryInstrumentMarginRate = dict(results =  [],
                                                  RequestID = self.__IncRequestID(),
                                                  ErrorID = 0,
                                                  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['ErrorID'] != 0:
                    return self.__rsp_QryInstrumentMarginRate['ErrorID']
                #assert len(self.__rsp_QryInstrumentMarginRate['results']) == 1
                return self.__rsp_QryInstrumentMarginRate['results']
            else:
                return -4
        return ret
 
    def QryInstrumentCommissionRate(self, InstrumentID):
        """ 请求查询合约手续费率 """
        QryInstrumentCommissionRate = dict(BrokerID = self.__BrokerID, InvestorID = self.__InvestorID, InstrumentID = InstrumentID)
        self.__rsp_QryInstrumentCommissionRate = dict(results       =  []
                                                      , RequestID   = self.__IncRequestID()
                                                      , ErrorID     = 0
                                                      , 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['ErrorID'] != 0:
                    return self.__rsp_QryInstrumentCommissionRate['ErrorID']
                #assert len(self.__rsp_QryInstrumentCommissionRate['results']) == 1
                return self.__rsp_QryInstrumentCommissionRate['results']
            else:
                return -4
        return ret
 
    def QryInvestorPosition(self, InstrumentID=b''):
        """ 请求查询投资者持仓 """
        QryInvestorPositionFiel = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID, InstrumentID=InstrumentID)
        self.__rsp_QryInvestorPosition = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, 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['ErrorID'] != 0:
                    return self.__rsp_QryInvestorPosition['ErrorID']
                return self.__rsp_QryInvestorPosition['results']
            else:
                return -4
        return ret
 
    def QryInvestorPositionDetail(self, InstrumentID=b''):
        """ 请求查询投资者持仓明细 """
        QryInvestorPositionDetail = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID, InstrumentID=InstrumentID)
        self.__rsp_QryInvestorPositionDetail = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        ret = self.ReqQryInvestorPositionDetail(QryInvestorPositionDetail, self.__rsp_QryInvestorPositionDetail['RequestID'])
        if ret == 0:
            self.__rsp_QryInvestorPositionDetail['event'].clear()
            if self.__rsp_QryInvestorPositionDetail['event'].wait(self.TIMEOUT):
                if self.__rsp_QryInvestorPositionDetail['ErrorID'] != 0:
                    return self.__rsp_QryInvestorPositionDetail['ErrorID']
                return self.__rsp_QryInvestorPositionDetail['results']
            else:
                return -4
        return ret
 
    def QryTradingAccount(self):
        """ 请求查询资金账户 """
        QryTradingAccountField = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID)
        self.__rsp_QryTradingAccount = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        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['ErrorID'] != 0:
                    return self.__rsp_QryTradingAccount['ErrorID']
                assert len(self.__rsp_QryTradingAccount['results']) == 1
                return self.__rsp_QryTradingAccount['results'][0]
            else:
                return -4
        return ret
 
    def QryInvestor(self):
        """ 请求查询投资者 """
        InvestorField = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID)
        self.__rsp_QryInvestor = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        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['ErrorID'] != 0:
                    return self.__rsp_QryInvestor['ErrorID']
                assert len(self.__rsp_QryInvestor['results']) == 1
                return self.__rsp_QryInvestor['results'][0]
            else:
                return -4
        return ret
 
    def QryExchange(self, ExchangeID=b''):
        """ 请求查询交易所 """
        QryExchangeField = dict(ExchangeID=ExchangeID)
        self.__rsp_QryExchange = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        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['ErrorID'] != 0:
                    return self.__rsp_QryExchange['ErrorID']
                return self.__rsp_QryExchange['results']
            else:
                return -4
        return ret
 
    def QrySettlementInfo(self, TradingDay=b''):
        """ 请求查询投资者结算结果 """
        QrySettlementInfo = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID, TradingDay=TradingDay)
        self.__rsp_QrySettlementInfo = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        ret = self.ReqQrySettlementInfo(QrySettlementInfo, self.__rsp_QrySettlementInfo['RequestID'])
        if ret == 0:
            self.__rsp_QrySettlementInfo['event'].clear()
            if self.__rsp_QrySettlementInfo['event'].wait(self.TIMEOUT):
                if self.__rsp_QrySettlementInfo['ErrorID'] != 0:
                    return self.__rsp_QrySettlementInfo['ErrorID']
                result = {'Content':b''}
                for item in self.__rsp_QrySettlementInfo['results']:
                    if result.get('TradingDay') is not None:
                        assert result['TradingDay'] == item['TradingDay']
                    if result.get('SettlementID') is not None:
                        assert result['SettlementID'] == item['SettlementID']
                    if result.get('BrokerID') is not None:
                        assert result['BrokerID'] == item['BrokerID']
                    if result.get('InvestorID') is not None:
                        assert result['InvestorID'] == item['InvestorID']
                    if result.get('SequenceNo') is not None:
                        assert result['SequenceNo'] == item['SequenceNo']
                    result['TradingDay'] = item['TradingDay']
                    result['SettlementID'] = item['SettlementID']
                    result['BrokerID'] = item['BrokerID']
                    result['InvestorID'] = item['InvestorID']
                    result['SequenceNo'] = item['SequenceNo']
                    result['Content'] += item['Content']
                return result
            else:
                return -4
        return ret
 
    def QrySettlementInfoConfirm(self):
        """ 请求查询结算信息确认 """
        QrySettlementInfoConfirm = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID)
        self.__rsp_QrySettlementInfoConfirm = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        ret = self.ReqQrySettlementInfoConfirm(QrySettlementInfoConfirm, self.__rsp_QrySettlementInfoConfirm['RequestID'])
        if ret == 0:
            self.__rsp_QrySettlementInfoConfirm['event'].clear()
            if self.__rsp_QrySettlementInfoConfirm['event'].wait(self.TIMEOUT):
                if self.__rsp_QrySettlementInfoConfirm['ErrorID'] != 0:
                    return self.__rsp_QrySettlementInfoConfirm['ErrorID']
                #assert len(self.__rsp_QrySettlementInfoConfirm['results']) == 1
                return self.__rsp_QrySettlementInfoConfirm['results']
            else:
                return -4
        return ret
 
    def SettlementInfoConfirm(self, ConfirmDate=b'', ConfirmTime=b''):
        """ 投资者结算结果确认 """
        SettlementInfoConfirm = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID,
                                     ConfirmDate=ConfirmDate, ConfirmTime=ConfirmTime)
        self.__rsp_SettlementInfoConfirm = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        ret = self.ReqSettlementInfoConfirm(SettlementInfoConfirm, self.__rsp_SettlementInfoConfirm['RequestID'])
        if ret == 0:
            self.__rsp_SettlementInfoConfirm['event'].clear()
            if self.__rsp_SettlementInfoConfirm['event'].wait(self.TIMEOUT):
                if self.__rsp_SettlementInfoConfirm['ErrorID'] != 0:
                    return self.__rsp_SettlementInfoConfirm['ErrorID']
                #assert len(self.__rsp_SettlementInfoConfirm['results']) == 1
                return self.__rsp_SettlementInfoConfirm['results']
            else:
                return -4
        return ret
 
    def QryDepthMarketData(self, InstrumentID):
        """ 请求查询行情 """
        QryDepthMarketData = dict(InstrumentID=InstrumentID)
        self.__rsp_QryDepthMarketData = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        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['ErrorID'] != 0:
                    return self.__rsp_QryDepthMarketData['ErrorID']
                #assert len(self.__rsp_QryDepthMarketData['results']) == 1
                return self.__rsp_QryDepthMarketData['results']
            else:
                return -4
        return ret
 
    def QryOrder(self, InstrumentID=b'', ExchangeID=b'', OrderSysID=b'', InsertTimeStart=b'', InsertTimeEnd=b''):
        """ 请求查询报单 """
        QryOrder = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID, InstrumentID=InstrumentID, ExchangeID=ExchangeID, OrderSysID=OrderSysID, InsertTimeStart=InsertTimeStart, InsertTimeEnd=InsertTimeEnd)
        self.__rsp_QryOrder = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        ret = self.ReqQryOrder(QryOrder, self.__rsp_QryOrder['RequestID'])
        if ret == 0:
            self.__rsp_QryOrder['event'].clear()
            if self.__rsp_QryOrder['event'].wait(self.TIMEOUT):
                if self.__rsp_QryOrder['ErrorID'] != 0:
                    return self.__rsp_QryOrder['ErrorID']
                return self.__rsp_QryOrder['results']
            else:
                return -4
        return ret
 
    def QryTrade(self, InstrumentID=b'', ExchangeID=b'', TradeID=b'', TradeTimeStart=b'', TradeTimeEnd=b''):
        """ 请求查询成交 """
        QryTrade = dict(BrokerID=self.__BrokerID, InvestorID=self.__InvestorID, InstrumentID=InstrumentID, ExchangeID=ExchangeID, TradeID=TradeID, TradeTimeStart=TradeTimeStart, TradeTimeEnd=TradeTimeEnd)
        self.__rsp_QryTrade = dict(results=[], RequestID=self.__IncRequestID(), ErrorID=0, event=threading.Event())
        ret = self.ReqQryTrade(QryTrade, self.__rsp_QryTrade['RequestID'])
        if ret == 0:
            self.__rsp_QryTrade['event'].clear()
            if self.__rsp_QryTrade['event'].wait(self.TIMEOUT):
                if self.__rsp_QryTrade['ErrorID'] != 0:
                    return self.__rsp_QryTrade['ErrorID']
                return self.__rsp_QryTrade['results']
            else:
                return -4
        return ret
 
    def OrderInsert(self, InstrumentID, Action, Direction, Volume, Price):
        """ 开平仓(限价挂单)申报, 注意,这是异步指令 """
        InputOrder = {}
        InputOrder['BrokerID'] = self.__BrokerID                            # 经纪公司代码
        InputOrder['InvestorID'] = self.__InvestorID                        # 投资者代码
        InputOrder['InstrumentID'] = InstrumentID                           # 合约代码
        InputOrder['OrderRef'] = 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['ContingentCondition'] = PyCTP.THOST_FTDC_CC_Immediately # 触发条件:立即
        InputOrder['ForceCloseReason'] = PyCTP.THOST_FTDC_FCC_NotForceClose # 强平原因:非强平
        InputOrder['RequestID'] = self.__IncRequestID()                     # 请求编号
        return self.ReqOrderInsert(InputOrder, InputOrder['RequestID'])
 
    def OrderAction(self, ActionFlag, FrontID, SessionID, OrderRef, ExchangeID, OrderSysID):
        """ 报单操作请求(撤单), 注意,这是异步指令 """
        assert ActionFlag == PyCTP.THOST_FTDC_AF_Delete
        InputOrderAction = {}
        InputOrderAction['BrokerID'] = self.__BrokerID                          # 经纪公司代码
        InputOrderAction['UserID'] = self.__UserID                              # 用户代码
        InputOrderAction['InvestorID'] = self.__InvestorID                      # 投资者代码
        InputOrderAction['OrderActionRef'] = int(self.__IncOrderRef())          # 操作引用
        InputOrderAction['OrderRef'] = OrderRef                                 # 报单引用
        InputOrderAction['RequestID'] = self.__IncRequestID()                   # 请求编号
        InputOrderAction['FrontID'] = FrontID                                   # 前置编号
        InputOrderAction['SessionID'] = SessionID                               # 会话编号
        InputOrderAction['ExchangeID'] = ExchangeID                             # 交易所代码
        InputOrderAction['OrderSysID'] = OrderSysID                             # 报单编号
        InputOrderAction['ActionFlag'] = ActionFlag                             # 操作标志:撤单
        return self.ReqOrderAction(InputOrderAction, InputOrderAction['RequestID'])
 
    def OnRspError(self, RspInfo,  RequestID, IsLast):
        """ 错误信息 """
        logger.error('%s' % repr(('OnRspError', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gbk')], RequestID, IsLast)))
 
    def OnFrontConnected(self):
        """ 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。 """
        logger.info('CThostFtdcTraderApi::OnFrontConnected()')
        self.__rsp_Connect['event'].set()
        #if self.__isLogined:
        #    sys.stderr.write('CThostFtdcTraderApi::ReqUserLogin...')
        #    result = self.Login(self.__BrokerID, self.__UserID, self.__Password)
 
    def OnFrontDisconnected(self, nReason):
        """ 当客户端与交易后台通信连接断开时,该方法被调用。当发生这个情况后,API会自动重新连接,客户端可不做处理。
        nReason 错误原因
        0x1001 网络读失败
        0x1002 网络写失败
        0x2001 接收心跳超时
        0x2002 发送心跳失败
        0x2003 收到错误报文
        """
        logger.warn('CThostFtdcTraderApi::OnFrontDisconnected(%s)' % hex(nReason))
 
    def OnRspUserLogin(self, RspUserLogin, RspInfo, RequestID, IsLast):
        """ 登录请求响应 """
        if RequestID == self.__rsp_Login['RequestID'] and IsLast:
            self.__rsp_Login['result'] = RspUserLogin
            self.__rsp_Login.update(RspInfo)
            self.__rsp_Login['event'].set()
 
    def OnRspUserLogout(self, RspUserLogout, RspInfo, RequestID, IsLast):
        """ 登出请求响应 """
        if RequestID == self.__rsp_Logout['RequestID'] and IsLast:
            self.__rsp_Logout['result'] = RspUserLogout
            self.__rsp_Logout.update(RspInfo)
            self.__rsp_Logout['event'].set()
 
    def OnRspQryInstrument(self, Instrument, RspInfo, RequestID, IsLast):
        """ 请求查询合约响应 """
        if RequestID == self.__rsp_QryInstrument['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryInstrument.update(RspInfo)
            if Instrument is not None:
                self.__rsp_QryInstrument['results'].append(Instrument)
            if IsLast:
                self.__rsp_QryInstrument['event'].set()
 
    def OnRspQryInstrumentMarginRate(self, InstrumentMarginRate, RspInfo, RequestID, IsLast):
        """ 请求查询合约保证金率响应 """
        if RequestID == self.__rsp_QryInstrumentMarginRate['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryInstrumentMarginRate.update(RspInfo)
            if InstrumentMarginRate is not None:
                self.__rsp_QryInstrumentMarginRate['results'].append(InstrumentMarginRate)
            if IsLast:
                self.__rsp_QryInstrumentMarginRate['event'].set()
 
    def OnRspQryInstrumentCommissionRate(self, InstrumentCommissionRate, RspInfo, RequestID, IsLast):
        """ 请求查询合约手续费率响应 """
        if RequestID == self.__rsp_QryInstrumentCommissionRate['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryInstrumentCommissionRate.update(RspInfo)
            if InstrumentCommissionRate is not None:
                self.__rsp_QryInstrumentCommissionRate['results'].append(InstrumentCommissionRate)
            if IsLast:
                self.__rsp_QryInstrumentCommissionRate['event'].set()
 
    def OnRspQryInvestorPosition(self, InvestorPosition, RspInfo, RequestID, IsLast):
        """ 请求查询投资者持仓响应 """
        if RequestID == self.__rsp_QryInvestorPosition['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryInvestorPosition.update(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.update(RspInfo)
            if InvestorPositionDetail is not None:
                self.__rsp_QryInvestorPositionDetail['results'].append(InvestorPositionDetail)
            if IsLast:
                self.__rsp_QryInvestorPositionDetail['event'].set()
 
    def OnRspQryTradingAccount(self, TradingAccount, RspInfo, RequestID, IsLast):
        """ 请求查询资金账户响应 """
        if RequestID == self.__rsp_QryTradingAccount['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryTradingAccount.update(RspInfo)
            if TradingAccount is not None:
                self.__rsp_QryTradingAccount['results'].append(TradingAccount)
            if IsLast:
                self.__rsp_QryTradingAccount['event'].set()
 
    def OnRspQryInvestor(self, Investor, RspInfo, RequestID, IsLast):
        """ 请求查询投资者响应 """
        if RequestID == self.__rsp_QryInvestor['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryInvestor.update(RspInfo)
            if Investor is not None:
                self.__rsp_QryInvestor['results'].append(Investor)
            if IsLast:
                self.__rsp_QryInvestor['event'].set()
 
    def OnRspQryExchange(self, Exchange, RspInfo, RequestID, IsLast):
        """ 请求查询交易所响应 """
        if RequestID == self.__rsp_QryExchange['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryExchange.update(RspInfo)
            if Exchange is not None:
                self.__rsp_QryExchange['results'].append(Exchange)
            if IsLast:
                self.__rsp_QryExchange['event'].set()
 
    def OnRspQrySettlementInfo(self, SettlementInfo, RspInfo, RequestID, IsLast):
        """ 请求查询投资者结算结果响应 """
        if RequestID == self.__rsp_QrySettlementInfo['RequestID']:
            if RspInfo is not None:
                self.__rsp_QrySettlementInfo.update(RspInfo)
            if SettlementInfo is not None:
                self.__rsp_QrySettlementInfo['results'].append(SettlementInfo)
            if IsLast:
                self.__rsp_QrySettlementInfo['event'].set()
 
    def OnRspQrySettlementInfoConfirm(self, SettlementInfoConfirm, RspInfo, RequestID, IsLast):
        """ 请求查询结算信息确认响应 """
        if RequestID == self.__rsp_QrySettlementInfoConfirm['RequestID']:
            if RspInfo is not None:
                self.__rsp_QrySettlementInfoConfirm.update(RspInfo)
            if SettlementInfoConfirm is not None:
                self.__rsp_QrySettlementInfoConfirm['results'].append(SettlementInfoConfirm)
            if IsLast:
                self.__rsp_QrySettlementInfoConfirm['event'].set()
 
    def OnRspSettlementInfoConfirm(self, SettlementInfoConfirm, RspInfo, RequestID, IsLast):
        """ 请求查询结算信息确认响应 """
        if RequestID == self.__rsp_SettlementInfoConfirm['RequestID']:
            if RspInfo is not None:
                self.__rsp_SettlementInfoConfirm.update(RspInfo)
            if SettlementInfoConfirm is not None:
                self.__rsp_SettlementInfoConfirm['results'].append(SettlementInfoConfirm)
            if IsLast:
                self.__rsp_SettlementInfoConfirm['event'].set()
 
    def OnRspQryDepthMarketData(self, DepthMarketData, RspInfo, RequestID, IsLast):
        """ 请求查询交易所响应 """
        if RequestID == self.__rsp_QryDepthMarketData['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryDepthMarketData.update(RspInfo)
            if DepthMarketData is not None:
                self.__rsp_QryDepthMarketData['results'].append(DepthMarketData)
            if IsLast:
                self.__rsp_QryDepthMarketData['event'].set()
 
    def OnRspQryOrder(self, Order, RspInfo, RequestID, IsLast):
        """ 请求查询报单响应 """
        if RequestID == self.__rsp_QryOrder['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryOrder.update(RspInfo)
            if Order is not None:
                self.__rsp_QryOrder['results'].append(Order)
            if IsLast:
                self.__rsp_QryOrder['event'].set()
 
    def OnRspQryTrade(self, Trade, RspInfo, RequestID, IsLast):
        """ 请求查询成交响应 """
        if RequestID == self.__rsp_QryTrade['RequestID']:
            if RspInfo is not None:
                self.__rsp_QryTrade.update(RspInfo)
            if Trade is not None:
                self.__rsp_QryTrade['results'].append(Trade)
            if IsLast:
                self.__rsp_QryTrade['event'].set()
 
    def OnRspOrderInsert(self, InputOrder, RspInfo, RequestID, IsLast):
        """ 报单录入请求响应 """
        if RspInfo is not None and RspInfo['ErrorID'] != 0:
            logger.info('%s' % repr(('OnRspOrderInsert', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gb2312')], RequestID, IsLast)) )
 
    def OnErrRtnOrderInsert(self, InputOrder, RspInfo):
        """ 报单录入错误回报 """
        if RspInfo is not None and RspInfo['ErrorID'] != 0:
            logger.warn('%s' % repr(('OnErrRtnOrderInsert', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gb2312')] )) )
 
    def OnRspOrderAction(self, InputOrderAction, RspInfo, RequestID, IsLast):
        """ 报单操作请求响应 """
        if RspInfo is not None and RspInfo['ErrorID'] != 0:
            logger.info('%s' % repr(('OnRspOrderAction', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gb2312')], RequestID, IsLast)) )
 
    def OnErrRtnOrderAction(self, OrderAction, RspInfo):
        """ 报单操作错误回报 """
        if RspInfo is not None and RspInfo['ErrorID'] != 0:
            logger.warn('%s' % repr(('OnErrRtnOrderAction', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gb2312')] )) )
 
    def OnRtnOrder(self, Order):
        """ 报单通知 """
        #print('OnRtnOrder:', Order, file=sys.stderr)
        logger.info('%s' % repr(('OnRtnOrder', Order['InstrumentID'], Order['FrontID'], Order['SessionID'], Order['OrderRef'], Order['ExchangeID'], Order['OrderSysID'], Order['OrderStatus'])) )
 
    def OnRtnTrade(self, Trade):
        """ 成交通知 """
        #print('OnRtnTrade:', Trade, file=sys.stderr)
        logger.info('%s' % repr(('OnRtnTrade', Trade['InstrumentID'], Trade['ExchangeID'], Trade['TradeID'], Trade['OffsetFlag'], Trade['Direction'] )) )
 
    def OnRtnInstrumentStatus(self, InstrumentStatus):
        """ 合约交易状态通知 """
        logger.info('%s' % repr(('OnRtnInstrumentStatus', InstrumentStatus['InstrumentID'],  InstrumentStatus['InstrumentStatus'])))
 
    def OnRtnFromBankToFutureByFuture(self, RspTransfer):
        """ 期货发起银行资金转期货通知 """
        logger.info('%s' % repr(('OnRtnFromBankToFutureByFuture', RspTransfer['TradeAmount'])))
 
    def OnRtnFromFutureToBankByFuture(self, RspTransfer):
        """ 期货发起期货资金转银行通知 """
        logger.info('%s' % repr(('OnRtnFromFutureToBankByFuture', RspTransfer['TradeAmount'])))
 
    def OnRtnCFMMCTradingAccountToken(self, CFMMCTradingAccountToken):
        """ 保证金监控中心用户令牌 """
        logger.info('OnRtnCFMMCTradingAccountToken')
 
class PyCTP_Trader(PyCTP_Trader_API):
 
    def OrderActionDelete(self, order):
        """ 撤销报单 """
        return super().OrderAction(PyCTP.THOST_FTDC_AF_Delete, order['FrontID'], order['SessionID'], order['OrderRef'], order['ExchangeID'], order['OrderSysID'])
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 25 13:00:07 2016
 
@author: Zhuolin
"""
 
import logging
import datetime
import time
import PyCTP
 
from PyPTP_TraderInterface import PyPTP_TraderInterface
from PyCTP_Trader import PyCTP_Trader
 
logger = logging.getLogger(__name__)
 
class PyPTP_Trader(PyPTP_TraderInterface, PyCTP_Trader):
 
    # 合约
    __Instruments = {}
    # 合约状态
    __InstrumentStatus = {}
    # 订单撤消限制
    __OrderActionDeleteLimit = {}
    # 订单撤消计数
    __OrderActionDeleteCount = {}
    # 等待中的挂单列表
    __PendingOrder = {}
    # 持仓 {InstrumentID + PosiDirection:{InstrumentID, PosiDirection, Position, OpenPrice, YdPosition, TodayPosition}}
    __InvestorPosition = {}
    # 持仓用于判断无效成交限制时间
    __InvestorPosition_OnRtnTrade_datetime = datetime.datetime.max
    # 报单错误计数
    __OrderInsertErrorCount = {}
    # 报单操作错误计数
    __OrderActionErrorCount = {}
    # 当前账户资金
    __TradingAccount = None
    # 最后一次查询账户时间
    __TradingAccount_LastTime = 0
 
    def setPTP(self, ptp):
        self.__ptp = ptp
 
    def Login(self, BrokerID, UserID, Password):
        result = super(PyCTP_Trader, self).Login(BrokerID, UserID, Password)
        # 登陆后对cache数据做清理和计算,防止重复计算
        # 合约状态
        self.__Instruments = {}
        # 合约状态
        self.__InstrumentStatus = {}
        # 订单撤消限制
        self.__OrderActionDeleteLimit = {}
        # 订单撤消计数
        self.__OrderActionDeleteCount = {}
        # 等待中的挂单列表
        self.__PendingOrder = {}
        # 持仓
        self.__InvestorPosition = {}
        # 持仓用于判断无效成交限制时间
        self.__InvestorPosition_OnRtnTrade_datetime = datetime.datetime.max
        # 报单错误计数
        self.__OrderInsertErrorCount = {}
        # 报单操作错误计数
        self.__OrderActionErrorCount = {}
        # 当前账户资金
        self.__TradingAccount = None
        # 最后一次查询账户时间
        self.__TradingAccount_LastTime = 0
        # 查询昨日持仓,并累计到总持仓数据中
        time.sleep(1.0)
        Instruments = super(PyCTP_Trader, self).QryInstrument()
        for Instrument in Instruments:
            self.__Instruments[Instrument['InstrumentID']] = Instrument            
        time.sleep(1.0)
        InvestorPosition = super(PyCTP_Trader, self).QryInvestorPosition()
        time.sleep(1.0)
        Trades = super(PyCTP_Trader, self).QryTrade()
        for p in InvestorPosition:
            if p['Position'] == 0: continue
            InstrumentID = p['InstrumentID']
            PosiDirection = p['PosiDirection']
            Position = p['Position']
            TodayPosition = p['TodayPosition']
            OpenPrice = p['OpenCost'] / self.__Instruments[InstrumentID]['VolumeMultiple'] / Position
            assert PosiDirection != PyCTP.THOST_FTDC_PD_Net
            key = (InstrumentID, PosiDirection)
            if key not in self.__InvestorPosition:
                self.__InvestorPosition[key] = dict(InstrumentID  = InstrumentID,
                                                    PosiDirection = PosiDirection,
                                                    Position = Position,
                                                    OpenPrice = OpenPrice,
                                                    TodayPosition = TodayPosition,
                                                    YdPosition = Position - TodayPosition)
            else:
                # 这里需要注意计算顺序
                pos = self.__InvestorPosition[key]
                NewOpenPrice = (OpenPrice * Position + pos['Position'] * pos['OpenPrice']) / (pos['Position'] + Position)
                pos['Position'] += Position
                pos['OpenPrice'] = NewOpenPrice
                pos['TodayPosition'] += TodayPosition
                pos['YdPosition'] = pos['Position'] - pos['TodayPosition']
        # 解锁Trade时间限制
        lastdatetime = datetime.datetime.min
        for Trade in Trades:
            datetimestr = '%s %s' % (str(Trade['TradeDate'],'gbk'), str(Trade['TradeTime'],'gbk'))
            currentdatetime = datetime.datetime.strptime(datetimestr, '%Y%m%d %H:%M:%S')
            if currentdatetime > lastdatetime:
                lastdatetime = currentdatetime
        self.__InvestorPosition_OnRtnTrade_datetime = lastdatetime
        return result
 
    def OnErrRtnOrderInsert(self, InputOrder, RspInfo):
        """ 报单录入错误回报 """
        logger.warn('%s' % repr(('OnErrRtnOrderInsert', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gbk')])))
        if InputOrder['InstrumentID'] not in self.__OrderInsertErrorCount:
            self.__OrderInsertErrorCount[InputOrder['InstrumentID']] = 0
        self.__OrderInsertErrorCount[InputOrder['InstrumentID']] += 1
 
    def OnErrRtnOrderAction(self, OrderAction, RspInfo):
        """ 报单操作错误回报 """
        logger.warn('%s' % repr(('OnErrRtnOrderAction', [RspInfo['ErrorID'], str(RspInfo['ErrorMsg'], encoding='gbk')])))
        if OrderAction is not None:
            if OrderAction['InstrumentID'] not in self.__OrderActionErrorCount:
                self.__OrderActionErrorCount[OrderAction['InstrumentID']] = 0
            self.__OrderActionErrorCount[OrderAction['InstrumentID']] += 1
 
    def OnRtnInstrumentStatus(self, InstrumentStatus):
        """ 合约交易状态通知 """
        key = (InstrumentStatus['ExchangeID'], InstrumentStatus['ExchangeInstID'], InstrumentStatus['InstrumentID'])
        if key in self.__InstrumentStatus:
            self.__InstrumentStatus[key].update(InstrumentStatus)
        else:
            self.__InstrumentStatus[key] = InstrumentStatus
 
    def OnRtnFromBankToFutureByFuture(self, RspTransfer):
        """ 期货发起银行资金转期货通知 """
        pass
 
    def OnRtnFromFutureToBankByFuture(self, RspTransfer):
        """ 期货发起期货资金转银行通知 """
        pass
 
    def OnRtnCFMMCTradingAccountToken(self, CFMMCTradingAccountToken):
        """ 保证金监控中心用户令牌 """
        pass
 
    def OnRtnOrder(self, Order):
        """ 报单通知 """
        key = (Order['FrontID'], Order['SessionID'], Order['OrderRef'])
        # 更新报单状态和初始化撤单计数器
        if Order['InstrumentID'] not in self.__PendingOrder:
            self.__PendingOrder[Order['InstrumentID']] = {}
        if Order['InstrumentID'] not in self.__OrderActionDeleteCount:
            self.__OrderActionDeleteCount[Order['InstrumentID']] = 0
        if key in self.__PendingOrder[Order['InstrumentID']]:
            self.__PendingOrder[Order['InstrumentID']][key].update(Order)
        else:
            self.__PendingOrder[Order['InstrumentID']][key] = Order
        # 计算挂单和撤单计数器
        if Order['OrderStatus'] == PyCTP.THOST_FTDC_OST_Canceled or Order['OrderStatus'] == PyCTP.THOST_FTDC_OST_AllTraded:
            del self.__PendingOrder[Order['InstrumentID']][key]
        if Order['OrderStatus'] == PyCTP.THOST_FTDC_OST_Canceled:
            self.__OrderActionDeleteCount[Order['InstrumentID']] += 1
 
    def OnRtnTrade(self, Trade):
        """ 成交通知 """
        # 计算持仓
        datetimestr = '%s %s' % (str(Trade['TradeDate'],'gbk'), str(Trade['TradeTime'],'gbk'))
        lastdatetime = datetime.datetime.strptime(datetimestr, '%Y%m%d %H:%M:%S')
        if self.__InvestorPosition_OnRtnTrade_datetime < lastdatetime:
            InstrumentID = Trade['InstrumentID']
            if Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_Open:
                if Trade['Direction'] == PyCTP.THOST_FTDC_D_Buy:
                    PosiDirection = PyCTP.THOST_FTDC_PD_Long
                if Trade['Direction'] == PyCTP.THOST_FTDC_D_Sell:
                    PosiDirection = PyCTP.THOST_FTDC_PD_Short
            if Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_Close \
            or Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_ForceClose \
            or Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_CloseToday \
            or Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_CloseYesterday:
                if Trade['Direction'] == PyCTP.THOST_FTDC_D_Buy:
                    PosiDirection = PyCTP.THOST_FTDC_PD_Short
                if Trade['Direction'] == PyCTP.THOST_FTDC_D_Sell:
                    PosiDirection = PyCTP.THOST_FTDC_PD_Long
 
            key = (InstrumentID, PosiDirection)
 
            if Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_Open:
                if key not in self.__InvestorPosition:
                    self.__InvestorPosition[key] = dict(InstrumentID = InstrumentID,
                                                        PosiDirection = PosiDirection,
                                                        Position = Trade['Volume'],
                                                        OpenPrice = Trade['Price'],
                                                        YdPosition = 0,
                                                        TodayPosition = Trade['Volume'])
                else:
                    # 这里需要注意计算顺序
                    pos = self.__InvestorPosition[key]
                    NewOpenPrice = (Trade['Price'] * Trade['Volume'] + pos['OpenPrice'] * pos['Position']) / (Trade['Volume'] + pos['Position'])
                    pos['Position'] += Trade['Volume']
                    pos['OpenPrice'] = NewOpenPrice
                    pos['TodayPosition'] += Trade['Volume']
 
            pos = self.__InvestorPosition[key]
 
            if Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_Close:
                pos['Position'] -= Trade['Volume']
                if pos['YdPosition'] >= Trade['Volume']:
                    pos['YdPosition'] -= Trade['Volume']
                else:
                    pos['TodayPosition'] -= (Trade['Volume'] - pos['YdPosition'])
                    pos['YdPosition'] = 0
            if Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_CloseToday:
                pos['Position'] -= Trade['Volume']
                pos['TodayPosition'] -= Trade['Volume']
            if Trade['OffsetFlag'] == PyCTP.THOST_FTDC_OF_CloseYesterday:
                pos['Position'] -= Trade['Volume']
                pos['YdPosition'] -= Trade['Volume']
 
        self.__ptp.OnRtnTrade(Trade)
 
    def OrderInsert(self, InstrumentID, Action, Direction, Volume, Price):
        """ 开平仓(限价挂单)申报, 注意,这是异步指令 """
        # 超过撤单限制后,不能执行开仓动作
        if Action == PyCTP.THOST_FTDC_OF_Open:
            if InstrumentID in self.__OrderActionDeleteLimit:
                if self.__OrderActionDeleteLimit[InstrumentID] <= self.getOrderActionDeleteCount(InstrumentID):
                    logger.warn('%s 达到或超过撤单限制 %d <= %d' % (InstrumentID, self.__OrderActionDeleteLimit[InstrumentID], self.getOrderActionDeleteCount(InstrumentID)) )
                    return 9
            else:
                if b'' in  self.__OrderActionDeleteLimit and InstrumentID in self.__OrderActionDeleteCount:
                    if self.__OrderActionDeleteLimit[b''] <= self.__OrderActionDeleteCount[InstrumentID]:
                        logger.warn('%s 达到或超过撤单限制 %d <= %d' % (InstrumentID, self.__OrderActionDeleteLimit[b''], self.getOrderActionDeleteCount(InstrumentID)) )
                        return 9
        return PyCTP_Trader.OrderInsert(self, InstrumentID, Action, Direction, Volume, Price)
 
    def OrderOpen(self, InstrumentID, Direction, Volume, Price):
        """ 开仓 """
        return self.OrderInsert(InstrumentID, PyCTP.THOST_FTDC_OF_Open, Direction, Volume, Price)
 
    def OrderClose(self, InstrumentID, Direction, Volume, Price):
        """ 平仓 """
        if InstrumentID in self.__Instruments and self.__Instruments[InstrumentID]['ExchangeID'] == b'SHFE':
            # 上期区分平昨平今
            if Direction == PyCTP.THOST_FTDC_D_Buy:
                PosiDirection = PyCTP.THOST_FTDC_PD_Short
            if Direction == PyCTP.THOST_FTDC_D_Sell:
                PosiDirection = PyCTP.THOST_FTDC_PD_Long
            pos = self.__InvestorPosition[(InstrumentID,PosiDirection)]
            # 判断总可平仓量
            if Volume > pos['Position']:
                # 发出平仓指令,避免高速开仓造成数据不同步而无法平仓
                return self.OrderInsert(InstrumentID, PyCTP.THOST_FTDC_OF_CloseToday, Direction, Volume, Price)
            # 判断昨仓可平量
            if Volume <= pos['YdPosition']:
                return self.OrderInsert(InstrumentID, PyCTP.THOST_FTDC_OF_CloseYesterday, Direction, Volume, Price)
            else:
                if pos['YdPosition'] > 0:
                    self.OrderInsert(InstrumentID, PyCTP.THOST_FTDC_OF_CloseYesterday, Direction, pos['YdPosition'], Price)
                # 判断今仓可平量
                return self.OrderInsert(InstrumentID, PyCTP.THOST_FTDC_OF_CloseToday, Direction, Volume - pos['YdPosition'], Price)
        else:
            return self.OrderInsert(InstrumentID, PyCTP.THOST_FTDC_OF_Close, Direction, Volume, Price)
 
    def OrderActionDelete(self, Order):
        """ 报单操作请求(撤单), 注意,这是异步指令 """
        return PyCTP_Trader.OrderActionDelete(self, Order)
 
    def getOrderInsertErrorCount(self, InstrumentID=b''):
        """ 报单录入错误回报计数 """
        if InstrumentID in self.__OrderInsertErrorCount:
            return self.__OrderInsertErrorCount[InstrumentID]
        if InstrumentID != b'':
            return 0
        return self.__OrderInsertErrorCount
 
    def getOrderActionErrorCount(self, InstrumentID=b''):
        """ 报单操作错误回报计数 """
        if InstrumentID in self.__OrderActionErrorCount:
            return self.__OrderActionErrorCount[InstrumentID]
        if InstrumentID != b'':
            return 0
        return self.__OrderActionErrorCount
 
    def getOrderActionDeleteCount(self, InstrumentID=b''):
        """ 报单撤消操作计数 """
        if InstrumentID in self.__OrderActionDeleteCount:
             return self.__OrderActionDeleteCount[InstrumentID]
        if InstrumentID != b'':
            return 0
        return self.__OrderActionDeleteCount
 
    def getPendingOrder(self):
        """ 获取挂单 """
        result = []
        for v in self.__PendingOrder.values():
            result.extend(list(v.values()))
        return result
 
    def getInvestorPosition(self):
        """ 获取持仓 """
        return list(self.__InvestorPosition.values())
 
    def getTradingAccount(self):
        """ 获取账户资金 """
        nowtime = time.time()
        if nowtime - self.__TradingAccount_LastTime > 1.0:
            self.__TradingAccount = super(PyCTP_Trader, self).QryTradingAccount()
            self.__TradingAccount_LastTime = nowtime
        return self.__TradingAccount
 
    def setOrderActionDeleteLimit(self, Limit=400, InstrumentID=b''):
        """ 设置撤销限制 """
        self.__OrderActionDeleteLimit[InstrumentID] = Limit
        return self.__OrderActionDeleteLimit
 
    def getInstruments(self, InstrumentID=b''):
        """ 获取合约 """
        if InstrumentID in self.__Instruments:
            return self.__Instruments[InstrumentID]
        return self.__Instruments
 
    def getInstrumentStatus(self):
        """ 获取合约状态 """
        return self.__InstrumentStatus

86 Responses to“Python-CTP PyCTP 接口”

  1. lucas.yuan
    2016年6月27日 at pm9:23 #

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

    • zhuolin
      2016年7月3日 at am12:02 #

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

  2. valleysong
    2016年7月27日 at am8:59 #

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

    • zhuolin
      2016年8月1日 at pm3:04 #

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

  3. new13
    2016年12月5日 at pm12:23 #

    您好,我运行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

    • zhuolin
      2016年12月7日 at am9:08 #

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

  4. Anonymous
    2017年1月16日 at pm4:09 #

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

  5. dongfang
    2017年1月18日 at pm4:29 #

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

    • zhuolin
      2017年1月22日 at am10:23 #

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

  6. Anonymous
    2017年2月19日 at pm2: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

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

    • zhuolin
      2017年2月20日 at pm9:04 #

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

  7. Anonymous
    2017年2月21日 at am10:28 #

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

    • zhuolin
      2017年3月7日 at pm9:37 #

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

  8. Anonymous
    2017年2月21日 at am10:34 #

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

  9. zfw
    2017年5月17日 at am7:37 #

    满屏全是
    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’

  10. zfw
    2017年5月20日 at pm7:02 #

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

  11. ltw
    2017年12月7日 at am10:32 #

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

    • zhuolin
      2017年12月20日 at pm7:54 #

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

  12. lin
    2017年12月30日 at pm9:00 #

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

    • zhuolin
      2018年1月11日 at pm2:27 #

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

  13. warrock
    2018年1月24日 at pm10:18 #

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

    • zhuolin
      2018年1月25日 at am9:41 #

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

  14. Anonymous
    2018年3月30日 at pm3:45 #

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

    • zhuolin
      2018年3月30日 at pm3:55 #

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

  15. lg
    2018年3月30日 at pm3:48 #

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

    • zhuolin
      2018年3月30日 at pm3:55 #

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

  16. lg
    2018年3月30日 at pm3:50 #

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

    • zhuolin
      2018年3月30日 at pm3:53 #

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

  17. rookie
    2018年4月8日 at pm8:13 #

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

    • zhuolin
      2018年4月9日 at am11:31 #

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

  18. j88r
    2018年5月18日 at am10:31 #

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

    • zhuolin
      2018年5月20日 at pm4:44 #

      github中已共享出此脚本

  19. jian
    2018年5月22日 at pm4:42 #

    请问博主 在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: 找不到指定的模块。

    • zhuolin
      2018年5月22日 at pm8:53 #

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

  20. shi
    2018年6月8日 at am12:10 #

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

    • zhuolin
      2018年6月9日 at pm7:50 #

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

  21. geekmo
    2018年6月8日 at pm4:08 #

    你好!刚研究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}
    这些结果能不能找个变量接收。如果能,怎么接收?

    • zhuolin
      2018年6月9日 at pm7:48 #

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

  22. imlooker
    2018年6月10日 at pm11:43 #

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

    • zhuolin
      2018年6月12日 at am9:16 #

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

  23. wlock
    2018年7月18日 at pm10:03 #

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

    • zhuolin
      2018年7月19日 at am8:18 #

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

  24. wlock
    2018年7月19日 at am9:38 #

    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;

    }

  25. wlock
    2018年7月19日 at am10:35 #

    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的也不行

    • zhuolin
      2018年7月20日 at pm5:06 #

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

      • wlock
        2018年7月20日 at pm9:52 #

        好的,多谢

  26. xiong
    2018年8月9日 at pm12:18 #

    您好,请问运行test_PyCTP.py产生
    trader = File “test_PyCTP.py”, line 598, in main
    trader = PyCTP_Trader.CreateFtdcTraderApi(b’tmp_t‘)
    ValueError: parameter invalid.
    报错是什么原因呢?

  27. xiong
    2018年8月9日 at pm12:18 #

    您好,请问运行test_PyCTP.py产生
    trader = File “test_PyCTP.py”, line 598, in main
    trader = PyCTP_Trader.CreateFtdcTraderApi(b’tmp_t‘)
    ValueError: parameter invalid.
    报错是什么原因呢?

    • zhuolin
      2018年8月10日 at pm3:00 #

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

  28. xiong
    2018年8月9日 at pm1:59 #

    你好,我使用python2.7 32位的环境编译成功但运行test_PyCTP报错:
    File “test_PyCTP.py”, line 598, in main
    trader = PyCTP_Trader.CreateFtdcTraderApi(b’tmp_t‘)
    ValueError: parameter invalid.
    请问这是什么原因呢?

  29. zhiyi
    2018年10月10日 at am10:46 #

    满屏都是下面的错误
    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’

    • zhuolin
      2018年10月11日 at pm1:50 #

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

  30. Tony
    2018年11月3日 at pm5:20 #

    您好, 很感谢您的开源代码, 看到这个函数不太明白, 好像是不用这个去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;
    };

    • zhuolin
      2018年11月6日 at pm1:54 #

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

  31. tuxingguo
    2018年11月12日 at pm1:32 #

    您好,运行脚本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”},其中大部分都是空的,请问是登陆成功了还是失败了??

  32. tuxingguo
    2018年11月12日 at pm2:58 #

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

  33. 涂兴国
    2018年11月13日 at am8:19 #

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

  34. 涂兴国
    2018年11月13日 at am8:20 #

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

  35. buyinfosun
    2018年11月16日 at am11:45 #

    请问一下,使用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 应用程序。

  36. tuxingguo
    2018年11月21日 at pm7:30 #

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

  37. Wahaha
    2018年11月21日 at pm10:49 #

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

  38. lihai
    2018年11月27日 at am10:05 #

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

    • zhuolin
      2018年12月11日 at pm1:49 #

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

  39. xiaolan
    2018年12月13日 at pm3:13 #

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

  40. xiaolan
    2018年12月13日 at pm3:15 #

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

    • zhuolin
      2018年12月19日 at pm11:05 #

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

  41. DDl
    2018年12月26日 at pm5:19 #

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

    • zhuolin
      2018年12月26日 at pm8:36 #

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

      • DDI
        2018年12月27日 at am9:34 #

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

        • zhuolin
          2018年12月27日 at am9:53 #

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

  42. DaoYuan
    2019年5月10日 at pm3:01 #

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

  43. daoyuan
    2019年5月10日 at pm3:04 #

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

  44. wowo
    2019年5月22日 at am7:26 #

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

  45. Andy
    2019年5月23日 at pm4:03 #

    请问博主,现在交易所更新了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++的代码的如何生成的?

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

    • zhuolin
      2019年6月2日 at pm9:36 #

      已更新到6.3.15穿透版

  46. chen2485
    2019年9月19日 at pm1:10 #

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

  47. Sifan Chen
    2019年9月19日 at pm1:14 #

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

  48. hushaojun
    2019年10月19日 at pm5:59 #

    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复制到相同目录去了

  49. 木古古
    2019年11月28日 at pm10:21 #

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

    • zhuolin
      2020年6月18日 at am9:39 #

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

  50. mugugu
    2019年11月28日 at pm11:27 #

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

  51. Andy Sun
    2019年12月8日 at am12:35 #

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

  52. chaowen li
    2020年1月25日 at pm9:13 #

    如何理解 看穿监管?

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

  53. chaowen li
    2020年1月26日 at am10:21 #

    代码方面的修改:

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

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

  54. peterlink
    2021年2月9日 at pm4:35 #

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

Leave a Reply

Your email address will not be published.

Time limit is exhausted. Please reload the CAPTCHA.

Proudly powered by WordPress   Premium Style Theme by www.gopiplus.com