VeighNa量化社区
你的开源社区量化交易平台
Member
avatar
加入于:
帖子: 2
声望: 1

我在将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);
}
Administrator
avatar
加入于:
帖子: 4501
声望: 321

感谢分享!精华送上

Member
avatar
加入于:
帖子: 9
声望: 0

dbevf1 wrote:

我在将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);
}

请问您试过中金所那两个有限制的市价单模式(THOST_FTDC_OPT_FiveLevelPrice和THOST_FTDC_OPT_BestPrice么)?
我遇到了些问题,详细见我发的贴子。有兴趣的话可以一起研究下。
https://www.vnpy.com/forum/topic/29575-zhong-jin-suo-de-wu-dang-shi-jie-dan-thost-ftdc-opt-fivelevelprice

Member
avatar
加入于:
帖子: 4
声望: 0

很好

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】