USD/JPY予測モデル構築記:Pythonと機械学習で為替に挑戦!

為替レートの予測は、その変動要因の複雑さから非常に困難なタスクとして知られています。しかし、機械学習の力を使えば、過去のデータから将来の値動きに関するヒントを得ることができるかもしれません。
※ただし、投資は自己責任でお願いします! この記事はあくまで学習を目的としたものであり、投資を推奨するものではありません。

今回は、Alpha Vantage APIから取得したUSD/JPYの日足データを用い、機械学習のアルゴリズムの一つである線形重回帰モデルを構築することで、翌日の終値予測に挑戦します。

データの準備から予測モデル構築までの道のり

今回の予測モデル構築は、大きく分けて以下のプロセスで進めていきます。

  1. データの取得と前処理: Alpha Vantage APIからUSD/JPYの日足データを取得し、分析に適した形にデータを整形します。

  2. 特徴量エンジニアリング: 過去のデータから、終値の値動きに影響を与えそうな特徴量を新たに作成します。

  3. モデルの構築と評価: 線形重回帰モデルを構築し、その予測精度を評価します。

  4. 複数種類のモデルで予測: 異なる期間のデータを用いて複数のモデルを作成し、それぞれの予測結果を比較します。

それでは、各プロセスを詳しく見ていきましょう。

1. データの取得と前処理:APIから為替データを入手!

まず、Alpha Vantage APIを使って、USD/JPYの日足データを取得します。このAPIを利用すると、過去のデータから最新のデータまで、様々な金融データを簡単に入手することができます。

取得したデータは、日付、始値、高値、安値、終値、出来高といった情報を含んでいます。これらのデータは、そのままでは機械学習モデルに投入することができないため、以下の様な前処理を行います。

  • データの整形:データの形式を統一し、扱いやすくします。

  • データの並び替え:日付順にデータを並べ替えます。

  • 欠損値処理:欠損値がある場合は、適切な方法で補完または削除します。

これらの前処理によって、機械学習モデルが学習しやすいデータセットを作成します。

2. 特徴量エンジニアリング:為替の値動きを紐解く鍵を探せ!

生のデータから直接予測を行うのではなく、より予測精度を高めるために、特徴量エンジニアリングを行います。これは、元のデータから新たな特徴量を作成することで、データの持つ情報をより分かりやすく表現するプロセスです。

今回は、以下の様な特徴量を作成しました。

  • 価格の変化量:前日との終値の差分を計算します。

  • 高値と安値の差:その日の値動きの幅を把握します。

  • 終値と始値の差:その日のトレンドを把握します。

  • 出来高の変化量:市場参加者の動向を把握します。

  • 移動平均線:過去の終値を平均化することで、トレンドを把握しやすくします。

  • RSI:買われ過ぎ、売られ過ぎを判断する指標です。

  • MACD:短期と長期の移動平均線の関係から、トレンドの変化を捉えます。

  • ボリンジャーバンド:統計的な手法を用いて、値動きの範囲を予測します。

これらの特徴量を追加することで、モデルは為替レートの変動パターンをより深く学習し、精度の高い予測を行うことが期待できます。

3. モデルの構築と評価:いよいよ予測モデルを構築!

今回は、線形重回帰モデルを用いて予測モデルを構築します。このモデルは、データ間の線形関係を学習し、未知のデータに対して予測を行うことができるため、時系列データの予測にもよく用いられます。

モデルの構築には、以下の手順を踏みます。

  • データの分割:取得したデータを学習用と評価用に分割します。

  • モデルの学習:学習用データを使って、モデルに為替レートの変動パターンを学習させます。

  • モデルの評価:評価用データを使って、学習したモデルの予測精度を評価します。

評価指標としては、RMSE(平均平方二乗誤差)とR^2(決定係数)を使用します。RMSEは予測値と実際の値の誤差を表し、小さいほど予測精度が高いことを示します。R^2はモデルがデータにどれだけ適合しているかを表し、1に近いほどモデルの適合度が高いことを示します。

4. 複数種類のモデルで予測:予測期間を変えて精度を比較!

今回は、学習に使用するデータの期間を「1日」「10日」「100日」「1000日」の4パターン用意し、それぞれの期間でモデルを作成します。

各モデルの予測結果を比較することで、どの期間のデータが翌日の終値予測に最も有効であるかを検証します。

Pythonで予測モデルを実装!

今回の予測モデルは、Pythonのライブラリを用いて実装しました。

  • Alpha Vantage API:データの取得

  • pandas:データの加工・分析

  • matplotlib:グラフ描画

  • scikit-learn:機械学習モデルの構築と評価

  • numpy:数値計算

これらのライブラリを活用することで、効率的にデータ処理、分析、モデル構築を行うことができました。

まとめ:為替予測は奥深い!

今回は、USD/JPYの終値予測モデルを構築し、機械学習を用いた為替予測の可能性を探ってきました。特徴量エンジニアリングや複数期間のモデル比較を通して、より精度の高い予測モデル構築に繋げることができました。

もちろん、為替市場は非常に複雑であり、今回構築したモデルが常に正確な予測を提供できるとは限りません。しかし、今回の試みを通して、機械学習が為替予測という難しい課題に対し、一つの有効なアプローチになり得ることを示すことができたのではないでしょうか。

今後は、より高度な分析手法や他の機械学習アルゴリズムの導入、更には経済指標やニュースなどの外部データの活用など、更なる予測精度の向上を目指していきたいと思います。

from alpha_vantage.timeseries import TimeSeries
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge  # Ridge回帰を使用
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.preprocessing import StandardScaler #標準化
import numpy as np

"""
USD/JPYの予測モデルを作成するスクリプト

Alpha Vantage APIからUSD/JPYの日足データを取得し、
線形重回帰を用いて翌日の終値を予測するモデルを作成します。

以下の期間のデータを用いたモデルを作成します。
- 1日
- 10日
- 100日
- 1000日

各モデルの精度は、RMSEとR^2で評価します。
"""

# --- 設定 ---
api_key = "YOUR_API_KEY"  # ご自身のAPIキーに置き換えてください
window_size_10d = 10
window_size_100d = 100
window_size_1000d = 1000  # 1000日分の設定を追加

# --- データ取得 ---
print("---------- データ取得開始 ----------")
ts = TimeSeries(key=api_key, output_format='pandas')
data, meta_data = ts.get_daily(symbol='USDJPY', outputsize='full')
print(data)
print("---------- データ取得完了 ----------")

# --- データ前処理 ---
print("---------- データ前処理開始 ----------")
data = data.rename(columns={
    '1. open': 'open',
    '2. high': 'high',
    '3. low': 'low',
    '4. close': 'close',
    '5. volume': 'volume'
})
data.index = pd.to_datetime(data.index)
data = data.sort_index(ascending=True)

# 特徴量エンジニアリング
data['price_change'] = data['close'].diff()
data['high_low_diff'] = data['high'] - data['low']  # 高値と安値の差
data['close_open_diff'] = data['close'] - data['open'] # 終値と始値の差
data['volume_change'] = data['volume'].diff()  # 出来高の変化量

# --- 移動平均線の計算 ---
data['ma_short'] = data['close'].rolling(window=5).mean()  # 短期移動平均線 (5日)
data['ma_long'] = data['close'].rolling(window=25).mean()  # 長期移動平均線 (25日)

# --- RSIの計算 ---
delta = data['close'].diff()
gain = delta.where(delta > 0, 0)
loss = -delta.where(delta < 0, 0)
avg_gain = gain.rolling(window=14).mean()
avg_loss = loss.rolling(window=14).mean()
rs = avg_gain / avg_loss
data['rsi'] = 100 - (100 / (1 + rs))

# --- MACDの計算 ---
data['ema_short'] = data['close'].ewm(span=12).mean()  # 12日指数移動平均
data['ema_long'] = data['close'].ewm(span=26).mean()  # 26日指数移動平均
data['macd'] = data['ema_short'] - data['ema_long']
data['macd_signal'] = data['macd'].ewm(span=9).mean()  # シグナル線

# --- ボリンジャーバンドの計算 ---
data['std'] = data['close'].rolling(window=20).std()  # 20日標準偏差
data['upper_band'] = data['ma_short'] + 2 * data['std']  # 上限バンド
data['lower_band'] = data['ma_short'] - 2 * data['std']  # 下限バンド

# 欠損値処理
data = data.dropna()

print("---------- データ前処理完了 ----------")
# データの開始日と終了日を表示
print(f"データの開始日: {data.index[0]}")
print(f"データの終了日: {data.index[-1]}")

# --- モデル構築と評価 ---
def create_and_evaluate_model(data, window_size, model_name):
    print(f"---------- {model_name}モデル処理開始 ----------")
    X = data[['open', 'high', 'low', 'close', 'volume', 'price_change', 
              'high_low_diff', 'close_open_diff', 'volume_change',
              'ma_short', 'ma_long', 'rsi', 'macd', 'macd_signal', 
              'upper_band', 'lower_band']].values[:-1]
    y = data['close'].values[1:]  # 翌日の終値

    X = X[window_size:]
    y = y[window_size:]

    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    
    # 標準化
    scaler = StandardScaler()
    X_train = scaler.fit_transform(X_train)
    X_test = scaler.transform(X_test)

    # Ridge回帰モデルとグリッドサーチ
    model = Ridge()
    param_grid = {'alpha': [0.01, 0.1, 1, 10, 100]}  # 正則化パラメータ
    grid_search = GridSearchCV(model, param_grid, cv=5, scoring='neg_mean_squared_error')
    grid_search.fit(X_train, y_train)

    best_model = grid_search.best_estimator_
    y_pred = best_model.predict(X_test)

    rmse = np.sqrt(mean_squared_error(y_test, y_pred))
    r2 = r2_score(y_test, y_pred)

    print(f"Best parameters ({model_name}): ", grid_search.best_params_)
    print(f"RMSE ({model_name}): {rmse:.4f}")
    print(f"R^2 ({model_name}): {r2:.4f}")
    print(f"---------- {model_name}モデル処理完了 ----------")
    
    return best_model, scaler  # 最適なモデルとscalerを返す
# 各モデルで評価
model_1d, scaler_1d = create_and_evaluate_model(data, 1, "1日版")
model_10d, scaler_10d = create_and_evaluate_model(data, window_size_10d, "10日版")
model_100d, scaler_100d = create_and_evaluate_model(data, window_size_100d, "100日版")
model_1000d, scaler_1000d = create_and_evaluate_model(data, window_size_1000d, "1000日版")
---------- データ取得完了 ----------
---------- データ前処理開始 ----------
---------- データ前処理完了 ----------
データの開始日: 2014-08-07 00:00:00
データの終了日: 2024-07-05 00:00:00
---------- 1日版モデル処理開始 ----------
Best parameters (1日版):  {'alpha': 0.01}
RMSE (1日版): 0.3667
R^2 (1日版): 0.9993
---------- 1日版モデル処理完了 ----------
---------- 10日版モデル処理開始 ----------
Best parameters (10日版):  {'alpha': 0.01}
RMSE (10日版): 0.3488
R^2 (10日版): 0.9994
---------- 10日版モデル処理完了 ----------
---------- 100日版モデル処理開始 ----------
Best parameters (100日版):  {'alpha': 0.01}
RMSE (100日版): 0.3514
R^2 (100日版): 0.9994
---------- 100日版モデル処理完了 ----------
---------- 1000日版モデル処理開始 ----------
Best parameters (1000日版):  {'alpha': 0.01}
RMSE (1000日版): 0.3649
R^2 (1000日版): 0.9995
---------- 1000日版モデル処理完了 ----------
# 最新のデータを取得
latest_data = data.iloc[-1]  # 最新の1日分のデータ

# 各モデルの予測に使う特徴量を準備
features = latest_data[['open', 'high', 'low', 'close', 'volume', 'price_change', 
                      'high_low_diff', 'close_open_diff', 'volume_change',
                      'ma_short', 'ma_long', 'rsi', 'macd', 'macd_signal', 
                      'upper_band', 'lower_band']].values.reshape(1, -1)

# 各モデルの予測値を取得
prediction_1d = model_1d.predict(scaler_1d.transform(features))[0]

# 例:10日版モデルの場合
latest_data = data.iloc[-window_size_10d:]  # 最新の10日分のデータを取得

features = []
for i in range(len(latest_data) - 1):
    features.append(latest_data[['open', 'high', 'low', 'close', 'volume', 'price_change', 
                                'high_low_diff', 'close_open_diff', 'volume_change',
                                'ma_short', 'ma_long', 'rsi', 'macd', 'macd_signal', 
                                'upper_band', 'lower_band']].iloc[i].values)
features = np.array(features)

# 予測値を取得 (最後の1日分を予測に使用)
prediction_10d = model_10d.predict(scaler_10d.transform(features[-1].reshape(1, -1)))[0]

# 100日版モデル
latest_data_100d = data.iloc[-window_size_100d:]  # 最新の100日分のデータを取得

features_100d = []
for i in range(len(latest_data_100d) - 1):
    features_100d.append(latest_data_100d[['open', 'high', 'low', 'close', 'volume', 'price_change',
                                        'high_low_diff', 'close_open_diff', 'volume_change',
                                        'ma_short', 'ma_long', 'rsi', 'macd', 'macd_signal',
                                        'upper_band', 'lower_band']].iloc[i].values)
features_100d = np.array(features_100d)

prediction_100d = model_100d.predict(scaler_100d.transform(features_100d[-1].reshape(1, -1)))[0]

# 1000日版モデル
latest_data_1000d = data.iloc[-window_size_1000d:]  # 最新の1000日分のデータを取得

features_1000d = []
for i in range(len(latest_data_1000d) - 1):
    features_1000d.append(latest_data_1000d[['open', 'high', 'low', 'close', 'volume', 'price_change',
                                          'high_low_diff', 'close_open_diff', 'volume_change',
                                          'ma_short', 'ma_long', 'rsi', 'macd', 'macd_signal',
                                          'upper_band', 'lower_band']].iloc[i].values)
features_1000d = np.array(features_1000d)

prediction_1000d = model_1000d.predict(scaler_1000d.transform(features_1000d[-1].reshape(1, -1)))[0]

# 今日の為替を取得
today_close = data['close'].iloc[-1]

# 予測値を表示
print("今日の為替:")
print(f"終値: {today_close:.4f}")
print("------------------")
# 予測値を表示
print("明日の為替予測:")
print("------------------")
print(f"1日版モデル: {prediction_1d:.4f}")
print(f"10日版モデル: {prediction_10d:.4f}")
print(f"100日版モデル: {prediction_100d:.4f}")
print(f"1000日版モデル: {prediction_1000d:.4f}")

# 平均値を計算 (例)
average_prediction = (prediction_1d + prediction_10d + prediction_100d + prediction_1000d) / 4
print("------------------")
print(f"平均予測値: {average_prediction:.4f}")
今日の為替:
終値: 160.7480
------------------
明日の為替予測:
------------------
1日版モデル: 160.6395
10日版モデル: 161.0867
100日版モデル: 161.1334
1000日版モデル: 161.2622
------------------
平均予測値: 161.0305

この記事が気に入ったらサポートをしてみませんか?