在·VNPY中按照CTA策略例項記錄交易條目和損益

張國平發表於2022-09-30


這個是一個常見需求,按照策略例項記錄交易條目和損益。整體的損益可以透過第三方行情軟體來分析,如果有多個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)



寫入資料庫方法這裡不做累述,這個顯示下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或者資料庫都可以。


這裡要注意的是移倉操作,可能對導致開平倉交易對對上。所以在移倉也要做對應的增強。具體我後面寫一下增強的移倉程式碼


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2916987/,如需轉載,請註明出處,否則將追究法律責任。

相關文章