3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
你好!这是一个在 JoinQuant(聚宽)平台编写策略时非常经典且常见的问题。Python 对日期时间类型的严格限制导致了 datetime.datetime 和 datetime.date 无法直接相减。你通过构造新的 datetime 对象来解决问题的思路是完全正确的,但确实还有更简洁、更高效的“最佳实践”。
下面针对你的四个问题逐一解答:
最简洁的方式不是把 date 升级为 datetime,而是把 datetime 降级为 date。你可以直接调用 context.current_dt.date() 方法,这样代码会非常优雅:
# 最佳实践:将 current_dt 转换为 date 类型再相减
if (context.current_dt.date() - get_security_info(stock).start_date).days < 60:
continue
如果你在循环内部每次都调用 context.current_dt.date(),虽然单次转换的开销极小,但在大量股票和长回测周期下,依然是不必要的重复计算。
性能优化建议:将当前日期的获取提取到循环外部(因为在同一个 handle_data 或 before_trading_start 周期内,当前日期是不变的)。
# 性能优化写法
current_date = context.current_dt.date() # 提取到循环外,O(1) 复杂度
for stock in stock_list:
start_date = get_security_info(stock).start_date
if (current_date - start_date).days < 60:
continue
# 后续的动量和估值逻辑...
强烈不建议使用字符串比较来计算时间差。
字符串比较(如 "2023-10-01" > "2023-08-01")只能用于判断日期的先后顺序,但无法直接计算出相差的天数。如果你需要判断“是否小于60天”,字符串是无能为力的,必须依赖 datetime 模块的 timedelta 对象(即相减后调用 .days)。
聚宽 API 中没有直接名为 get_listed_days(stock) 的函数,目前获取上市日期的标准做法就是你所使用的 get_security_info(stock).start_date。
⚠️ 一个重要的业务逻辑提示:自然日 vs 交易日
你目前的代码 (current_date - start_date).days < 60 计算的是自然日(Calendar Days)。通常情况下,剔除上市不足60天的自然日已经足够满足大部分策略的需求。
但如果你严格要求剔除上市不足60个交易日(Trading Days)的新股,你需要使用聚宽的 get_trade_days API:
# 严格计算交易日天数(注意:此方法在循环内调用性能开销较大,不推荐对全市场股票频繁使用)
trade_days_since_listed = len(get_trade_days(start_date, current_date))
if trade_days_since_listed < 60:
continue
实战妥协方案:由于 60 个交易日大约等于 85 个自然日,量化实战中为了追求极速的回测性能,通常直接用自然日近似替代:
# 用 85 个自然日近似替代 60 个交易日,性能极高
if (current_date - start_date).days < 85:
continue
结合动量与估值策略,你的选股过滤模块可以这样写:
def filter_new_stocks(context, stock_list):
"""剔除上市不足60个自然日的新股"""
current_date = context.current_dt.date() # 循环外获取当前日期
valid_stocks = []
for stock in stock_list:
# 获取上市日期
start_date = get_security_info(stock).start_date
# 降级相减,计算自然日差值
if (current_date - start_date).days >= 60:
valid_stocks.append(stock)
return valid_stocks
希望这个解答能帮到你!祝你的多因子策略早日跑出漂亮的超额收益(Alpha)!