使用Matlab進行國內期貨交易 作者:伍侃

第一節國內期貨櫃臺系統介紹

綜合交易平臺CTP

上海期貨信息技術有限公司(上海期貨交易所旗下子公司)開發的期貨經紀業務管理系統。在API的設計、業務模式、開放性上都比國內其它系統走得更遠,大部分期貨公司都支持CTP,目前已經是國內期貨程序化交易接入的事實標準。同時上期技術在證券API上也做了一定的工作,證券接口也已經發布。

金仕達:

市場佔有率極高的櫃檯系統,最初僅有B2B網關,用戶接入時必須同期貨公司商談,並在期貨公司機房內網架設服務器。在2012年時發佈了B2CKSFT_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 x64Windows x86iOS。微軟官方已經提到過,在64位進程中不能加載32位的dll,同理一個32位進程也不能加載一個64dll。所以在Windows平臺下采用一般的dll調用方式也就被限制在了主程序爲32位程序。其實,分別使用32位和64位兩個進程通訊的方式能解決這個問題。

 

在這,我們使用dll調用的方式。先確保自己安裝的是32位的Matlab,如果你是在64Windows上直接安裝,默認是安裝的64位系統,請進入到Matlab的安裝目錄,找到bin/win32下的setup.exe進行安裝。

第三節各種對接方式

  1. MEX版接口

運行效率最高,但開發起來工作量大,要做大量的數據結構轉換。目前已經有公司或個人推出了MEX版。

  1. 進程間通訊

這種方式比較靈活,對接64位平臺或者跨操作系統、跨主機都是沒有問題的,但在運行效率上略爲遜色。已經有網友提供了通用版本接口,即可以Matlab調用,也可以R語言調用。

  1. COM版接口

COM接口在Windows平臺下還是有一定的使用範圍的,MatlabExcel等都可以對接COM接口,目前網上可以下載到上海匯朋提供的盈佳COM接口。網址:http://www.winnerfutures.com.cn/

  1. Java版接口

目前已經有少量網友開源了Java對接CTP的接口,但Matlab對接Java接口的還沒有推出。同時轉換的技術也有多種,如JNABridJ

網址:      https://github.com/QuantBox/CTP/tree/master/Java-CTPJNA

http://download.csdn.net/detail/vcfriend/5054163BridJ

  1. 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呢?一般異步通知有兩種方式:一種是偏底層的函數回調,一種是偏高層的事件通知。

  1. 函數回調。C#版接口不用修改,直接用P/Invoke的方式,將函數句柄直接通過賦值的方式傳給最底層的C接口。可惜,實際測試行不通。表面上運行正常,能輸出行情數據,但過不了十幾秒Matlab就閃退。推斷原因是回調函數被Matlab清理回收了,C層記錄的函數據句柄在運行十幾秒後就無效了。
  2. 事件通知。此方式也有必須注意的地方。Matlab支持addlistener,但直接模仿上面的回調函數的參數接口進行調用會報錯,最下面的說明了爲何會報錯。http://www.mathworks.cn/cn/help/matlab/matlab_external/working-with-net-events-in-matlab.html

即事件所使用的委託的簽名必需要用指定的格式:兩個參數,第一個參數是object sender,而第二個參數必需繼承於.NETEventArgs類。

檢查這些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版都進了屏蔽。

  1. 請求ID,同一會話中嚴格單調遞增
  2. 報單引用,同一會話中嚴格單調遞增
  3. 發送請求流控,如果有在途的查詢,不允許發新的查詢。1秒鐘最多允許發送1個查詢。
  4. 部分期貨公司要求先驗證客戶端授權然後才能登錄
  5. 登錄成功後必須要結算單確認後才能下單
  6. 行情與交易的流文件同目錄可能引起數據紊亂
  7. 接收到的響應需立即處理,不然會阻塞後面的數據接收

 

主要添加的功能如下:

  1. 發送隊列:報單、撤單直接發送,而其它的請求都先添加到發送隊列,由發送線程去發送,發送失敗後自動延時重發。解決了CTP有流控的問題。
  2. 接收隊列:收到響應後,直接存到隊列中,立即返回,然後其它線程從隊列中取。解決用戶代碼用時過久產生未知錯誤的問題。
  3. 維護請求ID與報單引用,自動加鎖,不再糾結於細節,不會出現重複報單。
  4. 自動進行連接、客戶端授權、登錄認證、結算單確認等工作。保證用戶登錄成功後就能直接下單。
  5. 斷線重連後,行情與交易能重新登錄認證,其中行情接口還能自動訂閱斷線前已經訂閱的行情。
  6. 對行情與交易流文件自動分目錄,解決數據紊亂問題

第七節監控軟件的使用

在介紹Matlab對接.NET前,一定得先介紹監控軟件,否則在下一節要介紹的開發上完全是在摸黑。

能實現監控的原理是:

  1. CTP_API支持同一賬號多次登錄,目前期貨公司大多設置的是同時最大6個會話登錄
  2. 委託回報與成交回報等流會發向所有會話

所以在程序化交易時,另用一款比較好的手動交易軟件來監控是不錯的方式。可以查看委託狀態、委託價、成交回報等信息,方便查找錯誤。目前推薦使用快期。

同時,我們對接CTP平臺需要服務器的配置信息,在快期目錄下有brokers.xml,其中有三樣東西最重要:經紀商編號(BrokerID),行情服務器地址(MarketData)、交易服務器地址(Trading)

這個地方要注意,不管brokers.xml中地址如何寫,地址開頭沒有“tcp://”,實際使用CTP_API時就得補上,如果以“udp://”開頭,改成“tcp://”也能正常使用,在下一節的代碼中有模擬盤的地址示例。

第八節 Matlab對接期貨接口

https://github.com/QuantBox/CTP/tree/master/Matlab-DotNet

請保證相關文件都是最新的。

thostmduserapi.dllthosttraderapi.dll來自於上期技術

QuantBox.C2CTP.dll來自於C版接口

QuantBox.CSharp2CTP.dll來自於C#版接口

 

test.m是程序入口,做了以下工作

  1. 導入C#
  2. 創建行情對象、交易對象的實例
  3. 註冊事件
  4. 登錄
  5. 退出(已經註釋,沒有執行,需手工輸入退出)

 

%% 導入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接口已經向我們屏蔽,用戶只要需要知道如何使用。第一個參數就是流文件路徑,在此不用擔心行情與交易的流相互影響。

第七個參數用戶端產品信息,用戶可以用來標識軟件產品

第八個參數認證碼,只有在期貨公司要求並分配認證碼後才能填寫,需要與用戶端產品信息配合使用。

 

addlistenerMatlab提供的註冊事件的方法,第一個參數是需要註冊事件的對象,第二個參數是事件名,第三個參數是處理函數。

 

對於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;
 

OnConnectOnDisconnect是行情與交易都支持的事件。但在實際使用時還是有區別的。

交易有可能要進行客戶端認證,所以可能出現與客戶端認證有關的狀態E_authingE_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;

 

第八個參數是時間類型,根據國內交易所的情況,目前支持GFDIOC

第九、十參數是條件單使用,首先大商所支持止盈止損單,鄭商所支持止損單。

/////////////////////////////////////////////////////////////////////////
///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層再封一次,可行的封裝方式有:

  1. Buy/Sell,僅記錄淨持倉
  2. OpenLong/CloseLongOpenShort/CloseShort,區分了雙向持倉
  3. SetPostion,不管操作,只在乎最後持倉
  4. ……

 

每個單子在報出後都會有委託回報,在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即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章