分享

基于Python的国内期货证券解决方案

 黑马_御风 2017-09-08

Python最近几年已经越来越火,在很多领域已经可以完全替代MATLAB,用于金融计算与交易也不输MATLAB。

1. 云服务器上安装近十GB的MATLAB过于麻烦,而Python的Anaconda发布版只有几百MB

2.Python开源免费,而正版MATLAB则价格不菲。

3. 国内与Quantopian类似的在线策略编写网站开始流行,Python是它们的策略语言,建立了良性的互动交流社区,有大量的案例可参考。

4. Python有大量用于科学计算、统计分析、机器学习的开源工具库,适合用于做量化交易。



因此,已经有越来越多的用户向Python迁移。我们也紧跟趋势,在推出MATLAB版XAPI统一行情交易接口后也推出了Python版。在总结了社区中常遇到的问题后,在本文将进行专门的解答。


1.1  常见金融工具与接口

目前国内可以合法交易的金融工具主要是股票、期货、黄金T+D等品种,一些电子盘、比特币等暂时不在我们讨论范围内,有兴趣的朋友可以网上查阅相关资料。一定要选择国家承认的正规合法平台,以免上当受骗。表1. 常见金融工具与对应的接口


1.1.1 综合交易平台CTP

综合交易平台CTP(ComprehensiveTransaction Platform)是由上海期货信息技术有限公司(上海期货交易所旗下子公司)开发的经纪业务管理系统。

目前除了在期货市场占有率第一,它还有证券版支持股票,国际版支持外盘。

公司网址:http://www.sfit.com.cn/


1.1.2 金仕达Kingstar

期货市场占有率第二的老牌柜台系统,由于开放性不够,被CTP从第一的位置挤下,目前因历史遗留还有部分期货公司在使用。

金仕达所接的市场很多,期货、个股期权、证券,以及贵金属现货。

金仕达证券接口还是老式的不支持主动推送的接口,所以在ETF期权上市时,在期货公司推扩证券柜台也不顺利。而在贵金属现货接口上又推广不力,现已被飞鼠占去先机。

公司网址:http:// www.sungard.cn/


1.1.3 飞马Femas、飞创XSpeed、易胜Esunny

分别由中国金融期货信息技术有限公司(是中国金融期货交易所期下子公司)、大连飞创信息技术有限公司(大连商品交易所旗下子公司)、易盛信息技术有限公司(郑州商品交易所期下子公司)。


在2015年股灾之前,程序化交易监管较弱,高频交易火爆,如果使用每家专用的API,期货公司可以提供离交易所机柜更近的主机托管业务。但现在高频交易策略无法施展的情况下,用流行的CTP更省事。

他们的公司网址分别为:

Femas:http://www./

XSpeed:http://www./

Esunny:http://www./


1.1.4 飞鼠Sgit

上海飞鼠软件科技有限公司,一家主要提供跨市场交易解决方案的公司,提供了飞鼠API,可以接入期货、外盘、黄金现货。目前需要做黄金T+D程序化交易的用户一般会选择此接口。公司网址:http://www./


1.1.5 恒生UFX

恒生电子同时提供了证券、期货经纪业务解决方案的提供商,在机构用户中占有率极高。机构通过UFX接入到恒生O32系统中进行事前风控。

因为金融产品太多、业务复杂,导致字段也多。UFX更多的是类似于通讯协议数据包打包器。用户在开发时需要对照数据字典一个个自己组织请求包,使用起来比较麻烦。

公司网址:http://www.hundsun.com/


1.2 Python封装


1.2.1 封装原理

基本上,所有官方提供的接口都是C++接口,少量老的证券接口还提供C接口。C++接口导出了C++类,而其它语言中的类都是自己语言中经过特殊设计的,无法直接利用C++类。因此必须将C++接口转换成简单的C接口,将原接口函数中隐式的this指针改成显式的void*指针传入,这样才能在其它语言中进行调用。

还需要解决结构体的传入、传出与回调函数的实现,然后再进一步简单的用各种语言封装调用时的请求与响应,使用起来就方便了。


1.2.2 vn.py与XAPI

目前市面上主要的开源Python封装有vn.py与XAPI两款,其它的Python开源项目,已经不再维护或用户量极少,所以这就不再介绍了,有兴趣的朋友请网上搜索。



由于vn.py在官网已经有齐全的文档,本人对它也没有更深入的研究,所以在这只介绍自己开源的XAPI。


1.2.3 XAPI的迭代历史

一开始是为了对接OpenQuant3这款软件的个人项目,参考了海风开源的CTP C#版接口,不同的地方是将一些复杂的逻辑由C#层移动到C层,简化上层的开发。

为了推广OpenQuant3,所以决定将代码整理转成开源项目。由于Femas和XSpeed的推出,当时参考了CTP的封装方法,封装了其它几个接口和OpenQuant3的插件。但这种老式的封装方法工作量大,C#层和应用层有多少种接口就需要封装多少套,难于维护。

2015年同样是海风推出的新版封装里有动态延迟加载dll的示例,学习研究后,决定对接口进行重新设计,统一不同API的结构体为同一套,命名为XAPI。

后来根据网友的建议,对项目的命名空间、目录结构重新调整,升级为XAPI2。

专门设计的C接口,所以各种语言版本的封装陆续推出,.NET、Java、MATLAB、COM、Python。


1.2.4 XAPI设计思想:

1. 能接入主要的接口。根据流行程序来考虑是否优先接入;

2. 满足基本的交易功能。抛弃了不常用的功能,如银期转账功能;

3. 能支持不同的语言。一定要导出C接口;

4. 简化上层代码的开发。将流控,请求ID,请求发送队列等各项基本功能都封装在C层。


XAPI的核心就是队列。XAPI的队列数据格式与只有一个XRequest导出函数的dll风格完全一样。一个字节大小的数据包类型,两个指针,两个双精度数字,三个内存区指针和三个内存区大小数值,在目前的行情交易接口开发中基本够用。

上层的函数调用被封装成数据包,添加到请求队列中,另一线程从请求队列中取内容,然后调用API的各种函数,用这种方法来解决流控,请求ID递增等问题。

收到的响应也复制打包好后放入响应队列,另一线程从响应队列中取内容,调用注册好的回调函数通知到上层,解决应用层策略耗时过久可能导致底层崩溃的问题。


1.3 安装


1.3.1 Python环境

由于大部分接口只推出了32位版,而在Windows中同一进程中32位与64位不能相互调用,所以Python也只能选择32位的进行安装。目前CTP接口有64位版,有兴趣的朋友可以自行编译XAPI项目为64位版。

Python2.7不再推荐,推荐使用3.6及以上版本,做量化交易要用到的库基本都已经支持Python3。


1.3.2 XAPI安装

访问https://github.com/QuantBox/XAPI2/,点击Clone or download按钮,选择Download ZIP进行下载。

下载比同步repo的方式要快,对于没有能力贡献代码的朋友来说,zip方式最快速直接。

解压zip文件,目录众多,对于Python用户来说,如果不自行编译,只需要关注languages\Python这个文件夹即可,如果涉及需要编译dll的工作才需要其它文件夹下的项目。由于编译工作对一些用户来说有难度,还得下载安装VisualStudio 2015,用户可以加入Readme.md中提到的QQ群,到群中下载已经编译打包好的dll。

“缺少依赖库”是很多用户遇到最基础最常见的问题,本项目为了接入不同的API,每套API都要做一套C封装,这些封装都需要C++运行时库,为了减少发布包的大小,运行时库使用的动态编译,这样C++运行时库就能共享一套,它们默认放在了C:\Windows\System32或C:\Windows\SysWOW64\。这就导致在一台电脑上能正常使用,复制到另一台电脑上由于忘记复制C++运行时库就不能用了。

其实还有XAPI下还有一个队列库Queue_x86.dll/Queue_x86d.dll也是每个C封装都需要用到,并且它还有Debug与Release版本,由于C++中的new/delete必须与C++运行时库Debug/Release对应的问题,所以这两个版本不能混用。

不能运行一定是缺少依赖库导致吗?网上下载depends(http://www.dependencywalker.com/)查看CTP_Trade_x86.dll,从能正常运行的电脑上将缺少的库复制到其它电脑相应系统文件夹下即可。

同时也需要注意32位与64位的问题,很多同名文件是是同时存在32位与64位的区别的,32位版本放C:\Windows\SysWOW64\下,64位版本放C:\Windows\System32下。


图1.depends查看dll示例


图中演示的是用depends查看CTP_Trade_x86.dll,由于缺少Queue_x86d.dll, Python在调用它时会提示“找不到指定的模块”。CTP_Trade_x86.dll是32位的dll,图中显示的c:\windows\system32路径实际上是访问的c:\windows\syswow64。将Queue_x86d.dll文件复制到C:\Windows\SysWOW64下即可。

对这些细节不想了解的用户直接运行群共享提供的安装包(统一接口完整版.zip)中的“X1.复制bin和System32目录_需右键以管理员身份运行.bat”就可以省去以上所有麻烦。


1.4 运行测试


默认提供了test_ctp_api.py/test_tdx_api.py两个完整的脚本用来测试,它们分别实现了期货和股票的目标调仓功能,只要在文件中设置好每个金融产品的目标持仓、多空方向和数量,就能以最快的速度调整成指定的仓位。

对于每天交易频率不高,使用日线数据计算策略,然后第二天早上进行交易的机构用户来说,这两个脚本完全可以直接使用。


1.4.1 配置账号与服务器

目录下出现了config.py、config_default.py、config_override.py和config_tdx.py四个以config开头的文件。

config_default.py/config_override.py:分别是CTP的默认配置和CTP的特殊配置,为了实现特殊配置覆盖默认配置,config.py中实现了覆盖重复字段的功能。

config_tdx.py中没有CTP配置中那么复杂,直接就是配置信息。


#!/usr/bin/env python

# -*- coding: utf-8 -*-

configs = {

    # 根据目录,存放交易清单和中间文件等信息

   'root_dir': r'd:' + '\\test_tdx',

    # 交易,这下面的配置要求需要参考每种API的说明文档

    'td': {

        #TDX安装目录,注意最后有一个\

       'ExtInfoChar128': br'D:\new_hbzq' + b'\\',

        # 登录脚本。注意路径出现中文需要转码

        'Address':r'd:\test_tdx\Login_东方财富证券.lua'.encode('GBK'),

        # 资金账号

       'UserID': b'123456',

        # 用户密码

       'Password': b'654321',

        # 通迅密码。注意,不是验证码,不需要通迅密码的券商请留空

       'ExtInfoChar64': br'',

    },

}


注意配置文件中很多字符串出现了b前缀。这是因为XAPI的结构体对应字段就是C中的字数数组,b就是原始的bytes,我们将字符串直接传入。中文路径不能直接使用b前缀,需要先encode转换成bytes后才能使用。


1.4.2 编辑目标仓位文件

前面在config_default.py中已经配置了root_dir文件路径为d:\test_ctp。编辑其中的target_position.csv文件,下单时将从这它读取目标仓位。

不知道配置怎么办?登录成功后先输入2查询当前持仓,然后输入12将当前持仓保存到target_position.csv。


Symbol,InstrumentID,HedgeFlag,Side,Position,InstrumentName

i1709.,i1709,0,1,5.0,

i1709.,i1709,0,-1,0.0,

rb1710.,rb1710,0,-1,6.0,


Symbol:合约唯一代码,XAPI内部使用。由InstruemntID与ExchangeID组合而成;

InstrumentID:合约代码,供API使用,必须与对应API的合约代码完全一样,如果对应API区分大小写,这里也得区分;

HedgeFlag:投机套保标志,默认为0,这个功能一般是机构使用,机构可能出现同时持有投机仓位与套保仓位的情况;

Side:多空方向,1表示多,-1表示空;

Position:持仓数量,正数;

InstrumentName:合约名称。只用于显示,可为空,股票中表示股票名称。


Symbol,InstrumentID,HedgeFlag,Side,Position,InstrumentName

000001,000001,0,1,400.0,平安银行

000002,000002,0,1,200.0,万 科A

300001,300001,0,1,300.0,特锐德

300024,300024,0,1,200.0,机器人

 

对于前面提到的csv文件,如何清仓、反手、锁仓呢?

清仓:即没有项目,或对应的Position为0即可

Symbol,InstrumentID,HedgeFlag,Side,Position,InstrumentName

 

反手:只要改对应项目的Side的正负号即可

Symbol,InstrumentID,HedgeFlag,Side,Position,InstrumentName

i1709.,i1709,0,-1,5.0,

rb1710.,rb1710,0,1,6.0,

锁仓:同合约多空持仓数量相等即可

Symbol,InstrumentID,HedgeFlag,Side,Position,InstrumentName

i1709.,i1709,0,1,5.0,

i1709.,i1709,0,-1,5.0,

rb1710.,rb1710,0,1,6.0,

rb1710.,rb1710,0,-1,6.0,


1.4.3 运行脚本

直接运行会打印一个选择菜单,然后等待用户输入。菜单的主要内容如下:


1 - 读取目标仓位

2 - 查询实盘仓位

3 - 订阅行情

4 - 计算交易清单

5 - 批量下单

6 - 需延迟通过回报批量撤单


它就是目标仓位调整的主要流程:

1 - 首先从本地的target_position.csv读取设置的目标仓位;

2 - 从实盘柜台上查询当前的持仓;

3 - 根据目标仓位和实盘持仓合并得到要下单的合约集合,订阅合约的行情,后面下单时需要用到最新的行情价格;

4 - 对比目标仓位和实盘持仓,得到交易清单,这个清单已经处理好了买卖方向与开平方向等问题,对于上期所的的今仓与昨仓分两笔下单;

5 - 根据上一步生成的交易清单直接下单;

6 - 下单后并不是瞬间成交,还需要等待几秒,然后撤单。

人工循环执行2到6步,直到交易清单为空。注意有些情况下可能成交失败,交易清单永远不为空。例如:

1.    资金不够

2.    涨跌停,买入或卖出无法成交

3.    进入交割月了,交易手限制为整数倍,而下单手数不合要求

4.    股票停牌

 

有时输入这些数字也可能输入错误,我们提供了一个更简化的选项:

7 - 顺序执行1-6

 

只要输入7,就会自动执行1至6三次,直到交易清单为空或出错。


1.4.4 辅助菜单

这些是相当重要的菜单,也需要关注一下。

21 - 查合约列表(至少执行一次)

22 - 查资金

23 - 取消订阅行情

24 - 打印订单

 

33– 切换行情显示

 

q - 退出

21 - 从柜台上查询合约列表,并保存在本地。它保存了每个合约的最小变动价位,用于计算买卖时加几跳时具体加的是多少价格。如果不查询默认最小变动价位为1。对于某些合约使用默认1将产生错误。

对于cu铜,最小变动价位是10,如果使用默认1,加2跳,价格将不满足最小变动价格整数倍的条件。

对于IF沪深300,最小变动价位是0.2,如果使用默认1,加2跳,价格实际上加了10跳。

所以这个地方至少要执行一次,每次有新品种上市时也得查一次。

22 - 查账号资金

33 – 切换行情显示。对于CTP的主推行情,由于行情一直在界面中打印,干扰使用,所以提供了一个开关进行切换。

q – 人工输入模式会一直等待用户输入,输入‘q’可以退出


1.4.4 仓位相关菜单

11 - 合并对冲多个组合到目标持仓

12 - 回写查询持仓到目标持仓

13 - 合并对冲目标持仓和增量仓到目标持仓

 

1 - 读取目标仓位

2 - 查询实盘仓位


是否觉得要自己手工编写target_position.csv很麻烦呢?只要运行菜单2后再运行12就会将前一步查询出来的持仓写入到target_position.csv,然后再手工编辑少量即可。

一个实盘账号下跑了N个策略,分别生成了不同的持仓文件,能否先内部对冲一下?我们提供了11这个菜单项,它能将portfolio_1.csv/portfolio_2.csv/portfolio_3.csv三个文件中的持仓合并,对冲,然后写入到target_position.csv中。

如果投资组合数超过3个,请自己手工编辑代码支持更多组合。

每天的投资组合清单都已经生成,但我盘中想改总持仓怎么办?这个改动是算在哪个策略对应的投资组合呢?建议根据策略数量N创建N+1个投资组合,第N+1个组合文件中内容为空,当需要人工调整时仓位都放在第N+1个投资组合中。

如果我的策略逻辑并不是根据持仓数来计算,而是根据买卖数量呢?例如今天买入2手,而不是今天仓位是2手。这时就要用到菜单项13了,它会将target_position.csv和incremental_position.csv的持仓合并,不对冲,然后再写回target_position.csv。

用户一定要根据自己的实际情况灵活运用以上仓位相关的菜单,建议大家策略逻辑使用目标仓位法,而不是买卖法。


1.4.5股票相关功能

目前Tdx接口没有实现查询股票列表功能,所以菜单项21无效。股票无法卖空,所以target_position.csv中的Side都只能填1。

原本Tdx柜台就没有实现主动推送委托回报和成交回报的功能,只能过一会后去主动查询委托列表,所以股票接口只能等待一会后直接撤单,然后直接查持仓,不再关心委托回报这些细节。


1.5 无人值守(自动化)


无人值守是每个程序化交易员的目标。这里提供一个可用的参考。每天晚上数据都自动下载到服务器上,策略程序定时加载数据生成交易清单,第二天早上开盘自动全下,下完后日志自动发邮件到QQ邮箱,微信立即在手机上提醒新邮件。可以参考同目录下runme_auto2.bat等文件。


1.5.1 命令行参数

test_ctp_api.py/test_tdx_api.py默认在运行时都是手工输入,其实还提供了一个参数“—input”。

“--input=11;7”表示先运行菜单项11,然后运行菜单项7。即先从几个子组合csv文件中合并持仓,然后循环下单,下完单后退出。


REM 11 合并持仓

REM 22 查资金

REM 7 循环下单

set date_Ymd=%date:~0,4%%date:~5,2%%date:~8,2%

python.exe test_ctp_api.py --input=11;22;7;221>log/%date_Ymd%.log 2>&1


如何自动保存日志呢?我们使用到了DOS重定向。

<>

>:新建模式输出到文件

>>:追加模式输出到文件

>&:将一个句柄输出写入到另一个句柄的输入中

<>

0:标准输入,令在执行时所要的输入数据通过它来取得

1:标准输出,命令执行后的输出结果从该端口送出

2:标准错误,命令执行时的错误信息通过该端口送出

1>log/%date_Ymd%.log2>&1 表示将标准输出指向log日志文件,然后将标准错误都输出到标准输出中。这样标准输出和标准错误就都输出到log文件了。如果将2>&1放到前面,则达不到效果,错误还是输出到了窗口中,因为这时标准输出还没有被重定向。


1.5.2  发送邮件


python mail.py --username=123456--password=654321 --from=123456@qq.com

    --to=123456@qq.com;654321@qq.com--log=log --bat=%~f0


推荐使用QQ邮箱来收发通知邮件,收件箱使用QQ邮箱是因为收到邮件后微信能立即提醒。发件箱与收件箱使用同一家是因为这样邮件到达更快。

--username=用户名

--password=密码/授权码。QQ邮箱为了防止在第三方客户端登录时密码被盗,使用授权码来代替密码进行登录。

--from=发件箱。username账号所对应的发件箱

--to=收件箱。可以写多个邮箱。使用英文“;”进行分隔

通过QQ邮箱的,设置->账户->SMTP服务,开启SMTP发信功能,同时申请授权码。


1.5.3  计划任务

按Win键后,输入taskschd就会定位到“任务计划程序”。

1.    创建基本任务

2.    触发器选择“每周”,将“星期一”至“星期五”都选上,开始时间选择正确,比如8:59:55开始,这样算上登录和查持仓的时间,差不多9点正好下到柜台。请保证服务器时间与互联网时间同步。

3.    操作选择“启动程序”,“程序或脚本”填为bat文件路径,“起始于”填为bat文件所在路径。由于bat和python代码中大量用到了相对路径,所以“起始于”这一项绝对不能为空。

4.    注意。计划任务中不能出现阻塞进程的代码,比如不能在bat中加入pause或调用的python代码长时间不返回。否则任务运行中,第二天的计划任务不会触发。


1.6 代码解读


前面介绍了基本的使用方法,对于很多用户来说就已经完全足够,但这里还是要对代码简单解读一下,方便遇到问题时自行处理。

XApi.py/XSpi.py/XStruct.py/XEnum.py是核心代码,使用ctypes模块实现Python调用C。

XEnum.py中定义了枚举类型,包含值以及对应的英文名。当在结构体中取到枚举数值时,通过这里的定义得到对应的英文名。

XStruct.py中定义了结构体,与C接口的结构体一一对应,同时还为结构体添加了一些函数,方便使用,如__str__等。

XApi.py进行API的各项调用,对收到的响应进行转发,如登录、查询、下单等。

XSpi.py回调接口,提供给第三方继承使用。

MySpy.py继承了XSpi的类,目前用来进行实际目标持仓调整功能。

以CTP期货交易接口封装为例,CTP_Trade_x86.dll只导出了一个接口XRequest,需要对XRequest请求格式特别了解才能正确的调用,所以又在这之上套了一层XAPI_CPP_x86.dll,它导出了一些常用的C函数。接口封装人员可以按自己的能力选择合适自己的调用方式。

目前XApi.py中提供的方式是通过XAPI_CPP_x86.dll来调用CTP_Trade_x86.dll。


1.6.1 登录


    # 创建XApi对象,设置服务器地址与账号

td = XApi(r'C:\Program Files\SmartQuantLtd\OpenQuant 2014\XAPI\x86\XAPI_CPP_x86.dll')

td.ServerInfo.Address = config['td']['Address']

td.ServerInfo.BrokerID = config['td']['BrokerID']

td.UserInfo.UserID = config['td']['UserID']

td.UserInfo.Password = config['td']['Password']

 

# 指定加载的是CTP的交易模块,设置不同的路径可以加载其它模块

ret = td.init(br'C:\Program Files\SmartQuantLtd\OpenQuant 2014\XAPI\x86\CTP\CTP_Trade_x86.dll')

if not ret:

    print(td.get_last_error())

    exit(-1)

print(ord(td.get_api_type()))

print(td.get_api_name())

print(td.get_api_version())

 

# 关键一步,注册回调事件处理函数

td.register_spi(self)

# 连接并登录

td.connect()


init()中的路径如果加载的是CTP_Quote_x86.dll那实现的就是期货行情相关功能,如果加载的是Tdx_Trade_x86.dll实现的就是股票交易功能。

register_spi()中需要传入继承了XSpi的类,在test_ctp_api.py中当前类MySpi继承了XSpi。

connect()时会异步收到登录状态回报,MySpi.OnConnectionStatus最先得到触发。某一交易接口的登录日志如下,最后的Done表示登录的所有动作都已经执行完,可以用来下单了。


OnConnectionStatus=Initialized

OnConnectionStatus=Connecting

OnConnectionStatus=Connected

OnConnectionStatus=Logining

OnConnectionStatus=Logined

[TradingDay=20170905;LoginTime=205957;SessionID=1:1314163388;InvestorName=;XErrorID=0;RawErrorID=0;Text=]

OnConnectionStatus=Confirming

OnConnectionStatus=Confirmed

OnConnectionStatus=Done


1.6.2 代码调试

OnConnectionStatus等一些事件响应函数中能下断点调试吗?实测在PyCharm中下断点,有输出日志,但断点完全不生效。

Stack Overflow上的网友是这样解答的:在非Python线程中,你必须设置调试器机制才能正常工作(在Python线程创建时自动设置了,但在非Python线程创建时没有任何构造函数钩子,所以得自己做)。在你需要下断点的代码前加入如下代码即可。


import pydevd

pydevd.settrace(suspend=False,trace_only_current_thread=True)


你需要使用pip install pydevd先安装pydevd。


1.6.3 行情订阅

创建行情XAPI实例,对它设置行情接口库CTP_Quote_x86.dll,设置行情服务器的地址和端口号。不要弄混,对交易接口订阅行情是无效的。

 

ret = md.init(br'C:\Program Files\SmartQuantLtd\OpenQuant 2014\XAPI\x86\CTP\CTP_Quote_x86.dll')


订阅行情传入的合约代码也必须是有b前缀。

# 可直接传字一个用;分隔的字符串

md.subscribe(b'cu1709;SR801',b'')

 

# 或做一次转码再传入

symbols_ = pd.Series(symbols).str.encode('gbk')

for i in range(len(symbols_)):

    md.subscribe(symbols_[i],b'')

 

会在OnRtnDepthMarketData(self,ptr1, size1)中收到行情回报,ptr1是行情数据指针,size1是行情数据大小。其它OnXxx事件,输出的参数都是Python对象,只有行情接口特殊输出的是内存指针。因为为了支持多档行情,行情结构体设计成了可变内存块。就算是股票的五档行情,在涨跌停时,这个内存块的大小都是不一样的。

那如何取数据呢?使用的ctypes的cast即可


obj = cast(ptr1,POINTER(DepthMarketDataNField)).contents

# 打印行情,一般情况下都是关闭,因为内容太多了

print(obj)

 

#卖五价

ask_count = obj.get_ask_count()

if ask_count > 0:

    ask =obj.get_ask(ptr1, ask_count - 1)

 

# 买五价

bid_count = obj.get_bid_count()

if bid_count > 0:

    bid = obj.get_bid(ptr1, bid_count - 1)


对于CTP这种主动行情推送的接口,只要订阅了,行情有变化就会推送,OnRtnDepthMarketData会不停的被调用。而Tdx这种查询模式的接口,查一次推送一次,所以需要根据策略需求查询,不能高频率查询,否则严重影响服务器。


1.6.4 查询持仓


# 查询持仓请求

query =ReqQueryField()

td.req_query(QueryType.ReqQryInvestorPosition, query)

 

# 持仓响应

def OnRspQryInvestorPosition(self, pPosition,size1, bIsLast):

    if size1<=>

        return

 

    # 一定要用copy,不然最后一个会覆盖前面的

    self.position_dict[pPosition.get_id()]= copy.copy(pPosition)

    if notbIsLast:

        return


当账号上没有持仓,但还是需要通知到客户端请求已经得到响应了,所以会传回一个空数据,所以需要对size1进行判断。


1.6.5 查询资金

# 查资金请求

query = ReqQueryField()

td.req_query(QueryType.ReqQryTradingAccount,query)

 

# 资金响应

def OnRspQryTradingAccount(self, pAccount, size1,bIsLast):

    if size1<=>

        return

    print(pAccount)

1.6.6 下单

# 提供临时变量用于下单

order = (OrderField * 1)()

orderid = (OrderIDTypeField * 1)()

orderid[0].OrderIDType = b''

 

# 订单参数

order[0].InstrumentID = b'IF1710'

order[0].ExchangeID = b''

order[0].Type = OrderType.Limit

order[0].Side = OrderSide.Buy

order[0].Qty = 1

order[0].OpenClose = OpenCloseType.Open

order[0].Price = 3500.0

 

# 下单

ret = td.send_order(order[0], orderid[0], 1)

 

# 打印

print('LocalID:%s'% ret)


注意,涉及到字符串的地方都需要b前缀。有关OrderType、OrderSide、OpenCloseType的取值,可以参考XEnum.py文件中对应部分。

下完单后回收到委托回报和成交回报,委托回报可以存储起来用于立即撤单。


# 委托回报

def OnRtnOrder(self, pOrder):

    self.order_dict[pOrder.get_id()]= copy.copy(pOrder)

    print(pOrder)

 

# 成交回报

def OnRtnTrade(self, pTrade):

    print(pTrade)


1.6.7 撤单


在CTP接口中撤单需要三个字段,而XSpeed需要两个字段,Tdx一个字段,由于每种接口都不一样,为了简化,在XAPI中统一设计成通过一个唯一字段进行撤单。这需要XAPI内部维护一张映射表,在登录时底层会查询委托列表进行映射表重建。所以如果今天下单极多,重新登录时要等映射表重建完才能成功撤单。

OrderField中有三个字段跟识别订单有关:

OrderID是由交易所传回来的id。如果想比较不同柜台的速度,可以同时报单相同合约后比较OrderID的大小。

ID是XAPI维护的唯一ID,可用于撤单。需要保证在下一次重新登录后还能通过委托回报计算出这个唯一ID。

LocalID是本地XAPI在下单时立即返回的id,它只供内部临时映射时使用,Tdx这类的接口是下单返回后才能知道ID值,所以在柜台返回之前XAPI维护一个临时LocalID,等收到回报后就弃用LocalID。CTP接口中LocalID与ID是相等的,在Tdx中他们不相等


# 临时变量

orderid = (OrderIDTypeField * 2)()

orderid[0].OrderIDType = b''

orderid[1].OrderIDType = b''

 

# 设置要撤单的ID

orderid[0].OrderIDType = order.ID

# 撤单

td.cancel_order(orderid[0],orderid[1], 1)


撤单ID最好从委托回报中取,而不是手工填,因为部分API的ID前后可能出现空格,如果缺失了空格会导致找不到订单。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多