问题描述
我最近做了一个趋势策略;使用相同的止损标准,例如都是亏损5%或者回撤5%卖出;我的卖出时机有两种,第一种是9:26分以当日开盘价计算亏损或者回撤,然后在开盘后卖出;另一种是9:30开盘获取实时股票价格,计算亏损或者回撤,然后确定是否卖出。其中其他的变量都是一致的;
最终我回测时前者年化收益很低,后者年化收益很高;各位大佬能帮我分析下原因吗?
解决方案
在聚宽(JoinQuant)平台上进行回测时,系统本身不会“自动”或“恶意”地加入未来函数,但由于日线级别回测的底层数据结构以及API调用方式的特殊性,开发者在编写代码时非常容易不小心引入未来数据。
针对你描述的现象(9:26计算收益低,9:30计算收益极高),这几乎可以断定是在9:30的逻辑中引入了未来函数。以下是导致这种巨大差异的核心原因分析及排查建议:
1. 为什么9:30的逻辑极有可能引入了未来函数?
如果你使用的是按天回测(日频),在 handle_data 或 run_daily(..., time='09:30') 中获取当天的“实时价格”时,极易踩中以下几个“未来函数”陷阱:
- 误用全天均价或收盘价: 在日频回测的 9:30,如果你通过
data[security].price或data[security].avg获取价格,根据聚宽API文档,日频下的avg代表的是该股票全天的成交均价(成交额/成交量)。在早上9:30使用全天均价来判断是否止损,等于提前知道了全天的走势。 - 误取当天的最高/最低/收盘价: 如果你在9:30调用
get_price或attribute_history,且没有严格限制时间戳,可能会直接取到包含当天close、low、high的整根日K线。用当天的最低价(low)或收盘价(close)来计算回撤,意味着你的策略在9:30就已经“预知”了今天盘中会跌到多少,从而精准逃顶或止损,这会导致年化收益率出现极其夸张的飙升。
2. 为什么9:26的逻辑收益较低(但更真实)?
在 9:26(通常在 before_trading_start 或 run_daily(..., time='09:26') 中运行),当天的日K线尚未生成。此时你只能通过 get_current_data().day_open 获取到集合竞价产生的开盘价,或者使用昨天的收盘价。
- 数据真实: 此时你无法获取当天的
low或close,因此计算出的亏损/回撤是基于真实的开盘价,没有任何未来数据。 - 撮合机制: 在开盘前(9:26)下的市价单,会在9:30开盘时以开盘价(或加上滑点)进行撮合。这种交易逻辑完全符合真实市场的运作规律,因此测算出来的收益率虽然低,但往往是真实且可靠的。
3. 如何排查和解决这个问题?
为了验证并修复你的策略,建议采取以下几个步骤:
第一步:开启“避免未来数据”模式
在策略的 initialize 函数中,强制开启聚宽提供的防未来函数选项。如果你的9:30逻辑确实用到了未来数据,系统会直接报错抛出 FutureDataError:
def initialize(context):
# 开启避免未来数据模式
set_option("avoid_future_data", True)
第二步:检查9:30获取价格的代码
仔细检查你在9:30获取“实时价格”的代码:
- 错误做法(日频下含未来函数):
current_price = data[security].price或current_price = data[security].close - 正确做法(获取真实最新价): 使用
get_current_data()获取。
current_data = get_current_data()
current_price = current_data[security].last_price # 9:30时通常等于开盘价
第三步:使用“按分钟回测”进行日内止损
如果你希望在盘中(如跌破阈值的瞬间)实时止损,绝对不能使用按天回测。日频回测只能在每天的一个固定时间点(通常是开盘)做一次决策。
- 解决方案: 将回测频率改为按分钟回测(Minute)。在分钟回测下,
data[security].close代表的是上一分钟的收盘价,这样你在 9:30 或盘中任何一分钟计算回撤时,使用的数据都是严格截止到上一分钟的,彻底杜绝了日内未来函数。
总结
你所观察到的“9:30收益极高”,99%是因为在日频回测的9:30调用了包含当天全天信息的字段(如日均价、日最低价、日收盘价)作为止损依据。请开启 avoid_future_data 选项,并确保日内止损逻辑在分钟级回测下运行,以获得具有实盘指导意义的真实收益率。