问题描述
新建空模板,然后分别在init和open阶段调用这个函数,发现没复权
print(get_price('300600.XSHE', fq='pre', count=2, end_date='2019-2-1'))
这是bug吗还是个feature?
console输出如下
2019-02-03 00:00:00 - INFO - open close high low volume money
2019-01-31 11.31 11.36 11.46 11.11 4320218.0 48569830.37
2019-02-01 11.40 12.12 12.12 11.22 7174664.0 83992505.24
2019-02-11 09:30:00 - INFO - open close high low volume money
2019-01-31 20.53 20.63 20.81 20.17 2379710.0 48569830.37
2019-02-01 20.70 22.00 22.00 20.37 3952027.0 83992505.24
2019-02-12 09:30:00 - INFO - open close high low volume money
2019-01-31 20.53 20.63 20.81 20.17 2379710.0 48569830.37
2019-02-01 20.70 22.00 22.00 20.37 3952027.0 83992505.24
完整代码如下
# 导入函数库
from jqdata import *
# 初始化函数,设定基准等等
def initialize(context):
print(get_price('300600.XSHE', fq='pre', count=2, end_date='2019-2-1'))
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
# log.set_level('order', 'error')
### 股票相关设定 ###
# 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
## 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入'000300.XSHG'或'510300.XSHG'是一样的)
# 开盘前运行
run_daily(before_market_open, time='before_open', reference_security='000300.XSHG')
# 开盘时或每分钟开始时运行
run_daily(market_open, time='every_bar', reference_security='000300.XSHG')
# 收盘后运行
run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')
## 开盘前运行函数
def before_market_open(context):
pass
## 开盘时运行函数
def market_open(context):
print(get_price('300600.XSHE', fq='pre', count=2, end_date='2019-2-1'))
## 收盘后运行函数
def after_market_close(context):
pass
解决方案
这不是 Bug,而是 JoinQuant 平台动态复权机制(use_real_price)设计的一个 Feature(特性)。
导致你在 initialize 和 market_open 阶段打印出的前复权价格不一致的根本原因,在于 set_option('use_real_price', True) 的调用时机以及复权基准日(fq_ref_date)的变化。
深入原因解析
-
initialize阶段(未开启真实价格模式)
在你的代码中,print(get_price(...))写在了set_option('use_real_price', True)之前。此时,回测引擎处于默认的传统前复权模式。
根据 JoinQuant API 文档说明,在未开启use_real_price时,前复权的基准日期是建立回测的现实日期(即你坐在电脑前点击“运行回测”的今天)。因此,你看到的11.xx是基于现实今天的复权因子计算出的前复权价格。 -
market_open阶段(已开启真实价格模式)
当代码执行到market_open时,use_real_price已经被设置为True。此时引擎进入了动态复权(真实价格)模式。
文档中明确指出:开启真实价格回测后,通过get_price拿到的前复权价格,是**基于回测当天的逻辑日期(context.current_dt)**作为基准日的前复权价格。在你的日志中,逻辑时间是2019-02-11,所以你看到的20.xx是基于2019-02-11这一天的复权因子计算出的前复权价格。 -
为什么价格会变?
因为300600.XSHE在2019-02-11到你运行回测的现实日期之间,发生了分红送转(除权除息)。基准日不同,计算出来的前复权历史价格自然就不同了。
解决方案与建议
如果你希望在 initialize 阶段获取的数据与盘中一致,请将 set_option('use_real_price', True) 放在 initialize 函数的第一行:
def initialize(context):
# 1. 必须先开启动态复权模式
set_option('use_real_price', True)
# 2. 然后再获取数据
print(get_price('300600.XSHE', fq='pre', count=2, end_date='2019-2-1'))
# 其他初始化设置...
set_benchmark('000300.XSHG')
官方文档特别提醒:
开启真实价格回测之后,你在不同日期调用 get_price 返回的前复权价格可能是不一样的(因为随着时间推移,如果遇到除权除息,前复权价格会发生变化)。因此,千万不要跨日期缓存这些 API 返回的前复权结果,每次需要历史数据时,请在当前逻辑时间点重新调用 API 获取。