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

针对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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容