🚀 AI 一键生成 joinquant 策略代码

JoinQuant 策略报错 TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'datetime.date' 怎么解决?

joinquant | 发布时间: 2026/5/20
以下内容由 EasyQuant 生成。

问题描述

动量估值策略中日期类型错误如何处理?

大家好,我是一名量化新手,正在学习使用聚宽平台编写多因子策略。最近我在尝试实现一个结合动量(20日涨幅)和估值(PE/PB)的选股策略,并希望剔除上市不足60天的新股。
但在判断股票上市天数时,遇到了一个典型的 TypeError 错误:
python

我的原始代码
if (context.current_dt - get_security_info(stock).start_date).days < 60:
continue
运行后报错:
text

TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'datetime.date'
我查阅了 Python 文档,了解到:
context.current_dt 是 datetime.datetime 类型(含时分秒)
get_security_info(stock).start_date 是 datetime.date 类型(仅年月日)
我的问题如下:
最佳实践:在聚宽策略中,最简洁且高效的方式是什么?是否推荐使用 datetime.combine()?
性能考量:如果对300只股票都做此转换,是否会显著增加回测耗时?
替代方案:能否直接用字符串比较(如 str(context.current_dt.date()))?这样做是否有隐患?
官方建议:聚宽 API 是否有内置函数可直接获取“上市以来交易天数”?这样可避免手动计算。
附:我的修复尝试(已能运行,但不确定是否最优)
python

from datetime import datetime
start_date = get_security_info(stock).start_date
start_dt = datetime(start_date.year, start_date.month, start_date.day)
if (context.current_dt - start_dt).days < 60:
continue
期待社区大佬的指导!也希望能帮助其他遇到同样问题的新手。谢谢!

解决方案

你好!这是一个在 JoinQuant(聚宽)平台编写策略时非常经典且常见的问题。Python 对日期时间类型的严格限制导致了 datetime.datetimedatetime.date 无法直接相减。你通过构造新的 datetime 对象来解决问题的思路是完全正确的,但确实还有更简洁、更高效的“最佳实践”。

下面针对你的四个问题逐一解答:

1. 最佳实践:最简洁且高效的方式是什么?

最简洁的方式不是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

2. 性能考量:对300只股票做转换会显著增加耗时吗?

如果你在循环内部每次都调用 context.current_dt.date(),虽然单次转换的开销极小,但在大量股票和长回测周期下,依然是不必要的重复计算。

性能优化建议:将当前日期的获取提取到循环外部(因为在同一个 handle_databefore_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
    # 后续的动量和估值逻辑...

3. 替代方案:能否直接用字符串比较?

强烈不建议使用字符串比较来计算时间差。

字符串比较(如 "2023-10-01" > "2023-08-01")只能用于判断日期的先后顺序,但无法直接计算出相差的天数。如果你需要判断“是否小于60天”,字符串是无能为力的,必须依赖 datetime 模块的 timedelta 对象(即相减后调用 .days)。

4. 官方建议:是否有内置函数直接获取“上市以来交易天数”?

聚宽 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)!