🚀 AI 一键生成 joinquant 策略代码

JoinQuant 中如何高效定义自定义因子?max_window 与 dependencies 详解

joinquant | 发布时间: 2025/12/8
以下内容由 EasyQuant 生成。

问题描述

编写自定义因子时,怎样获取过去一段时间的收盘价和财务数据?

解决方案

在聚宽(JoinQuant)平台编写自定义因子时,主要通过继承 jqfactor.Factor 类,并利用 max_window(时间窗口)和 dependencies(依赖数据)这两个属性来获取历史行情和财务数据。

以下是具体的实现步骤和代码示例:

核心机制说明

  1. max_window (最大时间窗口):定义了你在计算因子时需要回溯多少个交易日的数据。例如,如果你需要过去10天的收盘价,max_window 应设为 10。
  2. dependencies (依赖数据):定义了你需要哪些字段的数据。
    • 行情数据:直接使用字段名,如 'close' (收盘价), 'open' (开盘价), 'volume' (成交量) 等。
    • 财务数据:使用财务数据表中的字段名,如 'net_profit' (净利润), 'operating_revenue' (营业收入)。
      • 获取前n季度数据:在字段名后加 _n,例如 'net_profit_1' 表示上一季度的净利润。
      • 获取前n年度数据:在字段名后加 _yn,例如 'net_profit_y1' 表示上一年度的净利润。
  3. calc 函数:这是计算逻辑的核心。传入的 data 参数是一个字典,key 是 dependencies 中的字段名,value 是 pandas.DataFrame(索引为日期,列为股票代码)。

代码示例

下面编写一个自定义因子示例:计算 "过去5日收盘价均值 / 上一季度净利润"

# -*- coding: utf-8 -*-
from jqfactor import Factor
import pandas as pd
import numpy as np

class PriceToNetProfitFactor(Factor):
    # 1. 设置因子名称
    name = 'price_to_net_profit'
    
    # 2. 设置获取数据的时间窗口长度
    # 这里我们需要计算过去5天的均值,所以窗口至少为5
    max_window = 5
    
    # 3. 设置依赖的数据
    # 'close': 收盘价
    # 'net_profit_1': 上一季度的净利润 (单季度数据)
    dependencies = ['close', 'net_profit_1']

    # 4. 计算因子的函数
    def calc(self, data):
        """
        data 是一个字典:
        data['close'] 是一个 DataFrame (行是日期,列是股票代码)
        data['net_profit_1'] 是一个 DataFrame
        """
        
        # 获取过去5天的收盘价数据
        # close_df 的 shape 为 (5, 股票数量)
        close_df = data['close']
        
        # 计算过去5日收盘价的平均值
        # axis=0 表示按列(对每只股票的时间序列)求均值
        avg_close_5d = close_df.mean()
        
        # 获取上一季度的净利润
        # 注意:财务数据在 max_window 期间内,如果没有新财报发布,数值是重复的。
        # 我们通常取最后一天(最新)的数据。
        # .iloc[-1] 取最后一行数据,返回一个 Series (索引为股票代码)
        last_quarter_profit = data['net_profit_1'].iloc[-1]
        
        # 处理分母为0或NaN的情况(可选,视策略需求而定)
        # 这里简单处理,将0替换为NaN以避免除零错误
        last_quarter_profit = last_quarter_profit.replace(0, np.nan)
        
        # 计算因子值:均价 / 净利润
        factor_values = avg_close_5d / last_quarter_profit
        
        # 返回结果必须是一个 pandas.Series,索引是股票代码
        return factor_values

如何验证和使用

在聚宽的研究环境或回测中,你可以使用 calc_factors 来调用这个自定义因子进行测试:

from jqfactor import calc_factors

# 定义股票池
securities = ['000001.XSHE', '600519.XSHG']

# 计算因子
# 注意:这里实例化了上面定义的类 PriceToNetProfitFactor()
factors = calc_factors(
    securities=securities, 
    factors=[PriceToNetProfitFactor()], 
    start_date='2023-01-01', 
    end_date='2023-01-05'
)

# 查看结果
print(factors['price_to_net_profit'])

关键点总结

  1. 数据结构calc 函数中接收到的 data['字段名'] 永远是 DataFrame。即使 max_window=1,它也是一个 1行 N列 的 DataFrame。
  2. 财务数据滞后性:在 calc 函数中获取的财务数据(如 net_profit),是基于当前回测日期已知的最新数据,系统会自动处理“未来函数”问题(即不会提前获取到未发布的财报数据)。
  3. 历史财务数据
    • net_profit: 当前可见的最新单季度净利润。
    • net_profit_1: 前一季度的单季度净利润。
    • net_profit_y1: 前一年度的年度净利润。

Q&A

Q: 如果我想获取过去10天的成交量数据,应该怎么写?
A: 将 max_window 设置为 10,并在 dependencies 中添加 'volume'。在 calc 函数中通过 data['volume'] 获取 DataFrame,该 DataFrame 将包含过去10天的成交量数据。

Q: 财务数据中的 _ttm 后缀是什么意思?
A: TTM (Trailing Twelve Months) 代表滚动12个月的数据。例如 operating_revenue_ttm 代表过去12个月的营业收入总和。在因子分析中,TTM数据常用于消除季节性影响。

Q: 自定义因子的 calc 函数返回值有什么要求?
A: calc 函数必须返回一个 pandas.Series 对象。Series 的索引(index)必须是股票代码,值(value)是计算出的因子值。

Q: 为什么获取财务数据时通常使用 .iloc[-1]
A: 因为 data 字典中的财务数据 DataFrame 是按 max_window 展开的时间序列。对于财务数据,在财报发布间隔期内,每天的数据通常是相同的。为了使用当前时间点上最新的财务数据,通常取 DataFrame 的最后一行,即 .iloc[-1]