MQL5 EAが実運用で崩れる原因と堅牢化の実装方法

目次

この記事の結論

MQL5で堅牢なトレーディングシステムを作るには、売買シグナルだけでなく、フィルター、リスク確認、注文前チェック、ポジション管理、検証条件を分離して設計する必要があります。
EAは「条件が一致したら注文する」だけではなく、相場状態、口座状態、銘柄仕様、約定条件を順番に確認する構造にすると破綻しにくくなります。
堅牢性を高める中心は、OnInitで必要なハンドルを作成し、OnTickで判定し、CopyBufferで値を取得し、OrderCheckで注文前の妥当性を確認する流れです。
ただし、どれだけ設計を整理しても、バックテスト結果は将来の利益を保証しません。実運用前にはフォワードテストでスプレッド、約定、ブローカー仕様の差を確認する必要があります。

1. このロジックの役割

【結論】
MQL5の堅牢なトレーディングシステムは、売買判断を一つの条件に依存させず、複数の確認工程でEAの動作を制御するための設計です。
目的は利益を保証することではなく、誤作動、過剰取引、条件未確認の注文、検証不能なロジックを減らすことです。

【定義】
堅牢なトレーディングシステムとは、シグナル、フィルター、リスク管理、注文処理、ポジション管理、検証手順を分離し、状況変化に対して動作を確認しやすくしたEA設計です。

EAの堅牢性は、エントリー条件の複雑さだけでは決まりません。
単純な移動平均クロスでも、スプレッド確認、ロット制限、証拠金確認、既存ポジション確認、決済条件が整理されていれば検証しやすい構造になります。

反対に、シグナルだけを増やしたEAは、どの条件が成績に影響したのか分かりにくくなります。
堅牢な設計では、次の流れを分けて考えます。

相場認識
↓
フィルター判定
↓
シグナル判定
↓
リスク確認
↓
注文前チェック
↓
注文送信
↓
約定後管理
↓
決済・停止判定

1.1 堅牢性が必要になる場面

MQL5のEAでは、バックテストでは動いても実運用で同じように動かない場合があります。
主な理由は、スプレッド、約定方式、スリッページ、取引時間、ストップレベル、口座タイプ、銘柄仕様が環境ごとに異なるためです。

堅牢なEAは、条件が合わない場面で無理に注文しません。
注文できない理由をログに残し、次のティックで再判定できる構造にします。

1.2 ロジックとシステム設計の違い

売買ロジックは「買うか、売るか、見送るか」を決める部分です。
トレーディングシステム設計は、売買ロジックを安全に動かすための全体構造です。

MQL5のEAでは、OnTick内にすべての処理を書き込むこともできます。
しかし、処理を分けない設計では、エラー原因、成績悪化の原因、約定失敗の原因を切り分けにくくなります。

2. 基本的な考え方

【結論】
堅牢なEA設計では、シグナルより先に「取引してよい状態か」を確認します。
相場条件、口座条件、銘柄条件、既存ポジション、リスク許容度を確認してから、エントリー判定へ進む構造が基本です。

MQL5のEAは、主にOnInit、OnTick、OnDeinitを使って動きます。
OnInitでは初期化、OnTickではティックごとの判定、OnDeinitでは終了時の後処理を行います。

インジケータ値を使う場合、MQL5では多くのインジケータ関数が値を直接返すのではなく、ハンドルを返します。
EAはOnInitでハンドルを作成し、OnTickでCopyBufferを使って値を取得します。

2.1 先に取引可能性を判定する

堅牢なEAでは、売買シグナルを確認する前に次の項目を確認します。

  • 自動売買が許可されているか
  • 銘柄が取引可能か
  • スプレッドが許容範囲内か
  • 既存ポジションがルールに反していないか
  • 証拠金が不足していないか
  • 最小ロット、最大ロット、ロットステップに合っているか
  • ストップレベルとフリーズレベルに抵触していないか
  • 取引時間外ではないか

この確認を先に行うと、シグナルが出ても注文すべきでない場面を除外しやすくなります。

2.2 シグナルとフィルターを分離する

シグナルは、エントリー方向やタイミングを決める条件です。
フィルターは、シグナルを採用するか見送るかを決める条件です。

例えば、移動平均クロスをシグナルに使い、ATRで低ボラティリティ相場を除外し、上位足の移動平均で方向を確認する設計があります。
この場合、各条件を分けてログに残すと、どの条件が取引を止めたのか確認しやすくなります。

3. 代表的な設計パターン

【結論】
堅牢なトレーディングシステムでは、単一シグナル型よりも、シグナル、フィルター、リスク制御、注文前チェックを段階化した設計が扱いやすくなります。
ただし、条件を増やしすぎると過剰最適化が起きやすくなります。

代表的な設計パターンは次のとおりです。

方法メリットデメリット向いている場面
単一シグナル型実装が簡単で検証しやすい相場環境の変化に弱い初期検証、学習用EA
シグナル+トレンドフィルター型方向性を確認しやすいレンジ相場で機会損失が増える場合があるトレンドフォロー型EA
シグナル+ボラティリティフィルター型低変動や急変動を除外しやすいATRなどの閾値依存が強くなるブレイクアウト、損切り幅調整
状態管理型ポジション状態や停止条件を扱いやすい実装量が増える実運用を想定したEA
リスク制御優先型ドローダウン管理を組み込みやすい取引機会が減る場合がある資金管理重視のEA

3.1 単一シグナル型

単一シグナル型は、移動平均クロスやRSI条件など、少数の条件で売買を判断する構造です。
学習や初期検証には向いていますが、スプレッド拡大やレンジ相場を十分に避けられない場合があります。

単一シグナル型を使う場合でも、注文前チェックとロット制限は省略しない方が安全です。

3.2 状態管理型

状態管理型では、EAの状態を明確に分けます。
例えば、待機中、買い保有中、売り保有中、取引停止中、エラー後の再確認中といった状態を使います。

状態を分けると、同じティックで複数回注文する問題や、決済直後に意図せず再エントリーする問題を減らしやすくなります。

4. 実装方法

【結論】
MQL5で堅牢なEAを実装する場合は、OnInitでインジケータハンドルを作り、OnTickで値取得と判定を行い、注文前にOrderCheckで妥当性を確認する流れにします。
処理を関数に分けると、原因調査と検証がしやすくなります。

実装の最小単位は、次のように分けます。

  • UpdateMarketData():インジケータ値や価格情報を更新する
  • IsTradingAllowedNow():取引可能性を確認する
  • CheckFilters():フィルター条件を判定する
  • CheckSignal():売買シグナルを判定する
  • CalculateLot():ロットを計算する
  • SendCheckedOrder():OrderCheck後に注文する
  • ManagePosition():保有中ポジションを管理する

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

MQL5では、移動平均線やATRなどのインジケータをEAで使う場合、ハンドルを作成してからCopyBufferで値を取得する流れになります。
ハンドルがINVALID_HANDLEの場合、EAは初期化失敗として止める方が原因を把握しやすくなります。

4.2 確定足で判定する

最新足は形成中のため、ティックごとに値が変わります。
シグナルを安定させたい場合は、配列のインデックス1を使い、確定足の値で判定します。

最新足を使う設計もありますが、バックテストと実運用で挙動が変わりやすくなります。
初心者から中級者向けのEAでは、まず確定足で判定する構造が扱いやすいです。

MQL5 robust EA execution flow with OnInit, CopyBuffer, OrderCheck, risk control, and OrderSend validation

5. サンプルコード

【結論】
次のコードは、移動平均線とATRを使ってフィルターを分離し、注文前にOrderCheckを行うMQL5 EAの検証用サンプルです。
実運用では、銘柄仕様、口座タイプ、約定方式、損切り幅、ログ設計を追加で検証する必要があります。

#property strict

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

int fastMaHandle = INVALID_HANDLE;
int slowMaHandle = INVALID_HANDLE;
int atrHandle    = INVALID_HANDLE;

enum SignalType
{
   SIGNAL_NONE = 0,
   SIGNAL_BUY  = 1,
   SIGNAL_SELL = -1
};

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

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

   return INIT_SUCCEEDED;
}

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

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

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

void OnTick()
{
   if(!IsTradingAllowedNow())
      return;

   if(PositionSelect(_Symbol))
   {
      ManagePosition();
      return;
   }

   SignalType signal = CheckSignal();

   if(signal == SIGNAL_NONE)
      return;

   double lot = CalculateLot(StopLossPoints);

   if(lot <= 0.0)
   {
      Print("Lot calculation failed");
      return;
   }

   SendCheckedOrder(signal, lot);
}

bool IsTradingAllowedNow()
{
   long tradeMode = 0;

   if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE, tradeMode))
   {
      Print("Failed to get symbol trade mode");
      return false;
   }

   if(tradeMode == SYMBOL_TRADE_MODE_DISABLED)
   {
      Print("Trading is disabled for this symbol");
      return false;
   }

   MqlTick tick;

   if(!SymbolInfoTick(_Symbol, tick))
   {
      Print("Failed to get current tick");
      return false;
   }

   int spreadPoints = (int)MathRound((tick.ask - tick.bid) / _Point);

   if(spreadPoints > MaxSpreadPoints)
   {
      Print("Spread is too wide: ", spreadPoints);
      return false;
   }

   return true;
}

SignalType CheckSignal()
{
   double fastMa[];
   double slowMa[];
   double atr[];

   ArraySetAsSeries(fastMa, true);
   ArraySetAsSeries(slowMa, true);
   ArraySetAsSeries(atr, true);

   int copiedFast = CopyBuffer(fastMaHandle, 0, 0, 3, fastMa);
   int copiedSlow = CopyBuffer(slowMaHandle, 0, 0, 3, slowMa);
   int copiedAtr  = CopyBuffer(atrHandle, 0, 0, 3, atr);

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

   double minAtr = 50 * _Point;

   int closedBar = 1;
   int previousClosedBar = 2;

   if(atr[closedBar] < minAtr)
      return SIGNAL_NONE;

   bool bullishCross = fastMa[previousClosedBar] <= slowMa[previousClosedBar] && fastMa[closedBar] > slowMa[closedBar];
   bool bearishCross = fastMa[previousClosedBar] >= slowMa[previousClosedBar] && fastMa[closedBar] < slowMa[closedBar];

   if(bullishCross)
      return SIGNAL_BUY;

   if(bearishCross)
      return SIGNAL_SELL;

   return SIGNAL_NONE;
}

double CalculateLot(int stopLossPoints)
{
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double riskMoney = equity * RiskPercent / 100.0;

   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double minLot    = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot    = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   if(equity <= 0.0 || riskMoney <= 0.0 || tickValue <= 0.0 || tickSize <= 0.0 || lotStep <= 0.0)
      return 0.0;

   double lossPerLot = (stopLossPoints * _Point / tickSize) * tickValue;

   if(lossPerLot <= 0.0)
      return 0.0;

   double rawLot = riskMoney / lossPerLot;
   double steppedLot = MathFloor(rawLot / lotStep) * lotStep;
   double normalizedLot = MathMax(minLot, MathMin(maxLot, steppedLot));

   return NormalizeDouble(normalizedLot, 2);
}

void SendCheckedOrder(SignalType signal, double lot)
{
   MqlTick tick;

   if(!SymbolInfoTick(_Symbol, tick))
   {
      Print("Failed to get current tick before order");
      return;
   }

   MqlTradeRequest request;
   MqlTradeCheckResult check;
   MqlTradeResult result;

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

   request.action = TRADE_ACTION_DEAL;
   request.symbol = _Symbol;
   request.volume = lot;
   request.deviation = 20;
   request.type_filling = ORDER_FILLING_FOK;

   if(signal == SIGNAL_BUY)
   {
      request.type = ORDER_TYPE_BUY;
      request.price = tick.ask;
      request.sl = NormalizeDouble(tick.ask - StopLossPoints * _Point, _Digits);
      request.tp = NormalizeDouble(tick.ask + TakeProfitPoints * _Point, _Digits);
   }
   else if(signal == SIGNAL_SELL)
   {
      request.type = ORDER_TYPE_SELL;
      request.price = tick.bid;
      request.sl = NormalizeDouble(tick.bid + StopLossPoints * _Point, _Digits);
      request.tp = NormalizeDouble(tick.bid - TakeProfitPoints * _Point, _Digits);
   }
   else
   {
      return;
   }

   if(!OrderCheck(request, check))
   {
      Print("OrderCheck call failed");
      return;
   }

   if(check.retcode != TRADE_RETCODE_DONE)
   {
      Print("OrderCheck rejected request. Retcode: ", check.retcode, " Comment: ", check.comment);
      return;
   }

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

   if(result.retcode != TRADE_RETCODE_DONE)
   {
      Print("Order was not completed. Retcode: ", result.retcode);
      return;
   }

   Print("Order completed. Ticket: ", result.order);
}

void ManagePosition()
{
   if(!PositionSelect(_Symbol))
      return;

   double profit = PositionGetDouble(POSITION_PROFIT);

   if(profit < 0.0)
   {
      Print("Position is currently in drawdown: ", profit);
   }
}

5.1 コードの確認ポイント

このサンプルは、MQL5の基本構造に合わせてOnInit、OnTick、OnDeinitを分けています。
インジケータ値はCopyBufferで取得し、取得件数が不足している場合は売買判定を止めます。

注文前にはOrderCheckを使い、証拠金や取引条件の問題を事前に確認します。
OrderSendの結果もretcodeで確認し、注文が完了しなかった場合はログに残します。

5.2 実装時に調整すべき点

ORDER_FILLING_FOK は、ブローカーや銘柄の約定方式に合わない場合があります。
実運用前には、銘柄ごとの約定方式を確認し、必要に応じて注文条件を変更します。

netting口座では同一銘柄のポジションが統合されます。
hedging口座では同一銘柄に複数ポジションを持てるため、PositionSelectだけでは管理が不足する場合があります。

6. パターン別比較

【結論】
堅牢なMQL5 EAでは、シグナルの精度だけでなく、リスク制御、注文前チェック、検証しやすさを比較して設計を選びます。
実運用を想定するほど、単純な売買条件よりも停止条件とログ設計が重要になります。

設計要素メリットデメリット向いている場面過剰最適化リスク
移動平均シグナル理解しやすく実装しやすいレンジでだましが出やすい初期設計、トレンド判定
ATRフィルターボラティリティを反映できる閾値の設定に依存しやすい低変動回避、損切り幅調整
上位足フィルター大きな方向に合わせやすいエントリーが遅れやすいトレンドフォロー
リスク率ロット損切り幅と許容損失を結び付けやすいティックバリューや銘柄仕様の確認が必要資金管理重視低から中
注文前チェック約定前の不備を検出しやすい実装量が増える実運用前提のEA
状態管理誤発注や重複処理を抑えやすい設計が複雑になる複数条件EA、運用EA低から中

6.1 条件を増やすほどよいとは限らない

フィルターを増やすと、バックテスト上の成績が改善する場合があります。
しかし、条件が多すぎるEAは、特定期間にだけ合った過剰最適化になりやすいです。

堅牢性を高めるための条件追加と、過去データに合わせすぎる調整は区別する必要があります。
条件を追加した場合は、取引回数、期間依存性、パラメータ依存性を確認します。

7. 誤作動しやすい場面

【結論】
MQL5 EAは、インジケータ値の取得失敗、形成中の足での判定、スプレッド拡大、口座タイプ差、約定方式の不一致で誤作動しやすくなります。
堅牢な設計では、これらを事前条件として確認し、条件を満たさない場合は注文を見送ります。

7.1 CopyBufferの取得失敗

CopyBufferは、必要なバー数が計算されていない場合や、ハンドル作成に失敗している場合に期待件数を返さないことがあります。
取得件数が不足したまま配列を読むと、誤ったシグナル判定につながります。

インジケータ値を使う処理では、取得件数を確認し、不足している場合はそのティックの売買判定を止めます。

7.2 最新足でのシグナル変動

インデックス0の値は形成中の足です。
最新足を使うと、ティックごとにシグナルが出たり消えたりする場合があります。

確定足を使う設計では、インデックス1と2を使ってクロスや条件成立を判定します。
この方法はエントリーが遅れる場合がありますが、シグナルの再現性を確認しやすくなります。

7.3 スプレッドと約定条件の変化

スプレッドが広がると、同じシグナルでも期待した価格で約定しにくくなります。
約定遅延やスリッページが発生すると、バックテストより不利な価格で取引される場合があります。

実運用では、重要指標発表前後、流動性が低い時間帯、週明けや週末付近の挙動に注意します。

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

【結論】
バックテストでは、総損益だけでなく、最大ドローダウン、取引回数、損益比、連敗数、スプレッド条件、期間依存性、パラメータ依存性を確認します。
成績がよく見える設定でも、取引回数が少なすぎる場合や特定期間に偏る場合は再現性を判断しにくくなります。

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

確認項目見る理由注意点
総損益全体の結果を把握する単独では判断しない
最大ドローダウン資金減少の大きさを見る許容できる水準を事前に決める
勝率取引の勝ち負けの傾向を見る損益比とセットで見る
損益比平均利益と平均損失の関係を見る勝率だけでは判断できない
取引回数検証量を確認する少なすぎると偶然の影響が大きい
連敗数心理的・資金的な耐性を見るロット設計と合わせて確認する
スプレッド条件実運用との差を確認する固定スプレッドだけに依存しない
期間依存性特定相場への偏りを見る複数期間で確認する
パラメータ依存性過剰最適化を確認する少し変えただけで崩れる設定に注意する

8.1 最適化結果を過信しない

最適化で見つかったパラメータは、過去データに合っているだけの場合があります。
特定の期間だけで良い結果になる設定は、フォワードテストで崩れやすくなります。

パラメータは一点だけを見るのではなく、周辺値でも極端に悪化しないか確認します。

8.2 取引回数と期間を分けて見る

取引回数が少ないEAは、数回の大きな利益で成績がよく見える場合があります。
堅牢性を確認するには、複数の相場局面で取引が発生しているかを見る必要があります。

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

【結論】
フォワードテストでは、バックテストと実際の約定条件の差を確認します。
約定差、スプレッド拡大、取引頻度、ドローダウン、ブローカー差、VPS環境での安定性を重点的に見ます。

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

  • 約定価格が想定より不利になっていないか
  • スプレッド拡大時に取引を見送れているか
  • バックテストと取引頻度が大きく違わないか
  • ドローダウンが想定範囲に収まっているか
  • ブローカーや口座タイプで挙動が変わらないか
  • VPS環境でEAが安定して稼働しているか
  • ログから見送り理由や注文失敗理由を追跡できるか

9.1 デモ口座とリアル口座の違い

デモ口座とリアル口座では、約定条件やスリッページが異なる場合があります。
デモ口座で安定していても、リアル口座で同じ結果になるとは限りません。

実運用前には、少額または低リスク設定で挙動を確認する段階を設けることが重要です。

9.2 ログを残して原因を追えるようにする

フォワードテストでは、取引した理由だけでなく、取引しなかった理由も重要です。
スプレッド超過、CopyBuffer失敗、OrderCheck拒否、既存ポジションあり、取引時間外などをログに残すと、改善点を見つけやすくなります。

10. 実運用での注意点

【結論】
実運用では、バックテストと同じ条件でEAが動くとは限りません。
スプレッド、約定、レバレッジ、証拠金、銘柄仕様、口座タイプ、VPS環境を確認し、想定外のドローダウンに備える必要があります。

EAの堅牢性は、利益の大きさではなく、想定外の場面で止まれるか、見送れるか、原因を追えるかで評価します。

10.1 レバレッジとドローダウン

レバレッジが高いほど、少ない証拠金で大きなポジションを持てます。
一方で、価格変動に対する損益の変化も大きくなり、ドローダウンが拡大しやすくなります。

ロット計算では、口座残高だけでなく、有効証拠金、損切り幅、銘柄のティックバリュー、ティックサイズを確認します。

10.2 ブローカー仕様の差

ブローカーや銘柄によって、最小ロット、最大ロット、ロットステップ、ストップレベル、フリーズレベル、約定方式、取引時間が異なります。
EAはこれらを固定値として扱うのではなく、SymbolInfo系の関数で取得して確認する設計が望ましいです。

10.3 口座タイプの差

netting口座では、同一銘柄のポジションは一つに統合されます。
hedging口座では、同一銘柄で複数ポジションを持てます。

ポジション管理を設計する場合、口座タイプによって「既存ポジションあり」の意味が変わります。
複数ポジションを扱うEAでは、チケット単位の管理が必要になる場合があります。

11. 改善案と代替手段

【結論】
堅牢性を高める改善は、シグナルを増やすことだけではありません。
停止条件、ロット制御、ログ出力、注文前チェック、検証手順を改善すると、EAの挙動を管理しやすくなります。

改善案は次のとおりです。

  • ATRに応じて損切り幅を調整する
  • 最大連敗数に応じて一時停止する
  • 1日の最大損失額を超えたら取引を止める
  • スプレッドが広い時間帯を除外する
  • 重要イベント前後の取引を避ける設計にする
  • シグナル、フィルター、注文処理のログを分ける
  • 最適化範囲を広げすぎない

11.1 ロット計算方式の比較

方式メリットデメリット向いている場面
固定ロット実装が簡単資金変動に対応しにくい初期検証、少額テスト
残高比例資金に応じて調整しやすい損切り幅を反映しにくい長期検証
リスク率ベース許容損失と損切り幅を結び付けやすい銘柄仕様の取得が必要実運用を想定したEA
ボラティリティ調整相場変動に合わせやすいATRなどの設定に依存する変動幅が大きい銘柄

11.2 代替手段としての半自動運用

完全自動化が難しい場合、EAをシグナル通知や注文前チェックに使い、最終判断を人が行う設計もあります。
半自動運用は、技術的な誤作動を減らしやすい一方で、判断の一貫性が課題になります。

12. まとめ

【結論】
MQL5で堅牢なトレーディングシステムを作るには、売買シグナル、フィルター、リスク管理、注文前チェック、ポジション管理、検証手順を分けて設計する必要があります。
特に、CopyBufferの取得確認、OrderCheckによる注文前確認、ロット制限、口座タイプ差、スプレッドと約定条件の確認は重要です。

堅牢なEAは、単に多くの条件を持つEAではありません。
条件が成立しない場面で見送り、注文できない理由を記録し、バックテストとフォワードテストで再現性を確認できるEAです。

バックテスト結果は将来の利益を保証しません。
実運用では、フォワードテスト、ブローカー条件の確認、ドローダウン許容度の設定、VPS環境の安定性確認が必要です。

FAQ

MQL5で堅牢なトレーディングシステムとは何ですか?

MQL5で堅牢なトレーディングシステムとは、売買シグナルだけでなく、フィルター、リスク管理、注文前チェック、ポジション管理、検証手順を分離したEA設計です。想定外の環境でも原因を追いやすくすることが目的です。

EAを堅牢にするために最初に分けるべき処理は何ですか?

最初に分けるべき処理は、シグナル判定、フィルター判定、ロット計算、注文前チェック、ポジション管理です。OnTickにすべてを書くより、役割ごとに関数化した方が検証しやすくなります。

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

MQL5では多くのインジケータ関数が値ではなくハンドルを返します。OnInitでハンドルを作成し、OnTickでCopyBufferを使い、取得件数が不足した場合は売買判定を止める必要があります。

OrderSendの前にOrderCheckを使う理由は何ですか?

OrderCheckは、注文前に証拠金や取引条件の妥当性を確認するために使います。OrderSendだけに依存すると、失敗理由の切り分けが難しくなる場合があります。

堅牢なEAにフィルターを多く入れればよいですか?

フィルターを増やせば必ず堅牢になるわけではありません。条件が多すぎると過剰最適化になりやすいため、各フィルターの目的と効果をバックテストとフォワードテストで分けて確認する必要があります。

バックテストで重視すべき項目は何ですか?

バックテストでは、総損益だけでなく、最大ドローダウン、勝率、損益比、取引回数、連敗数、スプレッド条件、期間依存性、パラメータ依存性を確認します。成績が良く見えても、取引回数が少ない場合は判断に注意が必要です。

フォワードテストで確認すべきことは何ですか?

フォワードテストでは、約定差、スプレッド拡大時の挙動、取引頻度、ドローダウン、ブローカー差、VPS環境での安定性を確認します。バックテストと実運用では条件が異なる場合があります。

netting口座とhedging口座でEA設計は変わりますか?

netting口座とhedging口座ではポジション管理の考え方が変わります。netting口座では同一銘柄のポジションが統合され、hedging口座では複数ポジションを持てるため、管理単位を設計時に分ける必要があります。