最近几天才开始着手研究ML量化,有点晚,个人的预期还是很看好ML在Quant买方策略里的效果。
此篇文章非结论性的文章,只是记录一下学习过程中的想法,大家有兴趣可以探讨。
在看一个kaggle竞赛kernel的时候,发现一个延迟相关性的问题。
df = pd.DataFrame({'C': [1, 4, 2, 4, 5, -1, 6, 9, 3, 5, 10, -3, -5]})
print(df["C"].corr(df["C"]))
print(df["C"].corr(df["C"].shift(1)))
结果是:
1.0
0.07459885356228374
当时我还是挺震惊的,自己和过去的自己完全不相关了吗?
我再回想一下现在的一些回归方法大部分(除了RNN这种含有历史信息的模型)都是 f(X_i)=y_i ,这个i是啥呢?我们做股价数据预测时,i就是时间。 X_i 就是i时刻对应的价格(Price),成交量(Vol),特征1(Feature1),特征2(Feature2) ......组成的向量。这应该是横截面回归,计算时不含有历史信息的。我预测明天的股价时,只用到了今天的信息,与昨天无关。
假如特征1的预测性很好,那么我们一般会检测一下特征1和y的相关性,结果应该是不错的。但是万一这个特征1的效果有点滞后,今天的新闻对明天本来没什么影响,经过某些专家解读后对后天的股价产生了影响。实际环境中有没有这样的特征呢?这个我也不太清楚。才入坑ML,这些对我来讲一切都是新的......
如果特征1的效果滞后了两天(今天->后天),而且我们一般把y会设置成明天的股价变化,那特征1和y的相关性就会发生前面代码中的情况(低相关性)。
来代码测试一下:
# 用的某篇论文中用GBM产生的股价方法,论文中为stock B,故起名为B
stock_price = B(12.0)
df = stock_price
# 计算 log return
df["log_return"] = np.log(df['close']).diff()
# 把y设置成明天的 log return
df["y"] = df["log_return"].shift(-1)
OFFSET = 2
# 我自己造的一个未来数据特征,其实就是后天的 log return 加了高斯噪声,我假如是个前天的新闻一定会对今天的股价产生影响
df["Feature1"] = df["log_return"].shift(-OFFSET)*100 + RS.normal(0, 0.2, len(df["log_return"]))
df.dropna(inplace=True)
有未来数据在,按理来讲应该是接近1的相关性才对,但是有偏移,按前面的结论应该相关性很低。
来看一下特征1和y相关性如何
print(df["y"].corr(df["Feature1"]))
print(df["log_return"].corr(df["Feature1"]))
-------- output
-0.02142933034920817
-0.0007564299402125546
在意料之中,和y基本不相关,和过去的自己也没有关系。直接回归试试看呢?我这里用了线性回归和树模型回归来进行测试。
make_regress(df)
-------- output
linear regression result:
in sample R^2: 0.01 out sample R^2: -0.06
ExtraTrees regression result:
in sample R^2: 0.05 out sample R^2: -0.15
样本内外的R2是负的,那是基本没有预测作用。如果我做如下一个变换呢?从特征1再衍生出一个特征
# 新增加一个特征
df["Feature1_shift1"] = df["Feature1"].shift(1)
df.dropna(inplace=True)
再来进行回归测试
make_regress(df)
-------- output
linear regression result:
in sample R^2: 0.90 out sample R^2: 0.90
ExtraTrees regression result:
in sample R^2: 0.87 out sample R^2: 0.86
结果显而易见,回到了用未来数据进行预测的效果。
当然这个例子有点特殊,实际生活中是不是这样还得去进行测试。如果通过简单的特征工程(shift)能增加预测效果,那么哪些特征需要做shift呢?shift多少个周期呢?如果新增出来的特征太多,怎么去评价哪些shift是有用的呢?
欢迎大家一起讨论。最后附上所有测试代码(python3)
import math
import numpy as np
import pandas as pd
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.linear_model import LinearRegression
RS = np.random.RandomState(42)
LENGTH = 1000
OFFSET = 2
def split(df):
sticker = df.columns.values.tolist()
sticker.remove("y")
X = df[sticker]
y = df["y"]
split_idx = int(len(df)*0.7)
X_train = X.iloc[:split_idx]
y_train = y.iloc[:split_idx]
X_test = X.iloc[split_idx:]
y_test = y.iloc[split_idx:]
return X_train, y_train, X_test, y_test
def make_regress(df):
X_train, y_train, X_test, y_test = split(df)
lr_model = LinearRegression()
lr_model.fit(X_train, y_train)
lr_in_sample_r2 = lr_model.score(X_train, y_train)
lr_out_sample_r2 = lr_model.score(X_test, y_test)
print("linear regression result:")
print("in sample R^2: %.2f out sample R^2: %.2f" % (lr_in_sample_r2, lr_out_sample_r2))
etr_model = ExtraTreesRegressor(n_estimators=100, max_depth=4)
etr_model.fit(X_train, y_train)
etr_in_sample_r2 = etr_model.score(X_train, y_train)
etr_out_sample_r2 = etr_model.score(X_test, y_test)
print("ExtraTrees regression result:")
print("in sample R^2: %.2f out sample R^2: %.2f" % (etr_in_sample_r2, etr_out_sample_r2))
def B(b0):
"""
gen B price Done
param bo: initial price
"""
global LENGTH
mu = 0.003
delta_t = 1/251.0
delta = 0.1
b_close = [b0] # first price
n_rand = RS.normal(0, 1, LENGTH) # N~(0,1) random numbers
for i in range(LENGTH):
b = pow(math.e, mu*delta_t+delta*n_rand[i]*math.sqrt(delta_t)) * b_close[-1]
b_close.append(round(b, 2))
dates_idx = pd.date_range('20130101', periods=LENGTH+1, freq='D')
df = pd.DataFrame({"close": b_close}, index=dates_idx)
return df
# 用的某篇论文中用GBM产生的股价方法,论文中为stock B,故起名为B
stock_price = B(12.0)
df = stock_price
# 计算 log return
df["log_return"] = np.log(df['close']).diff()
# 把y设置成明天的 log return
df["y"] = df["log_return"].shift(-1)
# 我自己造的一个未来数据特征,其实就是后天的 log return 加了高斯噪声,我假如是个前天的新闻一定会对今天的股价产生影响
df["Feature1"] = df["log_return"].shift(-OFFSET)*100 + RS.normal(0, 0.2, len(df["log_return"]))
df.dropna(inplace=True)
print(df["y"].corr(df["Feature1"]))
print(df["log_return"].corr(df["Feature1"]))
make_regress(df)
# 新增加一个特征
df["Feature1_shift1"] = df["Feature1"].shift(1)
df.dropna(inplace=True)
make_regress(df)
Comments
comments powered by Disqus