MQL4からMQL5への変換方法|コード対照表と段階的移植手順を解説
最終更新: 2026年07月
MQL4からMQL5への変換方法(要点):
Ask/Bid等のグローバル変数をSymbolInfoDouble()に、インジケータ呼び出しをハンドル方式(iMA()+CopyBuffer())に、OrderSend()をCTradeクラスに置き換え、ResultRetcode()による約定確認を追加するのがMQL4→MQL5変換の核心手順です。
MQL4で動いていたEAをMQL5に移植しようとして、いきなりコンパイルエラーの嵐に遭遇した経験は多いはずだ。OP_BUYが見つからない、OrderSendの引数が合わない、RefreshRatesは廃止された——そうした個々の書き換えは調べれば分かるものの、「なぜそう変わったのか」という設計思想を理解しないまま作業を進めると、同じミスを繰り返す羽目になる。
本記事では関数の1対1置換にとどまらず、MQL5がなぜその設計を選んだかという理由まで含めて解説する。特に日本語の解説記事で手薄になりがちな3点——CopyBuffer err=4806の根本原因、ResultRetcode()による約定確認の必須パターン、ネッティング/ヘッジ口座の動作差異——に重点を置いた。コード量は多いが、一通り読めば変換作業の全体像が見えるはずだ。

MQL4とMQL5の根本的な設計思想の違い
MQL4は「シングルスレッド・シングルEA」を前提とした設計だ。一つのチャートで一つのEAが動き、グローバル変数(Ask、Bid、Pointなど)に常に最新レートが入っている。この単純さが初学者に優しい反面、複数ストラテジーの並列実行や大規模最適化には構造的に耐えられない問題を抱えていた。
MQL5が解決しようとしたのは、まさにその並列処理の問題だ。ストラテジーテスターで数万のパスを最適化する際、スレッドセーフでないグローバル変数は致命的な競合状態を引き起こす。だからMQL5ではグローバル変数を排除し、「関数呼び出しのたびに型安全なAPIから値を取得する」設計に統一した。
直感的には「Askと書けば現在値が入っているほうが楽」に感じる。しかし並列最適化の文脈では、SymbolInfoDouble(_Symbol, SYMBOL_ASK)という明示的な呼び出しのほうが、スレッドごとに正しい値を返せるという設計上の理由がある。コードは冗長になるが、それは意図された冗長化だ。
もう一つの大きな変更がインジケータのハンドル方式だ。MQL4ではiMA()を呼ぶたびに内部で計算が走る設計だった。MQL5のハンドル方式では、OnInit()でインジケータを一度登録(ハンドル取得)し、OnTick()では計算済みバッファから値をコピーするだけに処理を分離した。この分離により、計算の二重実行を防ぐだけでなく、複数のEAが同一インジケータのキャッシュを共有できるようになった。計算コストが高いインジケータほど、この恩恵は大きい。
この二点——「型安全なAPIアクセス」と「ハンドル方式によるインジケータキャッシュ」——がMQL5の設計思想の核心であり、変換作業で詰まる原因の大半はここに起因する。
MQL4/MQL5両言語のコード構造や言語仕様の基礎については、MQL4/MQL5プログラミング入門:EAコーディングの基本も合わせて参照されたい。
自動変換ツールの限界と手動変換が必要な理由
MetaEditorには「MQL4コードをMQL5に変換する」補助機能が存在する。しかし実際に試した人の大半が、変換後をそのままでは動かせないと気づく。
理由は明快だ。自動変換ツールが扱えるのは構文レベルの置換だけであり、意味論的な変換は行えない。OrderSend()をCTrade::Buy()に置換するだけなら自動化できる。しかし「MQL4では戻り値のチケット番号でエラーを判定していたが、MQL5ではResultRetcode()で確認が必要」という文脈の差は、静的解析では検出し切れない。サーバー受付を示すtrueと、約定完了を示すTRADE_RETCODE_DONEを混同したままのコードは、コンパイルが通ってもリアル口座で誤動作する。
ネッティング口座とヘッジ口座の差異も自動変換では対処できない。MQL4はヘッジ口座(複数ポジション同時保有)を前提とした設計だが、MQL5は両方の口座タイプに対応しており、ナンピン系・両建て系のEAはネッティング口座では動作が変わる。この判定ロジックは人間が明示的に実装する必要がある。
手動変換は面倒に感じるかもしれない。しかしEAのロジックを一行ずつ確認する作業は、コードの意図を再理解する好機でもある。変換を機に、元のMQL4コードに潜んでいたバグを発見したケースは珍しくない。
Claudeと会話しながらMQL5コードを生成・デバッグできる hedgrow-fx はこちら。手動変換の手戻りを減らしたいEA開発者に使われている。
イベントハンドラの対応表と書き換え方法
MQL4ではinit()・start()・deinit()という3関数がEAの骨格を成していた。MQL5では目的ごとに分離された明示的なハンドラ群に置き換わる。
| MQL4 | MQL5 | 備考 |
|---|---|---|
init() | OnInit() | INIT_SUCCEEDED / INIT_FAILED を返す必要あり |
deinit() | OnDeinit(const int reason) | 理由コードを引数で受け取る |
start()(EA) | OnTick() | 新規ティック受信のたびに呼ばれる |
start()(インジケータ) | OnCalculate() | 計算専用 |
start()(スクリプト) | OnStart() | 一度だけ実行 |
| なし | OnTimer() | タイマーイベント(MQL5追加) |
| なし | OnTrade() | 注文・ポジション変化イベント(MQL5追加) |
| なし | OnChartEvent() | キーボード・マウス入力イベント(MQL5追加) |
特に注意すべきはOnInit()の戻り値だ。MQL4のinit()はintを返していたが、MQL5のOnInit()はINIT_SUCCEEDED(=0)またはINIT_FAILED(=1)を返す。INIT_FAILEDを返したEAはその場で停止する。インジケータのハンドル取得が失敗した場合にINIT_FAILEDを返す処理を入れていないと、初期化に失敗したままOnTick()が走り続けるという危険な挙動になる。この確認を怠ったEAがINVALID_HANDLEに対してCopyBuffer()を呼び続け、毎ティックエラーを垂れ流すケースを筆者は何度も目にしてきた。
OnDeinit()が受け取るreason引数は、チャートが閉じられたのか、EAが手動で外されたのか、ターミナルが終了したのかを区別する定数(REASON_CHARTCLOSE等)だ。ハンドルの解放処理は理由によらずOnDeinit()内で必ず行う設計にしておくと安全だ。
コード変換対照表(関数・変数編)
以下がMQL4のグローバル変数・関数からMQL5の型安全なAPIへの変換対応だ。
定義済み変数とSymbolInfo系への置換
最も頻繁に使う変数の対応関係から整理する。
| MQL4 | MQL5 | 注意点 |
|---|---|---|
Ask | SymbolInfoDouble(_Symbol, SYMBOL_ASK) | グローバル変数としては存在しない |
Bid | SymbolInfoDouble(_Symbol, SYMBOL_BID) | 同上 |
Point | _Point | アンダースコア付きに変更 |
Digits | _Digits | 同上 |
Symbol() | _Symbol | 関数→変数 |
Bars | Bars(_Symbol, _Period) | 引数付き関数に変更 |
_Pointと_Digitsはアンダースコアを付けるだけなので置換は単純だ。一方、AskとBidの廃止は記述量が増える。実務上は冒頭でdouble ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);のようにローカル変数に代入してから使うパターンが読みやすく、呼び出し回数も抑えられる。
MarketInfo() → SymbolInfo系への対応表
MarketInfo()は型に関わらずdoubleを返す単一関数だった。MQL5では型安全性のため、SymbolInfoDouble・SymbolInfoInteger・SymbolInfoStringの3関数に分離されている。
MQL4 MarketInfo() 定数 | MQL5 関数 | 型 |
|---|---|---|
MODE_SPREAD | SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) | int |
MODE_POINT | SymbolInfoDouble(_Symbol, SYMBOL_POINT) | double |
MODE_DIGITS | SymbolInfoInteger(_Symbol, SYMBOL_DIGITS) | int |
MODE_MINLOT | SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) | double |
MODE_MAXLOT | SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX) | double |
MODE_LOTSTEP | SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP) | double |
MODE_ASK | SymbolInfoDouble(_Symbol, SYMBOL_ASK) | double |
MODE_BID | SymbolInfoDouble(_Symbol, SYMBOL_BID) | double |
典型的なミスはMODE_SPREADをSymbolInfoDoubleで取得しようとすることだ。スプレッドは整数型(ポイント単位の整数)であるため、SymbolInfoIntegerを使わなければならない。型を間違えると0や意図しない値が返り、スプレッドフィルターが正常に機能しなくなる。MQL4のMarketInfo()が全てをdoubleで返していたせいで、型の意識が薄れている場合に起きやすい。
iMA() のハンドル方式への変更
MQL4ではiMA()を呼ぶたびに即座に値が返ってきた。MQL5では「ハンドル取得→バッファコピー」という2段階になる。
// MQL4: 直接値を取得
double ma = iMA(NULL, 0, 20, 0, MODE_SMA, PRICE_CLOSE, 0);
// MQL5: OnInit()でハンドル取得(1回のみ)
int maHandle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
// OnTick(): バッファから値をコピー
double maBuffer[];
ArraySetAsSeries(maBuffer, true);
CopyBuffer(maHandle, 0, 0, 3, maBuffer);
// OnDeinit(): ハンドル解放
IndicatorRelease(maHandle);
ArraySetAsSeries(maBuffer, true)の設定を忘れると、配列の並び順が「古い順」(インデックス0が最古の足)になる。MQL4の慣習である「インデックス0が最新足」を再現するには、この1行が必須だ。iMAOnArray()はMQL5で廃止されている。手動でSMAを実装する必要がある場合は、インデックス0が最新になるよう配列方向を合わせた上で実装すること。
RefreshRates() の廃止
MQL4ではRefreshRates()を呼ばないと古いレートを参照し続けるリスクがあった。MQL5ではOnTick()が呼ばれるたびに全データが最新状態で提供される設計に変わったため、RefreshRates()自体が廃止された。削除するだけでよく、代替関数を探す必要はない。SymbolInfoDouble(_Symbol, SYMBOL_ASK)を呼べば常に最新のアスク価格を取得できる。
注文ロジックの変換(OrderSend → CTrade)
これが変換作業の核心だ。関数のシグネチャが変わるだけでなく、エラーハンドリングの考え方が根本から異なる。
CTrade クラスの使い方
MQL5で標準提供されるCTradeクラスは、煩雑なOrderSend()の引数を整理したラッパーだ。インクルードとオブジェクト宣言からはじめる。
#include <Trade\Trade.mqh>
CTrade trade;
主要なメソッドの対応表を示す。
MQL4 OrderSend() 操作 | MQL5 CTrade メソッド |
|---|---|
OrderSend(...OP_BUY...) | trade.Buy(lots, symbol, price, sl, tp, comment) |
OrderSend(...OP_SELL...) | trade.Sell(lots, symbol, price, sl, tp, comment) |
OrderModify(ticket, price, sl, tp, ...) | trade.PositionModify(symbol, sl, tp) |
OrderClose(ticket, lots, price, ...) | trade.PositionClose(symbol) |
OrderSend(...OP_BUYLIMIT...) | trade.BuyLimit(lots, price, symbol, sl, tp) |
OrderSend(...OP_BUYSTOP...) | trade.BuyStop(lots, price, symbol, sl, tp) |
OnInit()内ではtrade.SetExpertMagicNumber()でマジックナンバーを設定しておく。これによりBuy()やSell()の呼び出し時に自動的にマジックナンバーが付与される。
CTrade以外のポジション管理・履歴照会クラスを含むMQL5標準ライブラリの全体像については、MQL5標準ライブラリを使ったEA開発に詳しい。
ResultRetcode() による結果確認(必須パターン)
ここが実務で一番よく見落とされる差異だ。
MQL4のOrderSend()はチケット番号(正の整数)を返し、失敗時は-1を返す。つまりif(ticket < 0)で全エラーを捕捉できていた。
MQL5のCTradeメソッドが返すboolは「サーバーがリクエストを受け付けたかどうか」を示すに過ぎない。trueが返っても、それは約定成功を意味しない。約定が完了したかどうかはResultRetcode()で確認しなければならない。
// MQL4(旧来のパターン)
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, sl, tp, "EA");
if(ticket < 0) {
Print("注文失敗: エラーコード ", GetLastError());
}
// MQL5(正しいパターン)
if(!trade.Buy(0.1, _Symbol, 0, sl, tp, "EA")) {
Print("送信失敗: ", trade.ResultRetcodeDescription());
} else if(trade.ResultRetcode() != TRADE_RETCODE_DONE) {
Print("未約定: コード=", trade.ResultRetcode(),
" 説明=", trade.ResultRetcodeDescription());
}
trade.Buy()がtrueを返した後でもTRADE_RETCODE_DONE以外のコードが返ることがある。例えばTRADE_RETCODE_REQUOTE(リクオート)やTRADE_RETCODE_PRICE_CHANGED(価格変動)だ。これを見落とすと「注文を出したつもりが実際には入っていない」という状態になる。特にニュース直後のスリッページが大きい局面では発生頻度が上がる。本番稼働前にこの確認ロジックを必ず実装すること。
trade.SetTypeFillingBySymbol(_Symbol)をOnInit()内で呼ぶことも忘れずに。ブローカーによってフィリングポリシー(ORDER_FILLING_FOK/ORDER_FILLING_IOC/ORDER_FILLING_RETURN)が異なり、設定が合っていないと注文が弾かれる。この1行で対象銘柄に対応したポリシーを自動選択してくれる。

実践:移動平均クロスEAのMQL4→MQL5完全変換例
抽象的な説明よりも、実際のコードを対比したほうが理解は早い。シンプルな移動平均クロスEAを例に、変換前後を完全に示す。
Before(MQL4)
int magicNumber = 123456;
int fastPeriod = 5;
int slowPeriod = 20;
double lots = 0.1;
int start() {
double fastMA_cur = iMA(NULL, 0, fastPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double slowMA_cur = iMA(NULL, 0, slowPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double fastMA_prev = iMA(NULL, 0, fastPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double slowMA_prev = iMA(NULL, 0, slowPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
if(OrdersTotal() == 0) {
if(fastMA_prev <= slowMA_prev && fastMA_cur > slowMA_cur) {
OrderSend(Symbol(), OP_BUY, lots, Ask, 3, 0, 0, "MA Cross", magicNumber);
}
if(fastMA_prev >= slowMA_prev && fastMA_cur < slowMA_cur) {
OrderSend(Symbol(), OP_SELL, lots, Bid, 3, 0, 0, "MA Cross", magicNumber);
}
}
return(0);
}
MQL4でEMAクロスEAを実装する際の詳細なコード解説は、EMAクロスEAのMQL4コード実装も参照されたい。変換前のMQL4コードの動作を正確に把握しておくことが、MQL5移植後の比較検証に役立つ。
After(MQL5・CTrade使用)
#include <Trade\Trade.mqh>
input int FastPeriod = 5;
input int SlowPeriod = 20;
input double Lots = 0.1;
int fastHandle = INVALID_HANDLE;
int slowHandle = INVALID_HANDLE;
CTrade trade;
int OnInit() {
fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastPeriod, 0, MODE_SMA, PRICE_CLOSE);
slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowPeriod, 0, MODE_SMA, PRICE_CLOSE);
if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE) {
Print("ハンドル取得失敗");
return(INIT_FAILED);
}
trade.SetExpertMagicNumber(123456);
trade.SetTypeFillingBySymbol(_Symbol);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) {
IndicatorRelease(fastHandle);
IndicatorRelease(slowHandle);
}
void OnTick() {
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
if(lastBar == currentBar) return;
lastBar = currentBar;
double fastMA[], slowMA[];
ArraySetAsSeries(fastMA, true);
ArraySetAsSeries(slowMA, true);
if(CopyBuffer(fastHandle, 0, 0, 3, fastMA) <= 0) return;
if(CopyBuffer(slowHandle, 0, 0, 3, slowMA) <= 0) return;
if(PositionsTotal() == 0) {
if(fastMA[2] <= slowMA[2] && fastMA[1] > slowMA[1]) {
if(!trade.Buy(Lots, _Symbol, 0, 0, 0, "MA Cross") ||
trade.ResultRetcode() != TRADE_RETCODE_DONE)
Print("買い注文エラー: ", trade.ResultRetcodeDescription());
}
if(fastMA[2] >= slowMA[2] && fastMA[1] < slowMA[1]) {
if(!trade.Sell(Lots, _Symbol, 0, 0, 0, "MA Cross") ||
trade.ResultRetcode() != TRADE_RETCODE_DONE)
Print("売り注文エラー: ", trade.ResultRetcodeDescription());
}
}
}
MQL4版と比較してコード量は約2倍になっている。増加分の大半は「OnInit/OnDeinit/OnTick」への処理分割とResultRetcode()確認という、MQL5で本来あるべき堅牢性の実装だ。コードが長くなるのは冗長化ではなく、設計の明確化によるものだと理解してほしい。
OnTick()内の「lastBarによる新規バー判定」は、MQL4でstart()がティックごとに呼ばれていたのと同様の動作を再現するための定番パターンだ。これを入れないとティックごとに注文ロジックが実行され、同一バーで複数回注文が飛ぶ可能性がある。MAクロスのように「バー確定後に判定」したいロジックでは特に重要な処理となる。
CopyBuffer()の戻り値が0以下なら即returnする処理も入れておきたい。市場オープン直後や接続不安定な場面では、バッファにデータが揃っていないことがある。チェックなしに配列にアクセスすると普通にクラッシュする。
変換後は必ずStrategyTesterのデモ環境でバックテストを実施し、意図した動作になっているか確認してください。
よくある変換エラーと対処法
変換作業で繰り返し引っかかるエラーパターンと対策をまとめた。
頻出エラー一覧
| # | エラー・症状 | 原因 | 対策 |
|---|---|---|---|
| 1 | 'OP_BUY' - undeclared identifier | MQL4の定数がMQL5に存在しない | ORDER_TYPE_BUYに置換、またはCTradeのBuy()を使用 |
| 2 | parameter can be a dynamic array only | 固定サイズ配列を参照渡し引数に使用 | int arr[](サイズ指定なし)に変更 |
| 3 | CopyBuffer failed / err=4806 | OnInit()内でCopyBuffer()を呼んでいる | OnInit()ではハンドル取得のみ。CopyBuffer()はOnTick()内で呼ぶ |
| 4 | SL/TP価格がinvalid stopsエラー | PointとDigitsの単位混同、または価格未正規化 | NormalizeDouble(price, _Digits)で正規化 |
| 5 | ネッティング口座でナンピン系EA動作不良 | ネッティング=1銘柄1ポジション限定 | AccountInfoInteger(ACCOUNT_MARGIN_MODE)で口座タイプ確認 |
| 6 | IndicatorRelease()書き忘れ | メモリリーク | OnDeinit()に必ず全ハンドルのリリース処理を追加 |
| 7 | ArraySetAsSeries()未設定 | MQL5配列のデフォルトは古い順 | ArraySetAsSeries(buf, true)で新しい順に設定 |
| 8 | trade.Buy()がtrueでも約定していない | trueはサーバー受付のみ | ResultRetcode() == TRADE_RETCODE_DONEで確認 |
CopyBuffer err=4806 の解説
err=4806は「インジケータがまだデータを用意できていない」状態を示すエラーコードだ(ERR_INDICATOR_DATA_NOT_FOUND)。このエラーで最も頻繁な原因は、OnInit()内でCopyBuffer()を呼んでいることだ。
OnInit()の実行タイミングはEA起動直後であり、ハンドルを取得した直後はバッファの初期化が完了していない。CopyBuffer()はバッファが計算済みのデータを参照するため、初期化前に呼ぶと必ずエラーになる。これはMQL5の仕様であり、バグではない。
解決策は単純だ。OnInit()内ではiMA()等でハンドルを取得するだけにとどめ、CopyBuffer()の呼び出しは必ずOnTick()(インジケータならOnCalculate())内で行う。またCopyBuffer()が返す要素数が0以下だった場合はスキップするガード処理を入れること。
// 正しいパターン(OnTick()内)
if(CopyBuffer(maHandle, 0, 0, 3, maBuffer) <= 0) {
Print("CopyBuffer失敗: ", GetLastError());
return;
}
ネッティング vs ヘッジ口座の違い
MQL4は実質的にヘッジ口座(1銘柄で複数ポジション保有可能)専用設計だった。MQL5は両方の口座タイプに対応しているが、ナンピン系EAや両建てEAはネッティング口座では意図した動作をしない場合が多い。
ネッティング口座では1銘柄に対して常に1ポジションしか保有できない。BUYポジション保有中にBUYを追加しようとすると、新規ポジションではなく既存ポジションのロット増加として処理される。逆方向の注文は相殺(クローズ)として扱われる。
long mode = AccountInfoInteger(ACCOUNT_MARGIN_MODE);
// ACCOUNT_MARGIN_MODE_RETAIL_NETTING = 0 → ネッティング
// ACCOUNT_MARGIN_MODE_RETAIL_HEDGING = 2 → ヘッジ(MT4互換)
if(mode == ACCOUNT_MARGIN_MODE_RETAIL_NETTING) {
Print("ネッティング口座検出: ナンピン・両建て系ロジックは動作が変わります");
}
複数ポジションを前提とするEAなら、OnInit()内で口座タイプを確認してネッティング口座ではINIT_FAILEDを返す実装にしておくべきだ。誤った口座でこっそり動き続けるより、はっきり停止させたほうがずっとマシだ。
MQL5ならではの機能を変換後のEAで活用する
変換を終えたなら、どうせならMQL5固有の機能で元のEAをもう一段強化したい。
MQL5でゼロから自作EAを組む場合の手順については、FX EA作り方・MQL5実装ガイドが参考になる。
フィリングポリシーの自動設定
前述のtrade.SetTypeFillingBySymbol(_Symbol)は、ブローカーが対応しているフィリングポリシーを自動選択する便利メソッドだ。MQL4では意識しなかった設定だが、MQL5でエラー4756(TRADE_RETCODE_INVALID_FILL)が出る場合はここを疑う。
OnTimer() による定期実行
OnTimer()を使うと、ティックが来ない時間帯でも一定間隔でロジックを動かせる。週明け初回ティックを待たずにポジション管理を行いたい場合や、ニュース前後のスプレッド監視にも便利だ。EventSetTimer(秒数)で設定し、OnDeinit()でのEventKillTimer()を忘れずに。
最適化効率の改善
MQL5のストラテジーテスターはMQL4比で大幅に高速化されており、マルチコアを使った並列最適化が可能だ。変換を機にEAのパラメータ範囲を広げてより詳細な最適化を行うことも検討できる。ただし過剰最適化(カーブフィッティング)のリスクは常に念頭に置くこと。バックテスト期間のプロフィットファクターが高くても、フォワードテストで大幅に劣化するケースは頻繁に観察される。in-sampleとout-of-sampleを明確に分けた検証設計が、変換後のEA評価でも不可欠だ。
Claudeと会話しながらインジケータが作れる hedgrow-fx はこちら。MQL5コードのデバッグや最適化パラメータ設計をAIと対話しながら進めたいEA開発者に使われている。
変換後のバックテスト確認手順
コンパイルが通っても、動作が正しいとは限らない。変換後の確認は段階的に進める。
Step 1: ビジュアルモードでの目視確認
まずStrategyTesterのビジュアルモードで数日分を目視確認する。注文が意図したタイミングで入り、決済が正しく行われているかをチャート上で見ていく。ログパネルの警告・エラーは全部確認する。エラーが一件も出ていなくても、注文タイミングがMQL4時代とずれているならロジック変換のどこかに問題がある。

Step 2: 過去データでの統計比較
十分な期間のバックテストを実施し、取引数・プロフィットファクター・最大ドローダウンがMQL4時代の結果と概ね一致するか確認する。大きく乖離している場合は変換時のロジック誤りが疑われる。ただし、バックテストのティックモデルや価格データが変わることで多少の差異が出ることは正常な範囲だ。
StrategyTesterの各ティックモデルの違いや詳細設定については、MT5バックテスト完全解説を参照してほしい。
Step 3: デモ口座でのフォワードテスト
バックテストで問題がなければ、最低2週間はデモ口座で動作させる。ネットワーク遅延・スリッページ・週またぎの動作・夏時間の切り替わりなど、バックテストでは再現できない事象を確認する期間だ。特にResultRetcode()がTRADE_RETCODE_DONE以外を返したケースのログを記録し、頻度と状況を分析する。
変換済みEAをリアル口座で稼働させる前に、デモ口座での動作確認を必ず実施してください。変換作業中のロジック誤りがリアルマネーの損失に直結するリスクがあります。バックテスト上の収益は将来の利益を保証するものではありません。
免責事項: 本記事で示すコード例・変換手順は技術的な説明を目的としており、特定のEAの収益性・動作を保証するものではありません。FX取引には相場変動リスクが伴い、元本割れの可能性があります。実際の運用にあたっては自己責任でご判断ください。
よくある質問(FAQ)
Q: MetaEditorの自動変換機能だけでMQL4からMQL5への変換は完了しますか?
A: 構文レベルの置換は補助できますが、ResultRetcode()による約定確認・ハンドルの解放処理・口座タイプ判定といった意味論的な変換は自動化できないため、必ず手動での確認・修正が必要です。自動変換後のコードをそのままリアル口座で動かすことは避けてください。
Q: CopyBuffer err=4806が発生します。どう対処すればよいですか?
A: OnInit()内でCopyBuffer()を呼んでいるケースがほとんどです。OnInit()ではハンドル取得(iMA()等)のみを行い、CopyBuffer()の呼び出しはOnTick()内に移動してください。それでも発生する場合は、CopyBuffer()の戻り値(コピー件数)が0以下ならスキップするガード処理を追加してください。
Q: MQL4のEAをMQL5に変換するとネッティング口座でナンピンが機能しないのですが?
A: ネッティング口座は1銘柄1ポジションのみ保有可能な設計のため、複数ポジションを前提としたナンピン系・両建て系のロジックは意図通りに動作しません。AccountInfoInteger(ACCOUNT_MARGIN_MODE)で口座タイプを確認し、ネッティング口座ではOnInit()でINIT_FAILEDを返してEAを停止させるか、ヘッジ口座(MT4互換)でのみ動作させるよう設計を見直してください。
Q: MQL4のOrdersTotal()はMQL5でどう書き換えればよいですか?
A: MQL5ではPositionsTotal()(現在保有中のポジション数)とOrdersTotal()(指値・逆指値など未約定の注文数)に役割が分離されています。MQL4のOrdersTotal()は両方を合算していたため、「ポジションを持っているかどうか」を確認したい場合はMQL5ではPositionsTotal()を使います。ナンピン系EAでポジション数を条件にしているコードは、この分離を意識して書き直す必要があります。
Q: AIツールを使ってMQL4→MQL5の変換作業を効率化できますか?
A: ChatGPTやClaude等のLLMはコードの書き換え案を提示できますが、ResultRetcode()による約定確認や口座タイプ判定といった意味論的な変換の正しさは人間が最終確認する必要があります。Claudeと会話しながらMQL5コードを生成・デバッグできる Hedgrow FX では、変換後のコードをAIと対話しながら段階的に検証するワークフローが可能です。ツールはあくまで補助であり、本記事で解説したパターン(ハンドル確認・ResultRetcode確認・口座タイプ判定)の実装は開発者自身が責任を持って行ってください。
著者: Hedgrow FX 編集部
