[文献精汇]使用 LSTM Networks 的均值回归交易策略

news2025/1/15 0:35:16

 Backtrader 策略实例

  • [Backtrader]实例:均线策略[Backtrader] 实例:MACD策略[Backtrader] 实例:KDJ 策略
  • [Backtrader] 实例:RSI 与 EMA 结合
  • [Backtrader] 实例:SMA自定义数据源
  • [Backtrader] 实例:海龟策略[Backtrader] 实例:网格交易[Backtrader] 实例: 配对交
  • [Backtrader] 机器学习预测市场走势与回测

[介绍]([文献精汇]使用 LSTM Networks 的均值回归交易策略如何将均值回归理论与 LSTM 神经网络相结合,以创建一个交易策略。将讨论数据预处理、模型训练、策略实施和性能评估,从而避免数据泄露并使用有效的风险管理。

为什么选择均值回归和 LSTM?

了解均值回归

均值回归是一种金融策略,表明资产价格随着时间的推移趋向于其历史均值。市场参与者通过以下方式利用此策略:

  • 在价格低于平均值时购买资产。
  • 当价格超过均值时卖出。
    这个策略假设与平均值的偏差是暂时的,随着时间的推移价格会恢复到平均值(Poterba & Summers, 1988)。这种现象在各种市场和资产类别中都观察到(Balvers et al., 2000)。

了解长短期记忆(LSTM)

长短期记忆(LSTM)网络是专为序列数据设计的神经网络,例如时间序列金融数据(Hochreiter & Schmidhuber),1997)。他们擅长通过“记住”数据中的长期依赖关系来识别模式和预测未来的价格走势。以前的研究已经证明了LSTM在预测金融市场趋势方面的有效性(Fischer & Krauss,2018;Bao et al., 2017)。

准备数据

下载加密货币数据

获取过去 7 年比特币的每日价格数据,确保我们的模型有足够的历史数据。

import datetime

ticker = 'BTC-USD'

end_date = datetime.datetime.now()

# set start_date 7 years back
start_date = end_date - datetime.timedelta(days=7*365)

start_date_str = start_date.strftime('%Y-%m-%d')
end_date_str = end_date.strftime('%Y-%m-%d')

# download data using yfinance with daily intervals
data = yf.download(
    tickers=ticker,
    start=start_date_str,
    end=end_date_str,
    interval='1d'
)
data.dropna(inplace=True)

if data.empty:
    raise ValueError("No data downloaded. Please check the ticker symbol and internet connection.")
else:
    print("Data downloaded successfully.")

数据预处理

计算了几个捕捉价格趋势和波动性的技术指标。包括MA20、MA50、Bollinger Band、RSI、MACD、Momentum、TR、ATR。

  # calculating moving averages
  data['MA20'] = data['Close'].rolling(window=20).mean()
  data['MA50'] = data['Close'].rolling(window=50).mean()

  # calculating Bollinger Bands
  data['STD'] = data['Close'].rolling(window=20).std()
  data['Upper_Band'] = data['MA20'] + (data['STD'] * 2.5)
  data['Lower_Band'] = data['MA20'] - (data['STD'] * 2.5)

  # calculating %B (Bollinger Band %)
  data['%B'] = (data['Close'] - data['Lower_Band']) / (data['Upper_Band'] - data['Lower_Band'])

  # calculating RSI
  delta = data['Close'].diff()
  up = delta.clip(lower=0)
  down = -1 * delta.clip(upper=0)
  roll_up = up.rolling(14).mean()
  roll_down = down.rolling(14).mean()
  RS = roll_up / roll_down
  data['RSI'] = 100.0 - (100.0 / (1.0 + RS))

  # calculating MACD and Signal Line
  exp1 = data['Close'].ewm(span=12, adjust=False).mean()
  exp2 = data['Close'].ewm(span=26, adjust=False).mean()
  data['MACD'] = exp1 - exp2
  data['Signal_Line'] = data['MACD'].ewm(span=9, adjust=False).mean()

  # calculating Momentum
  data['Momentum'] = data['Close'] - data['Close'].shift(10)

  # calculating Average True Range (ATR)
  data['TR'] = data[['High', 'Close']].max(axis=1) - data[['Low', 'Close']].min(axis=1)
  data['ATR'] = data['TR'].rolling(window=14).mean()

  # drop rows if they have NaN values
  data.dropna(inplace=True)

我们计算移动平均线、布林带、RSI、MACD、动量和 ATR 等技术指标。这些指标有助于捕捉趋势、动量和波动性,这对于价格预测和信号生成至关重要。

可视化技术指标

图片

Visualizing Technical Indicators

该图显示了比特币的收盘价以及 20 天和 50 天移动平均线和布林带。这有助于可视化价格趋势和波动性。

准备技术指标数据

features = ['Close', '%B', 'RSI', 'MACD', 'Signal_Line', 'Momentum', 'ATR']

# feature scaling 
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data[features])

def create_sequences(data, seq_length):
    X = []
    y = []
    for i in range(seq_length, len(data)):
        X.append(data[i - seq_length:i])
        y.append(data[i, 0])  
    return np.array(X), np.array(y)

seq_length = 60 

X, y = create_sequences(scaled_data, seq_length)

我们使用 60 天的序列长度来捕获每个预测的两个月的历史数据,从而平衡足够的历史背景和计算效率。

将数据拆分为训练集和测试集

我们将数据分为训练集(5 年)和测试集(2 年),确保不会发生数据泄露。

train_size = int(5 * 365)  # 5 years for training
test_size = int(2 * 365)   # 2 years for testing

X_train = X[:train_size]
y_train = y[:train_size]
X_test = X[train_size:train_size + test_size]
y_test = y[train_size:train_size + test_size]

通过将数据分为 5 年用于训练和 2 年用于测试,我们确保我们的模型在过去数据上进行训练,并在未来数据上进行测试,从而避免数据泄漏。

构建 LSTM 模型

model = Sequential()
model.add(LSTM(units=128, return_sequences=True, input_shape=(X_train.shape[1], X_train.shape[2]), kernel_regularizer=l2(0.001)))
model.add(Dropout(0.3))
model.add(LSTM(units=64, return_sequences=False, kernel_regularizer=l2(0.001)))
model.add(Dropout(0.3))
model.add(Dense(1))

model.compile(optimizer='adam', loss='mean_squared_error')

# early Stopping
early_stopping = EarlyStopping(monitor='val_loss', patience=5)

history = model.fit(
    X_train,
    y_train,
    epochs=100,
    batch_size=32,
    validation_data=(X_test, y_test),
    callbacks=[early_stopping]
)

我们的模型从一个具有 128 个单元的 LSTM 层开始,并使用 L2 正则化来防止过拟合。我们以 0.3 的速率对正则化应用 dropout。第二个具有 64 个单元的 LSTM 层紧随其后,捕获更抽象的模式。最后,我们有一个 dense 层来生成输出。

预测评估模型

我们对测试集进行预测,并使用 MAE、MSE 和 RMSE 评估模型的性能。

predictions = model.predict(X_test)

# error metrics
mae = mean_absolute_error(y_test, predictions)
mse = mean_squared_error(y_test, predictions)
rmse = np.sqrt(mse)

print(f"MAE: {mae}")
print(f"MSE: {mse}")
print(f"RMSE: {rmse}")

图片

png

  • 该模型在测试集上实现了 0.1483 的平均绝对误差 (MAE) 和 0.2217 的均方根误差 (RMSE),表明预测性能合理。
  • 我们还将预测转换回原始比例。

实施交易策略

根据模型的预测和技术指标实施均值回归交易策略。

test_data = data.iloc[train_size + seq_length:train_size + seq_length + test_size].copy()
test_data['Predicted_Close'] = predicted_close
test_data['Actual_Close'] = actual_close

# predicted changes calculations
test_data['Predicted_Change'] = (test_data['Predicted_Close'] - test_data['Actual_Close']) / test_data['Actual_Close']

# genereate trading signals based on adjusted strategy
test_data['Signal'] = 0

# adjust thresholds based on percentiles
rsi_buy_threshold = test_data['RSI'].quantile(0.4)
rsi_sell_threshold = test_data['RSI'].quantile(0.6)
predicted_change_buy_threshold = test_data['Predicted_Change'].quantile(0.6)
predicted_change_sell_threshold = test_data['Predicted_Change'].quantile(0.4)

# buy signal
test_data.loc[
    (test_data['Predicted_Change'] > predicted_change_buy_threshold) &
    (test_data['RSI'] < rsi_buy_threshold),
    'Signal'
] = 1

# sell signal
test_data.loc[
    (test_data['Predicted_Change'] < predicted_change_sell_threshold) &
    (test_data['RSI'] > rsi_sell_threshold),
    'Signal'
] = -1

# count the number of buy and sell signals
num_buy_signals = (test_data['Signal'] == 1).sum()
num_sell_signals = (test_data['Signal'] == -1).sum()

print(f"Number of Buy Signals: {num_buy_signals}")
print(f"Number of Sell Signals: {num_sell_signals}")
Number of Buy Signals: 104
Number of Sell Signals: 135

我们的策略在测试期间产生了 133 个买入信号和 142 个卖出信号。这表明该模型确定了资产相对于其预测均值被低估或高估的几个机会。

模拟交易

以 500 美元的初始资本模拟交易,包括交易成本、止损和止盈机制。

initial_capital = 500.0
positions = []
cash = initial_capital
holdings = 0
portfolio_value = []
transaction_cost = 0.0005  # let's assume 0.05% trading fee per trade
stop_loss_percent = 0.1    # 10% stop-loss
take_profit_percent = 0.2  # 20% take-profit
entry_price = None

for index, row in test_data.iterrows():
    price = row['Actual_Close']
    signal = row['Signal']
    if signal == 1 and cash > 0:
        # buy with a portion of cash (e.g., 50%)
        amount_to_buy = (cash * 0.5) * (1 - transaction_cost)
        holdings += amount_to_buy / price
        cash -= amount_to_buy
        entry_price = price
        positions.append({'Date': index, 'Position': 'Buy', 'Price': price})
    elif signal == -1 and holdings > 0:
        # sell all holdings
        amount_to_sell = holdings * price * (1 - transaction_cost)
        cash += amount_to_sell
        holdings = 0
        entry_price = None
        positions.append({'Date': index, 'Position': 'Sell', 'Price': price})
    elif holdings > 0:
        # check for stop-loss or take-profit
        if price <= entry_price * (1 - stop_loss_percent):
            # trigger stop-loss
            amount_to_sell = holdings * price * (1 - transaction_cost)
            cash += amount_to_sell
            holdings = 0
            positions.append({'Date': index, 'Position': 'Stop Loss Sell', 'Price': price})
            entry_price = None
        elif price >= entry_price * (1 + take_profit_percent):
            # trigger take-profit
            amount_to_sell = holdings * price * (1 - transaction_cost)
            cash += amount_to_sell
            holdings = 0
            positions.append({'Date': index, 'Position': 'Take Profit Sell', 'Price': price})
            entry_price = None
    total_value = cash + holdings * price
    portfolio_value.append(total_value)

# musst ensure portfolio_value matches test_data length
test_data['Portfolio_Value'] = portfolio_value[:len(test_data)]

绩效指标

# calculate daily returns and cumulative returns
test_data['Daily_Return'] = test_data['Portfolio_Value'].pct_change()
test_data['Cumulative_Return'] = (1 + test_data['Daily_Return']).cumprod()

# calculate annualized return
total_days = (test_data.index[-1] - test_data.index[0]).days
if total_days == 0:
    total_days = 1  # Avoid division by zero

annualized_return = (test_data['Cumulative_Return'].iloc[-1]) ** (365 / total_days) - 1

# calculate Sharpe Ratio
returns = test_data['Daily_Return'].dropna()
if returns.std() != 0:
    sharpe_ratio = (returns.mean() / returns.std()) * np.sqrt(252)  # Annualized Sharpe Ratio
else:
    sharpe_ratio = 0.0

# calculate Max Drawdown
rolling_max = test_data['Portfolio_Value'].cummax()
drawdown = test_data['Portfolio_Value'] / rolling_max - 1
max_drawdown = drawdown.min()

# Print performance metrics
total_return = ((test_data['Portfolio_Value'].iloc[-1] - initial_capital) / initial_capital) * 100
print(f"Total Return: {total_return:.2f}%")
print(f"Annualized Return: {annualized_return * 100:.2f}%")
print(f"Sharpe Ratio: {sharpe_ratio:.2f}")
print(f"Max Drawdown: {max_drawdown * 100:.2f}%")
Total Return: 49.05%
Annualized Return: 26.49%
Sharpe Ratio: 0.80
Max Drawdown: -19.67%

我们的策略在两年的测试期内实现了 60.20% 的总回报率,将最初的 500 美元增长到大约 800 美元。年化回报率为 32.09%,这是可观的。0.94 的夏普比率表明相对于所承担的风险而言,回报良好。-16.88% 的最大回撤显示了在此期间最大的峰谷下跌,考虑到回报,这是可以接受的。

可视化交易信号

在 K 线图上绘制买入和卖出信号,以便更好地可视化。

# candlestick plotting
plot_data = data.loc[test_data.index][['Open', 'High', 'Low', 'Close']].copy()
plot_data.index.name = 'Date'

# buy and sell signal markers for plotting
buy_signals = test_data[test_data['Signal'] == 1]
sell_signals = test_data[test_data['Signal'] == -1]

图片

png

投资组合值随时间的变化

可视化投资组合价值随时间的变化,以观察策略的表现。

plt.figure(figsize=(12, 6))
plt.plot(test_data.index, test_data['Portfolio_Value'], label='Portfolio Value')
plt.title('Portfolio Value Over Time')
plt.xlabel('Date')
plt.ylabel('Portfolio Value in USD')
plt.legend()
plt.show()

图片

png

评估性能

# performance Periods analysis
test_data['Strategy_Return'] = test_data['Portfolio_Value'].pct_change()
test_data['Rolling_Return'] = test_data['Strategy_Return'].rolling(window=30).sum()

# periods of good performance
good_performance = test_data[test_data['Rolling_Return'] > 0.02]

# Periods of poor performance
poor_performance = test_data[test_data['Rolling_Return'] < -0.02]

# Compare our strategy with Buy-and-Hold Strategy
test_data['Buy_and_Hold'] = initial_capital * (test_data['Actual_Close'] / test_data['Actual_Close'].iloc[0])

图片

png

图片

png

结论

在本文中,我们探讨了使用应用于比特币价格数据的 LSTM 模型实现均值回归交易策略。通过在预处理数据、防止数据泄露和整合风险管理方面采取谨慎的预防措施,我们制定了一项策略,在两年内将 500 美元的初始投资转换为大约 800 美元。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA Maven构建时报错:无效的目标发行版17

报错分析 报错原因&#xff1a;Maven 构建时&#xff0c;Java 版本配置不匹配 我安装的JDK版本是1.8&#xff0c;但由于种种原因&#xff0c;Maven构建时指定了 Java 17 作为目标发行版&#xff0c;从而导致错误 解决方案 首先&#xff0c;java -version&#xff0c;查看环…

计算机网络 (39)TCP的运输连接管理

前言 TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的传输协议&#xff0c;它在计算机网络中扮演着至关重要的角色。TCP的运输连接管理涉及连接建立、数据传送和连接释放三个阶段。 一、TCP的连接建立 TCP的连接建立采用三次握手机制&#xff0c;其过程如下&…

Navicat Premium 16.0.90 for Mac 安装与free使用

步骤 0.下载 通过网盘分享的文件&#xff1a;Navicat Premium 16.0.90 链接: https://pan.baidu.com/s/12O22rXa9MiBPKKTGMELNIg 提取码: yyds 1.打开下好的 dmg 文件 (这个界面不要关闭&#xff09; 2.将Navicat Premium 拖动至 Applications 这时出现 点击取消。 3.点开…

小创新模型!6种2024算法优化BiTCN-SVM单变量输入单步预测,MATLAB机器学习预测全家桶再更新...

截止到本期MATLAB机器学习预测全家桶&#xff0c;一共发了26篇关于机器学习预测代码的文章。算上这一篇&#xff0c;一共27篇&#xff01;参考文章如下&#xff1a; 1.五花八门的机器学习预测&#xff1f;一篇搞定不行吗&#xff1f; 2.机器学习预测全家桶&#xff0c;多步预测…

3_CSS3 渐变 --[CSS3 进阶之路]

CSS3 引入了渐变&#xff08;gradients&#xff09;&#xff0c;它允许在两个或多个指定的颜色之间显示平滑的过渡。CSS3 支持两种类型的渐变&#xff1a; 线性渐变&#xff08;Linear Gradients&#xff09;&#xff1a;颜色沿着一条线性路径变化&#xff0c;可以是水平、垂直…

25/1/13 嵌入式笔记 继续学习Esp32

PWM&#xff08;Pulse Width Modulation&#xff0c;脉宽调制&#xff09; 是一种通过快速切换高低电平来模拟中间电压值的技术。它广泛应用于控制 LED 亮度、电机速度、音频生成等场景。 analogWrite函数:用于在微控制器&#xff08;如 Arduino&#xff09;上生成模拟信号。 …

【端云一体化】云函数的使用

前言 为丰富HarmonyOS对云端开发的支持、实现端云联动&#xff0c;DevEco Studio以Cloud Foundation Kit&#xff08;云开发服务&#xff09;为底座、在传统的“端开发”基础上新增“云开发”能力&#xff0c;开发者在创建工程时选择合适的云开发工程模板&#xff0c;即可在De…

行业案例:高德服务单元化方案和架构实践

目录 为什么要做单元化 高德单元化的特点 高德单元化实践 服务单元化架构 就近接入实现方案 路由表设计 路由计算 服务端数据驱动的单元化场景 总结 系列阅读 为什么要做单元化 单机房资源瓶颈 随着业务体量和服务用户群体的增长,单机房或同城双机房无法支持服…

基于单片机的语音控制玩具汽车的设计

语音控制小汽车选用了两个单片机、一个语音识别芯片、两个无线收发模块、一个电机驱动模块、两个电机、一个音频解码模块。语音控制端选用了一个语音识别芯片&#xff0c;实现了将声音信号转换成数字信号&#xff0c;再将数据传输给单片机的功能。小车端选用了单片机来控制电机…

解锁“搭子小程序”开发新机遇,助力企业数字化转型

搭子作为一种新型的社交方式&#xff0c;逐渐进入到了年轻人的生活中&#xff0c;在日常旅游、学习、逛街等&#xff0c;年轻人都可以找到志同道合的“搭子”&#xff0c;提高生活的幸福指数。 随着搭子市场的发展&#xff0c;通过互联网寻找搭子已经成为了年轻人的必备方式。…

Spring Boot 2 学习全攻略

Spring Boot 2 学习资料 Spring Boot 2 学习资料 Spring Boot 2 学习资料 在当今快速发展的 Java 后端开发领域&#xff0c;Spring Boot 2 已然成为一股不可忽视的强大力量。它简化了 Spring 应用的初始搭建以及开发过程&#xff0c;让开发者能够更加专注于业务逻辑的实现&am…

31_搭建Redis分片集群

Redis的主从复制模式和哨兵模式可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:海量数据存储问题、高并发写的问题。由于数据量过大,单个master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Red…

IMX6U Qt 开发环境

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、交叉编译 1. 安装通用 ARM 交叉编译工具链 2. 安装 Poky 交叉编译工具链 二、编译出厂源码 1. U-boot 2. 内核和模块 3. 编译出厂 Qt GUI 综合 Demo 前言…

数据结构与算法之链表: LeetCode 92. 反转链表 II (Ts版)

反转链表 II https://leetcode.cn/problems/reverse-linked-list-ii/description/ 描述 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 示例 1 输入&…

【Uniapp-Vue3】@import导入css样式及scss变量用法与static目录

一、import导入css样式 在项目文件中创建一个common文件夹&#xff0c;下面创建一个css文件夹&#xff0c;里面放上style.css文件&#xff0c;编写的是公共样式&#xff0c;我们现在要在App.vue中引入该样式。 在App.vue中引入该样式&#xff0c;这样就会使样式全局生效&#…

MySQL中的四种表联结

目录 1、联结、关系表 &#xff08;1&#xff09;关系表 &#xff08;2&#xff09;为什么使用联结 2、如何创建联结 &#xff08;1&#xff09;笛卡尔积&#xff08;叉联结&#xff09;--用逗号分隔 &#xff08;2&#xff09;where子句的重要性 &#xff08;3&#xff…

DVWA靶场CSRF漏洞通关教程及源码审计

目录标题 CSRFlow源码审计 medium源码审计 high源码审计 impossible源码审计 CSRF low 先修改密码 看到地址栏 复制在另一个网页打开 成功登录 源码审计 没有任何过滤措施&#xff0c;很危险&#xff0c;并且采用了不安全的md5加密 <?phpif( isset( $_GET[ Change ] )…

JVM之垃圾回收器G1概述的详细解析

G1(并发) G1 特点 G1&#xff08;Garbage-First&#xff09;是一款面向服务端应用的垃圾收集器&#xff0c;应用于新生代和老年代、采用标记-整理算法、软实时、低延迟、可设定目标&#xff08;最大 STW 停顿时间&#xff09;的垃圾回收器&#xff0c;用于代替 CMS&#xff0…

宝塔面板 申请证书后 仍然提示不安全

证书显示有效&#xff0c;但是网站显示不安全 导致的原因是引入静态文件使用的是HTTP&#xff0c;查看方法为F12打开console控制台 可以看到静态文件全部都是HTTP 网站采用wordpress搭建&#xff0c;基于问题解决&#xff0c;其他方式搭建也是一样&#xff0c;处理掉所有的H…

【1】Word:邀请函

目录 题目 文字解析 流程 题目 文字解析 考生文件夹☞Word.docx☞一定要用ms打开&#xff0c;wps打开作答无效☞作答完毕&#xff0c;F12或者手动另存为&#xff08;考生文件夹&#xff1a;路径文件名&#xff09; 注意&#xff1a;一定要检查&#xff0c;很有可能你前面步…