最終更新: 2026年06月
LSTMより手軽で、それでいて多くのデータサイエンスコンペで実績を示してきたLightGBM。為替予測への応用は、LSTMほど実装コストが高くなく、特徴量重要度が可視化できるため「なぜこのシグナルが出たか」を解釈しやすい利点がある。本稿ではLightGBMでFXの方向予測モデルを構築し、公式のMetaTrader5 Pythonライブラリを使ってMT5と連携させるところまでを解説する。
LightGBMをFX予測に使う理由
LightGBMはMicrosoftが開発した勾配ブースティングの実装だ。Kaggle等のデータサイエンスコンペで多数の実績を持ち、以下の特徴から為替予測への応用にも使われている。
LSTMとの比較:
| 観点 | LSTM | LightGBM | |---|---|---| | 学習速度 | 遅い(GPU推奨) | 速い(CPU可) | | 実装難易度 | 高 | 中 | | 時系列長期依存 | 得意 | 手動で特徴量設計が必要 | | 特徴量重要度の可視化 | 難しい | 容易 | | 過学習耐性 | 低め | 比較的高め |
LSTMが時系列の「系列そのもの」を学習するのに対し、LightGBMは人間が設計した特徴量(過去N期間のリターン・ボラティリティ・テクニカル指標等)を入力として分類・回帰を行う。特徴量設計の質がそのまま予測性能に直結する。
環境構築
pip install lightgbm pandas numpy ta MetaTrader5 joblib scikit-learn
taは技術指標の計算ライブラリだ。RSI・MACD・ボリンジャーバンドをpandasデータフレームから簡単に計算できる。
Step 1: 特徴量エンジニアリング
LightGBMは特徴量の設計が性能を左右する。時系列を「手動で特徴量に変換」する必要がある。
import pandas as pd
import numpy as np
import ta
def create_features(df: pd.DataFrame, target_horizon: int = 1) -> pd.DataFrame:
"""
OHLCV データから特徴量とラベルを生成する。
未来情報の混入防止に注意。
"""
features = pd.DataFrame(index=df.index)
# 価格変化率(過去N期間)
for period in [1, 3, 5, 10, 20]:
features[f'return_{period}'] = df['close'].pct_change(period)
# 移動平均との乖離率
for window in [10, 20, 50]:
ma = df['close'].rolling(window).mean()
features[f'ma{window}_dev'] = (df['close'] - ma) / ma
# ボラティリティ(過去N期間の標準偏差)
for period in [5, 10, 20]:
features[f'vol_{period}'] = df['close'].pct_change().rolling(period).std()
# RSI
features['rsi_14'] = ta.momentum.rsi(df['close'], window=14)
# MACD
macd = ta.trend.MACD(df['close'])
features['macd'] = macd.macd()
features['macd_signal'] = macd.macd_signal()
features['macd_diff'] = macd.macd_diff()
# ボリンジャーバンド
bb = ta.volatility.BollingerBands(df['close'], window=20)
features['bb_pband'] = bb.bollinger_pband() # バンド内の相対位置(0〜1)
features['bb_wband'] = bb.bollinger_wband() # バンド幅
# ATR(平均真の値動き)
features['atr_14'] = ta.volatility.average_true_range(
df['high'], df['low'], df['close'], window=14
)
# ラベル: N期間後の方向(1=上昇, 0=下落)
# 注意: このラベルは未来情報を使うが、特徴量には絶対に含めない
features['label'] = (df['close'].shift(-target_horizon) > df['close']).astype(int)
return features.dropna()
# 使用例
features_df = create_features(df, target_horizon=1)
Step 2: モデルの訓練
import lightgbm as lgb
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import accuracy_score
import joblib
# 特徴量とラベルを分離
feature_cols = [c for c in features_df.columns if c != 'label']
X = features_df[feature_cols].values
y = features_df['label'].values
# 時系列を保持した分割(ランダムシャッフル禁止)
train_size = int(len(X) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
# LightGBMのパラメータ
params = {
'objective': 'binary', # 2値分類(上昇/下落)
'metric': 'binary_logloss',
'learning_rate': 0.05,
'num_leaves': 31, # 木の葉の数(過学習防止のため小さめ)
'min_child_samples': 50, # 最小サンプル数(過学習防止)
'feature_fraction': 0.8, # 各ツリーで使う特徴量の割合
'bagging_fraction': 0.8, # ブートストラップサンプルの割合
'bagging_freq': 5,
'verbose': -1
}
# データセット作成
dtrain = lgb.Dataset(X_train, label=y_train)
dval = lgb.Dataset(X_test, label=y_test, reference=dtrain)
# 学習(早期停止付き)
callbacks = [lgb.early_stopping(stopping_rounds=50), lgb.log_evaluation(100)]
model = lgb.train(
params,
dtrain,
num_boost_round=1000,
valid_sets=[dval],
callbacks=callbacks
)
# 評価
y_pred_proba = model.predict(X_test)
y_pred = (y_pred_proba > 0.5).astype(int)
accuracy = accuracy_score(y_test, y_pred)
print(f"テスト精度: {accuracy:.4f}")
# 方向予測精度の計算
direction_accuracy = accuracy_score(y_test, y_pred)
print(f"方向予測精度: {direction_accuracy:.2%}")
# モデル保存
model.save_model('lgbm_fx_model.txt')
joblib.dump(feature_cols, 'feature_cols.pkl')
Step 3: 特徴量重要度の可視化
LightGBMの強みの一つが特徴量重要度の可視化だ。
import matplotlib.pyplot as plt
# 特徴量重要度の取得
importance = model.feature_importance(importance_type='gain')
feature_names = feature_cols
# 上位15特徴量を表示
sorted_idx = np.argsort(importance)[-15:]
plt.figure(figsize=(10, 8))
plt.barh(range(len(sorted_idx)), importance[sorted_idx])
plt.yticks(range(len(sorted_idx)), [feature_names[i] for i in sorted_idx])
plt.title('LightGBM Feature Importance (Gain)')
plt.tight_layout()
plt.savefig('feature_importance.png', dpi=150)
どの特徴量がシグナル生成に貢献しているかを確認することで、モデルの解釈性が高まる。「RSI14が最も重要」という結果が出れば、そのシグナルの理由が説明できる。
Step 4: MT5との連携
MetaTrader5の公式Pythonライブラリを使って、LightGBMのシグナルをMT5の自動売買に連携させる。
import MetaTrader5 as mt5
import time
import joblib
import lightgbm as lgb
import pandas as pd
import ta
def connect_mt5():
"""MT5への接続"""
if not mt5.initialize():
raise RuntimeError(f"MT5初期化失敗: {mt5.last_error()}")
print(f"MT5接続成功: {mt5.terminal_info().name}")
def get_rates(symbol: str, timeframe, count: int) -> pd.DataFrame:
"""MT5から価格データを取得"""
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, count)
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df = df.rename(columns={'open': 'open', 'high': 'high',
'low': 'low', 'close': 'close',
'tick_volume': 'volume'})
df = df.set_index('time')
return df
def predict_signal(df: pd.DataFrame, model, feature_cols: list) -> int:
"""最新バーのシグナルを予測(1=買い, 0=売り)"""
features = create_features(df)
# 最新行のみ使用(ラベル列を除く)
latest = features[feature_cols].iloc[-1:].values
prob = model.predict(latest)[0]
return 1 if prob > 0.5 else 0
def execute_trade(symbol: str, signal: int, lot: float = 0.01):
"""シグナルに基づいてMT5で注文"""
positions = mt5.positions_get(symbol=symbol)
if signal == 1 and len(positions) == 0:
# 買いエントリー
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": lot,
"type": mt5.ORDER_TYPE_BUY,
"price": mt5.symbol_info_tick(symbol).ask,
"deviation": 10,
"magic": 20260609,
"comment": "LightGBM signal",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
result = mt5.order_send(request)
if result.retcode != mt5.TRADE_RETCODE_DONE:
print(f"注文失敗: {result.retcode}")
elif signal == 0 and len(positions) > 0:
# 決済
for pos in positions:
close_request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": pos.volume,
"type": mt5.ORDER_TYPE_SELL if pos.type == 0 else mt5.ORDER_TYPE_BUY,
"position": pos.ticket,
"price": mt5.symbol_info_tick(symbol).bid,
"deviation": 10,
"magic": 20260609,
"comment": "LightGBM close",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
mt5.order_send(close_request)
# メインループ(H1足で毎時実行)
def main():
connect_mt5()
model = lgb.Booster(model_file='lgbm_fx_model.txt')
feature_cols = joblib.load('feature_cols.pkl')
symbol = "USDJPY"
while True:
df = get_rates(symbol, mt5.TIMEFRAME_H1, 200)
signal = predict_signal(df, model, feature_cols)
execute_trade(symbol, signal, lot=0.01)
# 次の足まで待機(H1の場合は約3600秒)
print(f"シグナル: {'BUY' if signal == 1 else 'SELL/HOLD'}")
time.sleep(300) # 5分ごとにチェック
if __name__ == "__main__":
main()
Claudeと会話しながらインジケータが作れるhedgrow-fxはこちら。LightGBMで予測したシグナルロジックをClaude対話でさらに改良する使い方も有効だ。
実運用で注意すべき点
モデルの劣化問題
市場環境は変化する。3〜6ヶ月前に訓練したLightGBMモデルは、市場構造が変化するにつれて精度が低下していく。月次でモデルを再訓練・再評価するサイクルを設けることが必要だ。
スプレッドとスリッページの考慮
モデルの方向予測精度が55%あっても、スプレッドコストが大きいと利益が出ない。H1足以上の時間足でトレード頻度を抑えることが実用的だ。
デモ口座での十分なフォワードテスト
PythonとMT5の連携コードを本番口座で動かす前に、最低1〜3ヶ月のデモ口座運用で動作確認と実際のパフォーマンス確認を行うこと。
まとめ
LightGBMは実装コストとパフォーマンスのバランスが良く、FX予測への第一歩として適したライブラリだ。公式のMetaTrader5 Pythonライブラリを使えば、MT5との連携も比較的シンプルに実現できる。
ただし「モデルが高精度 = 利益が出る」ではない。スプレッド・スリッページ・モデル劣化・市場の非定常性という実運用の壁は、バックテスト設計の段階から意識する必要がある。
FXには損失リスクがある。機械学習モデルはバックテスト上で良い結果を示しても、本番運用で同等のパフォーマンスを保証するものではない。デモ口座での検証と小額からのスタートを強く推奨する。
よくある質問(FAQ)
Q: LightGBMとXGBoostはどちらがFX予測に向いていますか? A: 一般的にLightGBMのほうが学習速度が速く、大規模データでのメモリ効率が高い。予測精度での差は使用するデータ・特徴量に依存する。最初はLightGBMで試して、XGBoostと比較してみるのが実践的だ。
Q: MT5の公式Pythonライブラリに対応しているブローカーは? A: MT5を提供するほとんどのブローカーでMetaTrader5 Pythonライブラリが動作する。ただし、一部ブローカーは外部APIへのアクセス制限をかけている場合があるため、事前に確認が必要だ。
Q: モデルの再訓練頻度はどれくらいが適切ですか? A: 市場環境の変化速度によるが、月次での再訓練が実用的な出発点だ。精度の定期モニタリングを行い、精度が閾値を下回ったらトリガーする仕組みが理想的だ。
Q: LightGBMのnum_leavesを大きくするとどうなりますか?
A: 表現力が上がり訓練精度は向上するが、過学習のリスクが高まる。FXデータの場合はnum_leaves=15〜63程度に抑えることを推奨する。
Q: 連携ループが止まってしまった場合の復旧方法は?
A: MT5側のポジション状況を確認してから再起動すること。重複エントリーを防ぐため、mt5.positions_get()でポジション確認ロジックを必ず実装すること。Claudeと会話しながらインジケータが作れるhedgrow-fxはこちら。
