这个是一个常见需求,按照策略实例记录交易条目和损益。整体的损益可以通过第三方行情软件来分析,如果有多个CTA策略实例(策略-品种-参数组合实例),如何对每个策略实例记录交易条目和损益情况。
下面代码是在vntrader 2.6 级别的实例代码,并不完全,因为改动比较多,这个仅仅是一个思路的示例。
先简单介绍下思路,记录分为3个部分,一个是新建触发停止单对象加入触发价,首单价和成交均价信息,二个把完成的触发停止单和对应的交易单保存到数据库,第三个根据每次offset的触发停止单,平仓价格数量和开仓触发停止单的开方价格数量,统计出当前的历史平仓总收益,如果没平仓就是当前价格的开仓收益,保存到json或者数据库都可以。
首先, cta_strategy/base.py 中增加一个新的event类型 EVENT_CTA_TRIGGERED_STOPORDER,用做触发的stop order事件
EVENT_CTA_TRIGGERED_STOPORDER = 'eCtaTriggeredStopOrder'
在 cta_strategy/ engine.py 中,在方法 check_stop_order加入下面代码, 这个代码是创建一个新的对象triggered_stop_order,是一个触发的stop order, 加入已成交笔数,触发价,首单价和成交均价信息。
# Update stop order status if placed successfully
if vt_orderids:
# Remove from relation map.
if vt_orderids == 1:
self.write_log(f"取消挂单,{strategy.strategy_name} {stop_order.stop_orderid}")
self.cancel_order(strategy,stop_order.stop_orderid)
return
self.stop_orders.pop(stop_order.stop_orderid)
strategy_vt_orderids = self.strategy_orderid_map[strategy.strategy_name]
if stop_order.stop_orderid in strategy_vt_orderids:
strategy_vt_orderids.remove(stop_order.stop_orderid)
# Change stop order status to cancelled and update to strategy.
stop_order.status = StopOrderStatus.TRIGGERED
stop_order.vt_orderids = vt_orderids
# Billy add to triggerred_stop_order dict
triggered_stop_order = copy(stop_order)
# triggered_stop_order.vt_orderids = list(map(lambda name: name.split(".")[1], triggered_stop_order.vt_orderids))
triggered_stop_order.stop_orderid = triggered_stop_order.stop_orderid + "." + stop_order.strategy_name + "." + '.'.join(
vt_orderids)
triggered_stop_order.datetime = copy(tick.datetime)
triggered_stop_order.completed_volume = 0
triggered_stop_order.average_price = 0
triggered_stop_order.first_price = 0
triggered_stop_order.triggered_price = tick.last_price
for vt_orderid in vt_orderids:
self.vt_orderid_triggered_stop_order[vt_orderid] = triggered_stop_order
# end Billy add
self.call_strategy_func(
strategy, strategy.on_stop_order, stop_order
)
self.put_stop_order_event(stop_order)
在方法process_trade_event, 加入下面代码,这段代码是根据在triggered_stop_order中记录的真实交易单号,如果返回的交易信息属于这个triggered_stop_order,那么记录成交数量,成交价格,算出平均价格,并且把这个trade信息保存到数据。这个保存时候把 triggered_stop_order的id放到 trade里面,方便日后关联
如果搜到的真实交易单的累计笔数和挂单笔数一致,说明挂单已经完成状态,把 triggered_stop_order放入数据
# Update strategy pos before calling on_trade method
if trade.direction == Direction.LONG:
strategy.pos += trade.volume
else:
strategy.pos -= trade.volume
self.call_strategy_func(strategy, strategy.on_trade, trade)
# Billy added, update triggered_stop_order
cta_trade = copy(trade)
allComplete = False
if cta_trade.vt_orderid in self.vt_orderid_triggered_stop_order.keys():
triggered_stop_order = self.vt_orderid_triggered_stop_order[cta_trade.vt_orderid]
if triggered_stop_order.first_price == 0:
triggered_stop_order.first_price = cta_trade.price
triggered_stop_order.completed_volume += cta_trade.volume
triggered_stop_order.average_price += cta_trade.volume * cta_trade.price
# change orderid to stop_orderid
cta_trade.orderid = triggered_stop_order.stop_orderid
# current_triggered_stop_order = triggered_stop_order
if triggered_stop_order.completed_volume == triggered_stop_order.volume:
allComplete = True
self.call_strategy_func(strategy, strategy.on_cta_trade, cta_trade)
if allComplete:
# Sync strategy variables to data file
self.sync_strategy_data(strategy)
triggered_stop_order.average_price = round(
triggered_stop_order.average_price / triggered_stop_order.completed_volume, 2)
self.put_cta_triggered_stoporder_event(triggered_stop_order)
database_manager.save_triggered_stop_order_data(copy(triggered_stop_order))
for k in triggered_stop_order.vt_orderids:
self.vt_orderid_triggered_stop_order.pop(k)
self.call_strategy_func(strategy, strategy.save_setting_file)
# Update GUI
self.put_strategy_event(strategy)
写入数据库方法这里不做累述,主要是通过event事件来触发数据库保存操作,这个显示下trade和 triggered_stop_order的数据结构示例
Trade的orderidd
{
"_id" : ObjectId("628b9523134fcca9c87671ea"),
"datetime" : ISODate("2022-05-23T22:07:31.000Z"),
"strategy" : "oneBar",
"symbol" : "rb2210",
"tradeid" : " 78259",
"exchange" : "SHFE",
"orderid" : "STOP.4.oneBar.CTP.3_-1818434211_2",
"direction" : "空",
"offset" : "开",
"price" : 4594.0,
"volume" : 1.0,
"date" : ISODate("2022-05-23T00:00:00.000Z")
}
对应的triggered_stop_order,
{
"_id" : ObjectId("628b9523134fcca9c87671ec"),
"datetime" : ISODate("2022-05-23T22:07:31.500Z"),
"stop_orderid" : "STOP.4.oneBar.CTP.3_-1818434211_2",
"strategy_name" : "oneBar",
"vt_symbol" : "rb2210.SHFE",
"direction" : "空",
"offset" : "开",
"price" : 4594.0,
"volume" : 1.0,
"lock" : false,
"net" : false,
"vt_orderids" : [
"CTP.3_-1818434211_2"
],
"status" : "已触发",
"completed_volume" : 1.0,
"average_price" : 4594.0,
"first_price" : 4594.0,
"triggered_price" : 4594.0
}
至于之后查询很多方法,可以用第三方分析软件,也可以简单做个页面。
至于损益的分析,可以 根据每次offset的触发停止单,平仓价格数量和开仓触发停止单的开方价格数量,统计出当前的历史平仓总收益,如果没平仓就是当前价格的开仓收益,保存到json或者数据库都可以。
这里要注意的是移仓操作,可能对导致开平仓交易对对上。所以在移仓也要做对应的增强。具体我后面写一下增强的移仓代码