使用 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/161372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv8中训练参数中文解释

预测函数&#xff1a; from ultralytics import YOLO# Load a model model YOLO(yolov8n.pt) # Train the model model.train(datarD:\yolov8\ultralytics-main\data1.yaml, workers0, epochs100, batch16) 可选参数&#xff1a;

el-table树形数据隐藏子选择框

0 效果 1 代码 type是table数据中用来区分一级和二级的标识 // 隐藏子合同选择框 cellNone(row) {if (row.row.type 3 || row.row.type 4) {return "checkNone";} }, <style lang"scss" scoped>::v-deep {.checkNone .el-checkbox__input {displa…

数据结构与算法设计分析——常用搜索算法

目录 一、穷举搜索二、图的遍历算法&#xff08;一&#xff09;深度优先搜索&#xff08;DFS&#xff09;&#xff08;二&#xff09;广度优先搜索&#xff08;BFS&#xff09; 三、回溯法&#xff08;一&#xff09;回溯法的定义&#xff08;二&#xff09;回溯法的应用 四、分…

轻量级 Java 日志组件

日志记录功能在开发中很常用&#xff0c;不仅可以记录程序运行的细节&#xff0c;方便调试&#xff0c;也可以记录用户的行为&#xff0c;是框架中不可或缺的组件。为最大程度复用现有的组件&#xff0c;我们就地取材使用了 JDK 自带的 JUL&#xff08;java.util.logging&#…

学习模拟简明教程【Learning to simulate】

深度神经网络是一项令人惊叹的技术。 有了足够的标记数据&#xff0c;他们可以学习为图像和声音等高维输入生成非常准确的分类器。 近年来&#xff0c;机器学习社区已经能够成功解决诸如对象分类、图像中对象检测和图像分割等问题。 上述声明中的加黑字体警告是有足够的标记数…

手把手教你用C语言写出“走迷宫”小游戏(能看懂文字就会自己敲系列)

目录 设计迷宫地图 设计主角——小球 完整代码 这次教大家编写一个简单的“走迷宫”小游戏&#xff0c;我们可以通过键盘上的‘W’、‘S’、‘A’、‘D’四个键来控制一个“小球”向上&#xff0c;下&#xff0c;左&#xff0c;右移动&#xff0c;目的就是让这个“小球”从起…

Python3语法总结-数据转换②

Python3语法总结-数据转换② Python3语法总结二.Python数据类型转换隐式类型转换显示类型转换 Python3语法总结 二.Python数据类型转换 有时候我们&#xff0c;需要对数据内置的类型进行转换&#xff0c;数据类型的转换。 Python 数据类型转换可以分为两种&#xff1a; 隐式类…

原型网络Prototypical Network的python代码逐行解释,新手小白也可学会!!-----系列4

文章目录 原型网络进行分类的基本流程一、原始代码---计算欧氏距离&#xff0c;设计原型网络&#xff08;计算原型开始训练&#xff09;二、每一行代码的详细解释总结 原型网络进行分类的基本流程 利用原型网络进行分类&#xff0c;基本流程如下&#xff1a; 1.对于每一个样本…

信号完整性分析基础知识之有损传输线、上升时间衰减和材料特性(十):有损传输线在时域中的表现

如果高频衰减大于低频衰减&#xff0c;随着信号传输&#xff0c;上升时间将会增加。上升时间通常定义为边沿在最终值的 10% 到 90% 之间过渡的时间。这假设信号的边缘轮廓看起来有点高斯分布&#xff0c;中间是最快的斜率区域。对于该波形&#xff0c;10%−90% 的上升时间是有意…

MIB 6.1810实验Xv6 and Unix utilities(4)primes

难度: hard/moderate Write a concurrent prime sieve program for xv6 using pipes and the design illustrated in the picture halfway down this page and the surrounding text. This idea is due to Doug McIlroy, inventor of Unix pipes. Your solution should be in …

让你的Mac体验更便捷,快速启动工具Application Wizard为你助力!

亲爱的Mac用户们&#xff0c;你是否经常感到在繁琐的软件启动过程中浪费了太多时间&#xff1f;你是否希望能够以更快的速度找到并启动你所需的应用程序&#xff1f;如果是的话&#xff0c;那么不要犹豫&#xff0c;让我们来介绍一款强大的软件快速启动工具——Application Wiz…

23年宁波职教中心CTF竞赛-决赛

Web 拳拳组合 进去页面之后查看源码&#xff0c;发现一段注释&#xff0c;写着小明喜欢10的幂次方&#xff0c;那就是10、100、1000、10000 返回页面&#xff0c;在点击红色叉叉的时候抓包&#xff0c;修改count的值为10、100、1000、10000 然后分别获得以下信息 ?count1…

Spring面试题:(八)Spring事务

Spring事务概述 Spring事务基于数据库&#xff0c;基于数据库的事务封装了统一的接口。 编程式事务和声明式事务。 声明式事务分为Xml声明式或者注解声明式 实现事务相关的三个类 事务管理器 事务定义 事务状态 XML声明式事务的使用方法 导入坐标配置目标类配置切面 导入…

JS判断是否存在某个元素(includes、indexOf、find、findeIndex、some)(every 数组内所有值是否相同)

方法一&#xff1a;array.includes(searcElement[,fromIndex]) 此方法判断数组中是否存在某个值&#xff0c;如果存在返回true&#xff0c;否则返回false。 searchElement&#xff1a;需要查找的元素&#xff0c;必选。fromIndex&#xff1a;可选&#xff0c;从该索引处开始查…

浏览器页面被恶意控制时的解决方法

解决360流氓软件控制浏览器页面 提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、接受360安全卫士的好意&#xff08;尽量不要选&#xff09;二、拒绝360安全卫士的好意&#xff08;强烈推荐&#xff09;第…

【Vue渲染】 条件渲染 | v-if | v-show | 列表渲染 | v-for

目录 前言 v-if和v-show的区别和联系 v-show和v-if如何选择 条件渲染|v-if|v-show v-if v-if v-else v-if v-else-if v-else template v-show 列表渲染|v-for v-for 前言 本文介绍Vue渲染&#xff0c;包含条件渲染v-if和v-show的区别和联系以及列表渲染v-for v-if和…

“腾易视连”构建汽车生态新格局 星选计划赋能创作者价值提升

11月16日&#xff0c;在2023年广州国际车展前夕&#xff0c;以“腾易视连&#xff0c;入局视频号抓住增长新机会”为主题的腾易创作者大会在广州隆重举办。此次大会&#xff0c;邀请行业嘉宾、媒体伙伴、生态伙伴、视频号汽车领域原生达人等共济一堂&#xff0c;结合汽车行业数…

轻量级的资源授权:基于 OAuth 规范

了解 OAuth 感觉 OAuth 太负盛名了&#xff0c;以至于后来在 OIDC 反而难以企及前辈 OAuth。倒是大家谈论比较多的是 JWT&#xff08;例如https://www.cnblogs.com/lyzg/p/6132801.html&#xff09;&#xff0c;——实际谈 JWT 就是在实现 OIDC&#xff0c;反而 OIDC 大家不怎…

Android跨进程通信,IPC,RPC,Binder系统,C语言应用层调用

文章目录 Android跨进程通信&#xff0c;IPC&#xff0c;RPC&#xff0c;Binder系统&#xff0c;C语言应用层调用&#xff08;&#xff09;1.概念2.流程3.bctest.c3.1 注册服务&#xff0c;打开binder驱动3.2 获取服务 4.binder_call Android跨进程通信&#xff0c;IPC&#xf…