给回测K线图表增加自定义交易标记
效果图
用法
在cta策略中的buy等下单函数中,增加一个str类型的mark入参,作为自定义标记,即可在回测K线图上看到自定义的标记
如
self.buy(bar.high_price+30,self.fixed_size,mark="买点")
涉及修改的文件
- vnpy.app.cta_strategy.template.py
- vnpy.app.cta_strategy.backtesting.py
- vnpy.trader.object.py
- vnpy.app.cta_strategy.base.py
- vnpy.app.cta_backtester.ui.widget.py
在K线图表中绘制标记字段
vnpy.app.cta_backtester.ui.widget.BacktesterManager.show_candle_chart()函数绘制K线图及交易标记,我们要修改的是交易标记。
trades数据需要回测引擎提供
绘制由vnpy.app.cta_strategy.ui.widget.CandleChartDialog.update_trades()实现,参照原来的volume交易数量绘制格式,补充交易标记绘制即可
# 交易标记
openmark = d["open_mark"]
closemark = d["close_mark"]
if len(openmark)>0:
text_color = QtGui.QColor(scatter_color)
openmark_text = pg.TextItem(f"[{openmark}]", color=text_color, anchor=(0.5, 0.5))
openmark_text.setPos(open_ix, open_y - open_side * y_adjustment * 6)
self.items.append(openmark_text)
candle_plot.addItem(openmark_text)
if len(closemark)>0:
text_color = QtGui.QColor(scatter_color)
closemark_text = pg.TextItem(f"[{closemark}]", color=text_color, anchor=(0.5, 0.5))
closemark_text.setPos(close_ix, close_y - close_side * y_adjustment * 6)
self.items.append(closemark_text)
candle_plot.addItem(closemark_text)
update_trades()中的trade_pairs由generate_trade_pairs()生成,故还需要修改generate_trade_pairs()函数
d = {
"open_dt": open_trade.datetime,
"open_price": open_trade.price,
"close_dt": trade.datetime,
"close_price": trade.price,
"direction": open_trade.direction,
"volume": close_volume,
"open_mark": open_trade.mark,
"close_mark": trade.mark
}
在回测引擎中增加标记字段
vnpy.app.cta_strategy.ui.widget.BacktesterManager.show_candle_chart()---->
vnpy.app.cta_backtester.engine.BacktestEngine.get_all_trades()---->
vnpy.app.cta_strategy.backtesting.BacktestingEngine.get_all_trades()---->
vnpy.app.cta_strategy.backtesting.BacktestingEngine.trades
需要在self.trades字典的values中增加字段
而给self.trades字典增加trade元素的有两个函数cross_limit_order()、cross_stop_order()
修改这两个函数即可(增加标记字段)
cross_limit_order()
trade = TradeData(
symbol=order.symbol,
exchange=order.exchange,
orderid=order.orderid,
tradeid=str(self.trade_count),
direction=order.direction,
offset=order.offset,
price=trade_price,
volume=order.volume,
datetime=self.datetime,
gateway_name=self.gateway_name,
mark=order.mark
)
cross_stop_order()
order = OrderData(
symbol=self.symbol,
exchange=self.exchange,
orderid=str(self.limit_order_count),
direction=stop_order.direction,
offset=stop_order.offset,
price=stop_order.price,
volume=stop_order.volume,
traded=stop_order.volume,
status=Status.ALLTRADED,
gateway_name=self.gateway_name,
datetime=self.datetime,
mark = stop_order.mark
)
trade = TradeData(
symbol=order.symbol,
exchange=order.exchange,
orderid=order.orderid,
tradeid=str(self.trade_count),
direction=order.direction,
offset=order.offset,
price=trade_price,
volume=order.volume,
datetime=self.datetime,
gateway_name=self.gateway_name,
mark = order.mark
)
两个函数中trade数据都来自order
而order又来自self.active_limit_orders和self.active_stop_orders两个字典
给self.active_limit_orders字典增加order元素的函数是send_limit_order()
给self.active_stop_orders字典增加order元素的函数是send_stop_order()
需要修改这send_limit_order(),send_stop_order()两个函数以增加标记字段,并给vnpy.trader.object.OrderData,vnpy.trader.object.TradeData,vnpy.app.cta_strategy.base.StopOrder增加字段mark。
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 = ""
mark: str = ""
class TradeData(BaseData):
"""
Trade data contains information of a fill of an order. One order
can have several trade fills.
"""
symbol: str
exchange: Exchange
orderid: str
tradeid: str
direction: Direction = None
offset: Offset = Offset.NONE
price: float = 0
volume: float = 0
datetime: datetime = None
mark: str = ""
def send_limit_order(
self,
direction: Direction,
offset: Offset,
price: float,
volume: float,
mark: str
):
""""""
self.limit_order_count += 1
order = OrderData(
symbol=self.symbol,
exchange=self.exchange,
orderid=str(self.limit_order_count),
direction=direction,
offset=offset,
price=price,
volume=volume,
status=Status.SUBMITTING,
gateway_name=self.gateway_name,
datetime=self.datetime,
mark=mark
)
self.active_limit_orders[order.vt_orderid] = order
self.limit_orders[order.vt_orderid] = order
return order.vt_orderid
def send_stop_order(
self,
direction: Direction,
offset: Offset,
price: float,
volume: float,
mark: str
):
""""""
self.stop_order_count += 1
stop_order = StopOrder(
vt_symbol=self.vt_symbol,
direction=direction,
offset=offset,
price=price,
volume=volume,
stop_orderid=f"{STOPORDER_PREFIX}.{self.stop_order_count}",
strategy_name=self.strategy.strategy_name,
mark=mark
)
self.active_stop_orders[stop_order.stop_orderid] = stop_order
self.stop_orders[stop_order.stop_orderid] = stop_order
return stop_order.stop_orderid
调用send_limit_order(),send_stop_order()这两个函数的函数是send_order()函数
即也需要修改send_order()函数
def send_order(
self,
strategy: CtaTemplate,
direction: Direction,
offset: Offset,
price: float,
volume: float,
stop: bool,
lock: bool,
mark: str = ""
):
""""""
price = round_to(price, self.pricetick)
if stop:
vt_orderid = self.send_stop_order(direction, offset, price, volume ,mark)
else:
vt_orderid = self.send_limit_order(direction, offset, price, volume ,mark)
return [vt_orderid]
回测引擎中的send_order()函数被CTA策略调用
需要修改vnpy.app.cta_strategy.template.CtaTemplate策略模板中的buy()、sell()、short()、cover()、send_order()函数
def buy(self, price: float, volume: float, stop: bool = False, lock: bool = False, mark: str = ""):
"""
Send buy order to open a long position.
"""
return self.send_order(Direction.LONG, Offset.OPEN, price, volume, stop, lock, mark)
在实盘引擎中忽略新增的标记字段
因为实盘引擎中也有send_order()函数,且在实盘时也被调用,所以要对额外的标记字段做忽略处理
为了简化修改,也不想动实盘引擎减少风险,所以这个修改也在vnpy.app.cta_strategy.template.CtaTemplate策略模板中实现,修改send_order()函数
即根据EngineType区分
def send_order(
self,
direction: Direction,
offset: Offset,
price: float,
volume: float,
stop: bool = False,
lock: bool = False,
mark: str = "" # 增加标记字段
):
"""
Send a new order.
"""
if self.trading:
engine_Type = self.get_engine_type()
if engine_Type == EngineType.BACKTESTING:
vt_orderids = self.cta_engine.send_order(
self, direction, offset, price, volume, stop, lock, mark
)
else:
vt_orderids = self.cta_engine.send_order(
self, direction, offset, price, volume, stop, lock
)
return vt_orderids
else:
return []