3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在 JoinQuant(聚宽)量化平台中,history 和 get_price 都是用于获取历史行情数据的常用 API,但它们在底层设计逻辑、对未来函数的防范以及多标的数据对齐方式上存在显著差异。这就是导致你回测结果大不一样的根本原因。
具体来说,主要有以下几个核心不同点:
history:这是专门为回测和模拟交易设计的 API。为了严格防止“未来函数”,history 获取天数据时,永远不包括当天的数据(即使是在收盘后运行的函数中)。它获取的是截止到昨天为止的 count 个交易日的数据。get_price:这是一个通用的数据获取接口。它的截止时间由 end_date 参数决定。如果你传入的 date 是当天的日期(例如 context.current_dt),get_price 会返回包含当天的数据。如果在盘中(如 9:30)获取到了当天的收盘价,这就引入了未来函数,会导致回测收益异常虚高,而实盘完全无法复现。skip_paused=True 导致的多标的数据错位(最致命的区别)在你的代码中,history 传入了多只股票(g.stocks),并且设置了 skip_paused=True。这会导致极其严重的数据对齐问题:
history 的行为:当获取多只股票且设置 skip_paused=True 时,由于不同股票在历史上的停牌日期是不一样的,为了凑齐每只股票的 400 个有效交易日,返回的 DataFrame 的行索引将不再是日期,而是简单的数字索引(0, 1, 2...)。这意味着,DataFrame 中同一行的数据,实际上可能属于完全不同的日期!如果你按行计算均线或进行横截面比较,逻辑将完全错乱。get_price 的行为:在你的代码中,get_price 只传入了一只股票(g.stocks[0])。对于单只股票,跳过停牌日只是简单地剔除那些没有交易的日期,返回的 DataFrame 行索引依然是真实的交易日期,数据是完全准确且时间对应的。history:当你指定 field='close' 且传入多只股票时,返回的 DataFrame 的列是股票代码,行是时间(或数字索引),里面的值全是收盘价。get_price:当你传入单只股票且 fields=['close'] 时,返回的 DataFrame 的列是字段名('close'),行是时间。你的回测结果大不一样,主要是因为 get_price 可能引入了未来函数,以及 history 在 skip_paused=True 时发生了多股票时间轴错位。
正确的做法建议:
history 或 attribute_history,因为它们天然防范未来函数。skip_paused=True:在获取多只股票的数据时,强烈建议保持 skip_paused=False(默认值)。这样停牌日会用前一天的收盘价填充,保证所有股票在同一个日期索引下严格对齐。# 推荐的多标的获取方式(不对齐会导致严重逻辑错误)
df = history(400, unit='1d', field='close', security_list=g.stocks, df=True, skip_paused=False, fq='pre')
get_price,请务必确保 end_date 设置为前一个交易日(context.previous_date),以避免未来函数:# 避免未来函数的 get_price 用法
df = get_price(g.stocks, end_date=context.previous_date, frequency='daily', fields=['close'], count=400, panel=False)