VeighNa量化社区
你的开源社区量化交易平台
reg
Member
离线
11 帖子
声望: 2

用Python的交易员 wrote:

啊,请在Github开个Issue吧,我们安排同事来处理

https://github.com/vnpy/vnpy/issues/2738

xiaohe wrote:

  1. 你的日线数据是几点开始的呢?

我在github上提交了issue,具体重现方式可以查看issue上的描述。很奇怪的就是日数据的时间都是00:00的,只要把时间的类型从datetime.date变为datetime.datetime就正常了。

用Python的交易员 wrote:

啊,请在Github开个Issue吧,我们安排同事来处理

已提交issue。

加载数据周期:日K线
数据库:sqlite
今天偶然发现的,自己写的脚本回测和官方图形化引擎的回测结果有出入,加载的是从2015年1月5日至今的数据。细查发现官方回测引擎加载的数据少了不少天数,例如2015年2月4日的日K线数据就没有加载。最后定位到下方的源码:

# vnpy/app/cta_backtester/ui/widget.py BacktesterManager start_backtesting()
start = self.start_date_edit.date().toPyDate()
end = self.end_date_edit.date().toPyDate()

官方回测引擎的load_data()方法是按30天为周期加载数据的,而2015年2月4日刚好是2015年1月5日开始的第30天,在边界值上。这里是将图形化界面了日期编辑栏转换为Python的datetime.date类型。不知道为什么导致了漏数据。我将其修改为

# vnpy/app/cta_backtester/ui/widget.py BacktesterManager start_backtesting()
start = self.start_date_edit.dateTime().toPyDateTime()
end = self.end_date_edit.dateTime().toPyDateTime()

转换为datetime.datetime类型,官方图形化回测引擎加载的数据量就和自己的脚本一致了,2015年2月4日的K线也能看到了。不清楚其它数据周期会不会存在这个问题。

字面意思,这是说你的IP不是教育网的IP吧,学术账号必须通过教育网来访问RQData的接口

官方图形化回测界面是vnpy/app/cta_backtester/ui/widget.py中的BacktesterManager__init__()方法最后调用逻辑是这样的:

# vnpy/app/cta_backtester/ui/widget.py BacktesterManager __init__()
self.init_ui() # 初始化界面
self.register_event()
self.backtester_engine.init_engine()
self.init_strategy_settings() # 初始化策略配置

init_ui()方法里有一段代码似乎是从cta_backtester_setting.json中读取上一次回测时使用的策略,配置为默认策略:

# vnpy/app/cta_backtester/ui/widget.py BacktesterManager init_ui()
# Load setting
setting = load_json(self.setting_filename)
if not setting:
    return

self.class_combo.setCurrentIndex(
    self.class_combo.findText(setting["class_name"])
)

self.class_combo此时还没有添加任何策略选项,所以self.class_combo.findText(setting["class_name"])始终返回的是-1,没有实现设置当前策略为上一次回测策略的效果。

self.class_combo是在init_strategy_settings()方法里才添加了策略选项:

# vnpy/app/cta_backtester/ui/widget.py BacktesterManager
def init_strategy_settings(self):
    """"""
    self.class_names = self.backtester_engine.get_strategy_class_names()

    for class_name in self.class_names:
        setting = self.backtester_engine.get_default_setting(class_name)
        self.settings[class_name] = setting

    self.class_combo.addItems(self.class_names) # 给`self.class_combo`添加策略选项

所以可能应该将设置当前策略为上一次回测策略的代码改到init_strategy_settings()方法里:

def init_strategy_settings(self):
    """"""
    self.class_names = self.backtester_engine.get_strategy_class_names()

    for class_name in self.class_names:
        setting = self.backtester_engine.get_default_setting(class_name)
        self.settings[class_name] = setting

    self.class_combo.addItems(self.class_names) # 给`self.class_combo`添加策略选项
    # Load setting
    setting = load_json(self.setting_filename)
    # 设置当前策略为上一次回测策略
    self.class_combo.setCurrentIndex(
        self.class_combo.findText(setting["class_name"])
    )

今天回测的策略需要用到日K线,仿照默认BarGeneratorupdate_bar()方法的小时单位K线合成的逻辑,再下面添加了合成日K线的代码:

# vnpy/trader/utility.py BarGenerator update_bar()
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
elif self.interval == Interval.DAILY:
    # 以下是我添加的
    if self.last_bar and bar.datetime.day != self.last_bar.datetime.day:
        if self.window == 1:
            finished = True
        else:
            self.interval_count += 1

            if not self.interval_count % self.window:
                finished = True
                self.interval_count = 0

回测发现合成的日K线和从其它平台获取的有出入,特别是开盘价和收盘价都完全不同。仔细看了一下update_bar()方法逻辑,发现在判断是否合成完毕前,函数会将最新的bar的数据添加到self.window_bar里。这里貌似存在以下问题,例如我要合成1小时K线,那么9点开始的这个1小时K线的时间范围应该(按我理解)是从9:00到9:59。在发现新的barbar.datetime.hour值不是9而是10时,9点的K线合成完毕,下面是合成10点这1小时的K线。

但是update_bar()方法首先将最新的bar的数据添加到self.window_bar里,再进行判断,会导致10点的第1个bar被计算到了9点的1小时K线里。这也是为什么我直接在下面添加合成日线的逻辑,出来的日线数据会和实际数据的开盘、收盘价完全不同的原因,因为每次的开盘bar都被算到了前1个合成K线的收盘bar里,而收盘bar都算进了下一个合成K线的开盘bar里。

我想可能将判断是否合成完毕的代码放到update_bar()方法开头会比较合理,但我不知道自己的想法有没有错,纯作交流:

# vnpy/trader/utility.py BarGenerator
def update_bar(self, bar: BarData) -> None:
    """
    Update 1 minute bar into generator
    """

    if self.window_bar is not None:
        finished = False
        if self.interval == Interval.MINUTE:
            # 对于分钟,只能合成60分钟以内的K线
            # 不知道这么写对不对
            if not (bar.datetime.minute) % 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
        elif self.interval == Interval.DAILY:
            if self.last_bar and bar.datetime.day != self.last_bar.datetime.day:
                if self.window == 1:
                    finished = True
                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

    # 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)
        elif self.interval == Interval.HOUR:
            dt = bar.datetime.replace(minute=0, second=0, microsecond=0)
        elif self.interval == Interval.DAILY:
            dt = bar.datetime.replace(hour=0, 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

    # Cache last bar object
    self.last_bar = bar

另外按照回测引擎的逻辑,会导致最后1天的日K线无法合成出来,比如我要回测4月20日到4月27日,由于回测时4月27日没有bar数据进入,就会导致4月26日的日K线生成条件bar.datetime.day != self.last_bar.datetime.day不会满足,即使4月26日的数据跑完了,也不会出来4月26日的日K线数据(小时K线也是同理的),不知道这算不算bug。

用Python的交易员 wrote:

已经在dev分支修复,会在下个版本2.1.1中附上。需要的话直接手动更新下rqdata.py即可

好的,感谢回复

手动把open_interest这个字段去掉貌似可以正常跑回测。。。

只能多调用write_log来排查了

今天想试一下股票回测,但报open_interest字段非法的错误。想提问的是vnpy自带的回测平台只支持期货回测吗?是否可以支持股票回测?

过程是这样的:回测图形界面里填写的本地代码是688166.SSE

description

然后点下载数据,就报错了:

description

意思是rqdata返回说open_interest字段非法。

查看了一下回测引擎从rqdata获取数据的代码。发现vnpy.trader.rqdata.RqdataClientquery_history()方法里,从rqdata请求的字段是写死的:

# vnpy.trader.rqdata.RqdataClient query_history()
# ...
df = rqdata_get_price(
    rq_symbol,
    frequency=rq_interval,
    fields=["open", "high", "low", "close", "volume", "open_interest"],
    start_date=start,
    end_date=end,
    adjust_type="none"
)

固定请求["open", "high", "low", "close", "volume", "open_interest"]

查看rqdata的官方文档,open_interest字段是一个期货专用的字段:

description

单独测试rqdata的代码,不请求open_interest字段,数据是能正常返回不报错的,但带上就会报同样的错误:

import rqdatac as rq
from rqdatac import *
rq.init()
# 下面这句代码不报错
rq.get_price('688166.XSHG', frequency='1m', fields=["open", "high", "low", "close", "volume"],start_date='2020-01-01', end_date='2020-02-25', adjust_type='none')
# 下面这句代码报错
rq.get_price('688166.XSHG', frequency='1m', fields=["open", "high", "low", "close", "volume", "open_interest"], start_date='2020-01-01', end_date='2020-02-25', adjust_type='none')

所以想提问的是vnpy自带的回测平台只支持期货回测吗?是否可以支持股票回测?

© 2015-2022 微信 18391752892
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

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