1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LSTM(attention機構)で、為替予測してみた。

Last updated at Posted at 2024-10-29

イントロダクション

はじめに

トレードは自己責任でお願いします。
本記事は、学習を目的としているため、売買戦略はマスキングしています。
  
本記事では、LSTM(Attention機構)モデルを用いて、
為替レートの予測とバックテストを行います。

なぜ、為替予測なのか?

  • データが手に入りやすい:為替データはインターネット上で容易に取得できます。
  • 数値データなので、前処理が楽:テキストや画像データと比べて、数値データは前処理がシンプルです。
  • 利益に直結する:予測精度が向上すれば、直接的な利益につながる。(モチベ)

LSTM(Attention機構)の採用理由

  • LSTMの強み:時系列データの長期的な依存関係を学習するのに適しています。
  • Attention機構の必要性:最新のデータに重要性を持たせるため、Attention機構を導入してます。

今回の目標

  • モデルを開発してバックテスト(プレビューまで)を行う

    開発フロー:

    1. 前処理(正規化、特徴量エンジニアリング、学習とテストデータ切分)
    2. モデル構築(LSTM(Attention機構))
    3. 性能評価(CrossEntropyLoss)
    4. バックテスト(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)
特徴量を正規化する理由は以下

  1. 勾配消失・爆発の防止
    LSTMは複数のゲートを持ち、時系列データの長期的な依存関係を学習します。しかし、学習中に勾配が非常に大きくなったり非常に小さくなったりする「勾配爆発」や「勾配消失」が発生することがあります。
    それを防ぐために、正規化して、入力データが0〜1の間の小さな範囲に収ます、すると勾配の安定化に繋がります。
      
  2. 各特徴量の影響を均等にする
    正規化していないデータでは、値の大きい特徴量がモデルに与える影響が大きくなりすぎる可能性があります。
    例えば、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が上昇していることがわかる。
  
スクリーンショット 2024-10-30 16.08.55.png


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%)
スクリーンショット 2024-10-29 16.56.58.png

レンジでも、一定の利益は出してくれるみたいですね。(FinalRetern:105%)
スクリーンショット 2024-10-29 17.00.44.png


6. 課題と改善

価格データのみの限界

  • 経済指標は為替に大きな影響を与えることは有名だが、このモデルはそのことを知らない。
    • 経済指標のデータを取得することは容易ため、特徴量に追加することは、すぐに実装できると思う。
  • 市場心理という観点をモデルは考慮していない。
    • X(SNS)のデータをスクレイピングして、LLMに感情分析させて、特徴量に追加するとかでそういう観点を得られるのでは?と仮説を持ってる。

7. まとめ

成果と学び

  • LSTMとAttention機構を組み合わせたモデルで為替予測を行い、バックテストでその性能を評価しました。
  • LSTMは、為替予測において、有効であることが身を持って実感できた。

以上で、LSTM(attention機構)を用いた為替予測モデルの構築とバックテストの解説を終わります。最後までお読みいただき、ありがとうございました。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?