问题描述
回测时context.portfolio.total_value这个数值不会改变是为什么?
解决方案
在 JoinQuant(聚宽)平台上进行回测时,如果发现 context.portfolio.total_value(账户总权益)始终保持初始资金不变,通常是因为策略在回测期间没有产生任何实际的成交。总权益等于可用资金加上持仓价值,如果没有买入任何标的,总权益自然不会随市场波动而改变。
以下是导致该问题的常见原因及排查方法:
1. 交易条件从未被触发
这是最常见的原因。您的策略代码中可能包含了某些买入条件(例如:均线金叉、MACD突破等),但在您设定的回测时间段内,这些条件从未被满足。
- 排查方法:在触发下单的代码块前加上
log.info("准备下单"),查看日志中是否有该输出。如果没有,说明逻辑判断条件过严或有误。
2. 下单失败或订单未成交
即使代码执行到了 order 系列函数,订单也可能因为各种原因未能成交:
- 资金不足:尝试买入的数量超过了
context.portfolio.available_cash。 - 标的停牌或退市:下单的股票在当天处于停牌状态,无法撮合成交。
- 涨跌停限制:在未开启盘口撮合时,如果以市价单买入涨停股,或卖出跌停股,订单会被系统自动撤销。
- 限价单未达触发价:设置的限价单价格偏离市场价过大,导致一直挂单直到收盘被撤销。
- 排查方法:在
after_trading_end或handle_data中打印订单状态:orders = get_orders() for order_id, order in orders.items(): log.info("订单状态: %s, 未成交原因: %s" % (order.status, order.filled))
3. 标的代码错误或不存在
如果您硬编码了股票代码(如 g.security = '000001'),但忘记加后缀(正确的应为 '000001.XSHE'),系统会找不到该标的,导致下单失败(通常日志会有 Warning 提示)。
4. 打印时机不对
如果您只在 initialize 函数中打印了 context.portfolio.total_value,那么它只会输出一次初始资金。initialize 在整个回测中只运行一次。
- 排查方法:确保您是在
handle_data或run_daily调用的定时函数中打印总权益:def handle_data(context, data): log.info("当前总权益: %f" % context.portfolio.total_value)
5. 未开启动态复权(真实价格)模式
虽然这通常不会导致总权益完全不变,但可能会导致买入数量计算异常,进而影响成交。建议在 initialize 中始终开启真实价格模式:
set_option('use_real_price', True)
总结建议:请重点检查回测日志(Log)中的 Warning 信息,聚宽系统通常会在下单失败(如资金不足、停牌、涨跌停)时输出黄色的警告日志。通过分析日志,您可以快速定位是逻辑未触发还是订单被拒。