第一節國內期貨櫃臺系統介紹
綜合交易平臺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版接口
運行效率最高,但開發起來工作量大,要做大量的數據結構轉換。目前已經有公司或個人推出了MEX版。
- 進程間通訊
這種方式比較靈活,對接64位平臺或者跨操作系統、跨主機都是沒有問題的,但在運行效率上略爲遜色。已經有網友提供了通用版本接口,即可以Matlab調用,也可以R語言調用。
- COM版接口
COM接口在Windows平臺下還是有一定的使用範圍的,Matlab、Excel等都可以對接COM接口,目前網上可以下載到上海匯朋提供的盈佳COM接口。網址:http://www.winnerfutures.com.cn/
- Java版接口
目前已經有少量網友開源了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版接口
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做了自己的細節處理。
第四節 C#版對接原理
使用.NET版的好處就是省事,這麼多款.NET版,選一款能對接Matlab,使用簡單,自己能理解的代碼庫就成。
如何判斷是否能對接Matlab呢?一般異步通知有兩種方式:一種是偏底層的函數回調,一種是偏高層的事件通知。
- 函數回調。C#版接口不用修改,直接用P/Invoke的方式,將函數句柄直接通過賦值的方式傳給最底層的C接口。可惜,實際測試行不通。表面上運行正常,能輸出行情數據,但過不了十幾秒Matlab就閃退。推斷原因是回調函數被Matlab清理回收了,C層記錄的函數據句柄在運行十幾秒後就無效了。
- 事件通知。此方式也有必須注意的地方。Matlab支持addlistener,但直接模仿上面的回調函數的參數接口進行調用會報錯,最下面的說明了爲何會報錯。http://www.mathworks.cn/cn/help/matlab/matlab_external/working-with-net-events-in-matlab.html
即事件所使用的委託的簽名必需要用指定的格式:兩個參數,第一個參數是object sender,而第二個參數必需繼承於.NET的EventArgs類。
檢查這些C#版的接口,只要是指定格式的委託簽名就可以了。
第五節 QuantBox版項目介紹
在這我只介紹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版本的特點是沒有直接將C++版本的接口進行轉換,而是做了一定的處理。加上這些處理的理由很充分,就是簡化邏輯,讓其它語言對接CTP時能更簡單。
首先我們來看CTP接口開發要注意哪些關鍵地方,在其它網友公佈的直接接口轉換的封裝,都要自行處理這些繁瑣細節,但本人提供的C版都進了屏蔽。
- 請求ID,同一會話中嚴格單調遞增
- 報單引用,同一會話中嚴格單調遞增
- 發送請求流控,如果有在途的查詢,不允許發新的查詢。1秒鐘最多允許發送1個查詢。
- 部分期貨公司要求先驗證客戶端授權然後才能登錄
- 登錄成功後必須要結算單確認後才能下單
- 行情與交易的流文件同目錄可能引起數據紊亂
- 接收到的響應需立即處理,不然會阻塞後面的數據接收
主要添加的功能如下:
- 發送隊列:報單、撤單直接發送,而其它的請求都先添加到發送隊列,由發送線程去發送,發送失敗後自動延時重發。解決了CTP有流控的問題。
- 接收隊列:收到響應後,直接存到隊列中,立即返回,然後其它線程從隊列中取。解決用戶代碼用時過久產生未知錯誤的問題。
- 維護請求ID與報單引用,自動加鎖,不再糾結於細節,不會出現重複報單。
- 自動進行連接、客戶端授權、登錄認證、結算單確認等工作。保證用戶登錄成功後就能直接下單。
- 斷線重連後,行情與交易能重新登錄認證,其中行情接口還能自動訂閱斷線前已經訂閱的行情。
- 對行情與交易流文件自動分目錄,解決數據紊亂問題
第七節監控軟件的使用
在介紹Matlab對接.NET前,一定得先介紹監控軟件,否則在下一節要介紹的開發上完全是在摸黑。
能實現監控的原理是:
- CTP_API支持同一賬號多次登錄,目前期貨公司大多設置的是同時最大6個會話登錄
- 委託回報與成交回報等流會發向所有會話
所以在程序化交易時,另用一款比較好的手動交易軟件來監控是不錯的方式。可以查看委託狀態、委託價、成交回報等信息,方便查找錯誤。目前推薦使用快期。
同時,我們對接CTP平臺需要服務器的配置信息,在快期目錄下有brokers.xml,其中有三樣東西最重要:經紀商編號(BrokerID),行情服務器地址(MarketData)、交易服務器地址(Trading)。
這個地方要注意,不管brokers.xml中地址如何寫,地址開頭沒有“tcp://”,實際使用CTP_API時就得補上,如果以“udp://”開頭,改成“tcp://”也能正常使用,在下一節的代碼中有模擬盤的地址示例。
第八節 Matlab對接期貨接口
https://github.com/QuantBox/CTP/tree/master/Matlab-DotNet
請保證相關文件都是最新的。
thostmduserapi.dll、thosttraderapi.dll來自於上期技術
QuantBox.C2CTP.dll來自於C版接口
QuantBox.CSharp2CTP.dll來自於C#版接口
test.m是程序入口,做了以下工作
- 導入C#庫
- 創建行情對象、交易對象的實例
- 註冊事件
- 登錄
- 退出(已經註釋,沒有執行,需手工輸入退出)
%% 導入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層再封一次,可行的封裝方式有:
- Buy/Sell,僅記錄淨持倉
- OpenLong/CloseLong、OpenShort/CloseShort,區分了雙向持倉
- SetPostion,不管操作,只在乎最後持倉
- ……
每個單子在報出後都會有委託回報,在OnRtnOrder中將單子記錄下來,就可以進行撤單了
function OnRtnOrder(sender,arg)
% 委託回報
% 打印內容
disp(arg)
% 在某種情況下撤單,自己考慮各條件
%if arg.pOrder.VolumeTotal>2
global td;
% 撤單
td.CancelOrder(arg.pOrder);
%end
end
實際上,實盤中還有更多的工作要做
OnRspOrderInsert:當報單在期貨公司前置機參數檢測出錯時返回,如資金不足等
OnErrRspOrderInset:交易所報單出錯時返回,如不支持的交易指令等
OnRspOrderAction:當撤單在期貨公司前置機參數校驗出錯時返回,如找不到報單
OnErrRspOrderAction:交易所撤單出錯時返回,如報單已經成交等
第九節 Matlab對接證券
證券接口開放性就有些不夠了,金證、金仕達、恆生、根網、頂點都有證券櫃檯,但接口都不向外公開。只有國信證券向公衆提供了FIX接口.2012年下半年,上期技術又打破平靜,推出了CTP證券接口,特別是證券與期貨接口僅僅是類型定義有所區別。直接可以將期貨的代碼略做修改移動到證券。
直接看項目https://github.com/QuantBox/CTPZQ/tree/master/CSharp-CTPZQ即可。