MQL5 OnTradeTransactionで約定を追跡する方法
MQL5でEAの注文、約定、決済、履歴追加を追跡したい場合は、OnTradeTransactionの確認が重要です。
OnTradeTransactionは、取引口座で発生した注文追加、注文更新、約定追加、履歴追加、ポジション変化などのトランザクションを受け取るイベント関数です。
OrderSendの戻り値だけを見ていると、注文要求を送った結果は確認できても、その後の約定、部分約定、決済、履歴反映、コピーEA同期用のイベントを追い切れない場合があります。
このページでは、MQL5のOnTradeTransactionで約定を追跡する方法を、注文・約定・ポジションの違い、transaction / request / resultの見方、約定ログ、決済イベント、コピーEAでの活用、重複記録防止の観点から整理します。
なお、このページはMQL5開発における技術確認を目的とした内容です。売買判断、推奨エントリー、推奨ロット、利益保証、損失回避保証を行うものではありません。
OnTradeTransactionとは何か
OnTradeTransactionは、取引トランザクションが発生した時にEA側で呼ばれるイベント関数です。
EAでは、注文を出した瞬間だけでなく、注文が追加された、約定が追加された、履歴に移動した、ポジションが変化した、といった複数の段階を確認する必要があります。
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
PrintFormat("[TRADE_TRANSACTION] type=%d order=%I64u deal=%I64u position=%I64u symbol=%s price=%.5f volume=%.2f",
trans.type,
trans.order,
trans.deal,
trans.position,
trans.symbol,
trans.price,
trans.volume);
}OnTradeTransactionでは、まずtrans.typeを見て、どの種類の取引イベントなのかを分けます。
注文・約定・ポジションの違い
OnTradeTransactionを正しく使うには、Order、Deal、Positionの違いを分けて考える必要があります。
| 区分 | 意味 | ログで確認すること |
|---|---|---|
| Order | 注文要求または未約定注文 | order ticket、注文種別、注文追加、注文削除、履歴移動 |
| Deal | 実際に約定した取引 | deal ticket、約定価格、約定数量、エントリー種別、損益 |
| Position | 現在保有している建玉 | position ticket、symbol、volume、price open、Magic Number |
| History | 履歴へ移動した注文・約定 | HistoryDealGetInteger、HistoryDealGetDouble、HistoryOrderGetInteger |
EAの検証ログでは、「注文を出した」ことと「約定した」ことを混同しないようにしてください。
主なTrade Transactionタイプ
OnTradeTransactionでは、trans.typeに応じて処理を分けます。
すべてのtypeで同じ情報が入るわけではないため、約定追跡では、特にDEAL追加系、ORDER履歴移動系、REQUEST系を分けてログ化します。
| type | 意味 | 確認用途 |
|---|---|---|
| TRADE_TRANSACTION_REQUEST | 取引リクエストの処理結果 | request、result、retcodeの確認 |
| TRADE_TRANSACTION_ORDER_ADD | 注文追加 | 注文ticket、注文発生の確認 |
| TRADE_TRANSACTION_ORDER_UPDATE | 注文更新 | 注文状態変更、部分約定前後の確認 |
| TRADE_TRANSACTION_ORDER_DELETE | 注文削除 | 注文取消、約定後の注文削除確認 |
| TRADE_TRANSACTION_DEAL_ADD | 約定追加 | 実際の約定、決済、損益、コピーEA同期の起点 |
| TRADE_TRANSACTION_HISTORY_ADD | 履歴追加 | 注文または取引が履歴へ反映された確認 |
| TRADE_TRANSACTION_POSITION | ポジション変化 | ポジション数量や価格の変化確認 |
約定ログやコピーEA同期では、TRADE_TRANSACTION_DEAL_ADDを中心に確認すると整理しやすくなります。
transaction / request / resultの見方
OnTradeTransactionには、trans、request、resultの3つが渡されます。
ただし、requestとresultは常に有効な情報として使えるわけではありません。取引リクエストの処理結果を見る場合はTRADE_TRANSACTION_REQUESTを確認し、約定そのものを見る場合はtrans.dealから履歴情報を取得する流れに分けます。
| 構造体 | 主な役割 | 確認例 |
|---|---|---|
| MqlTradeTransaction trans | 発生した取引トランザクションの情報 | type、deal、order、position、symbol、price、volume |
| MqlTradeRequest request | 送信した取引リクエストの情報 | action、symbol、volume、price、magic、comment |
| MqlTradeResult result | 取引リクエスト処理結果 | retcode、deal、order、comment、request_id |
ログでは、REQUEST結果とDEAL追加を別イベントとして記録してください。
約定追加を検出する基本コード
実際の約定を追跡する場合は、TRADE_TRANSACTION_DEAL_ADDを検出し、trans.dealを使って履歴からDeal情報を取得します。
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type == TRADE_TRANSACTION_REQUEST)
{
PrintFormat("[TRADE_REQUEST_RESULT] retcode=%u deal=%I64u order=%I64u comment=%s request_id=%u",
result.retcode,
result.deal,
result.order,
result.comment,
result.request_id);
return;
}
if(trans.type != TRADE_TRANSACTION_DEAL_ADD)
return;
ulong deal_ticket = trans.deal;
if(deal_ticket == 0)
{
Print("[DEAL_ADD_SKIP] reason=deal_ticket_zero");
return;
}
if(!HistoryDealSelect(deal_ticket))
{
PrintFormat("[DEAL_SELECT_NG] deal=%I64u last_error=%d",
deal_ticket,
GetLastError());
return;
}
string symbol = HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
long magic = HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
long entry = HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
long type = HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
double volume = HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
double price = HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
double profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
datetime time = (datetime)HistoryDealGetInteger(deal_ticket, DEAL_TIME);
PrintFormat("[DEAL_ADD] deal=%I64u symbol=%s magic=%d entry=%d type=%d volume=%.2f price=%.5f profit=%.2f time=%s",
deal_ticket,
symbol,
(int)magic,
(int)entry,
(int)type,
volume,
price,
profit,
TimeToString(time, TIME_DATE|TIME_SECONDS));
}約定追跡では、trans.dealだけをログに出すのではなく、HistoryDealSelect後にDealの詳細を取得して記録します。
約定ログで残したい項目
約定ログは、あとから検証、コピーEA同期、決済漏れ調査、重複記録防止に使える形で残します。
| ログ項目 | 意味 | 確認用途 |
|---|---|---|
| deal | 約定ticket | 重複記録防止、履歴照合 |
| order | 注文ticket | 注文と約定の対応確認 |
| position | ポジションticket | ポジション単位の追跡 |
| symbol | 銘柄 | コピーEAやマルチシンボルEAでの振り分け |
| magic | Magic Number | EA別、ロジック別、コピー対象の判定 |
| entry | IN / OUT / INOUTなどの約定種別 | 新規約定、決済約定、反転の判定 |
| type | BUY / SELLなどのDeal種別 | 方向確認 |
| volume | 約定数量 | ロット同期、部分約定確認 |
| price | 約定価格 | 約定価格確認、スリッページ確認 |
| profit | 損益 | 決済約定、履歴集計 |
| time | 約定時刻 | イベント順、ログ照合、CSV記録 |
コピーEAや運用記録ツールでは、deal、symbol、magic、entry、type、volume、price、timeを必ず確認できる形にしておくと、あとから同期差分を追いやすくなります。
新規約定と決済約定を分ける
Dealには、エントリー種別があります。
新規建てなのか、決済なのか、反転なのかを分けることで、EAの履歴集計やコピーEAの同期処理を整理できます。
string DealEntryToText(const long entry)
{
if(entry == DEAL_ENTRY_IN)
return "IN";
if(entry == DEAL_ENTRY_OUT)
return "OUT";
if(entry == DEAL_ENTRY_INOUT)
return "INOUT";
if(entry == DEAL_ENTRY_OUT_BY)
return "OUT_BY";
return "UNKNOWN";
}
void PrintDealEntryLog(const ulong deal_ticket)
{
if(!HistoryDealSelect(deal_ticket))
return;
long entry = HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
string symbol = HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
double volume = HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
double profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
PrintFormat("[DEAL_ENTRY] deal=%I64u symbol=%s entry=%s volume=%.2f profit=%.2f",
deal_ticket,
symbol,
DealEntryToText(entry),
volume,
profit);
}決済イベントだけを追跡したい場合は、DEAL_ENTRY_OUTやDEAL_ENTRY_INOUTを条件にして、決済ログ、履歴CSV、コピー先決済処理へつなげます。
決済イベント追跡の考え方
EAの決済を検証する時は、OrderSendで決済要求を送ったログだけでなく、実際に決済Dealが追加されたかを確認します。
決済要求を送ったが決済Dealが出ていない場合、約定拒否、価格変動、部分約定、サーバー側の処理待ちなどを切り分ける必要があります。
| 確認段階 | ログ例 | 意味 |
|---|---|---|
| 決済要求 | CLOSE_REQUEST_SEND | EAが決済要求を送った |
| リクエスト結果 | TRADE_REQUEST_RESULT | サーバーがリクエストを処理した |
| 決済Deal追加 | DEAL_ADD entry=OUT | 実際に決済約定が発生した |
| 履歴記録 | HISTORY_DEAL_LOGGED | EA側のCSVや内部履歴へ記録した |
| 重複防止 | DEAL_DUP_SKIP | 同じDealの二重記録を防止した |
決済処理では、要求ログと約定ログを分けておくと、EAが要求を出していないのか、要求後に約定していないのか、約定後の記録だけが失敗したのかを確認できます。
コピーEAでの活用
コピーEAでは、コピー元口座で発生した約定イベントを検出し、コピー先へ反映する必要があります。
OnTradeTransactionのDEAL_ADDを使うと、コピー元で新規約定や決済約定が発生したタイミングを検出しやすくなります。
| コピーEAで見る項目 | 確認内容 |
|---|---|
| deal ticket | コピー元約定の一意識別 |
| symbol | コピー対象銘柄、シンボル名変換 |
| magic | コピー対象EAまたは対象ロジックの絞り込み |
| entry | 新規建て、決済、反転の判定 |
| type | BUY / SELL方向の判定 |
| volume | コピー先ロット換算の基準 |
| price | 参考価格、許容スリッページ確認 |
| time | 同期順序、遅延確認 |
コピーEAでは、約定イベントをそのまま注文処理へ直結せず、対象symbol、対象magic、対象entry、重複処理済みdealかどうかを確認してからコピー処理へ渡す設計が安全です。
重複記録防止
OnTradeTransactionでは、同じDealをCSV、履歴配列、外部ファイル、コピーキューへ記録する場合があります。
この時、同じdeal ticketを二重に処理しないように、処理済みDealを記録しておく必要があります。
ulong g_processed_deals[];
bool IsProcessedDeal(const ulong deal_ticket)
{
for(int i = 0; i < ArraySize(g_processed_deals); i++)
{
if(g_processed_deals[i] == deal_ticket)
return true;
}
return false;
}
void MarkProcessedDeal(const ulong deal_ticket)
{
int size = ArraySize(g_processed_deals);
ArrayResize(g_processed_deals, size + 1);
g_processed_deals[size] = deal_ticket;
}
bool CanProcessDealOnce(const ulong deal_ticket)
{
if(deal_ticket == 0)
return false;
if(IsProcessedDeal(deal_ticket))
{
PrintFormat("[DEAL_DUP_SKIP] deal=%I64u reason=already_processed", deal_ticket);
return false;
}
MarkProcessedDeal(deal_ticket);
return true;
}長時間稼働EAでは、処理済みDeal配列が増え続けないように、保持件数の上限、日付単位の整理、CSV記録済み判定などを設計してください。
履歴CSVへ記録する時の考え方
運用記録や検証補助では、約定DealをCSVへ記録することがあります。
CSV記録では、同じDealを二重に書かないこと、必要な項目を固定列で残すこと、機密情報や不要な個人情報を混ぜないことが重要です。
| CSV列 | 内容 |
|---|---|
| time | 約定時刻 |
| deal | Deal ticket |
| order | Order ticket |
| position | Position ticket |
| symbol | 銘柄 |
| magic | Magic Number |
| entry | IN / OUT / INOUT / OUT_BY |
| type | BUY / SELL |
| volume | 約定ロット |
| price | 約定価格 |
| profit | 損益 |
| comment | Dealコメント |
CSVへ出力する場合は、Webhook URL、APIキー、認証トークン、口座番号などの実値を混入させないようにしてください。
ログ出力例
約定追跡のログは、REQUEST結果、DEAL追加、決済種別、重複防止を分けると確認しやすくなります。
[TRADE_REQUEST_RESULT] retcode=10009 deal=123456789 order=987654321 comment=Request executed request_id=15
[DEAL_ADD] deal=123456789 symbol=GOLD magic=24014 entry=IN type=BUY volume=0.10 price=2350.12 profit=0.00 time=2026.05.26 10:15:04
[DEAL_ADD] deal=123456790 symbol=GOLD magic=24014 entry=OUT type=SELL volume=0.10 price=2353.12 profit=30.00 time=2026.05.26 11:22:31
[HISTORY_DEAL_LOGGED] deal=123456790 entry=OUT profit=30.00
[DEAL_DUP_SKIP] deal=123456790 reason=already_processed
この形式でログを残すと、注文要求、実約定、決済、履歴記録、重複スキップを分けて確認できます。
OnTradeTransaction実装時の注意点
OnTradeTransactionは便利ですが、重い処理を直接詰め込みすぎると、ログ過多や処理遅延の原因になります。
| 注意点 | 理由 | 対応 |
|---|---|---|
| 全イベントを詳細ログ化しない | 約定が多いEAやコピーEAでログが膨らむ | DEAL_ADD、REQUEST、NG系を中心に記録する |
| 重いファイル処理を直接連発しない | イベント処理が遅くなる | 必要情報をキュー化し、OnTimer側で処理する |
| 同じDealを二重記録しない | 履歴CSVやコピー処理が重複する | deal ticketで処理済み管理を行う |
| request / resultを常に信用しない | typeによって有効情報が異なる | TRADE_TRANSACTION_REQUEST時に確認する |
| DealとPositionを混同しない | 決済、部分約定、反転で意味が変わる | DEAL_ENTRY、DEAL_TYPE、positionを分けてログ化する |
| コピーEAで即時発注に直結しない | 対象外magicや重複dealまでコピーする恐れがある | symbol、magic、entry、重複状態を確認してから処理する |
OnTradeTransactionは、約定追跡の入口として使い、重い集計、外部送信、CSV整形、コピーキュー処理は必要に応じて別関数やOnTimerへ分けると安定しやすくなります。
OnTradeTransactionとあわせて確認したいイベント設計
OnTradeTransactionで約定や決済を追跡する場合は、イベント構造、OrderSend結果ログ、重複記録防止を分けて確認しておくと、約定後の状態反映を追いやすくなります。
| 確認したい内容 | 関連ページ | 確認ポイント |
|---|---|---|
| EAイベント構造 | MQL5 EAの基本イベント構造を実コードで解説 | OnInit、OnTick、OnDeinitの責務分離とイベントごとの役割を確認します。 |
| 重複エントリー防止 | MQL5重複エントリー防止を実コードで解説 | Duplicate Gate、同一バー制御、in-flight lock、注文直後の状態反映遅延を確認します。 |
関連ページ
OnTradeTransactionは、注文・ポジション・履歴、標準ライブラリ、注文関数、コピーEA、ログ確認と合わせて確認すると理解しやすくなります。
| 関連ページ | 確認できること |
|---|---|
| MQL5ポジション・注文・約定・履歴用語辞典 | Order、Position、Deal、Historyの違い |
| MQL5注文・ポジション・履歴管理完全ガイド | OrderSend、Position、Deal、History管理の全体像 |
| MQL5注文関数・取引構造体辞典 | OrderSend、MqlTradeRequest、MqlTradeResultの基本 |
| MQL5標準ライブラリ辞典 | CTrade、CPositionInfo、COrderInfo、CDealInfoの概要 |
| MT5コピーEA完全ガイド | コピー元、コピー先、Magic Number、ロット同期の確認 |
| MT5のログ確認方法 | ExpertsログとJournalログの確認方法 |
イベント処理と注文結果ログを確認する場合
OnTradeTransactionは、注文送信後のorder、deal、position、決済、部分約定、注文変更などを追跡するために使うイベント関数です。イベント処理全体の役割分担や、OrderSend直後の結果ログとあわせて確認したい場合は、関連する親記事も確認してください。
| 確認したいこと | 確認するページ | 主な確認内容 |
|---|---|---|
| MQL5イベント処理の全体を確認したい | MQL5イベント処理完全ガイド | OnInit、OnTick、OnTimer、OnTradeTransaction、OnChartEventの使い分け |
| OrderSend直後の結果ログを確認したい | MQL5 OrderSend結果ログを実コードで解説 | retcode、deal、order、comment、GetLastError、MqlTradeResultの確認 |
注文処理の確認では、OrderSendの戻り値だけで完了判定せず、OrderSend結果、OnTradeTransaction、HistoryDeal、Position情報を分けて確認してください。コピーEAや複数ロジックEAでは、Magic Number、symbol、ticket、deal、positionの対応関係もログで追跡できるようにします。
開発依頼前に整理したい情報
OnTradeTransactionを使った約定追跡、履歴CSV、コピーEA同期、決済ログを相談する場合は、次の情報を整理しておくと確認が進めやすくなります。
| 整理する情報 | 確認内容 |
|---|---|
| 対象EA | 新規EA、既存EA改修、コピーEA、運用記録ツールのどれか |
| 追跡したいイベント | 新規約定、決済約定、部分約定、履歴追加、ポジション変化など |
| 対象Magic Number | 全EA対象か、特定EA・特定ロジックのみか |
| 対象銘柄 | 単一銘柄か、複数銘柄か、シンボル名変換が必要か |
| 記録方法 | Expertsログ、CSV、Commonフォルダ、コピーキュー、外部連携など |
| 重複防止条件 | deal ticket、position ticket、basket idなど |
| コピーEA同期 | 新規建てだけか、決済同期も含めるか |
| ログ | OrderSend結果、OnTradeTransactionログ、HistoryDealログ、Journalログ |
約定追跡の不具合を相談する場合は、OrderSendログだけでなく、OnTradeTransactionで出ているDEAL_ADD、REQUEST、HistoryDealGet系のログも合わせて確認してください。
まとめ
OnTradeTransactionは、MQL5 EAで注文、約定、決済、履歴追加、ポジション変化を追跡するための重要なイベント関数です。
約定追跡では、TRADE_TRANSACTION_DEAL_ADDを検出し、trans.dealからHistoryDealSelect、HistoryDealGetInteger、HistoryDealGetDouble、HistoryDealGetStringを使ってDeal情報を取得します。
REQUEST結果、注文ticket、Deal ticket、Position ticket、Magic Number、DEAL_ENTRY、DEAL_TYPE、volume、price、profitを分けてログ化すると、EA検証、決済ログ、コピーEA同期、履歴CSV、重複記録防止に使いやすくなります。
OnTradeTransactionには重い処理を詰め込みすぎず、約定検出、重複確認、必要情報の記録に役割を絞ると、長時間稼働やコピーEA同期でも確認しやすい構造になります。
