综合交易平台CTP:
上海期货信息技术有限公司(上海期货交易所旗下子公司)开发的期货经纪业务管理系统。在API的设计、业务模式、开放性上都比国内其它系统走得更远,大部分期货公司都支持CTP,目前已经是国内期货程序化交易接入的事实标准。同时上期技术在证券API上也做了一定的工作,证券接口也已经发布。
金仕达:
市场占有率极高的柜台系统,最初仅有B2B网关,用户接入时必须同期货公司商谈,并在期货公司机房内网架设服务器。在2012年时发布了B2C版KSFT_API,与CTP接口相似,仅在一些开发细节上有所区别,直接减少了用户的迁移成本。目前大部分公司同时支持金仕达和CTP,不过存在的问题是出入金不便。CTP没有提供次席的快速出入金的方案,而金仕达方也不提供,最终在主席系统的选项上,期货公司必须得做出选择,目前有部分期货公司正酝酿将主席切换成CTP。
易胜:
由易胜信息技术有限公司(郑州商品交易所期下子公司)开发,提供了行情与交易接口,目前仅有部分期货公司部署了对应的程序化交易模块。易胜API最大的优点是提供了部分历史数据,这应当时是为了满足他们的程序化交易客户端所提供的功能,缺点是要开发时得申请授权认证码,这限制了不少开发者。
飞创信息X-Speed:
大连飞创信息技术有限公司(大连商品交易所旗下子公司)也提供了交易与行情的API,但目前成熟度不够,使用者少。
恒生:
专业的同时提供了证券、期货经纪业务解决方案的提供商,普及面也很广。基金公司等大型机构都有风险控制需求,而恒生在这方面做得不错,但目前没有推出面向普通客户的交易接口。
CTP_API官方下载地址为:http://202.109.110.121/api/
实际上此地址少有人维护,如想要最新版,还是得找CTP_API的官方QQ群,一般群共享有最新版的API及相关的文档,强烈建议提前将文档细读几遍。最关键的两个文档是《综合交易平台API技术开发指南》、《综合交易平台API特别说明》。
提供的CTP_API目前有三个版本:Linux x64、Windows x86、iOS。微软官方已经提到过,在64位进程中不能加载32位的dll,同理一个32位进程也不能加载一个64位dll。所以在Windows平台下采用一般的dll调用方式也就被限制在了主程序为32位程序。其实,分别使用32位和64位两个进程通讯的方式能解决这个问题。
在这,我们使用dll调用的方式。先确保自己安装的是32位的Matlab,如果你是在64位Windows上直接安装,默认是安装的64位系统,请进入到Matlab的安装目录,找到bin/win32下的setup.exe进行安装。
运行效率最高,但开发起来工作量大,要做大量的数据结构转换。目前已经有公司或个人推出了MEX版。
这种方式比较灵活,对接64位平台或者跨操作系统、跨主机都是没有问题的,但在运行效率上略为逊色。已经有网友提供了通用版本接口,即可以Matlab调用,也可以R语言调用。
COM接口在Windows平台下还是有一定的使用范围的,Matlab、Excel等都可以对接COM接口,目前网上可以下载到上海汇朋提供的盈佳COM接口。网址:http://www.winnerfutures.com.cn/
目前已经有少量网友开源了Java对接CTP的接口,但Matlab对接Java接口的还没有推出。同时转换的技术也有多种,如JNA、BridJ。
网址: https://github.com/QuantBox/CTP/tree/master/Java-CTP,JNA版
http://download.csdn.net/detail/vcfriend/5054163,BridJ版
NET版对接CTP的接口是百花齐放,版本比较多,网上目前比较知名的版本有
海风版:最早开源出来的C#版接口之一,P/Invoke封装
马不停蹄版:C++/CLI版封装 http://ishare.iask.sina.com.cn/f/34438582.html
LumenXH版:https://github.com/LumenXH/,P/Invoke封装
QuantBox版:https://github.com/QuantBox/CTP,也是使用了P/Invoke封装,但对API做了自己的细节处理。
使用.NET版的好处就是省事,这么多款.NET版,选一款能对接Matlab,使用简单,自己能理解的代码库就成。
如何判断是否能对接Matlab呢?一般异步通知有两种方式:一种是偏底层的函数回调,一种是偏高层的事件通知。
即事件所使用的委托的签名必需要用指定的格式:两个参数,第一个参数是object sender,而第二个参数必需继承于.NET的EventArgs类。
检查这些C#版的接口,只要是指定格式的委托签名就可以了。
在这我只介绍QuantBox版,因为这个版本是本人开发并开源的,对它的了解最清楚。
首先介绍下这个项目,此项目最初是为了对接国外一款非常有名的软件——OpenQuant的程序化交易平台而做的前期工作,同时为对接其它语言做了预留。
由于OpenQuant插件开发是用的C#,为了满足项目要求,首先得有C#版接口,考虑到还要为其它语言做准备,一定得有C版接口。当时网络上没有C版接口开源,附属在一些C#版接口中的C版在对接其它语言时又不够方便,故C层与C#层另行开发。
有部分网友希望我们能提供Matlab版,因实际我们生产环境中并不使用它进行交易,没有编写MEX版的动力。不过通过研究,使用了更简化的方式满足了大家的要求,也就是上一节提到的C#版与Matlab版对接原理。
Java版也是在网友的期盼中诞生的,当初是考虑到C#版对接Matlab的方案只能在Windows下用,推出Java版对接Matlab的方案就能在Linux中用了。可惜Java版的测试能用,但Java对接Matlab的方案目前没解决。
C版本的特点是没有直接将C++版本的接口进行转换,而是做了一定的处理。加上这些处理的理由很充分,就是简化逻辑,让其它语言对接CTP时能更简单。
首先我们来看CTP接口开发要注意哪些关键地方,在其它网友公布的直接接口转换的封装,都要自行处理这些繁琐细节,但本人提供的C版都进了屏蔽。
主要添加的功能如下:
在介绍Matlab对接.NET前,一定得先介绍监控软件,否则在下一节要介绍的开发上完全是在摸黑。
能实现监控的原理是:
所以在程序化交易时,另用一款比较好的手动交易软件来监控是不错的方式。可以查看委托状态、委托价、成交回报等信息,方便查找错误。目前推荐使用快期。
同时,我们对接CTP平台需要服务器的配置信息,在快期目录下有brokers.xml,其中有三样东西最重要:经纪商编号(BrokerID),行情服务器地址(MarketData)、交易服务器地址(Trading)。
这个地方要注意,不管brokers.xml中地址如何写,地址开头没有“tcp://”,实际使用CTP_API时就得补上,如果以“udp://”开头,改成“tcp://”也能正常使用,在下一节的代码中有模拟盘的地址示例。
https://github.com/QuantBox/CTP/tree/master/Matlab-DotNet
请保证相关文件都是最新的。
thostmduserapi.dll、thosttraderapi.dll来自于上期技术
QuantBox.C2CTP.dll来自于C版接口
QuantBox.CSharp2CTP.dll来自于C#版接口
test.m是程序入口,做了以下工作
%% 导入C#库,请按自己目录进行调整
cd 'D:\wukan\Documents\GitHub\CTP\Matlab-DotNet\test\'
NET.addAssembly(fullfile(cd,'QuantBox.CSharp2CTP.dll'));
import QuantBox.CSharp2CTP.*;
%% 行情
global md;
md = MdApiWrapper();
addlistener(md,'OnConnect',@OnMdConnect);
addlistener(md,'OnDisconnect',@OnMdDisconnect);
addlistener(md,'OnRtnDepthMarketData',@OnRtnDepthMarketData);
md.Connect('D:\',... %行情流文件路径
'tcp://27.115.78.35:41213',... %行情服务器地址
'1009',... %经纪公司代码
'123456',... %用户代码
'888888'); %密码
%% 交易
global td;
td = TraderApiWrapper();
addlistener(td,'OnConnect',@OnTdConnect);
addlistener(td,'OnDisconnect',@OnTdDisconnect);
addlistener(td,'OnRtnOrder',@OnRtnOrder);
td.Connect('D:\',... %交易流文件路径
'tcp://27.115.78.35:41205',... %交易服务器地址
'1009',... %经纪公司代码
'00000015',... %用户代码
'123456',... %密码
THOST_TE_RESUME_TYPE.THOST_TERT_QUICK,... %流重传方式
'',... %用户端产品信息
''); %认证码
%% 退出
% md.Disconnect() %行情退出
% td.Disconnect() %交易退出
对以上的部分参数做下说明,特别是交易比行情要多了三个参数。
第二个参数是服务器的地址,目前,行情服务器输入错误的用户名和密码也能登录。
第三个参数,经纪公司代码是用来区分各家公司时使用,当初的CTP设计时考虑了一台服务器上同时多家期货公司同时工作。
交易的第六个参数是流重传方式。
流重传方式有三种:
public enum THOST_TE_RESUME_TYPE
{
THOST_TERT_RESTART = 0, //从本交易日重传
THOST_TERT_RESUME, //从上次收到的续传
THOST_TERT_QUICK //只传送登录后的内容
};
其实这些重传方式的进度维护需要生成一些临时文件,记录已经传到了第几条数据,具体如何实现的CTP接口已经向我们屏蔽,用户只要需要知道如何使用。第一个参数就是流文件路径,在此不用担心行情与交易的流相互影响。
第七个参数用户端产品信息,用户可以用来标识软件产品
第八个参数认证码,只有在期货公司要求并分配认证码后才能填写,需要与用户端产品信息配合使用。
addlistener是Matlab提供的注册事件的方法,第一个参数是需要注册事件的对象,第二个参数是事件名,第三个参数是处理函数。
对于TraderApiWrapper到底支持哪些事件呢?
https://github.com/QuantBox/CTP/blob/master/CSharp-CTP/src/QuantBox.CSharp2CTP/TraderApiWrapper.cs
源码中有详细的事件列表。其实CTP还提供了很多功能,由于目前只是实现自己的简单程序化工具并用不到那些功能,所以并没有提供对应的事件支持。用户可以参与开原项目,一同完善。
public event OnConnectHander OnConnect;
public event OnDisconnectHander OnDisconnect;
public event OnErrRtnOrderActionHander OnErrRtnOrderAction;
public event OnErrRtnOrderInsertHander OnErrRtnOrderInsert;
public event OnRspErrorHander OnRspError;
public event OnRspOrderActionHander OnRspOrderAction;
public event OnRspOrderInsertHander OnRspOrderInsert;
public event OnRspQryDepthMarketDataHander OnRspQryDepthMarketData;
public event OnRspQryInstrumentHander OnRspQryInstrument;
public event OnRspQryInstrumentCommissionRateHander OnRspQryInstrumentCommissionRate;
public event OnRspQryInstrumentMarginRateHander OnRspQryInstrumentMarginRate;
public event OnRspQryInvestorPositionHander OnRspQryInvestorPosition;
public event OnRspQryInvestorPositionDetailHander OnRspQryInvestorPositionDetail;
public event OnRspQryOrderHander OnRspQryOrder;
public event OnRspQryTradeHander OnRspQryTrade;
public event OnRspQryTradingAccountHander OnRspQryTradingAccount;
public event OnRtnInstrumentStatusHander OnRtnInstrumentStatus;
public event OnRtnOrderHander OnRtnOrder;
public event OnRtnTradeHander OnRtnTrade;
OnConnect、OnDisconnect是行情与交易都支持的事件。但在实际使用时还是有区别的。
交易有可能要进行客户端认证,所以可能出现与客户端认证有关的状态E_authing、E_authed
只有结算单确认后才能下单,所以交易的最后状态不是E_logined,而是E_confirmed。
//自己定义的
public enum ConnectionStatus
{
E_uninit, //未初始化
E_inited, //已经初始化
E_unconnected, //连接已经断开
E_connecting, //连接中
E_connected, //连接成功
E_authing, //授权中
E_authed, //授权成功
E_logining, //登录中
E_logined, //登录成功
E_confirming, //确认中
E_confirmed, //已经确认
E_conn_max //最大值
};
OnMdConnect.m文件
function OnMdConnect(sender,arg)
% 交易连接回报
% 行情状态到E_logined就表示登录成功
if arg.result == QuantBox.CSharp2CTP.ConnectionStatus.E_logined
global md;
% 订阅行情,支持","和";"分隔
md.Subscribe('IF1305;IF1306,IF1309;IF1312');
end
end
OnTdConnect.m文件
function OnTdConnect(sender,arg)
% 交易连接回报
% 交易状态到E_confirmed就表示登录并确认成功
if arg.result == QuantBox.CSharp2CTP.ConnectionStatus.E_confirmed
global td;
% 下单
td.SendOrder('IF1309',... %合约
QuantBox.CSharp2CTP.TThostFtdcDirectionType.Buy,... %买卖
'0',... %开平标记
'1',... %投机套保标记
1,... %数量
2250,... %价格
QuantBox.CSharp2CTP.TThostFtdcOrderPriceTypeType.LimitPrice,... %价格类型
QuantBox.CSharp2CTP.TThostFtdcTimeConditionType.GFD,... %时间类型
QuantBox.CSharp2CTP.TThostFtdcContingentConditionType.Immediately,... %条件类型
0);
end
end
下单接口比较复杂,我们这在登录后立即发送一单
状态为已确认后,开始可以下单。
第一个参数是合约名,区分大小写,如果不清楚合约名可以查看快期软件中的“合约列表”。
第二个参数是买卖标记,只有两种选择,买/卖。
第三个参数是组合开平标记,第四个参数是组合投机套保标记。
组合开平标记和组合投机套保标记的写法有些特别,CTP支持组合单,组合单至少由两腿组成,如何区分每腿的开平与投保呢?那就是用的组合开平与组合投保了,第一个字符就标记的第一腿,第二个字符标示的第二腿,以此类推。
/////////////////////////////////////////////////////////////////////////
///TFtdcOffsetFlagType是一个开平标志类型
/////////////////////////////////////////////////////////////////////////
///开仓
#define THOST_FTDC_OF_Open '0'
///平仓
#define THOST_FTDC_OF_Close '1'
///强平
#define THOST_FTDC_OF_ForceClose '2'
///平今
#define THOST_FTDC_OF_CloseToday '3'
///平昨
#define THOST_FTDC_OF_CloseYesterday '4'
///强减
#define THOST_FTDC_OF_ForceOff '5'
///本地强平
#define THOST_FTDC_OF_LocalForceClose '6'
typedef char TThostFtdcOffsetFlagType;
/////////////////////////////////////////////////////////////////////////
///TFtdcHedgeFlagType是一个投机套保标志类型
/////////////////////////////////////////////////////////////////////////
///投机
#define THOST_FTDC_HF_Speculation '1'
///套利
#define THOST_FTDC_HF_Arbitrage '2'
///套保
#define THOST_FTDC_HF_Hedge '3'
typedef char TThostFtdcHedgeFlagType;
目前普通客户开通的账户只能下投机,所以第四个参数直接使用"11"最省事。
而第三个参数使用起来比较麻烦,因为上交所专门区分了平今与平昨,使用错了会提示平仓数不足。
第六个参数是价格,价格必需是最小价格变动单位的整数倍,不能超过涨跌停价
第七个参数是价格类型,接口预留了大量的类型,但交易所只部分支持,目前仅使用两种价格类型即可,限价与市价,上海不支持市价单,中金股指两个远月不支持市价。
/////////////////////////////////////////////////////////////////////////
///TFtdcOrderPriceTypeType是一个报单价格条件类型
/////////////////////////////////////////////////////////////////////////
///任意价
#define THOST_FTDC_OPT_AnyPrice '1'
///限价
#define THOST_FTDC_OPT_LimitPrice '2'
///最优价
#define THOST_FTDC_OPT_BestPrice '3'
///最新价
#define THOST_FTDC_OPT_LastPrice '4'
///最新价浮动上浮1个ticks
#define THOST_FTDC_OPT_LastPricePlusOneTicks '5'
///最新价浮动上浮2个ticks
#define THOST_FTDC_OPT_LastPricePlusTwoTicks '6'
///最新价浮动上浮3个ticks
#define THOST_FTDC_OPT_LastPricePlusThreeTicks '7'
///卖一价
#define THOST_FTDC_OPT_AskPrice1 '8'
///卖一价浮动上浮1个ticks
#define THOST_FTDC_OPT_AskPrice1PlusOneTicks '9'
///卖一价浮动上浮2个ticks
#define THOST_FTDC_OPT_AskPrice1PlusTwoTicks 'A'
///卖一价浮动上浮3个ticks
#define THOST_FTDC_OPT_AskPrice1PlusThreeTicks 'B'
///买一价
#define THOST_FTDC_OPT_BidPrice1 'C'
///买一价浮动上浮1个ticks
#define THOST_FTDC_OPT_BidPrice1PlusOneTicks 'D'
///买一价浮动上浮2个ticks
#define THOST_FTDC_OPT_BidPrice1PlusTwoTicks 'E'
///买一价浮动上浮3个ticks
#define THOST_FTDC_OPT_BidPrice1PlusThreeTicks 'F'
typedef char TThostFtdcOrderPriceTypeType;
第八个参数是时间类型,根据国内交易所的情况,目前支持GFD,IOC
第九、十参数是条件单使用,首先大商所支持止盈止损单,郑商所支持止损单。
/////////////////////////////////////////////////////////////////////////
///TFtdcContingentConditionType是一个触发条件类型
/////////////////////////////////////////////////////////////////////////
///立即
#define THOST_FTDC_CC_Immediately '1'
///止损
#define THOST_FTDC_CC_Touch '2'
///止赢
#define THOST_FTDC_CC_TouchProfit '3'
///预埋单
#define THOST_FTDC_CC_ParkedOrder '4'
///最新价大于条件价
#define THOST_FTDC_CC_LastPriceGreaterThanStopPrice '5'
///最新价大于等于条件价
#define THOST_FTDC_CC_LastPriceGreaterEqualStopPrice '6'
///最新价小于条件价
#define THOST_FTDC_CC_LastPriceLesserThanStopPrice '7'
///最新价小于等于条件价
#define THOST_FTDC_CC_LastPriceLesserEqualStopPrice '8'
///卖一价大于条件价
#define THOST_FTDC_CC_AskPriceGreaterThanStopPrice '9'
///卖一价大于等于条件价
#define THOST_FTDC_CC_AskPriceGreaterEqualStopPrice 'A'
///卖一价小于条件价
#define THOST_FTDC_CC_AskPriceLesserThanStopPrice 'B'
///卖一价小于等于条件价
#define THOST_FTDC_CC_AskPriceLesserEqualStopPrice 'C'
///买一价大于条件价
#define THOST_FTDC_CC_BidPriceGreaterThanStopPrice 'D'
///买一价大于等于条件价
#define THOST_FTDC_CC_BidPriceGreaterEqualStopPrice 'E'
///买一价小于条件价
#define THOST_FTDC_CC_BidPriceLesserThanStopPrice 'F'
///买一价小于等于条件价
#define THOST_FTDC_CC_BidPriceLesserEqualStopPrice 'H'
typedef char TThostFtdcContingentConditionType;
使用Immediately时即普通报单,使用LastPriceGreaterThanStopPrice一类的将按第十个参数的止损价进行触发。
SendOrder是有返回值的,就是当前会话的第几个委托。
此报单指令还是很复杂,实盘中使用肯定过于繁琐,
建议用户在Matlab层再封一次,可行的封装方式有:
每个单子在报出后都会有委托回报,在OnRtnOrder中将单子记录下来,就可以进行撤单了
function OnRtnOrder(sender,arg)
% 委托回报
% 打印内容
disp(arg)
% 在某种情况下撤单,自己考虑各条件
%if arg.pOrder.VolumeTotal>2
global td;
% 撤单
td.CancelOrder(arg.pOrder);
%end
end
实际上,实盘中还有更多的工作要做
OnRspOrderInsert:当报单在期货公司前置机参数检测出错时返回,如资金不足等
OnErrRspOrderInset:交易所报单出错时返回,如不支持的交易指令等
OnRspOrderAction:当撤单在期货公司前置机参数校验出错时返回,如找不到报单
OnErrRspOrderAction:交易所撤单出错时返回,如报单已经成交等
证券接口开放性就有些不够了,金证、金仕达、恒生、根网、顶点都有证券柜台,但接口都不向外公开。只有国信证券向公众提供了FIX接口.2012年下半年,上期技术又打破平静,推出了CTP证券接口,特别是证券与期货接口仅仅是类型定义有所区别。直接可以将期货的代码略做修改移动到证券。
直接看项目https://github.com/QuantBox/CTPZQ/tree/master/CSharp-CTPZQ即可。