下面都是对截面数据的一些基本处理流程,方便后续的分析与建模。基于截面数据的建模并不是说:当前截面的情况对应了未来就会如何如何,而是后验的去描述,未来表现如何如何时,历史上的截面数据大概有什么样子的表现
原始数据的分布在使用一些拟合的方法时,异常值(超大、超小)对结果的影响是毁灭性的,因此我们对异常值做处理,但是金融数据大多数都不是错误的数据,而是确实存在但难以处理的数据。所以,我们将这些异常值使用另一个数据替换,这样既保留原有的数据,也对原有数据的分布影响较小
这一部分对应示例的函数是mad_three_sigma
在有些模型中,数值的大小本身就代表“权重”,所以我们对原始数据做标准化,在保留数据分布的同时尽可能消除取值的影响
由于因子投资的前提假设就是收益源于因子暴露,因此有一个因子的影响是较为熟知的,就是“市值”。市值大的是真的大,小的是真的小,如果我们不对市值做处理,就没有办法对因子做“提纯”,奇怪的数据会得到奇怪的模型。为了解决杂质的影响,我们通过“市值中性化”这种方法,剔除市值的影响。
具体做法就是 建立 y=kx+b,这里y是因子,x是市值,我们拟合一个线性回归后,这里的b就是剔除市值影响的因子序列。
注意:我们所说的用线性方法处理中性化可以认为是减弱了线性相关性,并不代表其他维度的相关性
我们认为行业是一个不可计算不可比较的属性,如果不希望偏向任何一个行业,就需要(尽可能)进行行业中性化处理,将行业作为哑变量处理,用于市值中性化一样的线性模型取得残差作为提纯后的结果
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()