- 1 この記事の結論
- 2 1. この設計が必要な理由
- 3 2. EA全体の設計思想
- 4 3. 基本構造
- 5 4. 主要モジュールの役割
- 6 5. 実装パターン
- 7 6. サンプルコード
- 8 7. 設計パターン比較
- 9 8. バックテストで確認すべき項目
- 10 9. フォワードテストで確認すべき項目
- 11 10. 実運用での注意点
- 12 11. よくある設計ミス
- 13 12. まとめ
- 14 FAQ
- 14.1 Q1. MQL5のalgorithmic-trading-architectureとは何ですか?
- 14.2 Q2. MQL5のEAではOnTickにすべての処理を書いてもよいですか?
- 14.3 Q3. MQL5でインジケータ値を取得するときの基本は何ですか?
- 14.4 Q4. OrderSendの前にOrderCheckは必要ですか?
- 14.5 Q5. EA設計で固定ロットだけを使う問題は何ですか?
- 14.6 Q6. バックテストで最も注意すべき点は何ですか?
- 14.7 Q7. netting口座とhedging口座の違いはEA設計に影響しますか?
- 14.8 Q8. 実運用前に確認すべきリスクは何ですか?
この記事の結論
MQL5のalgorithmic-trading-architectureは、EAを単なる売買条件の集合ではなく、状態管理・リスク制御・注文前チェック・約定後管理に分けて設計する考え方です。
相場認識、フィルター判定、シグナル判定、ロット計算、注文送信、ポジション管理を分離すると、検証しやすく保守しやすいEAになります。
MQL5ではOnInit、OnTick、OnDeinitなどのイベント関数を軸に処理を配置し、インジケータ値はハンドル作成後にCopyBufferで取得する構造が基本です。
実運用では、バックテスト結果だけで判断せず、スプレッド、約定差、ブローカー仕様、ドローダウン許容度を含めてフォワードテストで確認する必要があります。
1. この設計が必要な理由
【結論】
MQL5のEA設計では、売買条件だけでなく、状態管理とリスク制御を含めた全体構造が必要です。
EAはティックごとに処理されるため、現在の相場状態、既存ポジション、注文可否、停止条件を分離して管理する必要があります。
MQL5のExpert Advisorは、主にOnTickで新しいティックを受け取って処理を進めます。
しかし、すべての判定をOnTickの中に直接書くと、条件追加や不具合調査が難しくなります。
algorithmic-trading-architectureでは、EAを次のような処理の流れとして設計します。
相場認識
↓
フィルター判定
↓
シグナル判定
↓
リスク確認
↓
注文前チェック
↓
注文送信
↓
約定後管理
↓
決済・停止判定
この構造にすると、どの条件が取引を許可し、どの条件が取引を止めたのかを追跡しやすくなります。
1.1 条件分岐だけのEAが壊れやすい理由
条件分岐だけで作られたEAは、機能が増えるほど処理の依存関係が見えにくくなります。
たとえば、トレンド判定、ロット計算、既存ポジション確認、注文送信を同じ場所に書くと、ひとつの変更が別の処理に影響しやすくなります。
MQL5のEAでは、取引状態は口座タイプや銘柄仕様によって変わります。
netting口座では同一銘柄のポジションが統合され、hedging口座では複数ポジションを持てるため、ポジション管理の前提が異なります。
1.2 設計を分けることで検証しやすくなる
EAの処理を分けると、バックテスト時に原因を切り分けやすくなります。
取引回数が少ない場合はフィルターが厳しすぎる可能性があります。
損失が大きい場合はロット計算、損切り幅、スプレッド条件、決済ロジックを個別に確認できます。
MQL5のalgorithmic-trading-architectureは、売買判断の精度を保証するものではありません。
設計を分離する目的は、検証、保守、リスク確認をしやすくすることです。
2. EA全体の設計思想
【結論】
MQL5のEAは、イベント駆動の処理を中心に、初期化、ティック処理、終了処理を分けて設計します。
OnInitで準備し、OnTickで判定し、OnDeinitで後処理を行う構成が基本になります。
MQL5のEA設計では、イベント関数ごとに役割を分けることが重要です。
EAは常に連続実行される単一の処理ではなく、ティック、タイマー、取引イベントなどを受けて処理されます。
| イベント関数 | 主な役割 | EA設計での使いどころ |
|---|---|---|
| OnInit | 初期化処理 | インジケータハンドル作成、入力値検証、初期状態設定 |
| OnTick | ティック受信時の処理 | シグナル判定、注文前チェック、ポジション管理 |
| OnDeinit | 終了時の後処理 | ハンドル解放、ログ出力、終了処理 |
| OnTimer | タイマー処理 | 定期監視、時間ベースの状態更新 |
| OnTradeTransaction | 取引詳細イベント処理 | 注文、約定、ポジション変化の追跡 |

2.1 OnInitで準備する処理
OnInitでは、EAが動作するために必要な準備を行います。
移動平均線、ATR、RSIなどのインジケータを使う場合、MQL5では多くの場合、関数が値を直接返すのではなくインジケータハンドルを返します。
ハンドル作成後は、INVALID_HANDLEでないか確認します。
初期化に失敗した状態でOnTickの処理を続けると、CopyBufferが失敗し、売買判定が不安定になります。
2.2 OnTickで実行する処理
OnTickでは、新しいティックを受け取るたびにEAの主要処理を実行します。
ただし、すべてのティックで注文処理を行う必要はありません。
実用的なEAでは、次のような順番で処理します。
- 取引可能状態を確認する
- 必要なインジケータ値を取得する
- 既存ポジションを確認する
- フィルター条件を判定する
- シグナル条件を判定する
- ロットとリスクを確認する
- OrderCheckで注文前チェックを行う
- OrderSendで注文を送信する
- 結果をログに残す
2.3 OnDeinitで後処理する理由
OnDeinitでは、EA終了時の後処理を行います。
インジケータハンドルを作成した場合は、必要に応じてIndicatorReleaseで解放します。
終了処理を明確にしておくと、チャート切り替え、EA停止、パラメータ変更時の状態を管理しやすくなります。
3. 基本構造
【結論】
MQL5のEA基本構造は、初期化、ティック処理、終了処理を分離し、各処理を関数化して組み立てます。
OnTickの中に全処理を直接書かず、役割ごとの関数へ分けることで保守しやすくなります。
基本構造は次のようになります。
#property strict
int maHandle = INVALID_HANDLE;
int OnInit()
{
maHandle = iMA(_Symbol, _Period, 20, 0, MODE_SMA, PRICE_CLOSE);
if(maHandle == INVALID_HANDLE)
{
Print("Failed to create MA handle");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(maHandle != INVALID_HANDLE)
{
IndicatorRelease(maHandle);
maHandle = INVALID_HANDLE;
}
}
void OnTick()
{
if(!IsTradingEnvironmentReady())
return;
if(!UpdateMarketData())
return;
if(HasOpenPosition())
{
ManageOpenPosition();
return;
}
if(!PassesFilters())
return;
if(!HasEntrySignal())
return;
ExecuteEntry();
}
このコードは設計の骨格を示す検証用サンプルです。
実運用では、銘柄仕様、口座タイプ、ストップレベル、スプレッド、証拠金、取引時間を追加で確認する必要があります。
3.1 状態を関数で分ける
関数を分ける目的は、処理の責任を明確にすることです。
たとえば、PassesFiltersは相場条件を判定し、HasEntrySignalは売買シグナルだけを判定します。
この分離により、フィルターの不具合とシグナルの不具合を別々に確認できます。
3.2 戻り値で処理継続可否を表す
MQL5のEAでは、判定関数の戻り値をboolにすると処理の流れが読みやすくなります。
処理を続けられない場合はfalseを返し、OnTick側でreturnします。
エラー時に何も記録しない設計は避けるべきです。
CopyBuffer失敗、注文前チェック失敗、OrderSend失敗などはログに残すことで、バックテストと実運用の差を調査しやすくなります。
4. 主要モジュールの役割
【結論】
EAの主要モジュールは、相場認識、フィルター、シグナル、ロット計算、注文前チェック、注文実行、ポジション管理、リスク制御に分けます。
各モジュールを分離すると、検証対象が明確になり、過剰最適化を避けやすくなります。
| モジュール | 役割 | 主な確認項目 |
|---|---|---|
| 相場認識 | トレンド、レンジ、ボラティリティを判定する | MA、ATR、ADX、上位足 |
| フィルター判定 | 取引してよい環境かを判定する | スプレッド、時間帯、ボラティリティ |
| シグナル判定 | エントリー条件を判定する | クロス、ブレイク、反転条件 |
| ロット計算 | 許容リスクに応じて数量を決める | 最小ロット、最大ロット、ロットステップ |
| 注文前チェック | 注文が可能かを確認する | OrderCheck、証拠金、ストップレベル |
| 注文実行 | MqlTradeRequestで注文を送る | OrderSend、MqlTradeResult |
| ポジション管理 | 保有中の建玉を管理する | 決済、トレーリング、部分決済 |
| リスク制御 | 損失拡大や過剰取引を抑える | ドローダウン、連敗、日次損失 |
4.1 相場認識モジュール
相場認識モジュールは、EAが現在の市場状態を判断する部分です。
移動平均線で方向を見たり、ATRでボラティリティを見たり、上位足で大きな流れを確認したりします。
インジケータを使う場合は、OnInitでハンドルを作成し、OnTickでCopyBufferから値を取得します。
最新足は形成中で値が変わるため、確定足を使うか最新足を使うかを設計時に決める必要があります。
4.2 リスク制御モジュール
リスク制御モジュールは、取引機会を増やすためではなく、損失拡大を抑えるための仕組みです。
日次損失、最大ドローダウン、連敗数、取引回数、スプレッド拡大などを条件にして、EAを一時停止する設計が考えられます。
バックテスト結果は将来の利益を保証しません。
リスク制御は、想定外の相場変化や約定条件の悪化に備えるために設計します。
4.3 注文前チェックモジュール
注文前チェックでは、OrderSendの前に取引可能性を確認します。
MQL5では、MqlTradeRequestに注文内容を設定し、必要に応じてOrderCheckで証拠金や取引条件を確認します。
確認すべき項目は次のとおりです。
- 取引が許可されているか
- スプレッドが許容範囲内か
- ロットが最小ロット、最大ロット、ロットステップに合っているか
- ストップロスとテイクプロフィットがストップレベルを満たすか
- 必要証拠金を満たすか
- 銘柄の取引時間内か
- netting口座とhedging口座の違いを考慮しているか
5. 実装パターン
【結論】
MQL5のEA実装では、単一関数型、関数分割型、クラス分割型、状態管理型のパターンがあります。
中級者以上のEAでは、関数分割型または状態管理型が検証と拡張に向いています。
実装パターンは、EAの規模と目的に合わせて選びます。
小さな検証用EAでは単純な構造でも足りますが、実運用を想定するEAでは状態管理を明確にする必要があります。
5.1 関数分割型
関数分割型は、OnTickから複数の判定関数を呼び出す構造です。
実装難易度が高すぎず、初心者から中級者が設計を整理しやすい方法です。
bool PassesSpreadFilter()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0.0 || bid <= 0.0)
{
Print("Invalid bid or ask");
return false;
}
double spreadPoints = (ask - bid) / _Point;
int maxSpreadPoints = 30;
if(spreadPoints > maxSpreadPoints)
{
Print("Spread is too wide: ", spreadPoints);
return false;
}
return true;
}
このサンプルはスプレッドフィルターの基本形です。
実運用では、銘柄の通常スプレッド、時間帯、重要指標前後の変動を考慮して閾値を検証する必要があります。
5.2 状態管理型
状態管理型は、EAの状態を明示して処理を切り替える設計です。
たとえば、WAIT_SIGNAL、READY_TO_ORDER、POSITION_OPEN、STOPPEDのような状態を持たせます。
enum EAState
{
STATE_WAIT_SIGNAL,
STATE_READY_TO_ORDER,
STATE_POSITION_OPEN,
STATE_STOPPED
};
EAState currentState = STATE_WAIT_SIGNAL;
void UpdateState()
{
if(IsRiskLimitReached())
{
currentState = STATE_STOPPED;
return;
}
if(HasOpenPosition())
{
currentState = STATE_POSITION_OPEN;
return;
}
if(PassesFilters() && HasEntrySignal())
{
currentState = STATE_READY_TO_ORDER;
return;
}
currentState = STATE_WAIT_SIGNAL;
}
状態を明示すると、EAがなぜ注文しないのか、なぜ停止しているのかを説明しやすくなります。
複数通貨EAや複数ロジックEAでは、状態管理の有無が保守性に大きく影響します。
6. サンプルコード
【結論】
MQL5のarchitectureサンプルでは、インジケータハンドル、CopyBuffer、ロット正規化、OrderCheck、OrderSendを分けて実装します。
注文処理は検証用の例として扱い、実運用前には銘柄仕様と口座条件を確認する必要があります。
次のコードは、移動平均線を使った簡易的な設計サンプルです。
売買ロジックの優位性を示すものではなく、MQL5 EAの構造を説明するための実装例です。
#property strict
input double RiskPercent = 1.0;
input int MAPeriod = 20;
input int MaxSpreadPoints = 30;
int maHandle = INVALID_HANDLE;
int OnInit()
{
maHandle = iMA(_Symbol, _Period, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);
if(maHandle == INVALID_HANDLE)
{
Print("Failed to create MA handle");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(maHandle != INVALID_HANDLE)
{
IndicatorRelease(maHandle);
maHandle = INVALID_HANDLE;
}
}
void OnTick()
{
if(!PassesBasicChecks())
return;
if(HasOpenPosition())
return;
double maValue = 0.0;
if(!GetMAValue(maValue))
return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double closePrice = iClose(_Symbol, _Period, 1);
if(closePrice > maValue)
{
SendBuyOrder(0.10);
}
}
bool PassesBasicChecks()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0.0 || bid <= 0.0)
{
Print("Invalid market price");
return false;
}
double spreadPoints = (ask - bid) / _Point;
if(spreadPoints > MaxSpreadPoints)
{
Print("Spread filter blocked trading: ", spreadPoints);
return false;
}
long tradeMode = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED)
{
Print("Trading is disabled for this symbol");
return false;
}
return true;
}
bool GetMAValue(double &value)
{
if(BarsCalculated(maHandle) < MAPeriod)
{
Print("Not enough calculated bars");
return false;
}
double maBuffer[];
ArraySetAsSeries(maBuffer, true);
int copied = CopyBuffer(maHandle, 0, 1, 1, maBuffer);
if(copied < 1)
{
Print("CopyBuffer failed or not enough data");
return false;
}
value = maBuffer[0];
return true;
}
bool HasOpenPosition()
{
return PositionSelect(_Symbol);
}
void SendBuyOrder(double lots)
{
double normalizedLots = NormalizeLots(lots);
if(normalizedLots <= 0.0)
{
Print("Invalid lot size");
return;
}
MqlTradeRequest request;
MqlTradeResult result;
MqlTradeCheckResult check;
ZeroMemory(request);
ZeroMemory(result);
ZeroMemory(check);
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = normalizedLots;
request.type = ORDER_TYPE_BUY;
request.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = 20;
request.type_filling = ORDER_FILLING_FOK;
if(!OrderCheck(request, check))
{
Print("OrderCheck failed. Retcode: ", check.retcode);
return;
}
if(check.retcode != TRADE_RETCODE_DONE)
{
Print("OrderCheck rejected request. Retcode: ", check.retcode);
return;
}
if(!OrderSend(request, result))
{
Print("OrderSend failed. Retcode: ", result.retcode);
return;
}
if(result.retcode != TRADE_RETCODE_DONE && result.retcode != TRADE_RETCODE_PLACED)
{
Print("Order was not completed. Retcode: ", result.retcode);
return;
}
Print("Buy order sent. Ticket: ", result.order);
}
double NormalizeLots(double lots)
{
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(minLot <= 0.0 || maxLot <= 0.0 || stepLot <= 0.0)
return 0.0;
lots = MathMax(minLot, MathMin(maxLot, lots));
lots = MathFloor(lots / stepLot) * stepLot;
return NormalizeDouble(lots, 2);
}
6.1 コードで重要なポイント
このサンプルでは、OnInitで移動平均線のハンドルを作成し、OnTickで確定足の値を使って判定しています。
CopyBufferではシフト1を指定しているため、形成中の最新足ではなく、直前に確定した足の値を取得します。
OrderSendの前にOrderCheckを使うことで、証拠金や取引条件を事前に確認できます。
ただし、OrderCheckを通過しても実際の約定を保証するものではありません。
相場変動、スプレッド、約定方式、ブローカー条件により、OrderSendの結果は変わる場合があります。
6.2 ロット計算を固定値で終わらせない
サンプルでは説明を簡潔にするため固定ロットを渡しています。
実際の設計では、口座残高、有効証拠金、損切り幅、ティックバリュー、ティックサイズ、最小ロット、最大ロット、ロットステップを使ってロットを計算します。
固定ロットは実装が簡単ですが、資金変動に対応しにくい設計です。
リスク率ベースのロット計算は、損切り幅と許容損失を結びつけやすくなります。
7. 設計パターン比較
【結論】
MQL5のEA設計では、規模が大きくなるほど状態管理型やモジュール分割型が有利になります。
ただし、過度な抽象化は検証を難しくするため、EAの目的に合わせて必要な分だけ分割します。
| 方法 | メリット | デメリット | 向いている場面 | 実装難易度 |
|---|---|---|---|---|
| 単一関数型 | すぐに書ける | 条件が増えると壊れやすい | 小さな検証用EA | 低い |
| 関数分割型 | 処理を追いやすい | 関数間の状態共有に注意が必要 | 中規模EA | 中程度 |
| クラス分割型 | モジュール化しやすい | 設計が過剰になる場合がある | 複数機能を持つEA | 高い |
| 状態管理型 | EAの状態を説明しやすい | 状態遷移の設計が必要 | 実運用を想定するEA | 中から高 |
| 複数通貨管理型 | ポートフォリオ管理に向く | 銘柄ごとの状態管理が複雑 | 複数通貨EA | 高い |
7.1 中級者に向いている構成
中級者には、関数分割型を基本にし、必要な部分だけ状態管理を追加する構成が向いています。
最初から大きなクラス設計にすると、売買ロジックよりも設計そのものが複雑になりやすいためです。
EAの目的が単一ロジックの検証であれば、関数分割型で十分な場合があります。
複数ロジック、複数通貨、リスク停止、再エントリー制御を扱う場合は、状態管理型を検討します。
7.2 過剰設計を避ける考え方
設計を分ける目的は、EAを複雑にすることではありません。
検証したい要素を分離し、結果を説明しやすくすることです。
相場認識とシグナル判定を混ぜると、何が成績に影響したのか分かりにくくなります。
ロット計算とエントリー条件を混ぜると、リスク変更時に売買条件まで変わってしまう場合があります。
8. バックテストで確認すべき項目
【結論】
バックテストでは、総損益だけでなく、最大ドローダウン、取引回数、連敗数、スプレッド条件、パラメータ依存性を確認します。
成績が良く見えるEAでも、特定期間や特定パラメータだけに依存している場合は再現性が低くなりやすいです。
バックテストは、EAの構造が意図どおりに動くかを確認する工程です。
将来の利益を保証するものではありません。
確認すべき項目は次のとおりです。
| 確認項目 | 見る理由 | 注意点 |
|---|---|---|
| 総損益 | 全体の損益傾向を把握する | 単独では判断しない |
| 最大ドローダウン | 資金減少の大きさを見る | 許容範囲を事前に決める |
| 勝率 | 取引の成功割合を見る | 損益比とセットで見る |
| 損益比 | 平均利益と平均損失の関係を見る | 高勝率でも損益比が悪い場合がある |
| 取引回数 | サンプル数を確認する | 少なすぎると判断しにくい |
| 連敗数 | 資金管理への影響を見る | ロット設計と合わせて確認する |
| スプレッド条件 | 実運用との差を確認する | 固定スプレッドだけに依存しない |
| 期間依存性 | 特定相場だけに強くないか見る | 複数期間で確認する |
| パラメータ依存性 | 過剰最適化を見つける | 周辺値でも極端に崩れないか見る |
8.1 過剰最適化を確認する
過剰最適化は、過去データに合わせすぎた状態です。
特定のMA期間、ATR倍率、時間帯だけで成績が良い場合、フォワードテストで崩れやすくなります。
パラメータを少し変えただけで成績が大きく悪化する場合、ロジックの再現性を慎重に確認する必要があります。
8.2 ログで設計ミスを見つける
EAのログには、取引しなかった理由を残すと検証しやすくなります。
たとえば、スプレッド超過、フィルター不一致、OrderCheck失敗、既存ポジションあり、リスク上限到達などを記録します。
ログがないEAは、バックテストで取引回数が少ない理由を後から追跡しにくくなります。
9. フォワードテストで確認すべき項目
【結論】
フォワードテストでは、バックテストでは見えにくい約定差、スプレッド拡大、取引頻度、VPS環境での安定性を確認します。
バックテストとフォワードテストの結果が異なることは珍しくないため、差が出る前提で検証します。
フォワードテストは、EAが現在の市場環境でどのように動くかを確認する工程です。
デモ口座とリアル口座では約定条件が異なる場合があります。
確認すべき項目は次のとおりです。
- 約定価格と想定価格の差
- スプレッド拡大時の挙動
- 実際の取引頻度
- ドローダウンの出方
- バックテストとの乖離
- ブローカーごとの差
- VPS環境での稼働安定性
- 取引停止条件が意図どおり動くか
9.1 約定条件の差を確認する
バックテストでは再現しきれない要素として、約定遅延、スリッページ、スプレッド拡大があります。
特に短期売買や小さな利幅を狙うEAでは、わずかな約定差が成績に大きく影響する場合があります。
OrderSendが成功しても、想定価格で必ず約定するわけではありません。
約定方式や流動性により、注文結果は変わります。
9.2 停止条件が機能するか確認する
フォワードテストでは、損失制限や取引停止条件も確認します。
最大ドローダウン、日次損失、連敗数、スプレッド上限などが正しく動作しないと、想定以上の損失につながる可能性があります。
停止条件は、売買条件と同じくらい重要な設計要素です。
10. 実運用での注意点
【結論】
MQL5のEAを実運用する場合は、ロジックの成績だけでなく、ブローカー仕様、口座タイプ、スプレッド、約定方式、レバレッジ、VPS安定性を確認します。
EA設計は損失をなくす仕組みではなく、リスクを把握しやすくするための構造です。
実運用では、バックテストやデモ口座では見えにくい差が発生します。
スプレッド拡大、約定拒否、取引時間外、ストップレベル変更、証拠金不足などが起こる場合があります。
10.1 口座タイプの違い
netting口座では、同一銘柄のポジションがひとつに統合されます。
hedging口座では、同一銘柄で複数ポジションを持てる場合があります。
PositionSelectだけで設計する場合、口座タイプによって意図と異なる管理になる可能性があります。
複数ポジションを扱うEAでは、チケット単位の管理やマジックナンバーの管理も検討します。
10.2 銘柄仕様の違い
銘柄ごとに最小ロット、最大ロット、ロットステップ、ティックサイズ、ティックバリュー、ストップレベル、フリーズレベルが異なります。
ロット計算や損切り幅を固定的に扱うと、別の銘柄で注文が失敗する場合があります。
EAを複数銘柄で使う場合は、SymbolInfoDoubleやSymbolInfoIntegerで銘柄仕様を取得してから判定します。
10.3 レバレッジとドローダウン
レバレッジが高いほど、少ない証拠金で大きなポジションを持てます。
同時に、価格変動による損益の変化も大きくなりやすく、ドローダウンが拡大する可能性があります。
実運用では、最大ドローダウンの許容範囲を事前に決め、EA側で取引停止条件を設けることが重要です。
11. よくある設計ミス
【結論】
よくある設計ミスは、OnTickに全処理を書くこと、CopyBufferの失敗を無視すること、OrderSend前の確認を省略すること、ロット制限を考慮しないことです。
これらのミスは、バックテストでは気づきにくく、実運用で不安定な動作につながる場合があります。
11.1 最新足と確定足を混同する
最新足は形成中の足であり、ティックごとに値が変わります。
確定足はすでに閉じた足であり、値が固定されています。
CopyBufferでシフト0を使うと最新足を取得します。
シフト1を使うと直前の確定足を取得します。
どちらを使うかはロジックの前提として明確にする必要があります。
11.2 CopyBufferの戻り値を確認しない
CopyBufferは、要求した件数を常に取得できるとは限りません。
取得件数が不足している場合に処理を続けると、未初期化の値や古い値で判定する可能性があります。
取得件数が期待値未満の場合は、注文判定へ進まずログを残してreturnする設計が安全です。
11.3 OrderSendだけで注文を判断する
OrderSendだけに依存すると、注文前に確認できる問題を見落としやすくなります。
OrderCheckを使うことで、証拠金、取引条件、リクエスト内容の問題を事前に確認できます。
ただし、OrderCheckの通過は約定の保証ではありません。
注文送信時点の価格変動やブローカー条件により、結果は変わる場合があります。
11.4 ロット制限を無視する
ロット計算では、最小ロット、最大ロット、ロットステップを必ず考慮します。
計算上のロットが銘柄仕様に合わない場合、注文は失敗する可能性があります。
リスク率ベースのロット計算では、損切り幅、ティックバリュー、ティックサイズ、有効証拠金も確認します。
12. まとめ
【結論】
MQL5のalgorithmic-trading-architectureでは、EAを相場認識、フィルター、シグナル、リスク管理、注文前チェック、注文実行、ポジション管理に分けて設計します。
設計を分けることで、検証しやすく、原因を追跡しやすく、実運用リスクを管理しやすいEAになります。
MQL5では、OnInitで準備し、OnTickで判定し、OnDeinitで後処理する構造が基本です。
インジケータ値を使う場合は、ハンドルを作成し、CopyBufferで値を取得し、取得失敗時の処理を必ず入れます。
注文処理では、MqlTradeRequest、MqlTradeResult、MqlTradeCheckResultを使い、OrderSend前にOrderCheckで注文内容を確認します。
実運用では、バックテストだけでなくフォワードテストを行い、スプレッド、約定差、ブローカー仕様、口座タイプ、ドローダウンを確認する必要があります。
FAQ
Q1. MQL5のalgorithmic-trading-architectureとは何ですか?
MQL5のalgorithmic-trading-architectureとは、EAを売買条件だけで作るのではなく、状態管理、リスク制御、注文前チェック、ポジション管理に分けて設計する考え方です。検証しやすく、実運用時の問題を追跡しやすくなります。
Q2. MQL5のEAではOnTickにすべての処理を書いてもよいですか?
小さな検証用EAなら可能ですが、実用的なEAでは処理を関数やモジュールに分ける方が保守しやすくなります。OnTickには処理の流れを置き、詳細な判定は別関数に分ける構成が扱いやすいです。
Q3. MQL5でインジケータ値を取得するときの基本は何ですか?
MQL5では、OnInitでインジケータハンドルを作成し、OnTickでCopyBufferを使って値を取得する構造が基本です。取得件数が不足した場合は、売買判定に進まず処理を止める必要があります。
Q4. OrderSendの前にOrderCheckは必要ですか?
OrderSendの前にOrderCheckを使うと、証拠金や取引条件に関する問題を事前に確認しやすくなります。ただし、OrderCheckを通過しても実際の約定を保証するものではありません。
Q5. EA設計で固定ロットだけを使う問題は何ですか?
固定ロットは実装しやすい一方で、口座残高や損切り幅の変化に対応しにくい方式です。実運用を想定する場合は、最小ロット、最大ロット、ロットステップ、許容リスク、損切り幅を考慮した設計が必要です。
Q6. バックテストで最も注意すべき点は何ですか?
バックテストでは総損益だけでなく、最大ドローダウン、取引回数、連敗数、スプレッド条件、パラメータ依存性を確認します。バックテスト結果は将来の利益を保証しないため、フォワードテストも必要です。
Q7. netting口座とhedging口座の違いはEA設計に影響しますか?
netting口座では同一銘柄のポジションが統合され、hedging口座では複数ポジションを持てる場合があります。ポジション管理、決済管理、マジックナンバー管理の設計に影響します。
Q8. 実運用前に確認すべきリスクは何ですか?
実運用前には、スプレッド拡大、約定遅延、スリッページ、ブローカー仕様、レバレッジ、最大ドローダウン、VPS環境の安定性を確認します。デモ口座とリアル口座では約定条件が異なる場合があります。