因子分析1——python因子的异常值处理、标准化、市值/行业中性化

文章目录

  • 基本数据清洗流程
    • 1. 异常值处理(绝对中位差)
    • 2. 数据标准化
    • 3. 市值中性化
    • 4. 行业中性化
  • 完整示例程序

基本数据清洗流程

下面都是对截面数据的一些基本处理流程,方便后续的分析与建模。基于截面数据的建模并不是说:当前截面的情况对应了未来就会如何如何,而是后验的去描述,未来表现如何如何时,历史上的截面数据大概有什么样子的表现

1. 异常值处理(绝对中位差)

原始数据的分布在使用一些拟合的方法时,异常值(超大、超小)对结果的影响是毁灭性的,因此我们对异常值做处理,但是金融数据大多数都不是错误的数据,而是确实存在但难以处理的数据。所以,我们将这些异常值使用另一个数据替换,这样既保留原有的数据,也对原有数据的分布影响较小

这一部分对应示例的函数是mad_three_sigma

2. 数据标准化

在有些模型中,数值的大小本身就代表“权重”,所以我们对原始数据做标准化,在保留数据分布的同时尽可能消除取值的影响

3. 市值中性化

由于因子投资的前提假设就是收益源于因子暴露,因此有一个因子的影响是较为熟知的,就是“市值”。市值大的是真的大,小的是真的小,如果我们不对市值做处理,就没有办法对因子做“提纯”,奇怪的数据会得到奇怪的模型。为了解决杂质的影响,我们通过“市值中性化”这种方法,剔除市值的影响。

具体做法就是 建立 y=kx+b,这里y是因子,x是市值,我们拟合一个线性回归后,这里的b就是剔除市值影响的因子序列。

注意:我们所说的用线性方法处理中性化可以认为是减弱了线性相关性,并不代表其他维度的相关性

4. 行业中性化

我们认为行业是一个不可计算不可比较的属性,如果不希望偏向任何一个行业,就需要(尽可能)进行行业中性化处理,将行业作为哑变量处理,用于市值中性化一样的线性模型取得残差作为提纯后的结果

完整示例程序

import pandas as pd
import numpy as np
import warnings
from sklearn.linear_model import LinearRegression

warnings.filterwarnings('ignore')


def mad_three_sigma(factor):
    """绝对中位差 剔除原始数据"""
    # 1.4826 只是一个参考值,按道理应该根据数据的分布确定不一样的值
    # 更多请参考:https://stats.stackexchange.com/questions/355943/how-to-estimate-the-scale-factor-for-mad-for-a-non-normal-distribution
    _mad_data = np.median(abs(factor - np.median(factor)))
    high = np.median(factor) + (3 * 1.4826 * _mad_data)
    low = np.median(factor) - (3 * 1.4826 * _mad_data)
    # 替换上下限
    factor = np.where(factor > high, high, factor)
    factor = np.where(factor < low, low, factor)
    return factor


def standard_factor(factor):
    """数据标准化"""
    return (factor - factor.mean()) / factor.std()


def get_metaphysics_data(factor_num=3, count=1000):
    """构建一个虚拟的数据"""
    data_df = pd.DataFrame(np.random.normal(0, 5, size=(count, factor_num)),
                           columns=[f"factor_{_i}" for _i in range(factor_num)])  # 生成5个随机因子
    data_df['close'] = np.random.normal(0, 2, count)  # 收盘价
    data_df['market_value'] = np.random.pareto(0.9, count)  # 市值
    data_df["industry"] = np.random.choice(["行业A", "行业B", "行业C", "行业D"], size=count, replace=True)  # 行业
    data_df.index = pd.date_range(start='2020-01-01', periods=count)
    return data_df


def eliminate_market_value(dataframe, factor_column: str, eliminate_columns: str):
    """消除另一个/组因子的影响"""
    _x = dataframe[eliminate_columns].values
    _y = dataframe[factor_column]
    linear_model = LinearRegression()
    linear_model.fit(_x, _y)
    y_predict = linear_model.predict(_x)  # 预测
    return _y - y_predict  # 残差为最后的因子值


def main():
    factor_df = get_metaphysics_data()
    factor_list = ["factor_0", "factor_1", "factor_2"]
    # 步骤1:去极值后标准化
    factor_df[factor_list] = factor_df[factor_list].apply(lambda x: standard_factor(mad_three_sigma(x)))

    # ===== 市值/行业 中性化 =====
    # 对 factor_0 进行市值中性化处理
    factor_0_eliminate_mv = eliminate_market_value(
        factor_df, factor_column="factor_0", eliminate_columns=["market_value"])
    factor_df['factor_0_eliminate_mv'] = factor_0_eliminate_mv

    # 对 factor_1 进行行业中性化处理
    industry_df = pd.get_dummies(factor_df["industry"])  # 类似于one_hot
    factor_1_eliminate_industry = eliminate_market_value(
        pd.concat([factor_df, industry_df], axis=1), factor_column="factor_1", eliminate_columns=industry_df.columns)
    factor_df['factor_1_eliminate_industry'] = factor_1_eliminate_industry


if __name__ == '__main__':
    main()

你可能感兴趣的:(【量化策略】系列文章,量化)