基於投入資金的CTA策略開平倉管理

張國平發表於2022-10-28

VeighNa的預設的CTA策略開平倉管理是按照手數fixedSize管理,確定固定手數的時候往往要自己心裡對應的策略投入資金,而且因為合約價格的變動和保證金率的改動,同樣手數對應的資金量也不是統一的。
而大多數時候,對於策略績效和benchmark的對比,都是基於投入資金(Nominal Amount)來講的,而且當策略數量很多時,也需要透過投入資金來做整體倉位管理,而不是粗略的手數估算。最後一個情況就是收益轉入本金的二次投資的計算,用投入資金來做CTA策略的開平倉管理比較方便。

第一步,是給個格策略定義一個統一引數記錄投入資金,可以叫 Init_Investment ,這個引數可以放在cta_template裡面定義,在cta_engine 裡面的update_strategy_setting和add_strategy方法裡面插入到setting,類似與stratege_name這樣建立必輸項, 這樣可以不用在每個具體策略裡面。當然也可以在每個策略裡面維護,這樣就方便透過修改策略引數來改變投入資金,我後面使用後者。

第二步,對於ctp介面,需要獲得保證金率,這個稍微有點麻煩,我是首先把ContractData這個合約資料類增加兩個屬性,開多保證金率和開空保證金率,浮點數預設為0

    LongMarginRatioByMoney: float = 0
    ShortMarginRatioByMoney: float = 0

在ctp_gateway, 原有程式碼是透過reqQryInstrument這個方法,這個方法如果不指定查詢物件,就可以獲得所有合約資訊,我一開始想在返回撥用方法中onRspQryInstrument加入保證金率查詢方法reqQryInstrumentMarginRate,就是返回一個合約資訊,取查詢合約對應的保證金率資訊,但是實際發現很容易被流控,返回十幾條以後就卡住了。想想怪步得這個保證金率查詢方法不讓做不指定全域性查詢。

後面就把保證金查詢方法放在訂閱行情這個方法裡面,一般要交易的品種,都要先做行情訂閱的

    def subscribe(self, req: SubscribeRequest) -> None:
        """訂閱行情"""
        self.md_api.subscribe(req)
        self.td_api.query_margin_rate(req)

然後在td_api 中,加入了兩個方法,一個是查詢,一個是返回撥用;返回撥用的方法就是在已有的contract裡面,把兩個保證金率的資訊增加到contract裡面,作為事件推出。

    def query_margin_rate(self, req):
        self.reqid += 1
        MarginRate_req = {
            "BrokerID": self.brokerid,
            "InvestorID": self.userid,
            "InstrumentID": req.symbol,
            "HedgeFlag": THOST_FTDC_HF_Speculation,
        }
        n: int = self.reqQryInstrumentMarginRate(MarginRate_req, self.reqid)
    def onRspQryInstrumentMarginRate(self, data: dict, error: dict, reqid: int, last: bool) -> None:
        contract = symbol_contract_map[data["InstrumentID"]]
        contract.LongMarginRatioByMoney = data["LongMarginRatioByMoney"]
        contract.ShortMarginRatioByMoney = data["ShortMarginRatioByMoney"]
        self.gateway.on_contract(contract)

那麼對應的,在OmsEngine裡面,就要改變下處理contract事件,如果已經有這個contract,就是嘗試更新保證金率。

    def process_contract_event(self, event: Event) -> None:
        """"""
        contract = event.data
        if contract.vt_symbol in self.contracts:
            self.contracts[contract.vt_symbol].LongMarginRatioByMoney = contract.LongMarginRatioByMoney
            self.contracts[contract.vt_symbol].ShortMarginRatioByMoney = contract.ShortMarginRatioByMoney
        else:
            self.contracts[contract.vt_symbol] = contract

第三步,在cta_template裡面新增一個方法calculation_fixedSize,來計算開倉手數,就是投入資金除以 (當前價格(這裡用bar.close_price)  合約乘數保證金率)等到一個取整。1.05可以作為一個安全係數,這樣大概在95%投入資金使用率。另外,如果每次收益虧損金額轉入到保證金,需要加一個max(0,..)來確保不會虧到開負倉。 這個方法可以放在每次發單之前,就是在cta_template的buy,short, 開倉方法中,更新發單的volume。或者還有個保險方法,就是開盤時候用tick的漲停價,確定一個當天所用fixedsize手數

    def calculation_fixedSize(self):
        contract = self.cta_engine.main_engine.get_contract(self.vt_symbol)
        if contract:
            self.fixed_size = max(0,int(self.init_investment/(bar.close_price*1.05*contract.size*contract.LongMarginRatioByMoney)))



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

相關文章