history-select-mql5.mdで、

MQL5の約定履歴を取得する、

`HistorySelect()`+`HistoryDealGetDouble()`の、

使い方を解説しました。

ところがこのコードだけでは、「指値注文を出したのに、

価格に届かないまま有効期限が切れてキャンセルされた」

というケースを一切拾えません。

Dealは「実際に約定した」記録であり、

注文したのに一度も約定しなかった指値・逆指値注文はDealとしては存在しないからです。

この記事では、約定の有無に関わらず「注文(Order)」

そのものの履歴を扱う`HistoryOrderGetDouble()`/

`HistoryOrderGetInteger()`/

`HistoryOrderGetString()`を使い、

出した指値注文のうち何割が実際に約定し、

何割がキャンセル・失効したのかを集計するスクリプトを解説します。

DealとOrderは別物: 「約定した記録」と「注文した記録」

MQL5では、あなたが出した1つの指値・逆指値注文は、まず「Order(注文)」

として記録されます。

その注文が実際に価格に届いて約定すると、初めて「Deal(約定)」

という別の記録が追加で作られます。

Order(注文): 発注した時点で作られる記録。約定したかどうかに関わらず必ず存在する
Deal(約定): 実際に売買が成立した時点で作られる記録。

キャンセルや失効で終わった注文にはDealが存在しない

つまり「指値がどれだけ刺さらずに終わったか」を調べたい場合、

Dealだけを見ていては絶対に分かりません。

Order単位の履歴を扱う`HistoryOrderGetDouble()`系の関数が、

必要になるのはこのためです。

HistoryOrderGetDouble/Integer/Stringの書式

`HistorySelect()`で対象期間を指定したあと、

次の3つの関数で注文の詳細を取得します。

double HistoryOrderGetDouble(ulong ticket_number, ENUM_ORDER_PROPERTY_DOUBLE property_id);
long   HistoryOrderGetInteger(ulong ticket_number, ENUM_ORDER_PROPERTY_INTEGER property_id);
string HistoryOrderGetString(ulong ticket_number, ENUM_ORDER_PROPERTY_STRING property_id);

`HistoryDealGetDouble()`と同じく、

値の型(double/long/string)ごとに呼び出す関数を変える設計です。

対象となる注文のチケット番号(`ticket_number`)は、

`HistoryOrdersTotal()`で件数を取得し、

`HistoryOrderGetTicket(index)`で1件ずつ取り出します。

int   HistoryOrdersTotal();
ulong HistoryOrderGetTicket(int index);

`HistoryDealsTotal()`/

`HistoryDealGetTicket()`と関数名が非常に似ているため、

`Deal`と`Order`を書き間違えるコピペミスに注意してください。

(コンパイルは通ってしまい、意図と違う集計結果になります)

注文の状態を判定するORDER_STATE

注文が最終的にどうなったかは、

`HistoryOrderGetInteger(ticket, ORDER_STATE)`

で取得できる`ENUM_ORDER_STATE`で判定します。

  • ORDER_STATE_FILLED: 約定して完了した(Dealが存在する)
  • ORDER_STATE_CANCELED: ユーザーまたはEAがキャンセルした
  • ORDER_STATE_EXPIRED: 有効期限(ORDER_TIME_EXPIRATION)に達して自動的に失効した
  • ORDER_STATE_PARTIAL: 一部数量のみ約定した
  • ORDER_STATE_REJECTED: サーバー側に拒否された

「指値が刺さらなかった」注文は、

このうち`ORDER_STATE_CANCELED`と

`ORDER_STATE_EXPIRED`の2つを数えれば集計できます。

実際に動くコード全体

指定期間の指値・逆指値注文を集計し、約定率とキャンセル・失効の内訳を表示する、

実際にMetaEditorでコンパイルできるスクリプトです。

//+------------------------------------------------------------------+
//|                                PendingOrderStatsDemo.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;//集計対象にする過去の日数

//+------------------------------------------------------------------+
//| 指値・逆指値注文(ORDER_TYPE_LIMIT/STOP系)かどうかを判定する         |
//+------------------------------------------------------------------+
bool IsPendingOrderType(ENUM_ORDER_TYPE type)
  {
   return(type==ORDER_TYPE_BUY_LIMIT  || type==ORDER_TYPE_SELL_LIMIT ||
          type==ORDER_TYPE_BUY_STOP   || type==ORDER_TYPE_SELL_STOP);
  }

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   datetime toDate   = TimeCurrent();
   datetime fromDate = toDate - LookbackDays*86400;

   if(!HistorySelect(fromDate, toDate))
     {
      Print("履歴の取得に失敗しました。エラーコード=", GetLastError());
      return;
     }

   int total    = 0;
   int filled   = 0;
   int canceled = 0;
   int expired  = 0;
   int other    = 0;

   int ordersTotal = HistoryOrdersTotal();
   for(int i=0; i<ordersTotal; i++)
     {
      ulong ticket = HistoryOrderGetTicket(i);
      if(ticket==0) continue;

      ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)HistoryOrderGetInteger(ticket, ORDER_TYPE);
      if(!IsPendingOrderType(type)) continue;

      total++;
      ENUM_ORDER_STATE state = (ENUM_ORDER_STATE)HistoryOrderGetInteger(ticket, ORDER_STATE);

      switch(state)
        {
         case ORDER_STATE_FILLED:   filled++;   break;
         case ORDER_STATE_CANCELED: canceled++; break;
         case ORDER_STATE_EXPIRED:  expired++;  break;
         default:                   other++;    break;
        }
     }

   if(total==0)
     {
      PrintFormat("過去%d日間に指値・逆指値注文はありませんでした。", LookbackDays);
      return;
     }

   PrintFormat("過去%d日間の指値・逆指値注文: 合計%d件", LookbackDays, total);
   PrintFormat("約定: %d件(%.1f%%)  キャンセル: %d件(%.1f%%)  失効: %d件(%.1f%%)  その他: %d件",
               filled,   filled  *100.0/total,
               canceled, canceled*100.0/total,
               expired,  expired *100.0/total,
               other);
  }
//+------------------------------------------------------------------+

MetaEditorで実際に動かす手順

  1. MetaTrader 5の「ツール」→「MetaQuotes Language Editor」でMetaEditorを開く
  2. 「ファイル」→「新規作成」→「スクリプト」を選び、名前を「PendingOrderStatsDemo」にして作成する(中身は空のテンプレートで構いません)
  3. テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
  4. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  5. チャートへドラッグ&ドロップし、`LookbackDays`を指定して「エキスパート」タブに集計結果が表示されることを確認する

コピペしてコンパイルが通らない・想定と違う結果になるときに確認すること

  • `HistoryDealGetTicket()`と`HistoryOrderGetTicket()`を混同していないか(どちらも引数・戻り値の形が似ているため、DealのつもりでOrderの関数を呼んでいるコピペミスが起きやすい)
  • 成行注文(`ORDER_TYPE_BUY`/`ORDER_TYPE_SELL`)まで含めて集計していないか(成行は基本的に即約定するため、指値・逆指値の分析に混ぜると約定率が不自然に高く見える。このサンプルは`IsPendingOrderType()`で意図的に除外している)
  • `HistorySelect()`の戻り値(`bool`)を確認せずに`HistoryOrdersTotal()`を呼んでいないか(失敗時は0件として扱われ、エラーに気づきにくい)
  • 集計結果が0件になる場合、`LookbackDays`の範囲にそもそも指値・逆指値注文を出していない可能性がある。デモ口座で意図的に成行から遠い価格の指値を出し、キャンセルしてから再実行すると動作確認しやすい

あわせて読みたい: 【MQL5入門】MarketInfo()は存在しない?SymbolInfoDouble/Integer/Stringでロット刻み・最小ストップ距離を取得する

まとめ

MQL5の履歴には「注文(Order)」と「約定(Deal)」

という2つの別々の記録があり、

指値・逆指値注文がキャンセル・失効した場合はDealが存在しません。

`HistoryOrderGetDouble()`/

`HistoryOrderGetInteger()`/

`HistoryOrderGetString()`を使えば、

約定の有無に関わらず注文そのものの履歴を追え、指値が刺さらなかった割合のような、

Dealだけでは分からない分析ができるようになります。

関連記事