見出し画像

機械学習モデルの訓練を繰り返しながら予測を行うML-EA(FX自動売買)のテンプレート

FX自動売買に関連する他の記事では、機械学習モデルを使用して売買判定フラグを作成し、そのフラグに基づく売買注文の一連の処理について説明してきました。これまでのアプローチでは、モデルの学習/訓練(トレーニング)工程は別のプロセスで行い、訓練済みのモデルを保存して後から呼び出す方法を採用していました。しかし、今回はMetaTrader内でモデルの訓練と予測を自動的に繰り返すEA(エキスパートアドバイザー)を作成してみました。この仕組みでは、放置しておいても定期的に最新情報を用いてモデルの訓練を繰り返すため、最近の環境変化に対応することが可能です。この方法について詳しくご紹介します。


MQLや Pythonが行う処理の概要

今回のEAは、機械学習(Machine Learning)を備えたEAということでML-EAと名付けます。以下はこのML-EAの処理の流れの簡単なイメージ図です。

ML-EAの処理の流れ

MQL4/MQL5で行う処理と、Windows上に保存されるファイル、Pythonで行う処理の3つに分かれています。今回の仕組みでは、PythonのMT5ライブラリを利用してPythonからMT5にアクセスするといったことは行いません。
MQLとPythonをそれぞれ独立に動かし、Windows上のテキストファイルやバッチファイルを介してお互いの処理を連動させるようにします。
※Windows PCにてMT4/MT5をインストールして実行する前提です。

この方法は、MT4でも同様の処理が可能です。MT4(MQL4)で機械学習を実装する方法の一例としても参考にしていただければと思います。

FX自動売買ロジックの概要

今回のML-EAの目的は、MQLとPythonを連携させてML-EA(機械学習を取り入れたEA)のベースモデルを作成することです。したがって、使用するテクニカル指標や売買ロジックは、比較的シンプルで、かつ、今後の応用ができそうなものを採用します。

  • 機械学習モデルはLight-GBMモデルを採用

  • 訓練や予測に使う特徴量は、4本の移動平均線(1分足、5分足、15分足、1時間足)のみ

  • 目的変数(y)は、4時間後の価格が0.1%以上上昇していればy=1、4時間後の価格が0.1%以上下落していればy=2、それ以外の場合はy=0とする。つまり、y=0,1,2とする三値分類モデル

  • 過去1,000時間のデータでモデルの訓練を行う。4時間毎にモデルの訓練を繰り返し、常に最新データで訓練されたモデルにアップデート

  • 訓練済みモデルによる予測は1分間隔で行い、予測結果がy=1であれば買い注文、y=2であれば売り注文を行う

この記事では、以上を実装する方法を具体的に説明していきます。なお、予測の的中率や勝率を向上させる工夫はそこまで考慮しません。それらはその後の別課題として、まずは仕組みを構築することを目的とします。

Input(グローバル変数)

MQL内のグローバル変数として以下を定義します。機械学習モデルの訓練を
行う間隔や、予測を行う間隔を比較的自由にカスタマイズ可能な仕様にしています。

//+------------------------------------------------------------------+
//| グローバル変数
//+------------------------------------------------------------------+
input double lot_size = 0.01;//ロット数
input int max_position = 10;//最大ポジション数
input int order_interval = 3600;//エントリー注文を行う間隔(秒)
input int retraining_interval = 4;//モデルを訓練する間隔(時間)
input int training_period = 1000;//訓練用データの取得期間(時間)
input int prediction_interval = 60;//モデルで予測する間隔(秒)
input int position_hold_time = 4; // ポジション保有期間(時間)
input double price_change_target = 0.1; // 価格変動の目標値(%)
input int magic_number = 10001;//マジックナンバー
input ulong slippage = 10;//スリッページ
input double spread_limit = 35;//許容スプレッド(point)

ロット数

売買注文を行う際のロット数です。ロット数を変動させることは想定していません。

最大ポジション数

ポジションを最大いくつまで同時に保有するかを指定します。

エントリー注文を行う間隔(秒)

複数ポジションを持とうとする場合、前のエントリー注文からしばらくの間は同じ方向にエントリーしないように調整します。単位は秒で設定します。
デフォルトは3,600秒=1時間です。

モデルを訓練する間隔(時間)

定期的にモデルを訓練する仕組みにしていますが、その間隔をこのパラメータで指定します。単位は時間で設定します。
デフォルトは4時間です。

訓練用データの取得期間(時間)

モデルを訓練するためのデータを何時間分取得するかを指定します。単位は時間で設定します。
デフォルトは1,000時間です。多ければ多いほど訓練には有効かもしれませんが、メモリや処理能力の問題もあるのでひとまず1,000時間です。

モデルで予測する間隔(秒)

このML-EAでは売買判定を機械学習モデルで予測することを繰り返しますが、その予測を行う間隔を指定します。単位は秒で設置します。
デフォルトは60秒=1分です。特徴量の1つである1分足のMAが1分おきに変動するので、この間隔にしています。

ポジション保有期間(時間)

このML-EAでは4時間後の価格が上昇しているか下降しているかを予測するので、ポジションも基本的に4時間後にクローズします。その保有期間を変更するためのパラメーターです。単位は時間で設定します。
デフォルトは4時間です。4時間後の価格をベースに予測するモデルなので、それに合わせています。

価格変動の目標値(%)

このML-EAでは4時間後の価格が0.1%以上上昇しているか下降しているかを予測します。その0.1%部分を変更するためのパラメーターです。単位は%で設定します。
デフォルトは0.1%です。

マジックナンバー

マジックナンバーとは、注文がどのEA・ロジックから実行されたかを識別するための番号です。このML-EAでは、複数のマジックナンバーを使い分けたりしませんので、任意の値で問題ありません。

スリッページ

スリッページとは、EAが発注したレートと、実際にFX会社側が約定するレートに乖離が生じること(またはその価格差)です。許容スリッページはFX会社によって異なり、設定不可の場合はスリッページの指定ができませんので、この値を設定しても意味がないこともあります。

許容スプレッド(point)

許容できるスプレッドの上限を指定します。このML-EAでは、スプレッドが拡大している時には余計な処理を行わないような対処をしています。これは注文だけではなく、機械学習モデルによる予測も行わないようにしています。
取引通貨ペアによって平常時の平均スプレッド等が変わってくると思います。それに合わせて設定します。

MQLのメイン処理

以下は、MQL内のOnInit関数およびOnTick関数のコード全文です。

//+------------------------------------------------------------------+
//| 起動時の処理
//+------------------------------------------------------------------+
void OnInit()
  {
   // プロセス完了ファイルを作成
   CreateProcessDoneFile();
   // モデルを訓練
   TrainModel();
  }

//+------------------------------------------------------------------+
//| ティック毎の処理
//+------------------------------------------------------------------+
void OnTick()
  {
   // モデルを訓練
   TrainModel();

   // Ask、Bidとスプレッドを取得
   GetMarketInfo(_Symbol, Ask, Bid, Spread);

   // スプレッドが許容スプレッドを超える場合は以降の処理をスキップ
   if(Spread > spread_limit * Point())
      return;

   // 訓練済みモデルによる予測
   PredictFlg();

   // 予測結果を読み込み
   LoadPredictionOutcome();

   // ポジションの確認
   CheckPositions();

   // buyエントリー注文
   if(order_flg == 1)
     {
      PlaceEntryOrder(1, buy_position, last_buy_time);
     }

   // sellエントリー注文
   if(order_flg == 2)
     {
      PlaceEntryOrder(2, sell_position, last_sell_time);
     }

   // buyクローズ注文
   if(buy_position > 0)
     {
      bool result = CloseBuyPositions();
     }

   // sellクローズ注文
   if(sell_position > 0)
     {
      bool result = CloseSellPositions();
     }
  }
ML-EAの処理の流れ

このイメージ図の左側のMQL4/MQL5の処理になります。以下では、これらの処理を順番に解説します。

起動時の処理

OnInit関数内の処理です。

   // プロセス完了ファイルを作成
   CreateProcessDoneFile();
   // モデルを訓練
   TrainModel();

このML-EAでは、MQLのティック処理(ループ処理)の途中で、Pythonの実行処理を組み込んでいます。Pythonで行うのは機械学習モデルの訓練や、機械学習による予測ですので、実行にはそれなりの時間がかかります。
その間もMQLの処理は次へ次へと流れていってしまいますので Pythonの処理が完了していることを確認して次の処理を行うというプロセスを構築する必要があります。
そこで、各処理を行うタイミングで「process_done.txt」という空のファイルを作成したり削除したりすることで、その制御を行います。

また、起動時に一度モデルの訓練を行って機械学習モデルを保存しておきます。以下で説明するティック毎の処理における訓練と同じ内容です。

ティック毎の処理

ここからはOnTick関数内の処理です。ティックが更新される度に行われます。いわゆるループ処理です。

モデルを訓練

   // モデルを訓練
   TrainModel();

「TrainModel」という関数を定義して以下の処理を行います。

  • 現在時刻が以前に訓練を実施した時間から4時間経過している場合のみ、以下の後続処理を実施

  • 1分足、5分足、15分足、1時間足の移動平均線を1,000時間分出力

  • 1分足の終値を1,000時間分取得し、4時間後に価格が0.1%以上上昇しているか0.1%以上下落しているかどうかで目的変数yを計算

  • 4つのMA(移動平均線)を特徴量としてLightGBMモデルの訓練を実施

  • 訓練後のLightGBMモデルをWindows PC上に保存

スプレッド判定

   // Ask、Bidとスプレッドを取得
   GetMarketInfo(_Symbol, Ask, Bid, Spread);

   // スプレッドが許容スプレッドを超える場合は以降の処理をスキップ
   if(Spread > spread_limit * Point())
      return;

現在のスプレッドを計算し、指定した許容スプレッド(spread_limit)を超えている場合は、後続の処理は行わずに終了する処理です。スプレッドが拡大している時は予測や取引を含めて余計な処理は行わないようにします。

訓練済みモデルによる予測

   // 訓練済みモデルによる予測
   PredictFlg();

   // 予測結果を読み込み
   LoadPredictionOutcome();

「PredictFlg」と「LoadPredictionOutcome」という関数を定義して以下の処理を行います。

  • 現在時刻が以前に予測を実施した時間から60秒間経過している場合のみ、以下の後続処理を実施

  • 1分足、5分足、15分足、1時間足の移動平均線について、直近の確定した値を出力

  • 4つのMA(移動平均線)を特徴量として、保存してある訓練済みLightGBMモデルを呼び出して予測を実施

  • 予測結果(0,1,2のいずれか)を読み込み、order_flgという変数に格納

ポジションの確認

   // ポジションの確認
   CheckPositions();

「CheckPositions」という関数を定義して以下の処理を行います。

  • 現在保有しているbuyポジションおよびsellポジションの数を取得

  • 各ポジションの最終注文時刻を確認し、最後にbuyエントリー注文を行った時間と最後にsellエントリー注文を行った時間を取得

立て続けにエントリー注文を行わないように、最大ポジション数を設定し、さらにはある程度時間を空けてエントリー注文を行う仕組みにしていますので、その判定を入れるための情報を取得する処理です。

エントリー注文

   // buyエントリー注文
   if(order_flg == 1)
     {
      PlaceEntryOrder(1, buy_position, last_buy_time);
     }

   // sellエントリー注文
   if(order_flg == 2)
     {
      PlaceEntryOrder(2, sell_position, last_sell_time);
     }

「PlaceEntryOrder」という関数を定義して、以下の条件を満たした場合のみエントリー注文を送信します。

  • order_flgが条件を満たす(buyエントリーであれば1、sellエントリーであれば2)

  • ポジション数が最大ポジション(指定した数)に達していない

  • 前回の注文成立から1時間(指定した時間)以上経過している

  • 現在価格が予測時点の終値を下回っている(もしくは上回っている)

最後の条件は、予測する段階で確定している1分足(バー)の終値やMAを基準に予測を行っているため、基本的にはその値でエントリーするのが望ましいためです。予測時点と現在価格が乖離しているような場合は不利なエントリーを避けるために注文は行わないという意図です。

クローズ注文

   // buyクローズ注文
   if(buy_position > 0)
     {
      bool result = CloseBuyPositions();
     }

   // sellクローズ注文
   if(sell_position > 0)
     {
      bool result = CloseSellPositions();
     }

「CloseBuyPositions」および「CloseSellPositions」という関数を定義して、以下の条件を満たしたポジションのみクローズ注文を送信します。

  • エントリー注文が成立してから4時間(指定した時間)以上経過している

  • 現在価格と取得価格の差が取得価格の0.1%(指定した値)以上である

つまり、少なくとも4時間はポジションをクローズせず、4時間経過して価格が0.1%以上上昇または下落すればポジションをクローズします。利確だけでなく、ロスカットも行います。ただ、4時間経過しても価格が0.1%以上動いていなければ、0.1%以上動くまではポジションを持ち続ける、というロジックです。

MQLとPythonの連携

ここからはMQLがPythonと連携する仕組みについて解説します。
左側のMQL4/MQL5と右側のPythonをつなぐ処理です。

ML-EAの処理の流れ

まずは概要です。

MQLからPythonへの連携

  • MQLから必要な情報はテキストファイル(training_data.csvやprediction_data)で保存

  • MQLからバッチファイル(train.batやpredict.bat)を実行

  • バッチファイルがPythonスクリプト(train.pyやpredict.py)を実行

  • Pythonスクリプトはテキストファイルをインプットとして実行

PythonからMQLへの連携

  • Pythonスクリプトの実行結果はテキストファイル(order_flg.txt)で保存

  • MQLはテキストファイルを読み込むことで結果(order_flg)を取得

つまり、常にバッチファイルあるいはテキストファイルを介していて、MQLとPythonで直接のやり取りはありません。

ShellExecuteW関数

MQLからバッチファイル(.bat)を実行するために、ShellExecuteW関数を使用します。この関数はWindows APIの一部で、外部プログラムやドキュメントを開くために使用されます。
以下のようにMQL内で"Shell32.dll"をインポートすることで使用可能です。

#import "Shell32.dll"
int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
#import

バッチファイルのパスを設定

//+------------------------------------------------------------------+
//| バッチファイルのパス ※[ユーザー名]をご自身の環境に合わせて変更してください。
//+------------------------------------------------------------------+
string train_bat = "C:\\Users\\[ユーザー名]\\train.bat";
string predict_bat = "C:\\Users\\[ユーザー名]\\predict.bat";

MQLからは、「train.bat」や「predict.bat」というバッチファイルを実行することでモデルの訓練や予測を行います。そのパスを指定しておきます。ここでは”C:¥Users¥[ユーザー名]¥”の直下にファイルを置いている場合を想定していますが、任意の場所でも問題ありません。[ユーザー名]部分は、それぞれの環境に合わせて書き換える必要があります。

バッチファイルの作成

バッチファイルの作成方法は、以下の内容をテキストファイルに記載した状態で.batという拡張子で保存するだけです。

train.bat

@echo off
C:\Users\[ユーザー名]\AppData\Local\Programs\Python\Python39\python.exe C:\Users\[ユーザー名]\train.py

predict.bat

@echo off
C:\Users\[ユーザー名]\AppData\Local\Programs\Python\Python39\python.exe C:\Users\[ユーザー名]\predict.py

ここでは、Python実行ファイル(python.exe)とPythonスクリプト(train.pyやpredict.py)のパスを同じラインに書いています。

python.exeは、Pythonをインストールした際の一般的なパスを記載しています。ここではPythonのバージョンが3.9なので”Python39”になっていますが、Pythonのバージョンが異なる場合は変更が必要です。

train.pyやpredict.pyは、実際にそのファイルを保存している場所でOKです。ここでは”C:¥Users¥[ユーザー名]¥”の直下に置いている想定です。
[ユーザー名]は、ご自身の環境のユーザー名に置き換える必要があります。

Pythonスクリプトの作成

続いて、右側のPythonにおける処理です。

ML-EAの処理の流れ

機械学習モデルの訓練

「train.py」というPythonスクリプトで以下の処理を実行します。

  • MQLから出力された1,000時間分のMAと目的変数(y)を結合した訓練用データ「training_data.csv」を作成

  • 訓練用データを用いてLightGBMモデルの訓練を実施し、モデル「ML-gbm.model」とスケーラー「ML-scaler.pkl」を保存

  • 機械学習モデルの性能評価(model_evaluation)に関する指標を出力

最後の性能評価について具体的に例示します。今回のモデルでは以下のような指標を出力するようにしています。

model_evaluation

以下、各指標の意味と見方を簡単に説明します。

  1. 訓練精度 (Training Accuracy: 0.8742):

    • これは、機械学習モデルが訓練データをどれくらい正確に分類できるかを示しています。

    • 数値は約0.87、つまりこれは、モデルが訓練データの約87%を正しく分類できることを意味します。

  2. テスト精度 (Test Accuracy: 0.8676):

    • これは、新しいデータ、つまりモデルが学習中に見たことがないデータに対するモデルの正確性です。

    • 数値は約0.87、つまりこれは、新しいデータの約87%を正しく分類できることを意味します。

  3. 分類レポート (Classification Report):

    • このレポートは、異なるクラス(この場合は0, 1, 2)ごとにモデルの性能を詳細に示しています。

    • 適合率 (Precision): たとえば、クラス0の適合率は0.84です。これは、モデルがクラス0と予測したデータのうち84%が実際にクラス0であることを意味します。

    • 再現率 (Recall): たとえば、クラス0の再現率は0.83です。これは、実際のクラス0のデータのうち83%がモデルによって正しくクラス0として検出されたことを意味します。

    • F1スコア (F1-Score): これは適合率と再現率のバランスをとった数値です。たとえば、クラス0のF1スコアは83%です。

    • サポート (Support): これは各クラスのデータの数です。例えば、クラス0に分類されたデータは4,543個あります。

  4. 全体の精度 (Accuracy): これは全クラスに渡るモデルの全体的な正確性を示しており、約87%です。

  5. マクロ平均 (Macro Avg) と 加重平均 (Weighted Avg): これらは、異なるクラスの数を考慮に入れた平均の適合率、再現率、F1スコアです。どちらも約87%です。

以上、これらの数値は、モデルが全体的にどれくらいうまく機能しているかを示す指標です。今後応用する際には、モデルの精度を向上させるために利用していくことになります。

機械学習による予測

「predict.py」というPythonスクリプトで以下の処理を実行します。

  • 訓練時に保存したLightGBMモデル「ML-gbm.model」とスケーラー「ML-scaler.pkl」を呼び出し

  • 予測用データ「prediction_data.csv」および、呼び出したモデルとスケーラーを用いてクラス(0, 1, 2のいずれか)を予測

  • 予測結果(0, 1, 2のいずれかの値)を「order_flg.txt」に書き込み

  • 予測結果(0, 1, 2のいずれかの確率)を記録のために別ファイルに出力

最後の予測結果(prediction_results)については、例えば以下のような結果が出力されます。

  • 2024-02-16 21:30:52, [0.5099, 0.4651, 0.0250]

  • 2024-02-16 22:31:13, [0.3258, 0.6509, 0.0234]

これは、クラス[0, 1, 2]のそれぞれの確率を意味しています。
1つ目は、クラス0が50.99%で採用されてorder_flg=0です。
2つ目は、クラス1が65.09%で採用されてorder_flg=1です。つまりbuy注文を行うフラグが成立したことになります。

その他事前準備

ご自身で環境の設定や調整が難しい方は、ひとまずここで行っているものと同じ環境を構築する必要があります。以下のリンク先に具体的な方法をまとめましたのでご確認ください。

有料部分の内容

以上、ML-EAで行う処理の流れを具体的に説明してきました。
有料部分では一連の処理を実行するための全コードを掲載した以下のファイルをダウンロード可能にしています。

  • ML-EA_note.mq5(MQL5のメイン処理だけでなく全ての関数を掲載)

  • train.bat(train.pyを実行するためのバッチファイル)

  • predict.bat(predict.pyを実行するためのバッチファイル)

  • train.py(機械学習モデルの訓練を行うためのPythonスクリプト)

  • predict.py(機械学習による予測を行うためのPythonスクリプト)

また、基本的にはMQL5を想定して作成されていますが、MQL4でも同様の実装は可能なため、MQL4向けにカスタマイズしたファイル一式もダウンロード可能にしています。

注意点

  • 当記事で掲載しているコードはPythonの環境設定含め、必要な準備が整っている上での実行を想定しています。環境設定に問題がある場合はご自身で解決していただかないと実際のプログラム実行まで辿り着けない可能性があります。

  • 記事執筆時点で稼働確認を行なっており、エラーが出ないことを確認しておりますが、その後の環境変化等で想定通りに稼働しない可能性はございます。動作保証等はいたしかねますのでご了承ください。

  • リアル口座にアクセスして取引を行うことも可能なコードになっておりますが、必ずデモ口座で事前に稼働確認をしていただくことを推奨いたします。

  • 当記事で解説しているロジック通りの動作を保証するものではございません。あくまでFX自動売買ツール開発のためのサンプルコードとしてご活用ください。

  • 今回作成したのはあくまでML-EAのベースモデルであり、様々なロジックに拡張するための土台になります。その後の改善の記事については以下の記事にまとめています。あわせてご覧ください。

コード全文

ここからは、これまでに掲載していないコードについて掲載しつつ、簡単な解説を入れていきます。最後に、全てのコードを盛り込んだファイル一式をダウンロード可能にしていますので、解説が不要であればそちらにお進みください。

ここから先は

10,814字

¥ 5,000

よろしければサポートお願いします。いただいたサポートは今後の記事の執筆に活用させていただきます。