Date Tags Quant

最近几天才开始着手研究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