问题描述
解决方案
在聚宽(JoinQuant)平台编写自定义因子时,主要通过继承 jqfactor.Factor 类,并利用 max_window(时间窗口)和 dependencies(依赖数据)这两个属性来获取历史行情和财务数据。
以下是具体的实现步骤和代码示例:
核心机制说明
max_window(最大时间窗口):定义了你在计算因子时需要回溯多少个交易日的数据。例如,如果你需要过去10天的收盘价,max_window应设为 10。dependencies(依赖数据):定义了你需要哪些字段的数据。- 行情数据:直接使用字段名,如
'close'(收盘价),'open'(开盘价),'volume'(成交量) 等。 - 财务数据:使用财务数据表中的字段名,如
'net_profit'(净利润),'operating_revenue'(营业收入)。- 获取前n季度数据:在字段名后加
_n,例如'net_profit_1'表示上一季度的净利润。 - 获取前n年度数据:在字段名后加
_yn,例如'net_profit_y1'表示上一年度的净利润。
- 获取前n季度数据:在字段名后加
- 行情数据:直接使用字段名,如
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'])
关键点总结
- 数据结构:
calc函数中接收到的data['字段名']永远是 DataFrame。即使max_window=1,它也是一个 1行 N列 的 DataFrame。 - 财务数据滞后性:在
calc函数中获取的财务数据(如net_profit),是基于当前回测日期已知的最新数据,系统会自动处理“未来函数”问题(即不会提前获取到未发布的财报数据)。 - 历史财务数据:
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]。