运用到期货上,拆了@一梦春秋的代码,最后结论都懒得copy了
参考: 广发证券《基于日内高频数据的短周期选股因子研究-高频数据因子研究系列一》;
@一梦春秋 https://www.joinquant.com/view/community/detail/417356f8e6a03f2b42952844e0c587b8?type=1
一、因子构建¶
因子构建过程摘自研报,具体因子指标构建如下:
对于每个个股在交易日t,首先计算个股在特定分钟频率下第i个的收益率 $r_{t,i}$, $r_{t,i}$ = $p_{t,i}$ - $p_{t,i-1}$,其中$p_{t,i}$表示在交易日t,个股在第i个特定分钟频率下的对数价格,$p_{i,i-1}$表示在交易日t,个股在第i-1个特定分钟频率下的对数价格。
对于每个个股,根据rt,i分别计算个股在交易日t下的已实现方差(Realized Variance) $RDVar_t$、已实现偏度(Realized Skewness)$RDSkew_t$,已实现峰度(Realized kurtosis) $RDKurt_t$。其中:
$$RDVar_t = \sum\limits_{ i=1}^{n}r_{t,i}^2$$
$$RDSkew_t = \frac {\sqrt N\sum\limits_{ i=1}^{n}r_{t,i}^3}{RDVar_t^{3/2}}$$
$$RDKurt_t = \frac {N \sum\limits_{ i=1}^{n}r_{t,i}^4}{RDVar_t^2}$$
其中N表示个股在交易日t中特定频率的分钟级别数据个数,如在1分钟行情级别下,数据个数N为60*4=240;在五分钟行情级别下,数据个数N为240/5=48。
- 对于每个个股在交易日t计算累计已实现波动(Realized Volatility)$RVol_t$,已实现偏度(Realized Skewness)$RSkew_t$、已实现峰度(Realized Kurtosis) $RKurt_t$, 其中: $$RVol_t = \left(\frac{242}{n} {\sum\limits_{ i=0}^{n}}RDVar_{t-i}\right)^{1/2}$$
$$RSkew = \frac{1}{n}{\sum\limits_{ i=0}^{n}}RDSkew_{t-i}$$
$$RKurt_t = \frac{1}{n}{\sum\limits_{ i=0}^{n}}RDKur_{t-i}$$
- 在每期调仓日截面上,按照上述公式计算每个个股的已实现波动(Realized Volatility)$RVol_t$,已实现偏度(Realized Skewness)$RSkew_t$、已实现峰度(Realized Kurtosis)$RKurt_t$指标,针对每个由高频数据计算得到的因子指标在历史上的分档组合表现,试图寻找出相对有效的因子指标。
二、 构造因子数据¶
import numpy as np
import pandas as pd
import math
from jqdata import *
import matplotlib.pyplot as plt
from datetime import date, timedelta
#上期所
shang = {'AG8888.XSGE':'白银期货指数', 'PB8888.XSGE':'铅期货指数',
'AU8888.XSGE':'黄金期货指数', 'RB8888.XSGE':'螺纹钢期货指数',
'AL8888.XSGE':'铝期货指数', 'RU8888.XSGE':'天然橡胶期货指数',
'BU8888.XSGE':'石油沥青期货指数', 'SN8888.XSGE':'锡期货指数',
'CU8888.XSGE':'铜期货指数',
'FU8888.XSGE':'燃料油期货指数', 'ZN8888.XSGE':'锌期货指数',
'HC8888.XSGE':'热轧卷板期货指数', 'NI8888.XSGE':'镍期货指数',
'SP8888.XSGE':'纸浆主力合约',}
#郑商所
zheng = {'RM8888.XZCE':'菜籽粕期货指数',
'CF8888.XZCE':'棉花期货指数', 'FG8888.XZCE':'玻璃期货指数',
'SF8888.XZCE':'硅铁期货指数',
'SM8888.XZCE':'锰硅期货指数', 'MA8888.XZCE':'甲醇期货指数',
'SR8888.XZCE':'白糖期货指数',
'TA8888.XZCE':'PTA期货指数', 'OI8888.XZCE':'菜籽油期货指数',
'ZC8888.XZCE':'动力煤期货指数',
'AP8888.XZCE':'苹果期货指数', 'CJ8888.XZCE':'红枣合约',}
#大商所
da = {'A8888.XDCE':'豆一期货指数', 'JD8888.XDCE':'鸡蛋期货指数',
'B8888.XDCE':'豆二期货指数', 'JM8888.XDCE':'焦煤期货指数',
'L8888.XDCE':'聚乙烯期货指数',
'C8888.XDCE':'玉米期货指数', 'M8888.XDCE':'豆粕期货指数',
'CS8888.XDCE':'玉米淀粉期货指数', 'P8888.XDCE':'棕榈油期货指数',
'PP8888.XDCE':'聚丙烯期货指数',
'I8888.XDCE':'铁矿石期货指数', 'V8888.XDCE':'聚氯乙烯期货指数',
'J8888.XDCE':'焦炭期货指数', 'Y8888.XDCE':'豆油期货指数',
'EG8888.XDCE':'乙二醇期货指数',}
futures = list(shang.keys()) + list(zheng.keys()) + list(da.keys())
futures[:5]
future_list = []
date_ = date(2014,1,1)
for future in futures:
start_date = get_security_info(future).start_date
if start_date < (date_ - timedelta(days=365)):
future_list.append(future)
print(len(future_list))
future_list[:5]
n=5
trade_days = get_trade_days(start_date='2018-01-01', end_date='2019-06-01')
panel_dict = {}
for i in range(1, len(trade_days)):
daily_start = str(trade_days[i - 1])+' 21:31:00'
daily_end = str(trade_days[i])+' 15:05:00'
factor_df_index = []
factor_df_data = []
for future in future_list:
price = get_price(future,start_date=daily_start,end_date=daily_end,frequency='5m',fields=['close'],
fq='pre')
sum_rt2 = 0.0
sum_rt3 = 0.0
sum_rt4 = 0.0
for j in range(1, len(price)):
pi = math.log(price.iloc[j]['close'])
pi_1 = math.log(price.iloc[j - 1]['close'])
rt = pi - pi_1
sum_rt2 += math.pow(rt, 2)
sum_rt3 += math.pow(rt, 3)
sum_rt4 += math.pow(rt, 4)
rd_var = sum_rt2
if sum_rt3 == 0:
rd_skew = 0
else:
rd_skew = math.sqrt(len(price)) * sum_rt3 / (math.pow(rd_var, 3 / 2))
if sum_rt4 == 0:
rd_kurt = 0
else:
rd_kurt = len(price) * sum_rt4 / (math.pow(rd_var, 2))
factor_df_index.append(future)
factor_df_data.append([price.close.iloc[-1], rd_var, rd_skew, rd_kurt])
factor_df = pd.DataFrame(data=factor_df_data, index=factor_df_index,
columns=['close', 'rd_var', 'rd_skew', 'rd_kurt'])
panel_dict[trade_days[i]] = factor_df
panel = pd.Panel(panel_dict)
panel
panel.major_xs('AG8888.XSGE')
rvol = np.sqrt(panel.minor_xs('rd_var').T.rolling(5).mean()*242).shift(1)
rvol.head(10)
rskew = panel.minor_xs('rd_skew').T.rolling(5).mean().shift(1)
rskew.tail()
rkurt = panel.minor_xs('rd_kurt').T.rolling(5).mean().shift(1)
rkurt.tail()
三、 因子特征展示¶
import matplotlib.dates as mdate
# 设置字体 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
# 用来正常显示负号
plt.rcParams['axes.unicode_minus'] = False
"""
绘制直方图
data:必选参数,绘图数据
bins:直方图的长条形数目,可选项,默认为10
density:是否将得到的直方图向量归一化,可选项,默认为0,代表不归一化,显示频数。normed=1,表示归一化,显示频率。
facecolor:长条形的颜色
edgecolor:长条形边框的颜色
alpha:透明度
"""
# 这个颜色是我把研报的图截图用取色器取出来的,为什么画出来还是有色差?
color = "#1F77B4"
plt.hist(rvol, bins=40, density=0, facecolor=color, edgecolor=None, alpha=1)
# 显示横轴标签
plt.xlabel("区间")
# 显示纵轴标签
plt.ylabel("频数")
# 显示图标题
plt.title("个股波动率分布")
plt.show()
plt.hist(rskew, bins=40, density=0, facecolor=color, edgecolor=None, alpha=1)
# 显示横轴标签
plt.xlabel("区间")
# 显示纵轴标签
plt.ylabel("频数")
# 显示图标题
plt.title("个股偏度分布")
plt.show()
plt.hist(rkurt, bins=40, density=0, facecolor=color, edgecolor=None, alpha=1)
# 显示横轴标签
plt.xlabel("区间")
# 显示纵轴标签
plt.ylabel("频数")
# 显示图标题
plt.title("个股峰度分布")
plt.show()
从以上因子分布三图看出,整个期货市场品种的波动率分布整体上呈现右偏分布;各品种的偏度分布,整体偏度水平保持在零附近,呈现较为明显厚尾状态;各品种的峰度分布与个股波动率水平类似,分布整体上右偏,且样本内的峰度水平大部分大于3,呈现厚尾的现象。
# 百分位走势5档颜色 蓝 橙 绿 红 紫
color_list = ['#5698c6', '#ff9e4a', '#60b760', '#e05c5d', '#ae8ccd']
label_list = ['10', '25', 'median', '75', '90']
all_df = [rvol, rskew, rkurt]
title_name = ['rvol', 'rskew', 'rkurt']
num = 0
for df in all_df:
# 这里需要用每天的因子数据分档,计算出5个折线
y_list = [[], [], [], [], []]
q_list = [0.10, 0.25, 0.50, 0.75, 0.90]
for i in range(len(df)):
factor = df.iloc[i].sort_values()
for j in range(len(q_list)):
num_signal = int(len(q_list) * q_list[j])
factor_value = factor.iloc[num_signal]
y_list[j].append(factor_value)
# 可以设置生成图片的大小
fig = plt.figure(figsize=(12, 8))
plt.title(title_name[num]+' 百分位走势')
num += 1
for i in range(len(y_list)):
plt.plot(y_list[i], color_list[i], label=label_list[i])
x = np.arange(0, len(df),50)
x_label = []
for i in range(0,len(df)):
if i in x:
date = list(df.index)[i]
x_label.append(date)
plt.xticks(x, x_label, rotation='vertical')
plt.xticks(rotation=360)
plt.xlabel("TRADE_DT")
plt.ylabel("因子值")
legend()
四、 实证分析¶
all_df = [rvol, rskew, rkurt]
Q_list = ['Q1', 'Q2', 'Q3', 'Q4', 'Q5']
df_list = []
num = 0
for i in range(len(all_df)):
price_close = panel.minor_xs('close').T
price_pct = price_close.pct_change().dropna().iloc[4:]
groups = [[], [], [], [], []]
length = int(len(all_df[i].T) / 5)
for j in range(len(price_pct)):
df = all_df[i].dropna().iloc[0:]
index_list = list(df.iloc[j].sort_values().index)
daily_price = price_pct[index_list].iloc[j]
groups[0].append(daily_price[0:5].mean())
groups[1].append(daily_price[5:9].mean())
groups[2].append(daily_price[9:14].mean())
groups[3].append(daily_price[14:19].mean())
groups[4].append(daily_price[19:].mean())
df_group = pd.DataFrame(groups).T
df_group.index = price_pct.index
df_group = df_group.cumsum()
df_group.columns = ['Q1', 'Q2', 'Q3', 'Q4', 'Q5']
df_list.append(df_group)
fig = plt.figure(figsize=(12, 8))
plt.title(title_name[num]+' 累计收益率')
plt.plot(df_group['Q1'])
plt.plot(df_group['Q2'])
plt.plot(df_group['Q3'])
plt.plot(df_group['Q4'])
plt.plot(df_group['Q5'])
plt.legend()
num += 1
num = 0
for df in df_list:
df = df['Q3'] - df['Q2']
plt.figure(figsize=(12, 8))
plt.title(title_name[num]+' 累计收益率')
plt.plot(df, label=title_name[num])
plt.legend()
num += 1
import scipy.stats as st
all_df = [rvol, rskew, rkurt]
name = ['rvol', 'rskew', 'rkurt']
color_list = ['#2B4C80', '#B00004']
label_list = ['IC', 'IC均值(12期)']
for i in range(len(all_df)):
# 每天的ic
ic_list = []
# ic均值(12期)
ic_ma_list = []
y_list = [ic_list, ic_ma_list]
for j in range(len(all_df[i].iloc[5:])):
ic = st.pearsonr(price_pct.iloc[j].values, all_df[i].iloc[5:].iloc[j].values)[0]
ic_list.append(ic)
ic_list = np.array(ic_list)
print("%s ic 小于0的个数占比:%s" % (name[i], np.sum(ic_list < 0) / len(ic_list)))
for z in range(len(ic_list)):
if z < 12:
ic_ma_list.append(np.nan)
continue
ic_ma = np.array(ic_list[z - 12:z]).mean()
ic_ma_list.append(ic_ma)
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(1, 1, 1)
ax.set_title(name[i] + "因子ic")
for i in range(len(y_list)):
yi = y_list[i]
ax.plot(yi, color_list[i], label=label_list[i])
# 绘制Y轴的网格线便于查看IC
plt.grid(axis='y')
plt.show()