イントロダクション
はじめに
トレードは自己責任でお願いします。
本記事は、学習を目的としているため、売買戦略はマスキングしています。
本記事では、LSTM(Attention機構)モデルを用いて、
為替レートの予測とバックテストを行います。
なぜ、為替予測なのか?
- データが手に入りやすい:為替データはインターネット上で容易に取得できます。
- 数値データなので、前処理が楽:テキストや画像データと比べて、数値データは前処理がシンプルです。
- 利益に直結する:予測精度が向上すれば、直接的な利益につながる。(モチベ)
LSTM(Attention機構)の採用理由
- LSTMの強み:時系列データの長期的な依存関係を学習するのに適しています。
- Attention機構の必要性:最新のデータに重要性を持たせるため、Attention機構を導入してます。
今回の目標
-
モデルを開発してバックテスト(プレビューまで)を行う
開発フロー:
- 前処理(正規化、特徴量エンジニアリング、学習とテストデータ切分)
- モデル構築(LSTM(Attention機構))
- 性能評価(CrossEntropyLoss)
- バックテスト(
backtesting
ライブラリを使用)
開発環境
- Google Colaboratory
使用ライブラリ
-
torch
,scikit-learn
,pandas
,numpy
,matplotlib
,backtesting
# Google Colaboratory用(GPUセッション推奨)
!pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu117
!pip install backtesting
!pip install scikit-learn pandas numpy matplotlib
早速始めていきましょう
1. 為替データの取得と前処理
データ収集
今回は、OANDA APIからUSD/JPYの為替データを取得しています。ただし、OANDAのAPIを利用するには一定の条件(50万通貨以上の取引実績)があるため、学習用データとしてはYahoo Financeなどを使用することも可能です。
データの例:
Datetime Volume Open High Low Close
2024-10-01 02:00:00 3988 143.712 143.888 143.658 143.878
2024-10-01 02:30:00 4512 143.881 144.030 143.867 144.013
2024-10-01 03:00:00 4399 144.014 144.192 143.955 144.180
2024-10-01 03:30:00 4349 144.182 144.334 144.142 144.280
特徴量エンジニアリング
テクニカル指標を計算し、特徴量としてデータに追加します。具体的には以下の指標を使用します。
欠損値の削除も忘れずに、実行しましょう。
- 移動平均線20(EMA)
- RSI14(相対力指数)
コード例:
class DataProcessor:
def add_features(self, df):
# EMAの計算
df[ema] = df['Close'].ewm(span=20, adjust=False).mean()
# RSIの計算
delta = df['Close'].diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)
avg_gain = gain.rolling(window=14).mean()
avg_loss = loss.rolling(window=14).mean()
rs = avg_gain / avg_loss
df['RSI'] = 100 - (100 / (1 + rs))
# 欠損値の削除
df = df.dropna()
return df
データの正規化
LSTMの学習を安定させるために、特徴量を正規化します。(MinMaxScaler)
特徴量を正規化する理由は以下
- 勾配消失・爆発の防止
LSTMは複数のゲートを持ち、時系列データの長期的な依存関係を学習します。しかし、学習中に勾配が非常に大きくなったり非常に小さくなったりする「勾配爆発」や「勾配消失」が発生することがあります。
それを防ぐために、正規化して、入力データが0〜1の間の小さな範囲に収ます、すると勾配の安定化に繋がります。
- 各特徴量の影響を均等にする
正規化していないデータでは、値の大きい特徴量がモデルに与える影響が大きくなりすぎる可能性があります。
例えば、Volumeは、1000から5000などの変化が頻繁に起こります。しかし、Closeは、143.878から143.930ぐらいの変化になる。このまま学習させてしまうと、volumeの重要度を高く評価してしまう。
それを防ぐために、正規化して、特徴量のスケールを揃て、モデルが各特徴量を均等に評価します。
コード例:
from sklearn.preprocessing import MinMaxScaler
class DataProcessor:
def preprocess(self, symbol):
# データの読み込み
df = pd.read_csv(f"{self.config.DATA_PATH}{symbol}", index_col='Datetime')
df.index = pd.to_datetime(df.index)
# 特徴量の追加
df = self.add_features(df)
# 特徴量とラベルの作成
X = df[self.feature_cols].values
y = self.create_labels(df)
# 特徴量の正規化
self.scaler_X = MinMaxScaler(feature_range=(0, 1))
X_scaled = self.scaler_X.fit_transform(X)
return X_scaled, y
訓練データとテストデータの分割
データを訓練用と検証用に分割します。
コード例:
def train_model(config, X, y):
# データ分割
total_samples = X.shape[0]
train_end = int(total_samples * config.TRAIN_RATIO)
val_end = train_end + int(total_samples * config.VAL_RATIO)
X_train, y_train = X[:train_end], y[:train_end]
X_val, y_val = X[train_end:val_end], y[train_end:val_end]
2. モデル構築のプロセス
ライブラリとフレームワークの準備
PyTorchを使用して、LSTMモデルとAttention機構を実装します。
コード例:
import torch
import torch.nn as nn
import torch.optim as optim
モデルの構成
LSTM層とAttention層を組み合わせたモデルを構築します。
モデルの定義:
class LSTMAttentionClassifier(nn.Module):
def __init__(self, input_dim, hidden_dim, target_dim, num_layers=2, dropout=0.2):
super(LSTMAttentionClassifier, self).__init__()
self.lstm = nn.LSTM(input_size=input_dim,
hidden_size=hidden_dim,
num_layers=num_layers,
dropout=dropout,
batch_first=True)
self.attention_layer = nn.Linear(hidden_dim, 1)
self.fc = nn.Linear(hidden_dim, target_dim)
def forward(self, x):
lstm_out, _ = self.lstm(x)
# Attentionの計算
attn_weights = torch.softmax(self.attention_layer(lstm_out), dim=1)
context_vector = torch.sum(attn_weights * lstm_out, dim=1)
out = self.fc(context_vector)
return out # SoftmaxはCrossEntropyLossが内部で適用
ハイパーパラメータの調整
ハイパーパラメータはConfig
クラスで管理し、最適な結果を得るために調整します。
コード例:
class Config:
# モデル関連
LSTM_HIDDEN_DIM = 32
TARGET_DIM = 3
BATCH_SIZE = 32
TIME_STEPS = 24
N_EPOCHS = 100
LEARNING_RATE = 1e-3
NUM_LAYERS = 3
DROPOUT = 0.2
3. モデルのトレーニングと評価
損失関数と評価指標
損失関数にはCrossEntropyLoss
を採用。
optimizerは、Adamを採用。
コード例:
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=config.LEARNING_RATE)
トレーニングの実行
モデルのトレーニングを行い、エポックごとに損失と精度を表示します。
コード例:
for epoch in range(config.N_EPOCHS):
model.train()
perm_idx = np.random.permutation(np.arange(config.TIME_STEPS, X_train.shape[0]))
for start in range(0, len(perm_idx), config.BATCH_SIZE):
batch_idx = perm_idx[start:start + config.BATCH_SIZE]
if len(batch_idx) == 0:
continue
feats = prep_feature_data(batch_idx, config.TIME_STEPS, X_train, device)
y_batch = torch.tensor(y_train[batch_idx], dtype=torch.long, device=device)
optimizer.zero_grad()
outputs = model(feats)
loss = criterion(outputs, y_batch)
loss.backward()
optimizer.step()
# バリデーション
model.eval()
with torch.no_grad():
val_indices = np.arange(config.TIME_STEPS, X_val.shape[0])
if len(val_indices) == 0:
continue
feats_val = prep_feature_data(val_indices, config.TIME_STEPS, X_val, device)
val_outputs = model(feats_val)
val_preds = val_outputs.argmax(dim=1).cpu().numpy()
val_actual = y_val[val_indices]
val_accuracy = accuracy_score(val_actual, val_preds)
val_loss = criterion(val_outputs, torch.tensor(val_actual, dtype=torch.long, device=device)).item()
print(f'Epoch {epoch+1}/{config.N_EPOCHS} - Loss: {loss.item():.8f} - Val Loss: {val_loss:.8f} - Val Accuracy: {val_accuracy:.4f}')
return model
# モデルの訓練
model = train_model(config, X, y)
Val Lossが減少し、Val Accuracyが上昇していることがわかる。
4. バックテストの実施
戦略クラスの定義
backtesting
ライブラリを用いて、戦略クラスを定義します。
無用なトラブルを避ける為、売買ロジックの公開は避けて、サンプルコードを提供します。
コード例:
from backtesting import Strategy
class MyLSTMStrategy(Strategy):
def init(self):
pass
def next(self):
self.buy()
self.sell()
pass
バックテストの実行
定義した戦略クラスを用いてバックテストを行い、結果を分析します。
コード例:
from backtesting import Backtest
bt = Backtest(
data,
MyLSTMStrategy,
cash=100000,
commission=0.0004,
exclusive_orders=True
)
stats = bt.run()
print(stats)
bt.plot()
5. 予測結果の可視化
予測と実際の為替レートの比較
バックテストの結果を可視化し、モデルの性能を評価します。
コード例:
bt.plot()
トレンド相場はかなり、得意な様子ですね。(FinalRetern:116%)
レンジでも、一定の利益は出してくれるみたいですね。(FinalRetern:105%)
6. 課題と改善
価格データのみの限界
- 経済指標は為替に大きな影響を与えることは有名だが、このモデルはそのことを知らない。
- 経済指標のデータを取得することは容易ため、特徴量に追加することは、すぐに実装できると思う。
- 市場心理という観点をモデルは考慮していない。
- X(SNS)のデータをスクレイピングして、LLMに感情分析させて、特徴量に追加するとかでそういう観点を得られるのでは?と仮説を持ってる。
7. まとめ
成果と学び
- LSTMとAttention機構を組み合わせたモデルで為替予測を行い、バックテストでその性能を評価しました。
- LSTMは、為替予測において、有効であることが身を持って実感できた。
以上で、LSTM(attention機構)を用いた為替予測モデルの構築とバックテストの解説を終わります。最後までお読みいただき、ありがとうございました。