当前位置: 首页 > article >正文

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

 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.kler.cn/a/501804.html

相关文章:

  • Redis优化建议详解
  • IMX6ULL的IOMUXC寄存器和SNVS复用寄存器似乎都是对引脚指定复用功能的,那二者有何区别?
  • 浅谈云计算05 | 云存储等级及其接口工作原理
  • Artec Leo 3D扫描仪与Ray助力野生水生动物法医鉴定【沪敖3D】
  • 网络应用技术 实验七:实现无线局域网
  • [免费]SpringBoot+Vue新能源汽车充电桩管理系统【论文+源码+SQL脚本】
  • 2024年开发语言热度排名
  • ECharts实战:在UniApp中实现动态数据可视化
  • 奇迹mu1.03单机版安装教程+无需虚拟机+GM工具
  • HTML和CSS相关详解,如何使网页为响应式?
  • vue如何把有效URL地址通过接口file文件重新上传
  • 作业:IO:day??
  • docker的数据卷和自定义镜像
  • Python 二次元初音未来桌宠
  • 什么是数据仓库?
  • Perl语言的循环实现
  • 深入了解 Redis Stream 数据类型及其在事件流系统中的应用
  • 【Android】直接使用binder的transact来代替aidl接口
  • nacos从1.x升级到2.4.3问题记录
  • 【C++指南】模板 深度解析
  • 如何使用队列规则(Qdisc)发送数据包
  • Git | git reset命令详解
  • python安装完成后可以进行的后续步骤和注意事项
  • leetcode 2270. 分割数组的方案数 中等
  • 【WPS】【WORDEXCEL】【VB】实现微软WORD自动更正的效果
  • windows wsl ubuntu22 远程桌面连接