vn.py量化社区
By Traders, For Traders.
Member
avatar
加入于:
帖子: 204
声望: 47

1. vnpy既可以手动下单,也可以自动下单

1.1 vnpy处理CTA交易的大致过程:

  1. 系统启动的过程中,OmsEngine注册系统委托单、成交单和持仓消息处理函数
  2. 手动或者自动发起CTP接口的委托申请,等待CTP接口的请求响应
  3. CTP接口接受委托申请,推送接受的委托单,OmsEngine保存并且显示委托单
  4. 委托单状态发生变化时,推送状态变化的委托单,OmsEngine保存并且显示委托单
  5. 委托单一旦成交,CTP接口推送成交单数据,OmsEngine保存并且显示成交单
  6. CTP接口周期性(4秒)推送成持仓数据,OmsEngine保存并且显示持仓

1.2 既可以手动下单,也可以在自动下单

Main_Window可以手动下单,也可以在StrategyManager中启动用户策略自动下单。这些下单动作无论怎么变化,都大致经过上述的过程。

1.3 不同的策略也可以同时交易相同的合约

如果用户使用不同的CTA策略,交易相同的合约,那么在当前的主界面(Main_Window)中它们如何区分:哪一笔委托单是属于哪一个策略的?哪一笔成交单是属于哪一个策略的?

1.4 目前vnpy没有建立委托单、成交单与策略的关系

也就是说你看到主界面(Main_Window)中有一个成交单,你无法确定它是手动下单的还是自动下单的,如果有不同策略交易同一合约,你可能也无法确定是属于哪个策略的。

2. 这样修改就可以

2.1 看看cta_strategy\engine.py

    def send_server_order(
        self,
        strategy: CtaTemplate,
        contract: ContractData,
        direction: Direction,
        offset: Offset,
        price: float,
        volume: float,
        type: OrderType,
        lock: bool
    ):
        """
        Send a new order to server.
        """
        # Create request and send order.
        original_req = OrderRequest(
            symbol=contract.symbol,
            exchange=contract.exchange,
            direction=direction,
            offset=offset,
            type=type,
            price=price,
            volume=volume,
        )

        # Convert with offset converter
        req_list = self.offset_converter.convert_order_request(original_req, lock)

        # Send Orders
        vt_orderids = []

        for req in req_list:
            req.reference = strategy.strategy_name      # Add strategy name as order reference

            vt_orderid = self.main_engine.send_order(
                req, contract.gateway_name)

            # Check if sending order successful
            if not vt_orderid:
                continue

            vt_orderids.append(vt_orderid)

            self.offset_converter.update_order_request(req, vt_orderid)

            # Save relationship between orderid and strategy.
            self.orderid_strategy_map[vt_orderid] = strategy
            self.strategy_orderid_map[strategy.strategy_name].add(vt_orderid)

        return vt_orderids

这是CtaEngine的send_server_order()函数,是把委托申请发送给交易服务器的函数,其中的这一句:
req.reference = strategy.strategy_name # Add strategy name as order reference
这是原来系统就有的一句,但是不知道为什么没有利用起来,现在我把它利用起来,它可以决定一个申请是手动的还是自动的。
手动的req.reference是空字符串,自动申请的req.reference是非空的策略名称!

2.2 委托申请变身为委托单

2.2.1 CtpGateway的TdApi的send_order()

    def send_order(self, req: OrderRequest):
        """
        Send new order.
        """
        if req.offset not in OFFSET_VT2CTP:
            self.gateway.write_log("请选择开平方向")
            return ""

        self.order_ref += 1

        ctp_req = {
            "InstrumentID": req.symbol,
            "ExchangeID": req.exchange.value,
            "LimitPrice": req.price,
            "VolumeTotalOriginal": int(req.volume),
            "OrderPriceType": ORDERTYPE_VT2CTP.get(req.type, ""),
            "Direction": DIRECTION_VT2CTP.get(req.direction, ""),
            "CombOffsetFlag": OFFSET_VT2CTP.get(req.offset, ""),
            "OrderRef": str(self.order_ref),
            "InvestorID": self.userid,
            "UserID": self.userid,
            "BrokerID": self.brokerid,
            "CombHedgeFlag": THOST_FTDC_HF_Speculation,
            "ContingentCondition": THOST_FTDC_CC_Immediately,
            "ForceCloseReason": THOST_FTDC_FCC_NotForceClose,
            "IsAutoSuspend": 0,
            "TimeCondition": THOST_FTDC_TC_GFD,
            "VolumeCondition": THOST_FTDC_VC_AV,
            "MinVolume": 1
        }

        if req.type == OrderType.FAK:
            ctp_req["OrderPriceType"] = THOST_FTDC_OPT_LimitPrice
            ctp_req["TimeCondition"] = THOST_FTDC_TC_IOC
            ctp_req["VolumeCondition"] = THOST_FTDC_VC_AV
        elif req.type == OrderType.FOK:
            ctp_req["OrderPriceType"] = THOST_FTDC_OPT_LimitPrice
            ctp_req["TimeCondition"] = THOST_FTDC_TC_IOC
            ctp_req["VolumeCondition"] = THOST_FTDC_VC_CV

        self.reqid += 1
        # print(f"!!!3 ctp_req={ctp_req}")  # hxxjava debug
        self.reqOrderInsert(ctp_req, self.reqid)

        orderid = f"{self.frontid}_{self.sessionid}_{self.order_ref}"
        order = req.create_order_data(orderid, self.gateway_name)
        self.gateway.on_order(order)

        return order.vt_orderid

这个函数是委托申请后最终执行的函数,这里的这一句非常重要:
order = req.create_order_data(orderid, self.gateway_name)
就是在create_order_data()里,完成了req.reference到order.reference的传递!

2.2.2 修改OrderData

@dataclass
class OrderData(BaseData):
    """
    Order data contains information for tracking lastest status
    of a specific order.
    """

    symbol: str
    exchange: Exchange
    orderid: str

    type: OrderType = OrderType.LIMIT
    direction: Direction = None
    offset: Offset = Offset.NONE
    price: float = 0
    volume: float = 0
    traded: float = 0
    status: Status = Status.SUBMITTING
    datetime: datetime = None
    reference:str = ""      # hxxjava add

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange.value}"
        self.vt_orderid = f"{self.gateway_name}.{self.orderid}"

    def is_active(self) -> bool:
        """
        Check if the order is active.
        """
        if self.status in ACTIVE_STATUSES:
            return True
        else:
            return False

    def create_cancel_request(self) -> "CancelRequest":
        """
        Create cancel request object from order.
        """
        req = CancelRequest(
            orderid=self.orderid, symbol=self.symbol, exchange=self.exchange
        )
        return req

2.2.3 修改OrderRequest的create_order_data()

@dataclass
class OrderRequest:
    """
    Request sending to specific gateway for creating a new order.
    """

    symbol: str
    exchange: Exchange
    direction: Direction
    type: OrderType
    volume: float
    price: float = 0
    offset: Offset = Offset.NONE
    reference: str = ""

    def __post_init__(self):
        """"""
        self.vt_symbol = f"{self.symbol}.{self.exchange.value}"

    def create_order_data(self, orderid: str, gateway_name: str) -> OrderData:
        """
        Create order data from request.
        """
        order = OrderData(
            symbol=self.symbol,
            exchange=self.exchange,
            orderid=orderid,
            type=self.type,
            direction=self.direction,
            offset=self.offset,
            price=self.price,
            volume=self.volume,
            gateway_name=gateway_name,
            reference = self.reference      # hxxjava add
        )
        return order

2.3 委托单决定成交单的归属

OrderData.reference表示它属于哪个策略,或者是不是自动委托的
TradeData中通过orderid找到成交单属于哪个委托单的,进而可以间接确定它属于哪个策略

2.4 修改OrderMonitor,为其增加“策略”显示字段

class OrderMonitor(BaseMonitor):
    """
    Monitor for order data.
    """

    event_type = EVENT_ORDER
    data_key = "vt_orderid"
    sorting = True

    headers: Dict[str, dict] = {
        "orderid": {"display": "委托号", "cell": BaseCell, "update": False},
        "symbol": {"display": "代码", "cell": BaseCell, "update": False},
        "exchange": {"display": "交易所", "cell": EnumCell, "update": False},
        "type": {"display": "类型", "cell": EnumCell, "update": False},
        "direction": {"display": "方向", "cell": DirectionCell, "update": False},
        "offset": {"display": "开平", "cell": EnumCell, "update": False},
        "price": {"display": "价格", "cell": BaseCell, "update": False},
        "volume": {"display": "总数量", "cell": BaseCell, "update": True},
        "traded": {"display": "已成交", "cell": BaseCell, "update": True},
        "status": {"display": "状态", "cell": EnumCell, "update": True},
        "datetime": {"display": "时间", "cell": TimeCell, "update": True},
        "gateway_name": {"display": "接口", "cell": BaseCell, "update": False},
        "reference": {"display": "策略", "cell": BaseCell, "update": False},
    }

    def init_ui(self):
        """
        Connect signal.
        """
        super(OrderMonitor, self).init_ui()

        self.setToolTip("双击单元格撤单")
        self.itemDoubleClicked.connect(self.cancel_order)

    def cancel_order(self, cell: BaseCell) -> None:
        """
        Cancel order if cell double clicked.
        """
        order = cell.get_data()
        req = order.create_cancel_request()
        self.main_engine.cancel_order(req, order.gateway_name)

2.5 显示效果

description

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

楼主这个补充感觉很有意义,在同时执行多策略的情况下尤其必要。

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

非常好,使用了 ^_^

Member
avatar
加入于:
帖子: 27
声望: 2

小白求教,手动下单具体如何步骤实现操作?

Member
avatar
加入于:
帖子: 204
声望: 47

欢乐马1618 wrote:

小白求教,手动下单具体如何步骤实现操作?

在 1楼的2.5节的图中有个“交易”组件,在其中输入合约代码、价格、数量、选择多/空和类型,确定就可以了。
注意不同交易所支持的价格类型可能不同。

Administrator
avatar
加入于:
帖子: 4646
声望: 266

已在2.1.7中加入这块功能,非常感谢hxxjava同学

Member
avatar
加入于:
帖子: 204
声望: 47

用Python的交易员 wrote:

已在2.1.7中加入这块功能,非常感谢hxxjava同学

能够得到您的表扬,非常高兴!
能够为系统奉献一点微薄之力,荣幸之至!
感谢你们为大家带来这么棒的开源交易平台!

Member
avatar
加入于:
帖子: 27
声望: 2

hxxjava wrote:

欢乐马1618 wrote:

小白求教,手动下单具体如何步骤实现操作?

在 1楼的2.5节的图中有个“交易”组件,在其中输入合约代码、价格、数量、选择多/空和类型,确定就可以了。
注意不同交易所支持的价格类型可能不同。

感谢指导!

Member
avatar
加入于:
帖子: 14
声望: 2

这个只是当前界面运行中的交易可以显示, 关闭后, 再次打开, 历史委托的reference没法显示了吧,因为没法保存啊

Member
avatar
加入于:
帖子: 204
声望: 47

cczhu wrote:

这个只是当前界面运行中的交易可以显示, 关闭后, 再次打开, 历史委托的reference没法显示了吧,因为没法保存啊

建议:
想办法把包含reference的order和trade保存起来,再次启动vnpy的时候把历史order和trade从文件读取,并且恢复到oms的orders,trades字典里就OK了。

Member
avatar
加入于:
帖子: 29
声望: 3

mark 其他交易软件下的单,能否也能区分一下?

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

hxxjava wrote:

2.5 显示效果

description

请教楼主,最下方右侧图的,策略账户的代码方便发一下吗,学习一下,谢谢~!

Member
avatar
加入于:
帖子: 204
声望: 47

chenyi wrote:

hxxjava wrote:

2.5 显示效果

description

请教楼主,最下方右侧图的,策略账户的代码方便发一下吗,学习一下,谢谢~!

好眼力!这个小小的窗口中可是包含了策略账户的完整功能,可以管理并且策略的出入金、委托记录查询和成交记录查询统计等复杂的更能!
只是涉及修改和增加代码的地方比较多,而且我很久没有升级代码,我的策略账户的一些关键代码被vnpy官方借用,可能会和目前的系统有冲突的地方,所以一直不好跟随升级。你的系统代码应该是最新的,公布了之后可能会有比较多的麻烦,也让你为难。
等我有时间了,备份起我目前的系统的代码,升级到最新,好好查询下兼容性的问题,再上传为宜。

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

hxxjava wrote:

chenyi wrote:

hxxjava wrote:
好眼力!这个小小的窗口中可是包含了策略账户的完整功能,可以管理并且策略的出入金、委托记录查询和成交记录查询统计等复杂的更能!
只是涉及修改和增加代码的地方比较多,而且我很久没有升级代码,我的策略账户的一些关键代码被vnpy官方借用,可能会和目前的系统有冲突的地方,所以一直不好跟随升级。你的系统代码应该是最新的,公布了之后可能会有比较多的麻烦,也让你为难。
等我有时间了,备份起我目前的系统的代码,升级到最新,好好查询下兼容性的问题,再上传为宜。

感谢楼主,楼主必发大财!

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

请问手动下单显示的接口是PAPER,自动下单显示接口是CTP,如何统一

Member
avatar
加入于:
帖子: 2104
声望: 139

dharma wrote:

请问手动下单显示的接口是PAPER,自动下单显示接口是CTP,如何统一
取消勾选paper_account模块

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

沪公网安备 31011502017034号