🚀 AI 一键生成 joinquant 策略代码
立即体验

joinquant 因子中性化指南:如何剔除行业和市值偏差?

joinquant | 发布时间: 2026/7/21
以下内容由 EasyQuant 生成。

问题描述

请根据以下 SEO 主题生成完整页面内容:

Title: joinquant 因子中性化指南:如何剔除行业和市值偏差?

Question: 我想对自定义因子进行行业和市值中性化处理,在聚宽中应该怎么写?

解决方案

在多因子选股模型中,许多因子(如估值、成长性等)往往会受到股票所属行业和自身市值大小的显著影响。为了获取纯净的超额收益(Alpha),我们需要对因子进行**中性化(Neutralization)**处理,剔除行业和市值带来的偏差。

聚宽(JoinQuant)因子库提供了非常便捷的 neutralize 函数,支持一键对 pandas.Seriespandas.DataFrame 格式的因子数据进行行业和市值中性化。

一、中性化 API 介绍

在聚宽中,中性化函数定义在 jqfactor 库中:

from jqfactor import neutralize

neutralize(series, how=None, date=None, axis=1, fillna=None, add_constant=False)

参数说明:

  • data (series): pd.Seriespd.DataFrame,待中性化的序列。若为 Series,其 index 必须为股票代码(如 '000001.XSHE')。
  • how: list,中性化使用的自变量因子名称列表。默认为 ['jq_l1', 'market_cap'](即聚宽一级行业 + 总市值)。
    • 行业数据支持:'jq_l1'(聚宽一级)、'sw_l1'(申万一级)等。
    • 财务数据支持:'market_cap'(总市值)、'ln_market_cap'(对数总市值)等。
  • date: str,指定中性化所依赖的财务和行业数据的日期(如 '2018-05-02')。注意:请勿传入当天实时日期,因为当天财务数据尚未更新,可能返回 Nan。
  • axis: 默认为 1。仅在 data 为 pd.DataFrame 时生效。0 表示对每列做中性化,1 表示对每行做中性化。
  • fillna: 缺失值填充方式,默认为 None。支持传入行业分类(如 'jq_l1'),表示使用行业均值填充缺失值。
  • add_constant: 中性化回归时是否添加常数项,默认为 False

二、代码实现示例

以下是一个完整的在聚宽研究环境(Notebook)中运行的示例。我们首先获取沪深300成分股在某一天的原始因子值(以“市净率因子”为例),然后对其进行聚宽一级行业和对数市值的双重中性化处理。

import pandas as pd
import numpy as np
from jqdata import *
from jqfactor import neutralize, get_factor_values

# 1. 设定参数
target_date = '2023-05-10'
# 获取沪深300成分股
stocks = get_index_stocks('000300.XSHG', date=target_date)

# 2. 获取原始因子数据(以聚宽因子库中的市净率因子 book_to_price_ratio 为例)
# get_factor_values 返回一个 dict,value 是 DataFrame
raw_factor_dict = get_factor_values(
    securities=stocks, 
    factors=['book_to_price_ratio'], 
    start_date=target_date, 
    end_date=target_date
)

# 提取出 Series 格式的因子值,index 为股票代码
raw_factor_series = raw_factor_dict['book_to_price_ratio'].loc[target_date]
print("原始因子前5行:")
print(raw_factor_series.head())

# 3. 进行行业和市值中性化处理
# 剔除聚宽一级行业(jq_l1)和对数总市值(ln_market_cap)的影响
neutralized_factor = neutralize(
    raw_factor_series, 
    how=['jq_l1', 'ln_market_cap'], 
    date=target_date, 
    fillna='jq_l1'  # 缺失值用行业均值填充
)

print("\n中性化后的因子前5行:")
print(neutralized_factor.head())

三、在自定义因子类(Factor)中应用中性化

如果您是在聚宽的单因子分析框架中自定义因子,可以直接在 calc 方法中获取数据,并在计算完成后调用 neutralize 进行处理:

from jqfactor import Factor, neutralize
import numpy as np

class MyNeutralizedFactor(Factor):
    name = 'my_neutralized_factor'
    max_window = 1
    # 依赖基础因子:营业收入、总资产
    dependencies = ['total_operating_revenue', 'total_assets']
    
    def calc(self, data):
        # 1. 计算原始因子值(例如:资产周转率 = 营业收入 / 总资产)
        revenue = data['total_operating_revenue']
        assets = data['total_assets']
        raw_factor = (revenue / assets).mean() # 转为 Series
        
        # 2. 获取当前逻辑日期
        current_date = self._current_date.strftime('%Y-%m-%d')
        
        # 3. 对计算结果进行中性化
        try:
            neutral_factor = neutralize(
                raw_factor, 
                how=['jq_l1', 'market_cap'], 
                date=current_date,
                fillna='jq_l1'
            )
            return neutral_factor
        except Exception as e:
            # 异常处理:若中性化失败,返回原始因子值
            return raw_factor

四、注意事项

  1. 避免未来函数:在回测或因子分析中,中性化传入的 date 参数必须是当前模拟的逻辑日期(context.current_dtself._current_date),绝对不能使用未来的日期。
  2. 停牌与退市:对于停牌或退市股票,其市值或行业数据可能缺失,建议在进行中性化前,先剔除停牌股,或在 neutralize 中设置 fillna 参数进行均值填充。