问题描述
解决方案
要将 JoinQuant(聚宽)获取的数据转换为 Alphalens 所需的格式,核心在于将因子的 DataFrame 转换为 MultiIndex Series(多重索引序列),并准备好对应的价格数据。
Alphalens 的核心函数 get_clean_factor_and_forward_returns 需要两个主要输入:
- factor: 一个 MultiIndex Series,索引为
(date, asset),值为因子值。 - prices: 一个 DataFrame,索引为
date,列为asset,值为收盘价。
以下是具体的操作步骤和代码示例。
核心步骤
- 获取因子数据:通常使用
get_factor_values或get_fundamentals,得到的是一个 DataFrame(索引为日期,列为股票代码)。 - 数据重塑 (Stack):使用 Pandas 的
.stack()方法将宽表(DataFrame)转换为长表(MultiIndex Series)。 - 获取价格数据:使用
get_price获取收盘价 DataFrame。 - 生成 Alphalens 数据:调用
alphalens.utils.get_clean_factor_and_forward_returns。
代码实现
以下代码可以在聚宽的研究环境中直接运行:
# -*- coding: utf-8 -*-
import pandas as pd
import alphalens
from jqdata import *
from jqfactor import get_factor_values
# 1. 设定参数
stock_pool = get_index_stocks('000300.XSHG') # 股票池:沪深300
start_date = '2023-01-01'
end_date = '2023-03-01'
# 2. 获取因子数据 (以市盈率 PE_ratio 为例)
# get_factor_values 返回一个字典,key是因子名,value是DataFrame(index=date, columns=asset)
factor_data_dict = get_factor_values(
securities=stock_pool,
factors=['pe_ratio'],
start_date=start_date,
end_date=end_date
)
factor_df = factor_data_dict['pe_ratio']
# 3. 【关键步骤】转换因子格式
# Alphalens 需要 MultiIndex Series (date, asset)
# 使用 stack() 将列转为二级索引
factor_series = factor_df.stack()
# 为索引命名,有助于 Alphalens 识别(可选,但推荐)
factor_series.index.names = ['date', 'asset']
# 4. 获取价格数据
# 注意:价格数据的结束日期需要比因子结束日期晚,以便计算未来的收益率
# 这里多取 20 天的数据
price_end_date = (pd.to_datetime(end_date) + pd.Timedelta(days=30)).strftime('%Y-%m-%d')
prices_df = get_price(
list(stock_pool),
start_date=start_date,
end_date=price_end_date,
fields=['close']
)['close']
# 确保价格数据的索引也是 datetime 类型,并且时区处理一致(通常聚宽返回的是 naive datetime)
prices_df.index = pd.to_datetime(prices_df.index)
# 5. 生成 Alphalens 所需的清洗后数据
# periods 定义了我们要分析持有 1天、5天、10天 的收益
factor_data_clean = alphalens.utils.get_clean_factor_and_forward_returns(
factor=factor_series,
prices=prices_df,
quantiles=5, # 分成 5 组
periods=[1, 5, 10], # 计算 1, 5, 10 日后的收益
max_loss=0.35 # 允许丢弃数据的最大比例
)
# 6. 查看结果或进行绘图
print("转换后的数据前5行:")
print(factor_data_clean.head())
# 示例:生成全部分析图表(在 Notebook 中运行会显示图表)
# alphalens.tears.create_full_tear_sheet(factor_data_clean)
详细说明
1. 因子数据的转换 (.stack())
聚宽的 get_factor_values 返回的数据结构如下(DataFrame):
000001.XSHE 000002.XSHE ...
2023-01-04 6.5 8.2
2023-01-05 6.6 8.1
...
Alphalens 需要的格式如下(MultiIndex Series):
date asset
2023-01-04 000001.XSHE 6.5
000002.XSHE 8.2
2023-01-05 000001.XSHE 6.6
000002.XSHE 8.1
...
Pandas 的 stack() 函数正是用于完成这种“宽表变长表”的转换。
2. 价格数据的日期范围
Alphalens 需要计算“未来收益率”(Forward Returns)。如果你计算的是 periods=[1, 5, 10],那么对于因子数据中的最后一天,Alphalens 需要往后查找至少 10 个交易日的价格数据来计算收益。因此,获取 prices 时,end_date 必须晚于因子的 end_date。
3. 时区问题
聚宽返回的日期通常是不带时区信息(Naive Datetime)的,这通常能直接配合 Alphalens 使用。如果遇到时区报错,可以尝试统一转换为 UTC:
# 如果需要统一时区
factor_series.index = factor_series.index.set_levels(factor_series.index.levels[0].tz_localize('UTC'), level=0)
prices_df.index = prices_df.index.tz_localize('UTC')
但在大多数聚宽研究环境中,直接使用默认格式即可。
Q&A
Q: 为什么运行 get_clean_factor_and_forward_returns 时提示价格数据不足?
A: 这是因为计算未来收益率需要未来的价格。例如,要计算因子在 3月1日 的 10日预期收益,必须拥有 3月1日 之后至少 10 个交易日的价格数据。请确保 get_price 的 end_date 比因子的 end_date 往后延足够多的天数。
Q: 如果我的因子数据包含 NaN 或 Inf 怎么办?
A: get_clean_factor_and_forward_returns 函数内部会自动处理部分无效数据。你可以通过参数 max_loss 控制允许丢弃的数据比例(默认 35%)。如果无效数据过多,建议在传入 Alphalens 之前使用 Pandas 的 dropna() 或 fillna() 进行预处理。
Q: 如何处理多因子?
A: Alphalens 一次只能分析一个因子。如果你有多个因子,需要对每个因子分别执行上述转换流程,或者将它们合并成一个大的 DataFrame 后,通过循环对每一列进行 stack() 和分析。