MQL5最適化手法の設計と実装

目次

この記事の結論

MQL5の最適化手法は、EAの入力パラメータを機械的に探す作業ではなく、売買ロジックの再現性を確認するための検証設計です。
最適化では、総損益だけでなく、最大ドローダウン、取引回数、損益比、連敗数、期間依存性を合わせて確認する必要があります。
パラメータを細かく合わせすぎると、バックテストでは良く見えてもフォワードテストで崩れやすくなります。
実運用では、スプレッド、約定、ブローカー仕様、口座タイプの違いにより成績が変動するため、最適化結果だけで運用判断を完結させてはいけません。

1. MQL5における最適化手法の役割

【結論】
MQL5における最適化手法の役割は、EAの入力パラメータが特定条件だけに依存していないかを確認することです。
最適化は利益を保証する作業ではなく、ロジックの弱点を見つけるための検証工程です。

【定義】
最適化とは、EAの入力値を複数パターンでテストし、売買ロジックの成績、安定性、リスク特性を比較する作業です。

MQL5のEAでは、input 変数として定義した期間、閾値、ロット、損切り幅、利確幅などをテスターで組み合わせて確認できます。
ただし、成績が最も良い組み合わせをそのまま採用するだけでは、過剰最適化になりやすくなります。

最適化で重要なのは、次の3点です。

  • どの条件で成績が悪化するか
  • パラメータを少し変えても成績が極端に崩れないか
  • バックテスト後のフォワードテストでも近い傾向が出るか
MQL5 parameter stability range detection visualization showing strategy tester optimization results, including sensitivity analysis of FastMA period with net profit, drawdown, and profit factor curves, highlighting stable performance range versus overfitting zones, alongside EA input parameters code and evaluation metrics for robust trading system design.

1.1 最適化は売買ロジックの検証工程

MQL5の最適化は、ロジックの良し悪しを一度で決める機能ではありません。
最適化は、EAが特定期間、特定銘柄、特定スプレッドだけに過度に合っていないかを調べる工程です。

売買ロジックが有効に見える場合でも、取引回数が少なすぎる場合は偶然の影響が大きくなります。
また、最大ドローダウンが大きい場合は、総損益が良くても実運用に耐えにくい可能性があります。

1.2 最適化で見つけるべきもの

最適化で見つけるべきものは、最高成績の一点ではありません。
EA設計では、ある程度広い範囲で成績が極端に崩れにくい領域を探すことが重要です。

たとえば、移動平均期間が20のときだけ良く、19や21で急に悪化する場合は、条件依存が強い可能性があります。
一方で、18から25の範囲で似た傾向が出る場合は、ロジックの再現性を確認しやすくなります。

2. 基本的な考え方

【結論】
MQL5の最適化では、パラメータ、評価指標、検証期間、取引条件を分けて考える必要があります。
総損益だけで順位を決めると、ドローダウンや取引頻度の問題を見落としやすくなります。

最適化の基本は、次の順序で設計します。

  1. 売買ロジックの目的を決める
  2. 最適化する入力パラメータを絞る
  3. 評価する指標を決める
  4. バックテスト期間を分ける
  5. フォワード期間で再現性を確認する
  6. 実運用に近い条件で再確認する

MQL5のEAでは、ロジック本体、フィルター、資金管理、決済条件を分離すると、最適化の対象を整理しやすくなります。

2.1 最適化対象を増やしすぎない

最適化対象が多すぎると、組み合わせ数が急増します。
組み合わせ数が増えるほど、偶然良い成績になった設定を選びやすくなります。

最初に最適化する対象は、ロジックの根幹に関係する少数のパラメータに絞ります。

  • シグナル期間
  • フィルター期間
  • 損切り幅
  • 利確幅
  • トレーリング条件
  • 取引時間帯

ロット倍率や複雑な資金管理を先に最適化すると、ロジック本体の弱点が見えにくくなります。

2.2 評価指標を複数に分ける

最適化では、総損益だけを見てはいけません。
MQL5のEA評価では、損益、リスク、取引頻度、期間依存性を分けて確認します。

確認すべき代表的な指標は次のとおりです。

  • 総損益
  • 最大ドローダウン
  • 勝率
  • 損益比
  • 取引回数
  • 連敗数
  • 期間ごとの損益
  • パラメータ変更時の成績変化

バックテスト結果は将来の利益を保証しません。
評価指標は、実運用前にリスクの大きさを把握するために使います。

3. 代表的な最適化パターン

【結論】
MQL5の最適化手法には、単純な総当たり、範囲を絞った段階的最適化、ロジック単位の分割最適化、フォワード期間を使う検証があります。
初心者は、最初から複雑な組み合わせを探すより、少数パラメータを段階的に確認するほうが失敗を減らしやすくなります。

3.1 総当たり型の最適化

総当たり型の最適化は、指定した入力値の組み合わせを広く確認する方法です。
探索範囲を広く取れる一方で、組み合わせが増えると時間がかかります。

総当たり型は、次の場面に向いています。

  • 最適化対象が少ない
  • パラメータ範囲を広く確認したい
  • ロジックの感度を把握したい

ただし、細かい刻み幅で大量に試すと、偶然良い結果を拾いやすくなります。

3.2 段階的な最適化

段階的な最適化は、まず広い範囲で大まかな傾向を見てから、成績が安定しやすい範囲を狭く確認する方法です。
この方法は、過剰最適化を避けながらパラメータの傾向を見やすくなります。

例として、移動平均期間を使うEAでは次のように確認します。

  1. 10から100まで大きな刻みで確認する
  2. 成績が極端に崩れにくい範囲を探す
  3. その範囲だけを小さな刻みで確認する
  4. 別期間で同じ傾向が残るか確認する

最終的な設定は、最高値の一点ではなく、周辺値でも成績が大きく崩れにくい範囲から選びます。

3.3 ロジック単位の分割最適化

EAは、シグナル判定、フィルター判定、リスク確認、注文処理、決済管理に分けて設計できます。
最適化も同じように、役割ごとに分けると原因を判断しやすくなります。

分割の例は次のとおりです。

  • エントリー条件だけを確認する
  • トレンドフィルターだけを確認する
  • 損切りと利確だけを確認する
  • 取引時間帯だけを確認する
  • ロット計算を固定してロジック本体を見る

資金管理を最初から複雑にすると、どの条件が成績に影響したのか判断しにくくなります。

4. MQL5で最適化しやすいEAの実装方法

【結論】
MQL5で最適化しやすいEAを作るには、調整対象を input 変数にし、シグナル判定と注文処理を分離します。
インジケータを使う場合は、OnInitでハンドルを作成し、OnTickでCopyBufferにより値を取得します。

MQL5では、インジケータ関数が常に値を直接返すわけではありません。
EAで移動平均やATRなどを使う場合は、インジケータハンドルを作成し、必要なタイミングでバッファ値を取得する構造になります。

4.1 input変数で最適化対象を定義する

最適化対象は、EAの先頭で input 変数として定義します。
変数名は、あとからテスター結果を見ても意味が分かる名前にします。

#property strict

input int    FastMAPeriod      = 20;
input int    SlowMAPeriod      = 50;
input double RiskPercent       = 1.0;
input int    StopLossPoints    = 300;
input int    TakeProfitPoints  = 600;
input int    MaxSpreadPoints   = 30;

最適化対象にする値は、ロジックの意味が説明できるものに限定します。
意味を説明できない値を増やすと、バックテスト結果だけに合わせた設定になりやすくなります。

4.2 インジケータハンドルを初期化する

移動平均線を使うEAでは、OnInitでハンドルを作成します。
ハンドル作成に失敗した場合は、EAの初期化を失敗として扱います。

#property strict

input int FastMAPeriod = 20;
input int SlowMAPeriod = 50;

int fastMaHandle = INVALID_HANDLE;
int slowMaHandle = INVALID_HANDLE;

int OnInit()
{
   fastMaHandle = iMA(_Symbol, _Period, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   slowMaHandle = iMA(_Symbol, _Period, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

   if(fastMaHandle == INVALID_HANDLE || slowMaHandle == INVALID_HANDLE)
   {
      Print("Failed to create moving average handles");
      return INIT_FAILED;
   }

   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(fastMaHandle != INVALID_HANDLE)
      IndicatorRelease(fastMaHandle);

   if(slowMaHandle != INVALID_HANDLE)
      IndicatorRelease(slowMaHandle);
}

OnInitは、EAの初期化処理を行うイベント関数です。
OnDeinitは、EA終了時の後処理を行うイベント関数です。
ハンドルを使うEAでは、終了時にIndicatorReleaseで解放する構造にしておくと管理しやすくなります。

4.3 OnTickで確定足を使って判定する

OnTickは、新しいティックを受信したときに実行されるEAの中心処理です。
最適化では、最新足の未確定値を使うか、確定足を使うかで結果が変わります。

確定足を使う場合は、配列を時系列方向に設定し、インデックス1の値を判定に使います。

bool GetMovingAverageValues(double &fastClosed, double &slowClosed)
{
   const int currentBarIndex = 0;
   const int closedBarIndex = 1;

   double fastBuffer[];
   double slowBuffer[];

   ArraySetAsSeries(fastBuffer, true);
   ArraySetAsSeries(slowBuffer, true);

   if(BarsCalculated(fastMaHandle) < 3 || BarsCalculated(slowMaHandle) < 3)
   {
      Print("Not enough calculated bars");
      return false;
   }

   int copiedFast = CopyBuffer(fastMaHandle, 0, currentBarIndex, 3, fastBuffer);
   int copiedSlow = CopyBuffer(slowMaHandle, 0, currentBarIndex, 3, slowBuffer);

   if(copiedFast < 3 || copiedSlow < 3)
   {
      Print("CopyBuffer failed or not enough data");
      return false;
   }

   fastClosed = fastBuffer[closedBarIndex];
   slowClosed = slowBuffer[closedBarIndex];
   return true;
}

時系列配列では、現在足の添字は currentBarIndex、直近の確定足の添字は closedBarIndex のように定数化すると意図が分かりやすくなります。
バックテストと実運用の乖離を抑えたい場合は、確定足で判定する設計が扱いやすくなります。

5. サンプルコード

【結論】
最適化用のEAサンプルでは、パラメータ定義、インジケータ取得、シグナル判定、スプレッド確認、注文前チェックを分離します。
実運用に使う前には、銘柄仕様、ロット制限、証拠金、約定条件を別途確認する必要があります。

次のコードは、移動平均の関係を使う検証用サンプルです。
売買判断を推奨するものではなく、最適化しやすい構造を示すための例です。

#property strict

input int    FastMAPeriod      = 20;
input int    SlowMAPeriod      = 50;
input double FixedLot          = 0.10;
input int    StopLossPoints    = 300;
input int    TakeProfitPoints  = 600;
input int    MaxSpreadPoints   = 30;
input ulong  MagicNumber       = 20260502;

int fastMaHandle = INVALID_HANDLE;
int slowMaHandle = INVALID_HANDLE;

int OnInit()
{
   fastMaHandle = iMA(_Symbol, _Period, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   slowMaHandle = iMA(_Symbol, _Period, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE);

   if(fastMaHandle == INVALID_HANDLE || slowMaHandle == INVALID_HANDLE)
   {
      Print("Failed to create indicator handles");
      return INIT_FAILED;
   }

   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(fastMaHandle != INVALID_HANDLE)
      IndicatorRelease(fastMaHandle);

   if(slowMaHandle != INVALID_HANDLE)
      IndicatorRelease(slowMaHandle);
}

void OnTick()
{
   if(PositionSelect(_Symbol))
      return;

   if(!IsSpreadAcceptable())
      return;

   double fastMa = 0.0;
   double slowMa = 0.0;

   if(!GetMovingAverageValues(fastMa, slowMa))
      return;

   if(fastMa > slowMa)
      SendMarketOrder(ORDER_TYPE_BUY);
   else if(fastMa < slowMa)
      SendMarketOrder(ORDER_TYPE_SELL);
}

bool IsSpreadAcceptable()
{
   long spread = 0;

   if(!SymbolInfoInteger(_Symbol, SYMBOL_SPREAD, spread))
   {
      Print("Failed to get spread");
      return false;
   }

   return spread <= MaxSpreadPoints;
}

bool GetMovingAverageValues(double &fastClosed, double &slowClosed)
{
   const int currentBarIndex = 0;
   const int closedBarIndex = 1;

   double fastBuffer[];
   double slowBuffer[];

   ArraySetAsSeries(fastBuffer, true);
   ArraySetAsSeries(slowBuffer, true);

   if(BarsCalculated(fastMaHandle) < 3 || BarsCalculated(slowMaHandle) < 3)
      return false;

   int copiedFast = CopyBuffer(fastMaHandle, 0, currentBarIndex, 3, fastBuffer);
   int copiedSlow = CopyBuffer(slowMaHandle, 0, currentBarIndex, 3, slowBuffer);

   if(copiedFast < 3 || copiedSlow < 3)
   {
      Print("CopyBuffer failed or not enough data");
      return false;
   }

   fastClosed = fastBuffer[closedBarIndex];
   slowClosed = slowBuffer[closedBarIndex];
   return true;
}

bool SendMarketOrder(ENUM_ORDER_TYPE orderType)
{
   double lot = NormalizeLot(FixedLot);

   if(lot <= 0.0)
      return false;

   double price = 0.0;
   double sl = 0.0;
   double tp = 0.0;

   if(orderType == ORDER_TYPE_BUY)
   {
      price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      sl = price - StopLossPoints * _Point;
      tp = price + TakeProfitPoints * _Point;
   }
   else
   {
      price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      sl = price + StopLossPoints * _Point;
      tp = price - TakeProfitPoints * _Point;
   }

   MqlTradeRequest request;
   MqlTradeResult result;
   MqlTradeCheckResult check;

   ZeroMemory(request);
   ZeroMemory(result);
   ZeroMemory(check);

   request.action       = TRADE_ACTION_DEAL;
   request.symbol       = _Symbol;
   request.volume       = lot;
   request.type         = orderType;
   request.price        = price;
   request.sl           = NormalizeDouble(sl, _Digits);
   request.tp           = NormalizeDouble(tp, _Digits);
   request.deviation    = 20;
   request.magic        = MagicNumber;
   request.type_filling = ORDER_FILLING_FOK;

   if(!OrderCheck(request, check))
   {
      Print("OrderCheck failed: ", check.retcode);
      return false;
   }

   if(!OrderSend(request, result))
   {
      Print("OrderSend failed: ", result.retcode);
      return false;
   }

   if(result.retcode != TRADE_RETCODE_DONE && result.retcode != TRADE_RETCODE_PLACED)
   {
      Print("Trade request was not completed: ", result.retcode);
      return false;
   }

   return true;
}

double NormalizeLot(double lot)
{
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double step   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   if(minLot <= 0.0 || maxLot <= 0.0 || step <= 0.0)
   {
      Print("Invalid volume settings");
      return 0.0;
   }

   lot = MathMax(minLot, MathMin(maxLot, lot));
   lot = MathFloor(lot / step) * step;

   return NormalizeDouble(lot, 2);
}

5.1 注文前チェックを入れる理由

MQL5の注文処理では、MqlTradeRequest に注文内容を設定し、OrderCheck で事前確認してから OrderSend を使う構造にできます。
注文前チェックを入れると、証拠金不足、ロット制限、銘柄仕様の問題を検出しやすくなります。

実運用では、次の条件により注文結果が変わる場合があります。

  • 約定方式
  • スプレッド
  • スリッページ
  • 最小ロット
  • 最大ロット
  • ロットステップ
  • 証拠金
  • ストップレベル
  • フリーズレベル
  • 取引可能時間
  • netting口座とhedging口座の違い

6. パターン別比較

【結論】
最適化手法は、速さ、網羅性、過剰最適化リスク、実運用へのつなげやすさで比較します。
EAの初期検証では、総当たりよりも段階的な確認とフォワードテストを組み合わせる方法が扱いやすくなります。

方法メリットデメリット向いている場面過剰最適化リスク
総当たり最適化広い範囲を確認しやすい組み合わせが多いと時間がかかるパラメータ数が少ないEA
段階的最適化傾向を見ながら範囲を絞れる手順を決めずに行うと主観が入りやすい初心者から中級者のEA検証低から中
ロジック単位の分割最適化どの条件が効いたか判断しやすい実装を分離しておく必要がある複数フィルターを持つEA低から中
最高成績だけを選ぶ方法結果を選びやすいフォワードで崩れやすい初期の粗い確認のみ
フォワード期間つき検証再現性を確認しやすい検証に時間がかかる実運用前の確認低から中

比較表から分かるように、単純に一番良い成績を選ぶ方法は扱いやすい反面、過剰最適化のリスクが高くなります。
実運用を想定するEAでは、最高成績よりも安定して崩れにくい条件を重視します。

7. 誤作動しやすい場面

【結論】
MQL5の最適化では、未確定足の値、スプレッド条件、取引回数不足、期間依存、パラメータ数の増やしすぎが誤判断の原因になります。
バックテストで良い結果が出ても、原因を分解しないと実運用で同じ傾向が続くとは限りません。

7.1 未確定足を使った判定

形成中の足は、ティックごとに値が変わります。
インジケータ値を現在足の添字で判定すると、バックテスト条件やティック生成条件により結果が変わりやすくなります。

確定足ベースで判定する場合は、直近の確定足を示す添字を使います。
シグナルの発生は遅れますが、検証条件をそろえやすくなります。

7.2 スプレッド条件の見落とし

スプレッドは、エントリーと決済の成績に直接影響します。
特に短期売買や小さな利幅を狙うEAでは、スプレッド拡大により成績が悪化しやすくなります。

最適化では、スプレッド条件を変えた場合に成績がどの程度変わるかを確認します。

7.3 取引回数が少なすぎる結果

取引回数が少ない最適化結果は、偶然の影響を受けやすくなります。
総損益が良くても、数回の大きな利益に依存している場合は再現性を判断しにくくなります。

取引回数、期間ごとの取引分布、連敗数を合わせて確認します。

7.4 パラメータを増やしすぎる設計

パラメータを増やすほど、バックテストに合わせ込む余地が増えます。
トレンドフィルター、時間帯フィルター、ボラティリティフィルター、複数決済条件をすべて同時に最適化すると、原因を分解しにくくなります。

最初はロジックの中心となる条件だけを確認します。
追加フィルターは、必要な理由を説明できる場合に限定します。

8. バックテストで確認すべき項目

【結論】
バックテストでは、利益だけでなく、損失の出方、取引回数、パラメータ依存性、スプレッド条件を確認します。
最適化結果は、複数の指標で確認して初めてロジックの特徴を判断しやすくなります。

バックテストで確認する主な項目は次のとおりです。

確認項目見る理由注意点
総損益ロジック全体の結果を見る単独では判断しない
最大ドローダウン資金の落ち込みを確認する許容できる水準を事前に決める
勝率取引の当たりやすさを見る損益比と合わせて確認する
損益比平均利益と平均損失の関係を見る勝率だけではリスクを判断できない
取引回数結果の偏りを確認する少なすぎる場合は偶然の影響が大きい
連敗数心理面と資金面の負荷を見る実運用の停止条件に関係する
スプレッド条件コスト耐性を見る銘柄や時間帯で変わる
期間依存性特定相場だけに合っていないか見る複数期間で比較する
パラメータ依存性設定が一点だけに依存していないか見る周辺値でも確認する

8.1 期間を分けて確認する

最適化に使った期間だけで評価すると、過剰最適化を見逃しやすくなります。
検証期間は、少なくとも学習的に使う期間と、確認用の期間に分けます。

例として、過去データを前半と後半に分け、前半で候補を探し、後半で傾向が残るか確認します。
市場環境が変わると、同じ設定でも結果が変わる場合があります。

8.2 パラメータ周辺の安定性を見る

最適化結果では、最高成績の一点だけでなく周辺値を確認します。
周辺値で成績が急に悪化する場合は、特定の条件に合わせ込みすぎている可能性があります。

安定した領域が見つからない場合は、ロジックそのものを見直す必要があります。

9. フォワードテストで確認すべき項目

【結論】
フォワードテストでは、バックテストで選んだ候補が実際に近い条件で崩れないかを確認します。
約定差、スプレッド拡大、ブローカー差、VPS環境での安定性は、バックテストだけでは十分に判断できません。

フォワードテストで確認する項目は次のとおりです。

  • 約定差
  • スプレッド拡大時の挙動
  • 取引頻度
  • ドローダウン
  • バックテストとの乖離
  • ブローカー差
  • VPS環境での安定性
  • ログの異常
  • 注文拒否や約定失敗

9.1 バックテストとの乖離を見る

フォワードテストの目的は、バックテストと完全に同じ結果を出すことではありません。
フォワードテストでは、売買頻度、損益の方向性、ドローダウンの大きさが極端に変わらないかを見ます。

乖離が大きい場合は、スプレッド、約定、取引時間、ティック条件、ブローカー仕様を確認します。

9.2 デモ口座とリアル口座の差

デモ口座とリアル口座では、約定条件が異なる場合があります。
同じEA、同じパラメータでも、スリッページ、約定拒否、スプレッド拡大により成績が変わることがあります。

リアル口座で使う前には、小さいリスクで挙動を確認する必要があります。
これは利益を狙うためではなく、EAが想定どおりに動くかを確認するためです。

10. 実運用での注意点

【結論】
MQL5の最適化結果を実運用に使う場合は、ロット制限、証拠金、スプレッド、約定方式、口座タイプ、停止条件を事前に確認します。
バックテスト結果は将来の利益を保証せず、実運用では想定外のドローダウンが発生する場合があります。

10.1 資金管理を別に検証する

最適化では、ロジック本体と資金管理を分けて確認します。
ロット計算を複雑にすると、成績が良く見えてもリスクが拡大している場合があります。

ロット計算では、次の条件を確認します。

  • 最小ロット
  • 最大ロット
  • ロットステップ
  • 証拠金
  • 許容リスク
  • 損切り幅
  • 口座残高
  • 有効証拠金
  • 銘柄仕様
  • 桁数
  • ティックバリュー
  • ティックサイズ

10.2 ロット計算方式の比較

方式メリットデメリット向いている場面
固定ロット実装が簡単で検証しやすい資金変動に合わせにくいロジック本体の初期検証
残高比例資金に応じて調整しやすい連敗時の縮小や回復が成績に影響する中期的な資金管理
リスク率ベース損切り幅と許容損失から計算できるティックバリューや銘柄仕様の確認が必要損失許容を明確にしたい場合
ボラティリティ調整ATRなどで相場変動を反映しやすい設計が複雑になりやすい変動幅が大きい銘柄の検証

ロット計算は、損失を消す仕組みではありません。
ロット計算は、損失が発生したときの資金変動を事前に管理するための仕組みです。

10.3 停止条件を決める

実運用では、EAを止める条件を事前に決めます。
停止条件がないと、想定外の相場で損失が拡大する場合があります。

停止条件の例は次のとおりです。

  • 最大ドローダウンに達した
  • 連敗数が想定を超えた
  • スプレッドが一定以上に広がった
  • 重要な時間帯を避けたい
  • 注文エラーが連続した
  • 通信やVPS環境に異常がある

停止条件は、売買ロジックとは別のリスク制御として設計します。

11. 改善案と代替手段

【結論】
最適化結果が安定しない場合は、パラメータ調整を続けるよりも、ロジック分解、フィルター整理、決済条件の見直しを行います。
設定値だけで問題を解決しようとすると、過剰最適化に近づきやすくなります。

11.1 フィルターを増やす前に目的を決める

フィルターは、無駄なエントリーを減らすために使います。
ただし、フィルターを増やしすぎると取引回数が減り、特定期間だけに合った条件になりやすくなります。

追加するフィルターには、次のような目的が必要です。

  • レンジ相場を避ける
  • トレンド方向に合わせる
  • 低ボラティリティを避ける
  • スプレッド拡大時を避ける
  • 取引時間帯を制限する

目的が重複するフィルターは、ロジックを複雑にするだけの場合があります。

11.2 決済条件を分けて検証する

エントリー条件が同じでも、決済条件により成績は大きく変わります。
損切り、利確、トレーリング、時間決済を同時に最適化すると、何が効いたのか分かりにくくなります。

まず固定の損切りと利確でエントリー条件を確認します。
その後、トレーリングや時間決済を追加し、改善の有無を確認します。

11.3 複数銘柄で使う場合の注意

同じパラメータがすべての銘柄に適するとは限りません。
銘柄ごとにボラティリティ、スプレッド、ティックバリュー、取引時間、約定条件が異なります。

複数銘柄EAでは、銘柄ごとの取引条件を読み取り、ロット、ストップ幅、スプレッド制限を分けて設計します。

12. まとめ

【結論】
MQL5の最適化手法は、最高成績のパラメータを探す作業ではなく、EAの再現性とリスクを確認する作業です。
最適化、バックテスト、フォワードテスト、実運用前確認を分けることで、過剰最適化を避けやすくなります。

この記事の要点は次のとおりです。

  • 最適化は利益保証ではなく検証工程である
  • input 変数で調整対象を明確にする
  • インジケータ値はハンドルとCopyBufferで取得する
  • 確定足と未確定足を分けて扱う
  • 総損益だけでなくドローダウンや取引回数を見る
  • 最高成績の一点ではなく周辺値の安定性を見る
  • フォワードテストで再現性を確認する
  • 実運用ではスプレッド、約定、ブローカー仕様を考慮する
  • 注文前にはOrderCheckで条件を確認する
  • バックテスト結果は将来の利益を保証しない

MQL5のEA最適化では、ロジック、フィルター、資金管理、注文処理を分けて設計することが重要です。
分解された設計にすると、どの条件が成績に影響しているかを確認しやすくなります。

FAQ

MQL5の最適化手法とは何ですか?

MQL5の最適化手法とは、EAの入力パラメータを複数パターンで検証し、売買ロジックの安定性やリスクを確認する方法です。最高成績だけを選ぶ作業ではありません。

最適化ではどの指標を見るべきですか?

最適化では、総損益、最大ドローダウン、勝率、損益比、取引回数、連敗数、期間依存性を確認します。総損益だけでは、実運用でのリスクを判断しにくくなります。

MQL5でインジケータ値を使う場合の注意点は何ですか?

MQL5では、OnInitでインジケータハンドルを作成し、OnTickでCopyBufferにより値を取得する構造が多く使われます。取得件数不足やINVALID_HANDLEを必ず確認します。

最適化で過剰最適化を避けるにはどうすればよいですか?

過剰最適化を避けるには、パラメータ数を絞り、最高成績の一点ではなく周辺値の安定性を確認します。別期間やフォワードテストで傾向が残るかも確認します。

バックテストとフォワードテストは何が違いますか?

バックテストは過去データでEAを検証する方法です。フォワードテストは、より実際に近い条件で約定差、スプレッド拡大、取引頻度、ドローダウンを確認する方法です。

最適化したパラメータはそのまま実運用できますか?

最適化したパラメータをそのまま実運用に使う判断は危険です。実運用前には、フォワードテスト、ロット制限、証拠金、スプレッド、約定条件、停止条件を確認する必要があります。

注文処理を含むEAでは何を確認すべきですか?

注文処理を含むEAでは、MqlTradeRequest、MqlTradeResult、OrderCheck、OrderSendの結果を確認します。最小ロット、最大ロット、ロットステップ、証拠金、ストップレベル、口座タイプの違いも確認します。