forked from xmduhan/CTPConverter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
draft.py
405 lines (341 loc) · 14.8 KB
/
draft.py
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
# -*- coding: utf-8 -*-
#%% 代码实线路径
#1、先将相关的函数列在列在CTraderHandler.cpp(ok)
#2、CApiWrapper类
# (0) 类的接口设计(头文件定义)
CApiWrapper 手工编写部分(基本ok)
-- 登录过程(ok)
-- requestId(ok)
CApiWrapper 自动编写部分(ok)
防止头文件重定义问题(Configure.h马上就有这个问题)(ok)
在CTraderHandler类中实现:
重载virtual void OnFrontConnected() (ok)
#2、在converter中增加登录和退出的代码。(已封装到CApiWrapper中)
#3、生成zmq套接字连接代码。
连接zmq环境成功(ok)
接受初步消息 (ok)
**按消息格式接受多段消息(ok)
**需要有一个文件实现消息格式的封装。(ok,基本完成,其中路由)
** 将socket改为非指针定义(ok,变相实现)
**禁止pushback管道使用tcp配置,而应使用inpc或者ipc(ok,解决了bind绑定和connect使用同一个地址问题,就不需要这么做了)
**OnFrontConnected中可以返回消息 (ok)
**OnRspUserLogin()中可以返回消息 (ok)
#4、json的处理(含打包和拆包)
(1)jsoncpp的安装 (ok)
(2)OnRsp,(ok)
(5)初步完成CApiWrapper代码编写 (ok)
1、完成调用到响应的贯通(ok)
1、处理返回信息的编码问题(ok)
2、主程序代码结构编写(ok)
1、c++如何使用hashmap问题(ok)
1、makefile改进提高速度(ok)
1、测试框架(ok)
1、完成api调用的测试的完整用例(ok)
(1)接受消息的时候需要保存测试用例
2、atom文件过滤的问题(ok)
1、测试用例的完善(ok)
(1)环境变量的重新命名,并完善到测试用例中(ok)
(2)请求和返回的变量名冲突问题(ok)
1、响应消息增加isLast字段(ok)
1、python的消息也要单独封装起来,这样修改消息格式的时候方便。(ok)
1、初步实线ctp同步库的封装代码生成(ok)
1、请求结构和返回结构数据的生成(ok)
1、将class转化为dict,将dict转化位class(ok)
1、CTPChannel类代码生成(可以先通过写死环境变量初始化连接)(ok)
1、解决errorCode和errorId不一致问题(ok)
1、在CTPChannel统一出错代码(ok)
1、让trader命令行参数而不是使用环境变量(ok)
1、实线MD API(ok)
(2)设计一个CTPChannel的类,一个channel对应一个converter(ok)
|--如何管理进程converter(何时创建何时销毁)
|--如果保证不同的converter进程使用的通讯管道都不重复?
(3)接口的python wrapper如何找到对应的channel (ok)
|--是否把api作为channel的成员函数(采用)
|--是否把让api的第一个参数是channel
|--在api函数中调用一个getChannel()函数(获取空闲的channel)
1、转换器要实现流量控制功能,并缓存过于频繁的查请求,并按能按指定频率进行
(1)RequestID要移到tarder主函数中来处理(ok)
(2)增加定义查询请求队列(ok)
(3)查询请求处理机制改为先入队再处理的方式。(ok)
(4)增加流量控制罗辑(ok)
(5)增加参数读取功能设置(ok)
1. 去掉md配置中的loadInstrumentIDList(ok)
1. 在md配置文件中增加request和response配置(ok)
1. 完成md设计图(ok)
1.增加MdRequestMessage和MdResponseMessage(注意py中也要做相应添加)(ok)
1.分配内存(ok)
1.OnRspSubMarketData需要做一个pullback动作(ok)
1.将json数据转化为对应的c数组格式(ok)
1.建立md的测试用例(ok)
1.具体化SubscribeDepthMarketData的内容并且抽象到模板,以实现UnSubscribeDepthMarketData(ok)
1. 增加一个REQ-REP接口,服务器接受请求后立马调用订阅或取消订阅,调用结果返回客户端(ok)
------------------------------------------------------------------------------
1.将测试用例分解为trader和md(考虑一下是否需要这么做)
问题:md 不可能通过response接口返回消息,因为订阅时没有requestID参数,无法做requestID返回路由对照
1. md程序中增加request和response通讯接口
1、需要处理服务器重启后不能执行之前缓存中的数据
1、编码问题应该是可以设置的,增加客户端编码和服务器编码
1、编写生成python测试用例拷贝原型的脚本(是否做成单独的库文件)
1、在响应函数中如果收到last标识需要删路由表,防止路由表无线膨胀
1、测试用例可以自行启动CTP接口(这样可以方便很多)
1、测试行情查询请求的性能是否符合要求。
1、需要设计出错信息
1、如何添加测试框架问题
1、确认c++ map是 linked 还是 hashmap
2、调用信息时放入一个空的Data对象会报错的问题()
(3)OnRtn,
(4)OnErrRtn
(6)OnRsp字符串编码转化的问题
#5、监听代码框架编写(使用CTRL+C退出程序)
暂缓:
1、c++如何写日志问题(暂缓)
1、简单阅读一下测试用例编写的章节(暂缓,似乎不能得到什么)
(1)Message.h和python中的message.py一个是服务端,一个是客户端要如何区分清楚(暂缓)
1、测试查询账号能否使用trader api(似乎不行,暂缓)
(2)如何给成员加注释(暂缓)
1、创建订单的结构体中居然有一个requestID,这需要从参数拷贝过去,需要一段特殊代码(暂缓)
# 其他备注:
# (1) ctp api 的初始化操作(含登录,涉及和spi类的交互)
# (2) ctpapi.cpp改为CApiWrapper 封装所有Req开头的函数,参数:(1)原始json数据包
# (3) req函数返回RequestId这个id由这个wrapper维护
# (4) 如果req函数返回-1表示出错,可以通过getLastError机制获取信息。
#%% 修改到项目目录
import os
os.chdir('/home/duhan/github/CTPConverter')
import cppheader
from pandas import DataFrame
import pandas as pd
from pandas.io.excel import ExcelWriter
#%% 获取ctp所有的方法名称
# 获取CThostFtdcTraderSpi的所有方法(以OnRsp,OnRtn,OnErrRtn开头)
# 获取CThostFtdcTraderApi的所有方法(以Req开头)
TraderApi_h = cppheader.getCppHeader('include/ThostFtdcTraderApi.h',['TRADER_API_EXPORT'])
MdApi_h = cppheader.getCppHeader('include/ThostFtdcMdApi.h',['MD_API_EXPORT'])
ApiStruct_h = cppheader.getCppHeader('include/ThostFtdcUserApiStruct.h')
typedefDict = cppheader.getTypedefDict('include/ThostFtdcUserApiDataType.h')
enumDict = cppheader.getEnumDict('include/ThostFtdcUserApiDataType.h')
SpiClass = cppheader.getClass(TraderApi_h,'CThostFtdcTraderSpi')
ApiClass = cppheader.getClass(TraderApi_h,'CThostFtdcTraderApi')
MdSpiClass = cppheader.getClass(MdApi_h,'CThostFtdcMdSpi')
MdApiClass = cppheader.getClass(MdApi_h,'CThostFtdcMdApi')
#%% 读取api和spi类中的所有函数
apiMethodInfoDict = cppheader.getClassMethods(ApiClass,'public')
spiMethodInfoDict = cppheader.getClassMethods(SpiClass,'public')
mdApiMethodInfoDict = cppheader.getClassMethods(MdApiClass,'public')
mdSpiMethodInfoDict = cppheader.getClassMethods(MdSpiClass,'public')
#合并api和spi函数,形成完整函数集
#这里之所以可能讲所有函数合并,在于api和spi方法没有重复,请看下面语句:
#set(spiMethodInfoDict.keys()).intersection(apiMethodInfoDict.keys())
methodInfoDict = {}
methodInfoDict.update(apiMethodInfoDict)
methodInfoDict.update(spiMethodInfoDict)
mdMethodInfoDict = {}
mdMethodInfoDict.update(mdApiMethodInfoDict)
mdMethodInfoDict.update(mdSpiMethodInfoDict)
#%% 简化函数信息保留我们需要信息,并对未解决类型进行处理
methodDict = {}
for mi in methodInfoDict.itervalues() :
method = {}
method['name'] = mi['name']
method['returns'] = mi['returns']
method['remark'] = mi['doxygen'].decode('utf8')
parameters=[]
for pi in mi['parameters'] :
parameter = {}
parameter['name'] = pi['name']
parameter['type'] = pi['type']
parameter['raw_type'] = pi['raw_type']
parameter['pointer'] = pi['pointer']
parameter['unresolved'] = pi['unresolved']
parameters.append(parameter)
method['parameters'] = parameters
methodDict[method['name']] = method
mdMethodDict = {}
for mi in mdMethodInfoDict.itervalues() :
method = {}
method['name'] = mi['name']
method['returns'] = mi['returns']
method['remark'] = mi['doxygen'].decode('utf8')
parameters=[]
for pi in mi['parameters'] :
parameter = {}
parameter['name'] = pi['name']
parameter['type'] = pi['type']
parameter['raw_type'] = pi['raw_type']
parameter['pointer'] = pi['pointer']
parameter['unresolved'] = pi['unresolved']
parameters.append(parameter)
method['parameters'] = parameters
mdMethodDict[method['name']] = method
#%% 将函数分成:Req,OnRsp,OnRtn,OnErrRtn,OnRspError四个大类
reqMethodDict = {k:v for k,v in methodDict.iteritems() if k.startswith('Req')}
onRspMethodDict = {k:v for k,v in methodDict.iteritems() if k.startswith('OnRsp') and k != 'OnRspError'}
onRtnMethodDict = {k:v for k,v in methodDict.iteritems() if k.startswith('OnRtn')}
onErrRtnMethodDict = {k:v for k,v in methodDict.iteritems() if k.startswith('OnErrRtn')}
onRspErrorMethodDict = {k:v for k,v in methodDict.iteritems() if k == 'OnRspError'}
mdReqMethodDict = {k:v for k,v in mdMethodDict.iteritems() if k.startswith('Req')}
mdOnRspMethodDict = {k:v for k,v in mdMethodDict.iteritems() if k.startswith('OnRsp') and k != 'OnRspError'}
#%% 查看所有函数的数量
print 'len(reqMethodDict.keys())=',len(reqMethodDict.keys())
print 'len(onRspMethodDict.keys())=',len(onRspMethodDict.keys())
print 'len(onRtnMethodDict.keys())=',len(onRtnMethodDict.keys())
print 'len(onErrRtnMethodDict.keys())=',len(onErrRtnMethodDict.keys())
# OnRsp之所以比Req多一个,因为有一个OnRspError
# 这个函数的处理方法是否要跟其他OnRsp函数一样,需要进一步确认
#%% 确认OnRsp比Req多一个的原因
d1 = {
'request':reqMethodDict.keys(),
'apiname':[ name[3:] for name in reqMethodDict.keys()]
}
df1 = DataFrame(d1)
d2 = {
'response':onRspMethodDict.keys(),
'apiname':[ name[5:] for name in onRspMethodDict.keys()]
}
df2 = DataFrame(d2)
df = df1.merge(df2,on='apiname',how='outer')
writer = ExcelWriter('/tmp/output.xls')
df.to_excel(writer)
writer.save()
# 导出excel查看
#%%
# 根据一个函数名获取函数的定义
method = methodDict['OnRspQryOrder']
print method['doxygen']
print method['name'],'(',
parameters = cppheader.getMethodParameters(method)
for p in parameters:
print p['type'],p['name']+',',
print ')'
#%%类型名称查找结构体定义
structDict = {}
structInfoDict = cppheader.getClasses(ApiStruct_h)
for si in structInfoDict.viewvalues():
struct = {}
struct['name'] = si['name']
struct['remark'] = si['doxygen'].decode('utf8')
fields = []
for pi in si['properties']['public']:
field = {}
field['name'] = pi['name']
field['type'] = pi['type']
field['raw_type'] = pi['raw_type']
field['pointer'] = pi['pointer']
field['unresolved'] = pi['unresolved']
field['remark'] = pi['doxygen'].decode('utf8')
fields.append(field)
struct['fields'] = fields
structDict[si['name']] = struct
#%%
structDict['CThostFtdcRspInfoField']
ErrorID
ErrorMsg
#%% 打印所有函数的声明
methodNameList = methodDict.keys()
methodNameList.sort()
for methodName in methodNameList:
print methodDict[methodName]['remark']
print methodName,'(',
parameters = methodDict[methodName]['parameters']
print ','.join([p['name'] for p in parameters]),
print ')'
if len(parameters) != 0:
for p in parameters:
if p['raw_type'] in structDict:
print '%s(%s)' % (p['name'],p['raw_type'])
fields = structDict[p['raw_type']]['fields']
for f in fields:
print '|-%s' % f['name'],
if 'requestid' in f['name'].lower():
print '(*****)',
print
print '---------------------------------------------------------------'
#%%
methodDict['OnRspQryInvestorPosition']['parameters'][0]['raw_type']
[i['name'] for i in structDict['CThostFtdcInvestorPositionField']['fields']]
Out[49]:
#%% 寻找ctp定义变量中最长的类型
maxLen = 0
for i in typedefDict.itervalues():
l = int(i.get('len',0) or '0')
if l > maxLen :
maxLen = l
print 'maxLen=',maxLen
#%% 遍历模板目录中的所有模板文件
import os
templates = [ i for i in os.listdir('template') if i.endswith('cpp.tpl') ]
for template in templates:
print '.'.join(template.split('.')[:-1])
#%% API 封装的基本结构
### 将一个json参数拆包,填如API的参数结构中,调用API。
#%% SPI 封装的基本结构
### 将返回参数打包成一个json接口然后发送到队列中
for name,method in onRspMethodDict.iteritems():
print name
#%% python执行ctp接口调用例子
import os
os.chdir('/home/duhan/github/CTPConverter/test')
from channel import CTPChannel
from CTPStruct import *
ch = CTPChannel()
data = CThostFtdcQryTradingAccountField()
errorID,errorMsg,responeDataList = ch.QryTradingAccount(data)
print errorID,errorMsg,responeDataList
print len(responeDataList)
print responeDataList[0].toDict()
#%% 使用命令行启动trader
import os
frontAddress = os.environ.get('CTP_FRONT_ADDRESS')
assert frontAddress
brokerID = os.environ.get('CTP_BROKER_ID')
assert brokerID
userID = os.environ.get('CTP_USER_ID')
assert userID
password = os.environ.get('CTP_PASSWORD')
assert password
requestPipe = os.environ.get('CTP_REQUEST_PIPE')
assert requestPipe
pushbackPipe = os.environ.get('CTP_PUSHBACK_PIPE')
assert pushbackPipe
publishPipe = os.environ.get('CTP_PUBLISH_PIPE')
assert publishPipe
instrumentIDConfigFile = os.environ.get('CTP_INSTRUMENT_ID_CONFIG_FILE')
assert instrumentIDConfigFile
command = ' '.join(['bin/trader',
'--FrontAddress %s' % frontAddress,
'--BrokerID %s' % brokerID,
'--UserID %s' % userID,
'--Password %s' % password,
'--RequestPipe %s' % requestPipe,
'--PushbackPipe %s' % pushbackPipe,
'--PublishPipe %s' % publishPipe,
'--PnstrumentIDConfigFile %s' % instrumentIDConfigFile,
])
command
#%%
import json
content = json.dumps(['IF1506','IF1507'])
with open('/tmp/config.json', 'w') as f:
f.write(content.encode('utf-8'))
#%% 为CThostFtdcDepthMarketDataField生成django model
for field in structDict['CThostFtdcDepthMarketDataField']['fields'] :
fieldName = field['name']
fieldRemark = field['remark'][3:]
fieldType = typedefDict[field['raw_type']]['type']
fieldLen = typedefDict[field['raw_type']]['len']
#print fieldName,fieldType,fieldLen,type(fieldLen)
if fieldType == 'char':
print '''%s = models.CharField(u'%s', max_length=%s, default='') ''' \
% (fieldName,fieldRemark,fieldLen)
if fieldType == 'int':
print '''%s = models.IntegerField(u'%s', default=0) ''' \
% (fieldName,fieldRemark)
if fieldType == 'double':
print '''%s = models.FloatField(u'%s', default=0) ''' \
% (fieldName,fieldRemark)
#%% 生成admin的fields列表
fields = []
for field in structDict['CThostFtdcDepthMarketDataField']['fields'] :
fieldName = field['name']
fields.append(fieldName)
fields