最近想到个需求,针对期货CTP接口,比如持有三个合约,每个合约占用的资金比率。
这个需求分解下,就是资金占用,这个在期货中就是保证金金额。
在CTP接口中,OnRspQryTradingAccount 方法是返回查询账户资金信息,可以返回账号总保证金金额。
而onRspQryInvestorPosition方法是返回查询持仓情况,持仓情况包括对应合约的保证金金额,这里要注意的地方是,CTP 系统将持仓明细记录按合约,持仓方向,开仓日期(针对上期所,区分昨仓、今仓)进行汇总,而vnpy是只区分合约和合约方向,vnpy会将不同开仓时间返回的进行二次汇总。
这里处理流程就和明确了,首先增强PositionData和 AccountData 这两个数据类,加入保证金属性;然后增强ctp_gateway的对应方法中获取保证金数据;这两步比较简单,代码略。
第三步增强仓位报表PositionMonitor,增加保证金相关header数据列。然后除了默认的“EVENT_POSITION”, 还要订阅注册“EVENT_ACCOUNT",这里要重写父类方法regist_event;其实vnpy中事件注册时多对多关系,一个方法可以处理多个事件,虽然默认不鼓励。
def register_event(self):
super(PositionMonitor,self).register_event()
if self.event_type_account:
self.signal.connect(self.process_event)
self.event_engine.register(self.event_type_account, self.signal.emit)
然后复写父类方法process_event,来处理这两个事件,如果是Account事件,就更新总保证金数据,如果是合约事件,就计算对应保证金金额和总保证金百分比。
def process_event(self, event: Event) -> None:
# Disable sorting to prevent unwanted error.
if self.sorting:
self.setSortingEnabled(False)
# Update data into table.
if event.type == self.event_type_account:
self.total_margin = event.data.CurrMargin
else:
data = copy(event.data)
if self.total_margin!=0:
data.useMarginRate = '{:.2%}'.format(data.useMargin/self.total_margin)
if not self.data_key:
self.insert_new_row(data)
else:
key = data.__getattribute__(self.data_key)
if key in self.cells:
self.update_old_row(data)
else:
self.insert_new_row(data)
# Enable sorting
if self.sorting:
self.setSortingEnabled(True)
其实修改逻辑很简单,就是一个方法处理多个事件要考虑下。实现效果如下图: