✅ Subscribe to Trade Events (OnTradeAsync)
Stream: Real-time trade execution and closure events on MT5. Continuously sends notifications when orders are filled, positions opened/closed, or orders modified.
API Information:
- SDK wrapper:
MT5Service.OnTradeAsync(...) (from class MT5Service)
- gRPC service:
mt5_term_api.SubscriptionService
- Proto definition:
OnTrade (defined in mt5-term-api-subscriptions.proto)
RPC
- Service:
mt5_term_api.SubscriptionService
- Method:
OnTrade(OnTradeRequest) → stream OnTradeReply
- Low‑level client (generated):
SubscriptionService.SubscriptionServiceClient.OnTrade(request, headers, deadline, cancellationToken)
- SDK wrapper:
namespace mt5_term_api
{
public class MT5Service
{
public async IAsyncEnumerable<OnTradeData> OnTradeAsync(
[EnumeratorCancellation] CancellationToken cancellationToken = default);
}
}
Request message:
OnTradeRequest {} (empty)
Reply message (stream):
OnTradeReply { data: OnTradeData }
| Parameter |
Type |
Description |
cancellationToken |
CancellationToken |
Token to stop the stream |
⬆️ Output — OnTradeData (stream)
| Field |
Type |
Description |
Type |
MT5_SUB_ENUM_EVENT_GROUP_TYPE |
Event group type |
EventData |
OnTradeEventData |
Trade event details |
AccountInfo |
OnEventAccountInfo |
Current account state |
TerminalInstanceGuidId |
string |
Terminal instance ID |
| Field |
Type |
Description |
NewOrders |
List<OnTradeOrderInfo> |
Newly placed orders |
DisappearedOrders |
List<OnTradeOrderInfo> |
Orders that were removed/cancelled |
StateChangedOrders |
List<OnTradeOrderStateChange> |
Orders with state changes |
NewHistoryOrders |
List<OnTradeHistoryOrderInfo> |
New orders in history (executed/cancelled) |
DisappearedHistoryOrders |
List<OnTradeHistoryOrderInfo> |
History orders that disappeared |
UpdatedHistoryOrders |
List<OnTradeHistoryOrderUpdate> |
History orders that were updated |
NewHistoryDeals |
List<OnTradeHistoryDealInfo> |
New deals in history |
DisappearedHistoryDeals |
List<OnTradeHistoryDealInfo> |
History deals that disappeared |
UpdatedHistoryDeals |
List<OnTradeHistoryDealUpdate> |
History deals that were updated |
NewPositions |
List<OnTradePositionInfo> |
Newly opened positions |
DisappearedPositions |
List<OnTradePositionInfo> |
Positions that were closed |
UpdatedPositions |
List<OnTradePositionUpdate> |
Positions that were modified |
| Field |
Type |
Description |
Index |
int32 |
Position index |
Ticket |
int64 |
Position ticket |
Type |
SUB_ENUM_POSITION_TYPE |
Position type (Buy/Sell) |
PositionTime |
Timestamp |
Position open time |
LastUpdateTime |
Timestamp |
Last update time |
PriceOpen |
double |
Open price |
Profit |
double |
Current profit |
Sl |
double |
Stop Loss |
Tp |
double |
Take Profit |
Volume |
double |
Position volume |
Swap |
double |
Swap charges |
Comment |
string |
Position comment |
Symbol |
string |
Symbol name |
Magic |
int64 |
Magic number |
PriceCurrent |
double |
Current price |
AccountLogin |
int64 |
Account login |
Reason |
SUB_ENUM_POSITION_REASON |
Position open reason |
FromPendingOrder |
bool |
Opened from pending order |
| Field |
Type |
Description |
Index |
int32 |
Order index |
Ticket |
int64 |
Order ticket |
State |
SUB_ENUM_ORDER_STATE |
Order state |
SetupTimeMsc |
int64 |
Setup time (milliseconds) |
StopLoss |
double |
Stop Loss |
TakeProfit |
double |
Take Profit |
StopLimit |
double |
Stop Limit price |
PriceCurrent |
double |
Current price |
TimeExpiration |
Timestamp |
Expiration time |
TimeType |
SUB_ENUM_ORDER_TYPE_TIME |
Time type (GTC, Day, etc) |
Comment |
string |
Order comment |
Symbol |
string |
Symbol name |
Magic |
int64 |
Magic number |
PriceOpen |
double |
Open price |
SetupTime |
Timestamp |
Setup time |
TimeExpirationSeconds |
int64 |
Expiration (seconds) |
VolumeCurrent |
double |
Current volume |
VolumeInitial |
double |
Initial volume |
AccountLogin |
int64 |
Account login |
OrderType |
SUB_ENUM_ORDER_TYPE |
Order type |
OrderTypeFilling |
SUB_ENUM_ORDER_TYPE_FILLING |
Filling mode |
OrderReason |
SUB_ENUM_ORDER_REASON |
Order reason |
PositionId |
int64 |
Position ID |
PositionById |
int64 |
Opposite position ID |
OnTradeHistoryDealInfo — History deal information
| Field |
Type |
Description |
Index |
int32 |
Deal index |
Ticket |
uint64 |
Deal ticket |
OrderTicket |
int64 |
Order ticket |
Type |
SUB_ENUM_DEAL_TYPE |
Deal type |
DealTime |
Timestamp |
Deal execution time |
Entry |
SUB_ENUM_DEAL_ENTRY |
Deal entry (In/Out/InOut/OutBy) |
DealPositionId |
int64 |
Position ID |
Commission |
double |
Commission |
Fee |
double |
Fee |
Price |
double |
Execution price |
Profit |
double |
Profit |
Sl |
double |
Stop Loss |
Tp |
double |
Take Profit |
Volume |
double |
Deal volume |
Comment |
string |
Deal comment |
Symbol |
string |
Symbol name |
Swap |
double |
Swap |
Reason |
SUB_ENUM_DEAL_REASON |
Deal reason |
Magic |
int64 |
Magic number |
AccountLogin |
int64 |
Account login |
OnEventAccountInfo — Account state snapshot
| Field |
Type |
Description |
Balance |
double |
Account balance |
Credit |
double |
Account credit |
Equity |
double |
Account equity |
Margin |
double |
Used margin |
FreeMargin |
double |
Free margin |
Profit |
double |
Current profit |
MarginLevel |
double |
Margin level (%) |
Login |
int64 |
Account login |
MT5_SUB_ENUM_EVENT_GROUP_TYPE (Event Group Type)
| Enum Value |
Description |
OrderProfit |
Position profit event |
OrderUpdate |
Order update event |
SUB_ENUM_POSITION_TYPE (Position Type)
| Enum Value |
Description |
SubPositionTypeBuy |
Buy position |
SubPositionTypeSell |
Sell position |
SUB_ENUM_POSITION_REASON (Position Open Reason)
| Enum Value |
Description |
SUB_POSITION_REASON_CLIENT |
Position opened from desktop terminal |
SUB_POSITION_REASON_MOBILE |
Position opened from mobile app |
SUB_POSITION_REASON_WEB |
Position opened from web platform |
SUB_POSITION_REASON_EXPERT |
Position opened by Expert Advisor |
SUB_ENUM_ORDER_STATE (Order State)
| Enum Value |
Description |
SubOrderStateStarted |
Order checked, not yet accepted by broker |
SubOrderStatePlaced |
Order accepted |
SubOrderStateCanceled |
Order canceled by client |
SubOrderStatePartial |
Order partially executed |
SubOrderStateFilled |
Order fully executed |
SubOrderStateRejected |
Order rejected |
SubOrderStateExpired |
Order expired |
SubOrderStateRequestAdd |
Order is being registered (placing to the trading system) |
SubOrderStateRequestModify |
Order is being modified (changing its parameters) |
SubOrderStateRequestCancel |
Order is being deleted (deleting from the trading system) |
SUB_ENUM_ORDER_TYPE_TIME (Order Time Type)
| Enum Value |
Description |
SUB_ORDER_TIME_GTC |
Good till cancel order |
SUB_ORDER_TIME_DAY |
Good till current trade day order |
SUB_ORDER_TIME_SPECIFIED |
Good till expired order |
SUB_ORDER_TIME_SPECIFIED_DAY |
The order will be effective till 23:59:59 of the specified day |
SUB_ENUM_ORDER_TYPE (Order Type)
| Enum Value |
Description |
SUB_ORDER_TYPE_BUY |
Market buy order |
SUB_ORDER_TYPE_SELL |
Market sell order |
SUB_ORDER_TYPE_BUY_LIMIT |
Buy Limit pending order |
SUB_ORDER_TYPE_SELL_LIMIT |
Sell Limit pending order |
SUB_ORDER_TYPE_BUY_STOP |
Buy Stop pending order |
SUB_ORDER_TYPE_SELL_STOP |
Sell Stop pending order |
SUB_ORDER_TYPE_BUY_STOP_LIMIT |
Buy Stop Limit pending order |
SUB_ORDER_TYPE_SELL_STOP_LIMIT |
Sell Stop Limit pending order |
SUB_ORDER_TYPE_CLOSE_BY |
Close by opposite position order |
SUB_ENUM_ORDER_TYPE_FILLING (Order Filling Mode)
| Enum Value |
Description |
SUB_ORDER_FILLING_FOK |
Fill or Kill - order must be filled completely or canceled |
SUB_ORDER_FILLING_IOC |
Immediate or Cancel - partial fills allowed, remainder canceled |
SUB_ORDER_FILLING_BOC |
Book or Cancel - order can only be placed in the order book |
SUB_ORDER_FILLING_RETURN |
Return - used for market orders on exchange |
SUB_ENUM_ORDER_REASON (Order Reason)
| Enum Value |
Description |
SUB_ORDER_REASON_CLIENT |
Order placed from desktop terminal |
SUB_ORDER_REASON_MOBILE |
Order placed from mobile app |
SUB_ORDER_REASON_WEB |
Order placed from web platform |
SUB_ORDER_REASON_EXPERT |
Order placed by Expert Advisor |
SUB_ORDER_REASON_SL |
Order triggered by Stop Loss |
SUB_ORDER_REASON_TP |
Order triggered by Take Profit |
SUB_ORDER_REASON_SO |
Order triggered by Stop Out |
SUB_ENUM_DEAL_TYPE (Deal Type)
| Enum Value |
Description |
SubDealTypeBuy |
Buy deal |
SubDealTypeSell |
Sell deal |
SubDealTypeBalance |
Balance operation |
SubDealTypeCredit |
Credit operation |
SUB_DEAL_TYPE_CHARGE |
Additional charge |
SUB_DEAL_TYPE_CORRECTION |
Correction |
SUB_DEAL_TYPE_BONUS |
Bonus |
SUB_DEAL_TYPE_COMMISSION |
Additional commission |
SUB_DEAL_TYPE_COMMISSION_DAILY |
Daily commission |
SUB_DEAL_TYPE_COMMISSION_MONTHLY |
Monthly commission |
SUB_DEAL_TYPE_COMMISSION_AGENT_DAILY |
Daily agent commission |
SUB_DEAL_TYPE_COMMISSION_AGENT_MONTHLY |
Monthly agent commission |
SUB_DEAL_TYPE_INTEREST |
Interest rate |
SUB_DEAL_TYPE_BUY_CANCELED |
Canceled buy deal |
SUB_DEAL_TYPE_SELL_CANCELED |
Canceled sell deal |
SUB_ENUM_DEAL_ENTRY (Deal Entry Direction)
| Enum Value |
Description |
SubDealEntryIn |
Entry in (opening position) |
SubDealEntryOut |
Entry out (closing position) |
SubDealEntryInout |
Reverse (reversing position) |
SubDealEntryOutBy |
Close a position by an opposite one |
SUB_ENUM_DEAL_REASON (Deal Reason)
| Enum Value |
Description |
SUB_DEAL_REASON_CLIENT |
Deal executed from desktop terminal |
SUB_DEAL_REASON_MOBILE |
Deal executed from mobile app |
SUB_DEAL_REASON_WEB |
Deal executed from web platform |
SUB_DEAL_REASON_EXPERT |
Deal executed by Expert Advisor |
SUB_DEAL_REASON_SL |
Deal executed by Stop Loss |
SUB_DEAL_REASON_TP |
Deal executed by Take Profit |
SUB_DEAL_REASON_SO |
Deal executed by Stop Out |
SUB_DEAL_REASON_ROLLOVER |
Deal executed due to rollover |
SUB_DEAL_REASON_VMARGIN |
Deal executed due to variation margin |
SUB_DEAL_REASON_SPLIT |
Deal executed due to split |
SUB_DEAL_REASON_CORPORATE_ACTION |
Deal executed due to corporate action |
💬 Just the essentials
- What it is. Real-time notifications for all trade-related events: order placement, execution, position opens/closes, modifications.
- Why you need it. Monitor trade execution in real-time, track position lifecycle, build event-driven systems without polling.
- Sanity check. Check
EventData arrays to see what changed: NewPositions, DisappearedPositions, NewHistoryDeals, etc.
🎯 Purpose
Use it for real-time trade monitoring:
- Monitor order execution as it happens.
- Track position opens and closes.
- Detect SL/TP hits immediately.
- Build trade journaling systems.
- React to trade events in real-time.
🧩 Notes & Tips
- Comprehensive events: Includes orders, positions, deals, and history. Filter arrays you need.
- Delta updates: Each event shows what changed since last update.
- Account snapshot: Includes current account state with each event.
- Performance: Events arrive immediately when trades execute. Keep processing fast.
- Comparison with OnTradeTransaction:
OnTrade is simpler, focused on trades. OnTradeTransaction is more detailed with full transaction lifecycle.
🔗 Usage Examples
1) Monitor new positions
// svc — MT5Service instance
var cts = new CancellationTokenSource();
try
{
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
foreach (var position in trade.EventData.NewPositions)
{
Console.WriteLine($"🆕 New position opened:");
Console.WriteLine($" Ticket: #{position.Ticket}");
Console.WriteLine($" Symbol: {position.Symbol}");
Console.WriteLine($" Type: {position.Type}");
Console.WriteLine($" Volume: {position.Volume}");
Console.WriteLine($" Price: {position.PriceOpen}");
Console.WriteLine();
}
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Stream stopped");
}
2) Monitor position closes
var cts = new CancellationTokenSource();
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
foreach (var position in trade.EventData.DisappearedPositions)
{
var profitSign = position.Profit >= 0 ? "+" : "";
Console.WriteLine($"✅ Position closed:");
Console.WriteLine($" Ticket: #{position.Ticket}");
Console.WriteLine($" Symbol: {position.Symbol}");
Console.WriteLine($" Profit: {profitSign}${position.Profit:F2}");
Console.WriteLine($" Swap: ${position.Swap:F2}");
Console.WriteLine();
}
}
3) Track all trade activity
var cts = new CancellationTokenSource();
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
var e = trade.EventData;
if (e.NewOrders.Count > 0)
Console.WriteLine($"📝 {e.NewOrders.Count} new order(s) placed");
if (e.NewPositions.Count > 0)
Console.WriteLine($"🆕 {e.NewPositions.Count} position(s) opened");
if (e.DisappearedPositions.Count > 0)
Console.WriteLine($"✅ {e.DisappearedPositions.Count} position(s) closed");
if (e.NewHistoryDeals.Count > 0)
Console.WriteLine($"💼 {e.NewHistoryDeals.Count} new deal(s) in history");
if (e.UpdatedPositions.Count > 0)
Console.WriteLine($"🔄 {e.UpdatedPositions.Count} position(s) modified");
}
4) Log trade executions
var cts = new CancellationTokenSource();
var logFile = "trade_executions.csv";
// Write CSV header
File.WriteAllText(logFile, "Timestamp,Ticket,Symbol,Type,Volume,Price,Profit\n");
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
foreach (var deal in trade.EventData.NewHistoryDeals)
{
var timestamp = DateTime.UtcNow;
var logEntry = $"{timestamp:yyyy-MM-dd HH:mm:ss}," +
$"{deal.Ticket}," +
$"{deal.Symbol}," +
$"{deal.Type}," +
$"{deal.Volume}," +
$"{deal.Price}," +
$"{deal.Profit}\n";
File.AppendAllText(logFile, logEntry);
Console.WriteLine($"Deal logged: {deal.Ticket} on {deal.Symbol}");
}
}
5) Monitor account balance changes
var cts = new CancellationTokenSource();
double? previousBalance = null;
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
var currentBalance = trade.AccountInfo.Balance;
if (previousBalance.HasValue && currentBalance != previousBalance)
{
var change = currentBalance - previousBalance.Value;
var sign = change >= 0 ? "+" : "";
Console.WriteLine($"💰 Balance changed: ${previousBalance:F2} → ${currentBalance:F2} ({sign}${change:F2})");
}
previousBalance = currentBalance;
}
6) Detect SL/TP modifications
var cts = new CancellationTokenSource();
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
foreach (var update in trade.EventData.UpdatedPositions)
{
var prev = update.PreviousPosition;
var curr = update.CurrentPosition;
if (prev.Sl != curr.Sl)
{
Console.WriteLine($"🔄 Position #{curr.Ticket} SL modified:");
Console.WriteLine($" {prev.Sl} → {curr.Sl}");
}
if (prev.Tp != curr.Tp)
{
Console.WriteLine($"🔄 Position #{curr.Ticket} TP modified:");
Console.WriteLine($" {prev.Tp} → {curr.Tp}");
}
}
}
7) Real-time trade statistics
var cts = new CancellationTokenSource();
int totalTrades = 0;
int winningTrades = 0;
int losingTrades = 0;
double totalProfit = 0;
await foreach (var trade in svc.OnTradeAsync(cts.Token))
{
foreach (var deal in trade.EventData.NewHistoryDeals)
{
// Count only entry/exit deals (not balance operations)
if (deal.Entry == SUB_ENUM_DEAL_ENTRY.SubDealEntryOut)
{
totalTrades++;
totalProfit += deal.Profit;
if (deal.Profit >= 0)
winningTrades++;
else
losingTrades++;
var winRate = totalTrades > 0 ? (winningTrades * 100.0 / totalTrades) : 0;
Console.WriteLine($"\n📊 Trade Statistics:");
Console.WriteLine($" Total trades: {totalTrades}");
Console.WriteLine($" Wins: {winningTrades} | Losses: {losingTrades}");
Console.WriteLine($" Win rate: {winRate:F1}%");
Console.WriteLine($" Total P/L: ${totalProfit:F2}");
}
}
}