1. vnpy既可以手动下单,也可以自动下单
1.1 vnpy处理CTA交易的大致过程:
- 系统启动的过程中,OmsEngine注册系统委托单、成交单和持仓消息处理函数
- 手动或者自动发起CTP接口的委托申请,等待CTP接口的请求响应
- CTP接口接受委托申请,推送接受的委托单,OmsEngine保存并且显示委托单
- 委托单状态发生变化时,推送状态变化的委托单,OmsEngine保存并且显示委托单
- 委托单一旦成交,CTP接口推送成交单数据,OmsEngine保存并且显示成交单
- 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)