本文采用万矿平台的WindAlpha单因子研究框架,从股票因子的角度探究COVID-19疫情对股市的影响。IC分析发现,疫情严重程度与股市收益具有显著的负相关性,这种负相关性随时间递增并在第10天达到峰值;根据COVID-19疫情因子对股票分组,第一组的收益率显著高于其他组,多空组合能够跑赢大盘。(代码克隆点这里)
目录
1. 因子构建
2. 因子预处理
2. 因子分析
2.1 IC分析
2.2 收益率分析
1. 因子构建
首先获取各省疫情数据的时间序列,获取方法见获取COVID-19疫情历史数据的n种方法。此处的数据源是R语言里的nCov2019
库:
library(nCov2019)
x <- load_nCov2019()
data <- summary(x)[1:3]
write.csv(data,"Data/nCovProvince.csv",row.names = FALSE)
## province time cum_confirm
## 上海 2020/2/10 302
## 云南 2020/2/10 149
## 内蒙古 2020/2/10 58
## 北京 2020/2/10 342
## 台湾 2020/2/10 18
然后在万矿平台上提取非ST,非PT的全部A股的注册地和办公地点,找到地点所对应的省份。根据省份信息在此前获取的疫情数据中找到对应的确诊人数,并将确诊人数作为股票当日的因子取值。
from WindPy import * #api
from datetime import datetime
from scipy import stats, optimize
from WindCharts import *
import pandas as pd
import WindAlpha as wa
w.start(show_welcome=False)
# 读取疫情数据:读取之前需要把csv文件上传到“数据文件/nCov”文件夹中
nCov_province = pd.read_csv("data/nCov/nCovProvince.csv",encoding = "gbk",index_col = ["time", "province"])
start_date = nCov_province.index[-1][0]
end_date = nCov_province.index[1][0]
def get_province(address):
# 根据地址信息返回地址对应的省份
province_list = ['上海','云南','内蒙古','北京','台湾','吉林','四川','天津','宁夏','安徽','山东',
'山西','广东','广西','新疆','江苏','江西','河北','河南','浙江','海南','湖北',
'湖南','澳门','甘肃','福建','西藏','贵州','辽宁','重庆','陕西','青海','香港','黑龙江']
for province in province_list:
if province in address:
return province
def factor_prepare(nCov_province):
# 获取交易日列表,并转换成字符串格式
trade_dates = w.tdays(start_date, end_date, period="d").Data[0]
trade_dates = [dt.strftime("%Y-%m-%d") for dt in trade_dates]
# 获取非ST,非PT的全部A股股票代码
stock_set = w.wset("sectorconstituent", "date="+start_date+";sectorId=a001010f00000000;field=wind_code", usedf=True)
stock_list = list(stock_set[1]['wind_code'])
# 获取公司的注册地与办公地址并提取所在省
raw_data=w.wss(stock_list, "address,office", "rptDate= %s" %(start_date),usedf=True)[1]
raw_data["ADDRESS"] = raw_data["ADDRESS"].map(get_province)
raw_data["OFFICE"] = raw_data["OFFICE"].map(get_province)
# 将数据整理成因子所需的数据格式
index=pd.MultiIndex.from_product([trade_dates,stock_list])
factor = pd.Datafr ame(columns=raw_data.columns,
index=index)
for trade_date in trade_dates:
factor.loc[trade_date,"ADDRESS"] = np.asarray(raw_data["ADDRESS"])
factor.loc[trade_date,"OFFICE"] = np.asarray(raw_data["OFFICE"])
# 将公司所在省信息转换该省当日的累计确诊人数并作为最终的因子值
for trade_date in trade_dates:
for security_code in stock_list:
for columns in factor.columns :
security_prvince = factor.loc[(trade_date,security_code),columns]
try:
# 此处赋值存在索引不到对应值的可能性,是因为对应的省当时没有病例
factor.loc[(trade_date,security_code),columns] = nCov_province.loc[(trade_date,security_prvince), "cum_confirm"]
except (KeyError):
factor.loc[(trade_date,security_code),columns] = 0
# 上传因子的date索引需要转化为datetime格式
date_index = [datetime.datetime.strptime(dt,"%Y-%m-%d") for dt in index.levels[0]]
index.set_levels(date_index, level=0, inplace = True)
factor.set_index(index,inplace = True)
inds_ret = wa.load_local_factors(factor)
return inds_ret
raw_ind_ret = factor_prepare(nCov_province)
上传之后的因子数据结构如下,其中最后两列为上传因子的过程中万矿平台自动添加的数据,MKT_CAP_ASHARE
表示A股市值(不含限售股),NEXT_RET
表示股票的下一期收益。
2. 因子预处理
接下来进行因子的预处理:
- 缺失值处理:直接删除缺失行
- 去极值:进行极值处理会将湖北省的作为极值删除,因此没有进行去极值的处理
- 标准化:采用了市值加权标准化方法,相当于同时进行了市值中性化处理
- 行业中性化:为了消除行业对因子表现的潜在影响,此处通过建立线性回归的形式进行中性化处理,即以原始因子值为因变量,行业哑变量为自变量进行回归,回归得到的残差是原始因子中无法被行业解释的部分,所以提取残差作为中性化后的新因子。
# 以上处理过程直接调用WindAlpha的process_raw_data函数即可
processed_inds_ret = wa.process_raw_data(raw_ind_ret, missing_method = "dele",
extreme_method = False, scale_method = "cap",
neutralize_method = 'sw_1',isinclude_cap = False)
原始因子与处理后的因子对比:
import seaborn as se
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(7,5))
plt.subplot(211)
processed_inds_ret.loc['2020-02-04']["OFFICE"].hist()
plt.subplot(212)
raw_inds_ret.loc['2020-02-04']['OFFICE'].hist()
plt.suptitle(u"Processed Data VS Raw Data(Date:2020-02-04)")
2. 因子分析
2.1 IC分析
信息系数(IC),指每一期因子值与下一期股票收益率之间的相关性。IC的大小反应了因子暴露值与股票表现之间的线性关系,IC的值越大说明因子的预测性越高,选股的效果就越好。此处采用RankIC方法计算IC值,即计算因子暴露值排名与股票下期回报排名的截面相关系数。
# 直接调用WindAlpha的ic_analysis函数
ic_ana = wa.ic_analysis(processed_inds_ret)
ind = "OFFICE"
fig_ic=WLine("IC序列:{}".format(ind),"{}-{}".format(start_date, end_date), ic_ana.ic_series.loc[ind])
fig_ic.plot()
IC信号衰减
此前的IC序列计算的是当期的因子值与下一期收益率之间的相关性,也就是因子值与收益率之间相差一个周期。而IC衰减描述的是因子值和相隔LAG期的收益率的相关性。具体的计算方法为,如果一共有N期的因子数据和收益率数据,先把所有i期因子和i+1期收益率的IC值算出来求平均,再把i期因子和i+2收益率的IC求平均…(i=1,…,N-LAG),最终得到LAG个IC的均值,这几个均值就体现了IC的衰减。
ic_ana.ic_decay
ADDRESS | OFFICE | |
---|---|---|
LAG0 | -0.016098 | -0.014985 |
LAG1 | -0.037245 | -0.035465 |
LAG2 | -0.055568 | -0.054450 |
LAG3 | -0.081646 | -0.080446 |
LAG4 | -0.100494 | -0.099304 |
LAG5 | -0.114949 | -0.112260 |
LAG6 | -0.131797 | -0.132035 |
LAG7 | -0.142606 | -0.145498 |
LAG8 | -0.143235 | -0.149350 |
LAG9 | -0.141615 | -0.150159 |
LAG10 | -0.146176 | -0.154830 |
LAG11 | -0.144909 | -0.152806 |
将IC衰减的结果可视化,可以发现非常明显的规律,疫情严重程度与股市收益具有显著的负相关性,这种负相关性随时间递增并在第10天达到峰值。
# 根据IC衰减的结果,办公地点的疫情数据对股市影响更大
ind = "OFFICE"
fig_decay=WBar('{} IC Decay'.format("COVID-19"), '',ic_ana.ic_decay[ind].to_fr ame())
fig_decay.plot()
2.2 收益率分析
按照因子值大小将股票分为5组,并构建多1空5的多空组合,直接用每组每期股票的平均收益率(市值加权)来计算的每组的累计收益率等指标。
# 以Wind全A指数作为基准
direction_dict = {ind: 'descending'}
ret_ana = wa.return_analysis(processed_inds_ret,'881001.WI',start_date, end_date,ind_direction=direction_dict)
将每组收益结果可视化,其中第一组的收益率显著高于其他组,多空组合能够跑赢大盘。
sig_ret_line = WLine("多空组合收益率:{}".format("COVID-19"),"{}-{}".format(start_date, end_date) , round(ret_ana.group_cum_return[['G01','G02','G03','G04','G05','G01-G05','BENCH_RET']].loc[ind],4),auto_yaxis=True)
sig_ret_line.plot()
相关文章
Logistic模型拟合COVID-19疫情以及Python实现
以上是本篇的全部内容,欢迎关注我的知乎|简书|CSDN|微信公众号PurePlay
, 会不定期分享研究与学习干货。