我在将vnpy用c++重构重新设计多线程策略模型,过程中参考vnpy的CTP发单逻辑,发现只支持限价单,这里我完善了一下,希望对大家有帮助,如果逻辑上有问题望大家不吝赐教,谢谢
enum定义
make_enum(OrderStatus,
SUBMITTING, //提交中
NOTTOUCH, //未触发
TOUCH, //已触发
NOTTRADED, //未成交
PARTTRADED, //部分成交
ALLTRADED, //全部成交
CANCELLED, //已撤销
REJECTED, //拒单
);
//Order type
make_enum(OrderType,
MARKET, //市价
LIMIT, //限价
COND, //止损(市价)
CONDLIMIT, //止损(限价)
FAK, //fill-and-kill(立即完成任何数量剩余撤销)
FOK, //fill-or-kill (立即完成全部数量否则撤销)
);
//Contingent condition
make_enum(Condition,
IMME, //立即
GT, //最新价大于条件价
GE, //最新价大于等于条件价
LT, //最新价小于条件价
LE, //最新价小于等于条件价
);
//order time-in-force
make_enum(OrderTimeInForce,
GFD, //good-for-day
GTC, //good-til-canceled
);
报单函数
OrderData CCtpTdApi::ReqOrderInsert(const OrderRequest& req)
{
//委托报单
auto& config = m_gateway.config();
int frontid = m_frontid;
int sessionid = m_sessionid;
int order_ref = ++m_order_ref;
auto field = util::zero_declear<CThostFtdcInputOrderField>();
util::copy_field(config.brokerid, field.BrokerID);
util::copy_field(config.accountid, field.InvestorID);
util::copy_field(config.accountid, field.UserID);
util::copy_field(req.symbol, field.InstrumentID);
util::copy_field(util::enum_cast<Exchange>::name(req.exchange), field.ExchangeID);
util::copy_field(std::to_string(order_ref), field.OrderRef);
util::copy_field(ORDERTYPE_VT2CTP.at(req.type), field.OrderPriceType);
util::copy_field(DIRECTION_VT2CTP.at(req.direction), field.Direction);
util::copy_field(OFFSET_VT2CTP.at(req.offset), field.CombOffsetFlag[0]);
util::copy_field(req.price, field.LimitPrice);
util::copy_field(int(req.volume), field.VolumeTotalOriginal);
util::copy_field(0, field.IsAutoSuspend); //自动挂起标志
util::copy_field(0, field.IsSwapOrder); //互换单标志: 交易所的移仓换月功能
util::copy_field(THOST_FTDC_FCC_NotForceClose, field.ForceCloseReason); //强平原因: 非强平
util::copy_field(THOST_FTDC_HF_Speculation, field.CombHedgeFlag[0]); //投机套保标志: 投机
util::copy_field(CONDITION_VT2CTP.at(req.condition), field.ContingentCondition); //触发条件
util::copy_field(THOST_FTDC_VC_$, field.VolumeCondition); //成交量类型: 任何数量
//CTP报单 https://www.zhihu.com/people/nicai0609/posts
//CTP指令 https://www.shinnytech.com/blog/ctp-insert-order-field/
//CTP条件单 https://www.bilibili.com/read/cv7692977
//CTP客户端开发指南 https://www.eastmoneyfutures.com/software/9809db08-c8cb-4cc4-b631-8a16f0c2dfb9/0f757a06-10a5-48c2-817e-362b42202b51/a7a19350-141d-48c0-$6-253cb3ec5b18/CTPcdg_ch_2016-10-27.pdf
if (req.type == OrderType::MARKET) { //报价类型: THOST_FTDC_OPT_AnyPrice
util::copy_field(0.0, field.LimitPrice);
util::copy_field(THOST_FTDC_TC_IOC, field.TimeCondition); //有效期类型: immediate-or-cancel
}
else if (req.type == OrderType::LIMIT) { //报价类型: THOST_FTDC_OPT_LimitPrice
util::copy_field(THOST_FTDC_TC_GFD, field.TimeCondition); //有效期类型: 当日有效
}
else if (req.type == OrderType::COND) { //报价类型: THOST_FTDC_OPT_AnyPrice
util::copy_field(0.0, field.LimitPrice);
util::copy_field(req.stop_price, field.StopPrice);
util::copy_field(THOST_FTDC_TC_GTC, field.TimeCondition); //有效期类型: 撤销前有效
}
else if (req.type == OrderType::CONDLIMIT) { //报价类型: THOST_FTDC_OPT_LimitPrice
util::copy_field(req.stop_price, field.StopPrice);
util::copy_field(THOST_FTDC_TC_GTC, field.TimeCondition); //有效期类型: 撤销前有效
}
else if (req.type == OrderType::FAK) { //报价类型: THOST_FTDC_OPT_LimitPrice
util::copy_field(THOST_FTDC_TC_IOC, field.TimeCondition); //有效期类型: immediate-or-cancel
if (util::math::is_finite(req.min_volume)) {
util::copy_field(int(req.min_volume), field.MinVolume); //最小成交量
util::copy_field(THOST_FTDC_VC_MV, field.VolumeCondition); //成交量类型: 最小数量
}
}
else if (req.type == OrderType::FOK) { //报价类型: THOST_FTDC_OPT_LimitPrice
util::copy_field(THOST_FTDC_TC_IOC, field.TimeCondition); //有效期类型: immediate-or-cancel
util::copy_field(THOST_FTDC_VC_CV, field.VolumeCondition); //成交量类型: 全部数量
}
OrderData order = req.create_order_data(util::format_string("%d_%d_%d", frontid, sessionid, order_ref));
int ret = m_api->ReqOrderInsert(&field, m_reqid++);
if (ret != 0) {
order.errors = make_str("下单接口失败", ret);
order.status = OrderStatus::REJECTED;
}
return order;
}
回调
void CCtpTdApi::OnRtnOrder(CThostFtdcOrderField* pOrder)
{
//委托更新推送
if (!is_ready()) {
m_cache_orders.push_back(std::make_shared<CThostFtdcOrderField>(*pOrder));
return;
}
OrderData order;
order.gateway_name = m_gateway.name();
order.accountid = pOrder->InvestorID;
order.symbol = pOrder->InstrumentID;
order.exchange = util::enum_cast<Exchange>::type(pOrder->ExchangeID);
order.traded = pOrder->VolumeTraded;
order.type = ORDERTYPE_CTP2VT(pOrder);
order.direction = DIRECTION_CTP2VT.at(pOrder->Direction);
order.offset = OFFSET_CTP2VT.at(pOrder->CombOffsetFlag[0]);
order.condition = CONDITION_CTP2VT.at(pOrder->ContingentCondition);
order.price = pOrder->LimitPrice;
order.volume = pOrder->VolumeTotalOriginal;
order.stop_price = pOrder->StopPrice;
order.min_volume = pOrder->MinVolume;
order.status = ORDERSTATUS_CTP2VT.at(pOrder->OrderStatus);
order.datetime = util::time(std::string(pOrder->InsertDate) + pOrder->InsertTime, "%Y%m%d%H:%M:%S");
//该笔报单请求首次到达CTP,风控通过后返回的第1个OnRtnOrder回报,此时因为还没有报入到交易所,所以回报中OrderSysID为空
/*条件订单:
FrontID|SessionID|OrderRef|LimitPrice|ContiningentCondition|StopPrice|OrderSysID |RelativeOrderSysID|OrderStatus
1 -14427505 1 9695 6 9690 TJBD_0000105 尚未触发
1 -14427505 1 9095 6 9690 TJBD_0000105 已触发
0 0 3 9095 1 0 TJBD_0000105 未知
0 0 3 9095 1 0 154169 TJBD_0000105 未成交还在队列
0 0 3 9095 1 0 154169 TJBD_0000105 未成交还在队列
0 0 3 9095 1 0 154169 TJBD_0000105 全部成交
*/
if (pOrder->FrontID != 0 && pOrder->SessionID != 0) {
order.orderid = util::format_string("%d_%d_%s", pOrder->FrontID, pOrder->SessionID, pOrder->OrderRef);
if (pOrder->OrderSysID[0] != 0) {
m_sysid_orderid[pOrder->OrderSysID] = order.orderid;
}
}
else if (pOrder->RelativeOrderSysID[0] != 0) {
auto it_orderids = m_sysid_orderid.find(pOrder->RelativeOrderSysID);
if (it_orderids != m_sysid_orderid.end()) {
order.orderid = it_orderids->second;
}
else {
log_warn("订单找不到关联SysID", pOrder->FrontID, pOrder->SessionID, pOrder->OrderRef,
pOrder->OrderSysID, pOrder->RelativeOrderSysID, order);
}
}
else {
log_warn("订单ID拼接失败", pOrder->FrontID, pOrder->SessionID, pOrder->OrderRef,
pOrder->OrderSysID, pOrder->RelativeOrderSysID, order);
}
m_gateway.on_order(order);
}