VeighNa量化社区
你的开源社区量化交易平台
Member
加入于:
帖子: 158
声望: 71

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)))
Member
avatar
加入于:
帖子: 1486
声望: 106

感谢分享!

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】