使用 Python进行量化交易:前向验证分析

运行环境:Google Colab

1. 利用 yfinance 下载数据

import yfinance as yf

ticker = 'AAPL'
df = yf.download(ticker)
df
  • 下载苹果的股票数据
df = df.loc['2018-01-01':].copy()

df

在这里插入图片描述

df['change_tomorrow'] = df['Adj Close'].pct_change(-1)
df.change_tomorrow = df.change_tomorrow * -1
df.change_tomorrow = df.change_tomorrow * 100
df

在这里插入图片描述

df = df.dropna().copy()
df

在这里插入图片描述

2. 变量准备

y = df.change_tomorrow
X = df[['Open','High','Low','Close','Volume']]

3. 时间序列数据的交叉验证

from sklearn.model_selection import TimeSeriesSplit

ts = TimeSeriesSplit(test_size=200)
  • 使用了 Scikit-learn 库中的 TimeSeriesSplit 方法来创建时间序列交叉验证的实例。
  • test_size=200 表示将数据集分成多个交叉验证折叠(folds),并且每个折叠的测试集大小为 200 个样本。

4. 随机森林回归模型来进行时间序列交叉验证

from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error

model_dt = RandomForestRegressor(max_depth=15, random_state=42)

error_mse_list = []

for index_train, index_test in ts.split(df):
    X_train, y_train = X.iloc[index_train], y.iloc[index_train]
    X_test, y_test = X.iloc[index_test], y.iloc[index_test]
    
    model_dt.fit(X_train, y_train)
    
    y_pred = model_dt.predict(X_test)
    error_mse = mean_squared_error(y_test, y_pred)
    
    error_mse_list.append(error_mse)
  • 使用之前创建的时间序列交叉验证实例 ts 在数据集 df 上进行拆分,index_train, index_test 是每次交叉验证的训练集和测试集的索引。
  • 使用随机森林模型 model_dt 对训练集进行拟合。
  • 使用训练好的模型对测试集的特征进行预测,得到预测结果 y_pred
  • 使用均方误差(MSE)方法,计算了真实值 y_test 和预测值 y_pred 之间的均方误差。
error_mse_list

[9.29226288135296,
6.621204525309144,
5.117431788350876,
5.570462756189788,
2.627530106136459]

5. 在每个交易周期根据模型的预测值执行买入或卖出操作

from backtesting import Backtest, Strategy
class Regression(Strategy):
    limit_buy = 1
    limit_sell = -5
    
    n_train = 600
    coef_retrain = 200
    
    def init(self):
        self.model = RandomForestRegressor(max_depth=15, random_state=42)
        self.already_bought = False
        
        X_train = self.data.df.iloc[:self.n_train, :-1]
        y_train = self.data.df.iloc[:self.n_train, -1]
        
        self.model.fit(X=X_train, y=y_train)

    def next(self):
        explanatory_today = self.data.df.iloc[[-1], :-1]
        forecast_tomorrow = self.model.predict(explanatory_today)[0]
        
        if forecast_tomorrow > self.limit_buy and self.already_bought == False:
            self.buy()
            self.already_bought = True
        elif forecast_tomorrow < self.limit_sell and self.already_bought == True:
            self.sell()
            self.already_bought = False
        else:
            pass
  • limit_buy = 1limit_sell = -5:买入和卖出的阈值。
  • n_train = 600coef_retrain = 200:用于模型训练的数据量和重新训练的频率。
  • def next(self):: 这是每个交易周期调用的方法,用于执行具体的交易操作。

在这里插入图片描述
在前向优化中,回测数据被分成多个样本内和样本外段。策略在每个样本内段(蓝色)上进行优化,然后最优参数被应用到紧随其后的样本外段(黄色)。这种样本内优化和样本外测试的循环将随着整个数据集的进行而重复,形成一系列样本外回测。

Anchored Walk-Forward “锚定型”:样本内时间窗口始终从历史数据序列的开始开始,并逐步增加。

Unanchored Walk-Forward “非锚定型”:样本内时间窗口始终具有相同的持续时间,并且每个时间窗口都在前一个时间窗口之后开始。

6. 每经过一定时间后重新训练模型,以适应新的市场情况

class WalkForwardAnchored(Regression):
    def next(self):
        
        # we don't take any action and move on to the following day
        if len(self.data) < self.n_train:
            return
        
        # we retrain the model each 200 days
        if len(self.data) % self.coef_retrain == 0:
            X_train = self.data.df.iloc[:, :-1]
            y_train = self.data.df.iloc[:, -1]

            self.model.fit(X_train, y_train)

            super().next()
            
        else:
            
            super().next()
  • if len(self.data) < self.n_train:: 这部分代码检查数据集长度是否小于预先设定的训练数据量 n_train。如果是,表示数据还不足以进行模型训练,于是不执行任何操作,直接跳转到下一个交易日。
  • if len(self.data) % self.coef_retrain == 0:: 这部分代码检查当前数据长度是否达到了重新训练模型的时间节点(每经过 coef_retrain 天就重新训练一次模型)。
  • 从当前数据集中重新获取特征集 X_train 和目标集 y_train
  • 使用这些数据重新训练模型 self.model
from backtesting import Backtest
bt = Backtest(df, WalkForwardAnchored, cash=10000, commission=.002, exclusive_orders=True)
stats_skopt, heatmap, optimize_result = bt.optimize(
    limit_buy = range(0, 6), limit_sell = range(-6, 0),
    maximize='Return [%]',
    max_tries=500,
    random_state=42,
    return_heatmap=True,
    return_optimization=True,
    method='skopt'
    )

dff = heatmap.reset_index()
dff = dff.sort_values('Return [%]', ascending=False)
dff
indexlimit_buylimit_sellReturn [%]
00-6128.2607345315552
20-4128.2607345315552
10-5128.2607345315552
30-3118.6897769815064
61-572.99951079330444
71-472.99951079330444
81-365.70472863082887
163-40.0
235-50.0
224-10.0
214-20.0
204-30.0
194-50.0
183-20.0
173-30.0
122-40.0
153-50.0
142-10.0
132-30.0
112-50.0
102-60.0
245-40.0
40-2-15.848805708007804
91-1-37.85255523803709
50-1-48.291288581848114

7. 使用最近的 n_train 天数据作为训练集,从而保持模型更敏感地反映最近的市场情况

class WalkForwardUnanchored(Regression):
    def next(self):
        
        # we don't take any action and move on to the following day
        if len(self.data) < self.n_train:
            return
        
        # we retrain the model each 200 days
        if len(self.data) % self.coef_retrain == 0:
            X_train = self.data.df.iloc[-self.n_train:, :-1]
            y_train = self.data.df.iloc[-self.n_train:, -1]

            self.model.fit(X_train, y_train)

            super().next()
            
        else:
            
            super().next()
bt_unanchored = Backtest(df, WalkForwardUnanchored, cash=10000, commission=.002, exclusive_orders=True)

stats_skopt, heatmap, optimize_result = bt_unanchored.optimize(
    limit_buy = range(0, 6), limit_sell = range(-6, 0),
    maximize='Return [%]',
    max_tries=500,
    random_state=42,
    return_heatmap=True,
    return_optimization=True,
    method='skopt'
    )

dff = heatmap.reset_index()
dff = dff.sort_values('Return [%]', ascending=False)
dff
indexlimit_buylimit_sellReturn [%]
00-6128.2607345315552
10-4128.2607345315552
20-3118.6897769815064
51-572.99951079330444
61-472.99951079330444
71-365.70472863082887
153-40.0
225-50.0
214-10.0
204-20.0
194-30.0
184-50.0
173-20.0
163-30.0
122-30.0
143-50.0
132-10.0
112-40.0
102-50.0
92-60.0
235-40.0
30-2-16.90633944244384
81-1-34.242432039184564
40-1-46.87595996093751
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值