MQL4で書いていた「保有中と決済済みの注文を両方数えて勝率を出す」

処理を、そのままMQL5でやろうとして`OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)`をMetaEditorに打ち込んだら、

そんな関数は存在しないというエラーが出た——MQL4からMQL5に移った人が最初に驚くポイントの一つです。

MQL5では約定履歴の取得方法そのものが別の設計に変わっています。

この記事では、MQL5で過去の約定履歴を取得する`HistorySelect()`と`HistoryDealGetDouble()`/`HistoryDealGetInteger()`の使い方を、

実際に勝率と純損益を集計して表示するスクリプトで解説します。

あわせて、MQL5特有の「発注と決済が別々の記録になる」ことで初心者がほぼ必ず踏む二重集計の罠も扱います。

MQL4とMQL5で履歴取得の設計がまったく違う

MQL4では、決済済みの注文は`OrderSelect(位置, SELECT_BY_POS, MODE_HISTORY)`のように、

保有中の注文と同じ`OrderSelect`系関数の第3引数を`MODE_HISTORY`に切り替えるだけで参照できました(詳しくはOrder系関数の記事を参照)。

MQL5にはこの`MODE_HISTORY`という概念自体がありません。

代わりに、次の2ステップに分かれた設計になっています。

①HistorySelect(from, to)で「この期間の履歴を対象にする」と宣言する(内部キャッシュを作る)
②HistoryDealsTotal()/HistoryDealGetTicket()でチケット番号を取得し、HistoryDealGetDouble()等でそのチケットの詳細を取得する

`HistorySelect()`を呼ばずにいきなり`HistoryDealGetDouble()`を呼んでも、

コンパイルエラーにも実行時エラーにもならず、静かに0が返ってくるだけなので気づきにくい落とし穴です。

必ず`HistorySelect()`を先に呼び、戻り値の`bool`で成功しているか確認してください。

HistorySelect関数の書式

bool HistorySelect(
   datetime from_date,  //取得したい履歴の開始日時
   datetime to_date      //取得したい履歴の終了日時
   );

指定した期間内の決済済み注文(deal)と発注済みの注文(order)を、

内部のキャッシュに読み込みます。

戻り値は成功したかどうかの`bool`です。

この後に呼ぶ`HistoryDealsTotal()`や`HistoryDealGetXxx()`系の関数は、

すべてこの`HistorySelect()`で選択した範囲のデータを対象に動きます。

deal単位でデータを取得する3つの関数

MQL5では、約定の記録1件を「deal(ディール)」と呼びます。

dealのチケット番号さえ分かれば、そのdealのあらゆる情報を次の3つの関数で取得できます。

double HistoryDealGetDouble(ulong ticket_number, ENUM_DEAL_PROPERTY_DOUBLE property);//数値(利益・スワップ等)
long   HistoryDealGetInteger(ulong ticket_number, ENUM_DEAL_PROPERTY_INTEGER property);//種別・時刻・マジックナンバー等
string HistoryDealGetString(ulong ticket_number, ENUM_DEAL_PROPERTY_STRING property);//通貨ペア・コメント等

この記事でよく使うプロパティは以下の通りです。

  • DEAL_PROFIT(double) — そのdeal単体の損益
  • DEAL_SWAP(double) — そのdealのスワップ
  • DEAL_COMMISSION(double) — そのdealの手数料
  • DEAL_ENTRY(long、実体はENUM_DEAL_ENTRY) — 新規(DEAL_ENTRY_IN)か決済(DEAL_ENTRY_OUT)かの区分
  • DEAL_TYPE(long、実体はENUM_DEAL_TYPE) — 買い(DEAL_TYPE_BUY)・売り(DEAL_TYPE_SELL)・入出金(DEAL_TYPE_BALANCE)等の種別

最大の罠: 発注と決済は別々のdealとして記録される

MQL4の「1つの注文=1行」という感覚のままMQL5の履歴を数えると、

必ずつまずくポイントがあります。

MQL5では、ポジションを新規に持った瞬間の「発注」と、それを閉じた瞬間の「決済」

が、それぞれ独立した1件のdealとして記録されます。

つまり、1回の取引(エントリーから決済まで)は最低でも2件のdealになります。

USDJPYを買う(発注deal、DEAL_ENTRY_IN、DEAL_PROFITは0)
→決済する(決済deal、DEAL_ENTRY_OUT、DEAL_PROFITに実際の損益)

このため、`HistoryDealsTotal()`で数えた件数をそのまま「取引回数」

として扱うと2倍になり、`DEAL_PROFIT`を全dealについて単純合計しても(発注deal側は通常0なので合計自体は狂いませんが)件数のズレに気づけないまま「勝率」

を計算すると、発注deal(損益0)まで母数に含めてしまい正しい勝率が出ません。

**決済(`DEAL_ENTRY_OUT`)側のdealだけを数える**のが正しいやり方です。

さらに、入金・出金などの資金操作も`DEAL_TYPE_BALANCE`という種別のdealとして同じ履歴に混ざって記録されます。

取引の集計をするときは、`DEAL_TYPE`が`DEAL_TYPE_BUY`または`DEAL_TYPE_SELL`であることも合わせて確認し、

資金操作を除外する必要があります。

実際に動くコード全体

直近N日間の決済済み取引だけを正しく絞り込んで、決済件数・勝ち数・勝率・純損益(利益+スワップ+手数料の合計)を計算して表示するスクリプトです。

//+------------------------------------------------------------------+
//|                                        HistorySelectWinRate.mq5   |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property script_show_inputs

input int LookbackDays = 30;//さかのぼって集計する日数

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   datetime to   = TimeCurrent();
   datetime from = to - LookbackDays * 24 * 60 * 60;

   if(!HistorySelect(from, to))
     {
      Print("HistorySelectに失敗しました。エラーコード=", GetLastError());
      return;
     }

   int    totalDeals  = HistoryDealsTotal();
   int    closedCount = 0;
   int    winCount    = 0;
   double netProfit   = 0;

   for(int i = 0; i < totalDeals; i++)
     {
      ulong ticket = HistoryDealGetTicket(i);
      if(ticket == 0)
         continue;

      //入出金(DEAL_TYPE_BALANCE)等の売買以外のdealを除外
      ENUM_DEAL_TYPE dealType = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE);
      if(dealType != DEAL_TYPE_BUY && dealType != DEAL_TYPE_SELL)
         continue;

      //発注(IN)側のdealは損益が確定していないため、決済(OUT)側だけを集計する
      ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY);
      if(entry != DEAL_ENTRY_OUT)
         continue;

      double profit     = HistoryDealGetDouble(ticket, DEAL_PROFIT);
      double swap        = HistoryDealGetDouble(ticket, DEAL_SWAP);
      double commission = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
      double dealNet     = profit + swap + commission;

      netProfit += dealNet;
      closedCount++;
      if(dealNet > 0)
         winCount++;
     }

   double winRate = (closedCount > 0) ? (double)winCount / closedCount * 100.0 : 0.0;

   Print("直近", LookbackDays, "日: 決済件数=", closedCount,
         " 勝ち=", winCount,
         " 勝率=", DoubleToString(winRate, 1), "%",
         " 純損益=", DoubleToString(netProfit, 2));
  }
//+------------------------------------------------------------------+
Copy

MetaEditorで実際に動かす手順

  1. MT5の「ツール」→「MetaQuotes Language Editor」でMetaEditorを開く
  2. 「ファイル」→「新規作成」→「スクリプト」を選び、名前を「HistorySelectWinRate」にして作成する(中身は空のテンプレートで構いません)
  3. テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
  4. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  5. MT5の「ナビゲーター」パネルからスクリプトを探し、チャートへドラッグ&ドロップする(入力ダイアログが出るのでLookbackDaysを確認して「OK」)
  6. MT5下部の「エキスパート」タブに「直近30日: 決済件数=… 勝率=…% 純損益=…」のようなログが表示されれば成功です

コピペしてコンパイルが通らない・値がおかしいときに確認すること

  • 拡張子が「.mq5」になっているか(このコードはMQL5専用で、MQL4環境ではコンパイルできません)
  • `HistorySelect()`の戻り値を確認せずに`HistoryDealGetDouble()`を呼んでいないか(選択に失敗していると全て0が返り、エラーが出ないまま気づきにくい)
  • 決済件数が想定より多い場合、`DEAL_ENTRY_OUT`の絞り込みが抜けていて発注(IN)側のdealまで数えていないか
  • 純損益が想定とズレる場合、`DEAL_TYPE_BALANCE`(入出金)を除外できているか

まとめ

MQL5の履歴取得は、MQL4の`OrderSelect(MODE_HISTORY)`とは別物の`HistorySelect()`+`HistoryDealGetXxx()`という設計です。

最大の注意点は、1回の取引が「発注」と「決済」という2つの別々のdealとして記録されることで、

`DEAL_ENTRY_OUT`(決済側)と`DEAL_TYPE`(売買のみ)で正しく絞り込まないと、

件数も損益も二重・過大に集計してしまいます。

この記事のコードをベースに期間や通貨ペアの絞り込みを足せば、

実運用中のEAの成績集計にもそのまま応用できます。

関連記事