3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在多因子选股策略中,为了消除量纲影响、极端值干扰以及行业和市值因子的暴露,必须对原始因子进行去极值(Winsorization)、**标准化(Standardization)和中性化(Neutralization)**处理。以下是在聚宽(JoinQuant)平台中完成这一标准流程的详细指南及代码实现。
去极值 (Winsorize)
jqfactor.winsorize 或 jqfactor.winsorize_med(中位数去极值)。标准化 (Standardize)
jqfactor.standardlize(采用 Z-Score 方法:$(x - \mu) / \sigma$)。中性化 (Neutralize)
jqfactor.neutralize(通过线性回归残差法,对行业哑变量和对数市值进行中性化)。以下是一个完整的聚宽策略片段,展示了如何获取 roe_ttm(ROE)、growth(成长)、momentum(动量)和 size(市值)因子,并进行预处理后合成综合得分进行选股:
import pandas as pd
import numpy as np
from jqfactor import get_factor_values, winsorize, standardlize, neutralize
from jqdata import get_index_stocks
def preprocess_factors(context):
# 1. 定义股票池(以沪深300为例)和目标因子
stocks = get_index_stocks('000300.XSHG', date=context.previous_date)
factors = ['roe_ttm', 'growth', 'momentum', 'size']
# 2. 获取因子值(获取前一交易日的数据)
# get_factor_values 返回 dict, key为因子名,value为DataFrame(index为日期,columns为股票代码)
factor_data_dict = get_factor_values(
securities=stocks,
factors=factors,
start_date=context.previous_date,
end_date=context.previous_date
)
processed_factors = {}
for factor in factors:
# 提取单日因子数据,转为 Series (index为股票代码)
raw_series = factor_data_dict[factor].iloc[0]
# 步骤一:Winsorize 去极值 (分位数去极值,保留5%到95%之间)
clean_series = winsorize(raw_series, qrange=[0.05, 0.95], inclusive=True)
# 步骤二:行业与市值中性化
# 动量、ROE和成长因子需要剔除行业(jq_l1)和市值(market_cap)的影响
# 市值因子本身不需要做市值中性化,这里我们对非size因子进行中性化
if factor != 'size':
clean_series = neutralize(clean_series, how=['jq_l1', 'market_cap'], date=context.previous_date)
# 步骤三:Z-Score 标准化
final_series = standardlize(clean_series)
processed_factors[factor] = final_series
# 3. 构建多因子DataFrame
df = pd.DataFrame(processed_factors)
# 4. 因子合成(等权重加权,注意:size因子通常作为风险控制,若作为Alpha,小市值通常给负权重)
# 综合得分 = ROE + 成长 + 动量 - 市值(偏好小市值)
df['score'] = df['roe_ttm'] * 0.3 + df['growth'] * 0.3 + df['momentum'] * 0.3 - df['size'] * 0.1
# 5. 排序并选择前10只股票
buy_list = df['score'].sort_values(ascending=False).head(10).index.tolist()
return buy_list
start_date 和 end_date 必须设为 context.previous_date(前一交易日),切勿使用 context.current_dt,因为当前交易日的日频因子在盘中尚未生成。neutralize 函数依赖当天的行业分类和市值数据。如果传入的 date 对应的数据缺失(例如非交易日),函数将直接返回原始数据而不进行中性化。get_current_data() 获取 paused 属性),避免将资金浪费在无法交易的标的上。