Python-CTP PyCTP 接口

已经编译好文件,内附调用示例.具体参数可参考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
调用示范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/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())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
# -*- 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'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# -*- 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

76 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穿透版

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
渝公网安渝公网安备 50010702500270号 渝ICP备09056628号-7