大家知道,期货CTP接口是由上期技术公司提供的,它提供的源码和范例都是用C++语言写的,这在应用上有一定局限性。比如我在实盘中需要数据库,需要程序化,需要K线图,需要这样那样的功能,下单之前要做一堆一堆的事……用C++来写会是很麻烦的。但是C#不怕做这些麻烦事,C#就是用来干脏活累活的,你把界面、应用逻辑啥的都交给C#,C++就只要管好自己的一件事就行了——怎么和交易所对话,这样,工作量会小得多。
实际上对大多数人来说,没有C#这样的高级语言,根本就干不了活儿。举个例子来说,我要下单,我用C++怎么下单,在命令行窗口里,输入合约代码,输入价格,输入手数……噼里啪啦一通猛敲键盘,才能把一个单子下完,是吧?但在c#界面里,只需要点一个按钮:
就是“挂买”这个按钮,我点一下,一瞬间,买进就完成了。在这一瞬间,程序为我干了一连串的事:
● 看看我要买的是哪个合约;
● 读取该合约最新盘口,读取预设的挂单偏移值,算出挂单价格;
● 读取默认手数;
● 准备报单,更新报单序号,记录报单信息;
● 对此单进行预检(有无自成交风险、报单序号是否合规、资金是否足够、是否在交易时间、其他方方面面是否合规);
● 是否有旧单要撤(有时需要撤完旧单再下新单,比如平仓时);
● 账户中是否有反向单需要对冲,如果需要,最终是开仓还是平仓;
● 向交易模块发单;
● 向C++模块发单;
● 向交易所报单。
就这一系列的事情,C++所做的只有最后一件,向交易所报单,也就是ReqOrderInsert。
前面的所有啰嗦事,都由C#包办了。
如果都用C++来写,工作量可想而知(实际上对大多数程序员来说是不可行的)。
虽然事情看起来多,但刚才的那一连串,是在约十毫秒内发生的。
这十毫秒的事情,即使用C#语言来写,也是很长很长的,我们把它精简一下,先不考虑什么报单记录、报单校验之类的了,假设点了按钮直接就报单,便于我们看清C#与C++的关系,那么——
C#中的代码是:
///C++所输出的dll的位置
const string dllPath = @"CTP_se.dll";
///引用C++中的下单函数(在C++中,下单的入口是函数“_ReqOrderInsert”
[DllImport(dllPath, EntryPoint = "_ReqOrderInsert")]
///C#的下单函数,“extern”标签决定了它要按照上一句话的规定引用C++中的函数“_ReqOrderInsert”
static extern void ReqOrderInsert(
char[] BrokerID, //经纪公司代码(字符串,必填)
char[] InvestorID, //投资者代码(字符串,必填)
char[] InstrumentID, //合约代码(字符串,必填)
char[] ExchangeID, //交易所代码(字符串,必填)
char[] OrderRef, //报单引用(字符串,必填)
char[] UserID, //用户代码(字符串,不需要就填“null”)
int OrderPriceType, //报单价格条件(必填,1为任意价,2为限价,3为最优价,4为最新价,
//5为最新价浮动上浮1个ticks,6为最新价浮动上浮2个ticks,7为最新价浮动上浮3个ticks,8为卖一价,
//9为卖一价浮动上浮1个ticks,10为卖一价浮动上浮2个ticks,11为卖一价浮动上浮3个ticks,12为买一价,
//13为买一价浮动上浮1个ticks,14为买一价浮动上浮2个ticks,15为买一价浮动上浮3个ticks,16为五档价)
int Direction, //买卖方向(1为买 ,-1为卖)
int CombOffsetFlag, //组合开平标志(必填,1为开,0为平,-1为平今,-2为平昨,-3为强平,-4为强减,-5为本地强平)
int CombHedgeFlag, //组合投机套保标志(必填,0为投机,1为套利,2为套保)
double LimitPrice, //价格(实数,必填)
int VolumeTotalOriginal, //手数(必填)
int TimeCondition, //有效期类型(必填,1为立即完成,否则撤销,2为本节有效,3为当日有效,一般填3
//4为指定日期前有效,5为撤销前有效,6为集合竞价有效)
char[] GTDDate, //GTD日期(字符串,不需要就填“null”)
int VolumeCondition, //成交量类型(必填,1为任何数量 ,2为最小数量 ,3为全部数量,一般填1)
int MinVolume, //最小成交量(必填)
int ContingentCondition, //触发条件(必填,1为立即,2为止损,3为止赢,4为预埋单,一般填1
//5为最新价大于条件价,6为最新价大于等于条件价,7为最新价小于条件价,8为最新价小于等于条件价,
//9为卖一价大于条件价,10为卖一价大于等于条件价,11为卖一价小于条件价,12为卖一价小于等于条件价,
//13为买一价大于条件价,14为买一价大于等于条件价,15为买一价小于条件价,16为买一价小于等于条件价)
double StopPrice, //止损价(实数,不需要就填“0”)
int ForceCloseReason, //强平原因(必填,0为非强平 ,1为资金不足 ,2为客户超仓 ,一般填0
//3为会员超仓 ,4为持仓非整数倍 ,5为违规 ,6为其它 ,7为自然人临近交割 )
int IsAutoSuspend, //自动挂起标志(整数,必填,一般填0,表示不自动挂起)
char[] BusinessUnit, //业务单元(字符串,不需要就填“null”)
int RequestID, //请求编号(整数,不需要就填“Int32.MinValue”)
int UserForceClose, //用户强评标志(整数,必填,一般填0,表示否)
int IsSwapOrder, //互换单标志(整数,不需要就填“Int32.MinValue”)
int tdClientI //交易通讯线程序号
);
private void 挂买按钮_Click(object sender, EventArgs e)
{
string brokerID = "9999";
string userID = "071988";
string insID = "ni2003";
string exchangeID = "SHFE";
string ordID = "10000";
int direction = 1;
int oc = 1;
double price = 108590;
int lot = 1;
int tdClientI = 0;
ReqOrderInsert(brokerID.ToCharArray(), userID.ToCharArray(), insID.ToCharArray(), exchangeID.ToCharArray(), ordID.ToCharArray(), null,
2, direction, oc, 0, price, lot, 3, null, 1, 1, 1, 0, 0, 0, null, Int32.MinValue, 0, Int32.MinValue, tdClientI);
}
而在C++中,与上述C#功能对接的函数,名叫“_ReqOrderInsert”:
///C++内部的报单函数
int ReqOrderInsert(CThostFtdcInputOrderField* pInputOrder, int tdClientI) {
string str_ref(pInputOrder->OrderRef);
string insID(pInputOrder->InstrumentID);
logWithTimeBUI(tdClientI, insID, "尝试报单 ordID." + str_ref);
tdApiList