因子分析(by alphalens) - ChannelCMT/OFO GitHub Wiki

目录

  1. Alphalens是什么?
  2. 使用Alphalens的数据准备工作?
  3. 如何用Alphalens测试选股因子效果?
  4. 什么是信息系数?
  5. 如何通过信息系数的可视化呈现进一步观测因子效果?
  6. 更多:因子在不同板块的选股能力比较分析

Alphalen是什么?

Alphalens是一个Python第三方库,专门用于选股因子alpha(α)的绩效分析。

  • 因子: 对股价未来涨跌有预测效果的指标

下载方式: pip install alphalens == 0.2.1

官方网站: http://quantopian.github.io/alphalens/index.html

历史数据 Factor链接:https://pan.baidu.com/s/1QHFTn4ya1Z2ph8VFeokP7Q 提取码:zlxr

使用Alphalens的数据准备工作?

  • factor: MultiIndex(用stack()方法来转换)

  • prices: DataFrame

  • 索引要求均为日期-datetime日期格式

  • 将以上数据通过alphalens处理成标准格式-factor_data 用于进一步分析

下面以沪深300成分股为例,构造选股因子(factor)

如果出现cannot set WRITEABLE flag to True of this array的报错,降级numpy的版本。

windows:https://www.lfd.uci.edu/~gohlke/pythonlibs/

中找到numpy的1.15.4的版本进行下载安装

mac: pip install numpy==1.15.4

    from jaqs_fxdayu.data import DataView # 可以视为一个轻量级的数据库,数据格式基于pandas,方便数据的调用和处理
    from jaqs_fxdayu.data import RemoteDataService # 数据服务,用于下载数据
    import os
    import warnings
    warnings.filterwarnings("ignore")
    dataview_folder = '../Factor'
    if not (os.path.isdir(dataview_folder)):
        os.makedirs(dataview_folder)
    dv = DataView()
    dv.load_dataview(dataview_folder)    
Dataview loaded successfully.
dv.fields
['close_adj',
 'low_adj',
 'ps',
 'index_weight',
 'open',
 'volume',
 'high',
 'low',
 'ann_date',
 'high_adj',
 'pe',
 'roe',
 'vwap',
 'adjust_factor',
 'vwap_adj',
 'open_adj',
 'float_mv',
 'sw1',
 'index_member',
 'pb',
 'close',
 'quarter',
 'trade_status']
import pandas as pd
from datetime import datetime

factor = dv.get_ts("pb")
factor.index = pd.Index(map(lambda x: datetime.strptime(str(x),"%Y%m%d") , factor.index)) #索引调整为datetime日期格式
factor = factor.stack()#处理成MultiIndex格式(alphalens分析因子必须的格式)

factor.head()
            symbol   
2014-01-02  000001.SZ    1.0563
            000002.SZ    1.2891
            000008.SZ    4.8981
            000009.SZ    3.5794
            000012.SZ    2.3725
dtype: float64
def change_index(df):
    df.index = pd.Index(map(lambda x: datetime.strptime(str(x),"%Y%m%d") , df.index)) #索引调整为datetime日期格式 
    return df
prices = dv.get_ts("close_adj") #获取价格
prices = change_index(prices)
prices.head()
symbol 000001.SZ 000002.SZ 000008.SZ 000009.SZ 000012.SZ 000024.SZ 000027.SZ 000039.SZ 000046.SZ 000059.SZ ... 601998.SH 603000.SH 603160.SH 603288.SH 603699.SH 603799.SH 603833.SH 603858.SH 603885.SH 603993.SH
2014-01-02 685.432796 869.28004 54.679500 57.244120 155.894015 97.545898 45.840241 348.968736 130.848969 7.635187 ... 4.617178 79.318951 NaN NaN NaN NaN NaN NaN NaN 6.541196
2014-01-03 668.619236 852.96064 54.375725 55.721670 153.407362 95.883723 44.750802 354.379104 128.507153 7.359216 ... 4.545408 78.055589 NaN NaN NaN NaN NaN NaN NaN 6.438347
2014-01-06 654.047484 813.79408 51.702505 55.234486 144.799717 90.754728 43.745166 342.656640 126.458064 7.006587 ... 4.473638 77.762489 NaN NaN NaN NaN NaN NaN NaN 6.191510
2014-01-07 651.805676 808.35428 50.973445 55.112690 144.034593 88.760118 43.577560 353.026512 124.408975 6.883933 ... 4.461677 79.743441 NaN NaN NaN NaN NaN NaN NaN 6.191510
2014-01-08 659.091552 807.26632 50.001365 54.138322 143.652031 86.338093 44.080378 351.448488 126.165337 6.853270 ... 4.473638 81.865890 NaN NaN NaN NaN NaN NaN NaN 6.098946

5 rows × 488 columns

import alphalens

#计算目标股票池每只股票的持有期收益,和对应因子值的quantile分类
factor_data = alphalens.utils.get_clean_factor_and_forward_returns(factor, prices, quantiles=5, periods=(1,5,10))
factor_data.head()
1 5 10 factor factor_quantile
date asset
2014-01-02 000001.SZ -0.024530 -0.033524 -0.044154 1.0563 1
000002.SZ -0.018773 -0.066333 -0.100125 1.2891 2
000008.SZ -0.005556 -0.102222 -0.054444 4.8981 4
000009.SZ -0.026596 -0.075532 -0.057447 3.5794 4
000012.SZ -0.015951 -0.093252 -0.039264 2.3725 3

如何用Alphalens测试选股因子效果?

  • demeaned:是否计算相对收益(减去当日所有assets持有期收益的均值)
  • 累计收益计算方法:日化复利再逐日累乘-无法根据该方式复制投资组合
import matplotlib.pyplot as plt
mean_return_by_q, std_err_by_q = alphalens.performance.mean_return_by_quantile(factor_data, by_date=True, demeaned=True)

alphalens.plotting.plot_cumulative_returns_by_quantile(mean_return_by_q, 1)
alphalens.plotting.plot_cumulative_returns_by_quantile(mean_return_by_q, 5)
alphalens.plotting.plot_cumulative_returns_by_quantile(mean_return_by_q, 10)
plt.show()

什么是信息系数?

度量变量的预测值与实际值之间的相关关系。信息系数是用来评估金融分析师预测技能的一种表现方法。

系数在-1到1之间,越大表示正相关程度强。

这里,我们可以用信息系数来评价一个选股因子效力的好坏。也即,用因子值大小与下一期股票收益大小的相关程度来评估因子。

经验来看,通常|mean(IC)|>0.02可以作为判定一个选股因子有收益预测效力的标准

什么是spearman相关系数?

其中d为秩次差。

什么是秩次差?

A = [1,3,5,7,9] B = [3,2,4,5,1]

A的排序是1,2,3,4,5

B的排序是3,2,4,5,1

d为排序相减

接下来我们要计算的因子IC值,即为当期因子值与下期股票收益值的spearman相关系数。

ic = alphalens.performance.factor_information_coefficient(factor_data)
ic.head()
1 5 10
date
2014-01-02 0.325364 0.372408 0.346461
2014-01-03 0.094661 0.233455 0.287933
2014-01-06 0.211379 0.156397 0.210309
2014-01-07 0.375963 0.150975 0.144572
2014-01-08 -0.029050 0.167510 0.014627

如何通过信息系数的可视化呈现进一步观测因子效果?

alphalens.plotting.plot_ic_hist(ic) #因子IC分布
alphalens.plotting.plot_ic_ts(ic) #因子IC时间序列曲线
plt.show()

# 月均IC热度图
mean_monthly_ic = alphalens.performance.mean_information_coefficient(factor_data, by_time='M')
alphalens.plotting.plot_monthly_ic_heatmap(mean_monthly_ic)#因子月均IC表现
plt.show()

将Quantile1的选股结果保存成excel

import numpy as np
excel_data = factor_data[factor_data['factor_quantile']==1]["factor_quantile"].unstack().replace(np.nan, 0)
excel_data.to_excel('./pb_quantile_1_by_alphalens.xlsx')
excel_data.head()
asset 000001.SZ 000002.SZ 000024.SZ 000027.SZ 000039.SZ 000059.SZ 000063.SZ 000069.SZ 000157.SZ 000338.SZ ... 601901.SH 601918.SH 601939.SH 601985.SH 601988.SH 601991.SH 601992.SH 601997.SH 601998.SH 603799.SH
date
2014-01-02 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 1.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0
2014-01-03 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 1.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0
2014-01-06 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 1.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0
2014-01-07 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 1.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0
2014-01-08 1.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 1.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0

5 rows × 201 columns

更多:因子在不同板块的选股能力比较分析

  • 通过groupby参数指定每只股票对应的行业(按行业分组)
  • 构造factor_data
  • 计算不同行业的平均IC,并可视化呈现
sectors = dv.get_ts("sw1")
sectors = change_index(sectors)
sectors.head()
symbol 000001.SZ 000002.SZ 000008.SZ 000009.SZ 000012.SZ 000024.SZ 000027.SZ 000039.SZ 000046.SZ 000059.SZ ... 601998.SH 603000.SH 603160.SH 603288.SH 603699.SH 603799.SH 603833.SH 603858.SH 603885.SH 603993.SH
2014-01-02 银行 房地产 休闲服务 综合 建筑材料 房地产 公用事业 机械设备 房地产 化工 ... 银行 传媒 电子 食品饮料 机械设备 有色金属 轻工制造 医药生物 交通运输 有色金属
2014-01-03 银行 房地产 休闲服务 综合 建筑材料 房地产 公用事业 机械设备 房地产 化工 ... 银行 传媒 电子 食品饮料 机械设备 有色金属 轻工制造 医药生物 交通运输 有色金属
2014-01-06 银行 房地产 休闲服务 综合 建筑材料 房地产 公用事业 机械设备 房地产 化工 ... 银行 传媒 电子 食品饮料 机械设备 有色金属 轻工制造 医药生物 交通运输 有色金属
2014-01-07 银行 房地产 休闲服务 综合 建筑材料 房地产 公用事业 机械设备 房地产 化工 ... 银行 传媒 电子 食品饮料 机械设备 有色金属 轻工制造 医药生物 交通运输 有色金属
2014-01-08 银行 房地产 休闲服务 综合 建筑材料 房地产 公用事业 机械设备 房地产 化工 ... 银行 传媒 电子 食品饮料 机械设备 有色金属 轻工制造 医药生物 交通运输 有色金属

5 rows × 488 columns

factor_data = alphalens.utils.get_clean_factor_and_forward_returns(factor,
                                                                   prices,
                                                                   groupby=sectors.stack(),
                                                                   quantiles=5,
                                                                   periods=(1, 5, 10))
factor_data.head()
1 5 10 factor group factor_quantile
date asset
2014-01-02 000001.SZ -0.024530 -0.033524 -0.044154 1.0563 银行 1
000002.SZ -0.018773 -0.066333 -0.100125 1.2891 房地产 2
000008.SZ -0.005556 -0.102222 -0.054444 4.8981 休闲服务 4
000009.SZ -0.026596 -0.075532 -0.057447 3.5794 综合 4
000012.SZ -0.015951 -0.093252 -0.039264 2.3725 建筑材料 3
ic_by_sector = alphalens.performance.mean_information_coefficient(factor_data, by_group=True)
ic_by_sector.head()
1 5 10
group
交通运输 -0.054600 -0.076287 -0.101861
休闲服务 -0.008219 -0.047374 -0.066913
传媒 -0.034612 -0.043479 -0.047153
公用事业 -0.029263 -0.038912 -0.046744
农林牧渔 -0.023074 -0.033296 -0.043930
from matplotlib import pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
alphalens.plotting.plot_ic_by_group(ic_by_sector)
plt.show()

⚠️ **GitHub.com Fallback** ⚠️