美文网首页数字货币量化交易
针对VNPY的软件bugs的修改总结

针对VNPY的软件bugs的修改总结

作者: 51bitquant | 来源:发表于2020-12-06 22:16 被阅读0次

    针对VNPY的软件bugs的修改总结

    前言

    本人fork了VNPY的2.1.7版本,但是为了别人能够安装和使用官方的版本,所以取名叫howtrader, 意思就是如何Trader,如何成为一个quant或者trader。

    但是在使用和测试的过程中,发现针对币安的api部分,有不少的bug进行修改。现将部分的修改总结一下。

    bugs分析和修复过程

    1. k先部分,VNPY Trader是根据tick的数据来合成分钟的K线,然后再讲分钟的K线合成更高界别的K线数据。但是币安的api他们推送的ticker数据是滚动24小时的ticker数据,成交的价格和量都是过去24小时的数据,数据格式如下:
     {
     "e": "24hrTicker",  // 事件类型
     "E": 123456789,     // 事件时间
     "s": "BNBBTC",      // 交易对
     "p": "0.0015",      // 24小时价格变化
     "P": "250.00",      // 24小时价格变化(百分比)
     "w": "0.0018",      // 平均价格
     "x": "0.0009",      // 整整24小时之前,向前数的最后一次成交价格
     "c": "0.0025",      // 最新成交价格
     "Q": "10",          // 最新成交交易的成交量
     "b": "0.0024",      // 目前最高买单价
     "B": "10",          // 目前最高买单价的挂单量
     "a": "0.0026",      // 目前最低卖单价
     "A": "100",         // 目前最低卖单价的挂单量
     "o": "0.0010",      // 整整24小时前,向后数的第一次成交价格
     "h": "0.0025",      // 24小时内最高成交价
     "l": "0.0010",      // 24小时内最低成交加
     "v": "10000",       // 24小时内成交量
     "q": "18",          // 24小时内成交额
     "O": 0,             // 统计开始时间
     "C": 86400000,      // 统计结束时间
     "F": 0,             // 24小时内第一笔成交交易ID
     "L": 18150,         // 24小时内最后一笔成交交易ID
     "n": 18151          // 24小时内成交数
    }
    

    根据这样的ticker数据处理,实际上得不到分钟的数据的。所以需要订阅分钟的数据,然后拿到分钟的数据进行合成。

    1. vnpy的分钟合成小时数据实际上它是慢了一分钟的的,我们看下它合成的逻辑
    def update_bar(self, bar: BarData) -> None:
            """
            Update 1 minute bar into generator
            """
            # If not inited, creaate window bar object
            if not self.window_bar:
                # Generate timestamp for bar data
                if self.interval == Interval.MINUTE:
                    dt = bar.datetime.replace(second=0, microsecond=0)
                else:
                    dt = bar.datetime.replace(minute=0, second=0, microsecond=0)
    
                self.window_bar = BarData(
                    symbol=bar.symbol,
                    exchange=bar.exchange,
                    datetime=dt,
                    gateway_name=bar.gateway_name,
                    open_price=bar.open_price,
                    high_price=bar.high_price,
                    low_price=bar.low_price
                )
            # Otherwise, update high/low price into window bar
            else:
                self.window_bar.high_price = max(
                    self.window_bar.high_price, bar.high_price)
                self.window_bar.low_price = min(
                    self.window_bar.low_price, bar.low_price)
    
            # Update close price/volume into window bar
            self.window_bar.close_price = bar.close_price
            self.window_bar.volume += int(bar.volume)
            self.window_bar.open_interest = bar.open_interest
    
            # Check if window bar completed
            finished = False
    
            if self.interval == Interval.MINUTE:
                # x-minute bar
                if not (bar.datetime.minute + 1) % self.window:
                    finished = True
            elif self.interval == Interval.HOUR:
                if self.last_bar and bar.datetime.hour != self.last_bar.datetime.hour:
                    # 1-hour bar
                    if self.window == 1:
                        finished = True
                    # x-hour bar
                    else:
                        self.interval_count += 1
    
                        if not self.interval_count % self.window:
                            finished = True
                            self.interval_count = 0
    
            if finished:
                self.on_window_bar(self.window_bar)
                self.window_bar = None
    
            # Cache last bar object
            self.last_bar = bar
    

    主要看这个代码, 它是根据当前小时和上一个小时数据不同的时候,就是一小时的数据,但是币安的K线数的时间是开始的时间的,所以一小时的结束的时候,实际上是59分钟,也就是59分钟的时候这个小时就走完了,另外成交量是可以float的类型,它强制转成int的类型,也是不对的。

    if self.interval == Interval.MINUTE:
                # x-minute bar
            if not (bar.datetime.minute + 1) % self.window:
                    finished = True
    elif self.interval == Interval.HOUR:
                if self.last_bar and bar.datetime.hour != self.last_bar.datetime.hour:
                    # 1-hour bar
                    if self.window == 1:
                        finished = True
                    # x-hour bar
                    else:
                        self.interval_count += 1
    
                        if not self.interval_count % self.window:
                            finished = True
                            self.interval_count = 0
    
    

    正确的做法是:

    if self.interval == Interval.MINUTE:
                # x-minute bar
                if not (bar.datetime.minute + 1) % self.window:
                    finished = True
    elif self.interval == Interval.HOUR:
                # if self.last_bar and bar.datetime.hour != self.last_bar.datetime.hour: # vnpy的判断条件
                if (bar.datetime.minute == 59 and bar.interval == Interval.MINUTE) or (self.last_bar and bar.datetime.hour != self.last_bar.datetime.hour and bar.interval == Interval.HOUR):
                    # if the bar is one minute, then the 59minute is the last one bar for one hour.
                    # 1-hour bar
                    if self.window == 1:
                        finished = True
                    # x-hour bar
                    else:
                        self.interval_count += 1
    
                        if not self.interval_count % self.window:
                            finished = True
                            self.interval_count = 0
    
    1. 关于订单的on_trade的推送
      on_trade事件的推送非常重要,如果on_trade的事件没有收到,那么策略的仓位数据pos是计算错误的。

    VNPY的on_trade更新只来自服务器的推送,如果不凑巧,某个时间你刚好成交了,但是由于你的websocket断开,你没有收到服务器推送的on_trade的更新,导致你的策略的pos计算错误。

    所以解决问题的方法是,我们更过监听on_trade的方法,然后计算每次订单的成交量来计算on_trade, 同时我们会在主引擎那里,定时去查询挂单时间比较长的订单,防止由于服务器的断开导致我们的订单状态更新没有收到。

    1. 关于推送的on_position
      同样on_position是我们做合约非常重要的数据,我们可能成交了,但是position的数据由于websocket没有及时的推送,另外VNPY查询的on_position如果通过rest api查询的话,实际上它只推送position不为零的数据,这个也不太合理,合理的做法是我们也应该推送订阅行情的仓位数据,这样如果我们的平仓的时候,我们的仓位也是为零的,但是也应该推送给我们,这样我们才能监听到我们的具体仓位的变化。

    代码更新

    具体查看github的代码

    VNPY学习资料

    课程封面.png

    请参考网易云课堂的课程视频

    相关文章

      网友评论

        本文标题:针对VNPY的软件bugs的修改总结

        本文链接:https://www.haomeiwen.com/subject/hnyawktx.html