问题描述
解决方案
在聚宽(JoinQuant)策略代码中,若需要一次性计算一段时间内的因子值,可以使用 jqfactor 库提供的 calc_factors 函数。
这通常涉及以下三个步骤:
- 导入模块:引入
Factor类和calc_factors函数。 - 定义因子:创建一个继承自
Factor的类,设置因子的名称、数据窗口大小(max_window)、依赖数据(dependencies)以及具体的计算逻辑(calc方法)。 - 调用计算:在策略函数(如
initialize或handle_data)中调用calc_factors,传入标的列表、因子实例以及起止时间。
代码实现
以下是一个完整的策略代码示例。该示例定义了一个简单的“5日均价”因子,并在 initialize 函数中一次性计算了指定股票在一段时间内的因子值。
# -*- coding: utf-8 -*-
from jqdata import *
from jqfactor import Factor, calc_factors
import numpy as np
import pandas as pd
# 第一步:定义因子类
class MeanPrice5(Factor):
# 设置因子名称,计算结果的字典key将使用此名称
name = 'mean_price_5'
# 设置获取数据的时间窗口长度(例如计算5日均值,需要过去5天的数据)
max_window = 5
# 设置依赖的数据字段,这里依赖收盘价
dependencies = ['close']
# 计算因子的函数
# data 是一个字典,key是依赖的字段名,value是DataFrame(index为日期,columns为股票代码)
def calc(self, data):
# 获取收盘价数据
close = data['close']
# 计算过去5天的均值
# 注意:close是一个 (max_window x 股票数) 的DataFrame
return close.mean()
def initialize(context):
# 设定基准
set_benchmark('000300.XSHG')
# 开开启真实价格
set_option('use_real_price', True)
# 第二步:准备计算参数
# 设定要计算的股票列表
securities = ['000001.XSHE', '000002.XSHE', '600000.XSHG']
# 设定计算的时间段
# 注意:在回测中,end_date 不应超过 context.current_dt,避免未来函数
start_date = '2023-01-01'
end_date = '2023-01-15'
# 第三步:调用 calc_factors 进行批量计算
# 参数说明:
# securities: 股票池
# factors: 因子实例列表,这里实例化了上面定义的 MeanPrice5
# start_date, end_date: 计算区间
print("开始计算因子...")
factors_result = calc_factors(
securities=securities,
factors=[MeanPrice5()],
start_date=start_date,
end_date=end_date,
use_real_price=True, # 使用真实价格(复权后)
skip_paused=False # 是否跳过停牌
)
# 第四步:获取和使用结果
# calc_factors 返回一个字典,key是因子名称,value是DataFrame
factor_df = factors_result['mean_price_5']
print("因子计算完成,结果如下:")
print(factor_df)
# 示例:打印 000001.XSHE 在这段时间内的因子值
if '000001.XSHE' in factor_df.columns:
print("\n平安银行(000001.XSHE)的5日均价因子值:")
print(factor_df['000001.XSHE'])
def handle_data(context, data):
pass
关键点说明
-
calc_factors函数:- 这是核心API。它返回一个字典,键(Key)是你在因子类中定义的
name,值(Value)是一个pandas.DataFrame。 - 返回的 DataFrame 中,Index(行索引)是日期,Columns(列索引)是股票代码。
- 这是核心API。它返回一个字典,键(Key)是你在因子类中定义的
-
Factor类定义:max_window:非常重要。它决定了calc方法中data参数包含的历史数据长度。例如计算5日均线,max_window必须至少为 5。dependencies:定义需要哪些基础数据(如close,open,volume,money等,也可以是财务数据字段)。calc方法:接收的data参数包含了max_window长度的历史数据。你需要在这里编写逻辑,返回一个 Series(索引为股票代码,值为因子值)。calc_factors会自动按天滚动调用这个方法来生成最终的时间序列 DataFrame。
-
注意事项:
- 未来函数:在回测环境中使用时,请确保
end_date不要超过当前回测时间context.current_dt,否则可能引入未来数据。 - 性能:
calc_factors适合批量计算。如果只是在handle_data中需要当天的因子值,直接使用data获取历史数据计算可能更轻量;但如果需要历史序列分析,calc_factors是最佳选择。
- 未来函数:在回测环境中使用时,请确保
Q&A
Q: calc_factors 返回的数据结构是什么样的?
A: 它返回一个字典(dict)。字典的 Key 是因子类中定义的 name 属性值,Value 是一个 pandas DataFrame。该 DataFrame 的行索引(Index)是日期,列索引(Columns)是股票代码,单元格内的值是对应的因子值。
Q: 在 calc 方法中可以使用财务数据吗?
A: 可以。在 dependencies 列表中添加财务字段名称即可(例如 total_operating_revenue)。系统会自动匹配对应日期可获取的最新财报数据(Point-in-time),避免未来函数。
Q: max_window 设置为 1 会发生什么?
A: 如果 max_window=1,calc 方法中接收到的 data 将只包含当天的 1 行数据。此时在 calc 内部进行计算时(如求均值),通常需要先将 DataFrame 转换为 Series 或直接取值。
Q: 可以在 handle_data 中每天调用 calc_factors 吗?
A: 技术上可以,但不推荐。calc_factors 设计用于批量计算一段时间的数据,开销较大。如果在 handle_data(按天或按分钟运行)中调用,建议只计算当前时间点的数据,或者直接使用 attribute_history 获取数据后手动计算,效率会更高。