3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在多因子选股策略中,原始因子数据通常存在量纲不同、包含异常值以及受行业或市值因素主导等问题。为了得到纯净且具有可比性的因子暴露度,通常需要进行去极值(Winsorization)、**标准化(Standardization)和中性化(Neutralization)**处理。
聚宽(JoinQuant)的 jqfactor 库提供了现成的函数来完成这些步骤。
通常的处理顺序为:
推荐使用 中位数去极值法 (MAD),相比于标准差法(3-sigma),它对异常值更不敏感,更加稳健。
winsorize_med(data, scale=3, inclusive=True, axis=1)中位数 ± 3 * 绝对中位差 的值替换为边界值。通常使用 Z-Score 标准化。
standardlize(data, inf2nan=True, axis=1)通过回归分析剔除特定风险因子的影响(通常是行业和市值)。
neutralize(data, how=['jq_l1', 'market_cap'], date=date, axis=1)how 列表指定要剔除的风险因子,jq_l1 代表聚宽一级行业,market_cap 代表市值。以下代码展示了如何获取沪深300成分股的市盈率(PE)因子,并依次进行去极值、标准化和中性化处理。
# -*- coding: utf-8 -*-
from jqdata import *
from jqfactor import get_factor_values, winsorize_med, standardlize, neutralize
import pandas as pd
import numpy as np
def initialize(context):
# 设定基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 每天开盘前运行
run_daily(process_factors, '09:00')
def process_factors(context):
# 1. 设定参数
date = context.previous_date # 使用前一交易日数据,避免未来函数
universe = get_index_stocks('000300.XSHG', date=date) # 获取沪深300股票池
print(f"正在处理 {date} 的因子数据...")
# 2. 获取原始因子数据
# 这里以市盈率(pe_ratio)为例,也可以通过 get_fundamentals 获取
# get_factor_values 返回的是一个字典,key是因子名,value是DataFrame
factor_data_dict = get_factor_values(
securities=universe,
factors=['pe_ratio'],
end_date=date,
count=1
)
# 提取 DataFrame,行是日期,列是股票代码
# 我们只需要最近一天的数据,并转置为 Series (index为股票代码) 以便处理
raw_factor = factor_data_dict['pe_ratio'].iloc[-1, :]
# 过滤掉 NaN 值
raw_factor = raw_factor.dropna()
if raw_factor.empty:
print("无有效因子数据")
return
# --- 数据处理流水线 ---
# 3. 去极值 (Winsorization)
# 使用中位数去极值法,将极值压缩到边界
factor_winsorized = winsorize_med(
raw_factor,
scale=3, # 3倍中位数绝对偏差
inclusive=True, # 边界外的值替换为边界值
inf2nan=True, # 无穷大转为NaN
axis=0 # 对 Series 处理时 axis=0
)
# 4. 标准化 (Standardization)
# Z-Score 标准化,使均值为0,标准差为1
factor_standardized = standardlize(
factor_winsorized,
inf2nan=True,
axis=0
)
# 5. 中性化 (Neutralization)
# 剔除行业(jq_l1)和市值(market_cap)的影响
# 注意:neutralize 需要 date 参数来获取当天的行业和市值数据进行回归
factor_neutralized = neutralize(
factor_standardized,
how=['jq_l1', 'market_cap'],
date=date,
axis=0
)
# --- 打印结果对比 ---
print("\n处理前前5只股票数据:")
print(raw_factor.head())
print("\n去极值后前5只股票数据:")
print(factor_winsorized.head())
print("\n标准化后前5只股票数据:")
print(factor_standardized.head())
print("\n中性化后(最终)前5只股票数据:")
print(factor_neutralized.head())
# 此时 factor_neutralized 即为可用于打分或回归的纯净因子
jqfactor 的处理函数既支持 pandas.Series 也支持 pandas.DataFrame。如果是 DataFrame(多行多列),通常设置 axis=1 表示在横截面(按天)上对所有股票进行处理。neutralize 函数内部会自动获取指定日期的行业分类和市值数据,因此必须传入准确的 date 参数。NaN 值,虽然聚宽的函数有 inf2nan 等参数,但保持输入数据的整洁可以避免很多潜在错误。Q: 为什么要进行行业和市值中性化?
A: 很多因子(如换手率、波动率)天然与市值大小或特定行业(如银行股通常低PE)相关。如果不进行中性化,选出的股票可能会集中在小市值或某些特定行业,导致组合风险暴露过大。中性化是为了剥离这些风险,保留因子纯粹的选股能力(Alpha)。
Q: winsorize 和 winsorize_med 有什么区别?
A: winsorize 通常指基于标准差(如3-sigma)或分位数(如1%-99%)的去极值;winsorize_med 基于中位数和绝对中位差(MAD)。由于金融数据往往不服从正态分布且存在厚尾,均值和标准差容易受极值影响,因此基于中位数的 winsorize_med 通常更加稳健。
Q: 处理后的因子数据如何使用?
A: 处理后的因子数据可以直接用于多因子打分模型(加权求和)、计算IC值(信息系数)进行因子有效性检验,或者作为输入放入机器学习模型中进行训练。