Stream Trade Transaction Events¶
Request: subscribe to real-time trade transaction events (detailed audit trail).
API Information:
- Python API:
MT5Account.on_trade_transaction(...)(defined inpackage/MetaRpcMT5/helpers/mt5_account.py) - gRPC service:
mt5_term_api.SubscriptionService - Proto definition:
OnTradeTransaction(defined inmt5-term-api-subscriptions.proto)
RPC¶
- Service:
mt5_term_api.SubscriptionService - Method:
OnTradeTransaction(OnTradeTransactionRequest) -> stream OnTradeTransactionReply - Low-level client (generated):
SubscriptionServiceStub.OnTradeTransaction(request, metadata)
from MetaRpcMT5 import MT5Account
class MT5Account:
# ...
async def on_trade_transaction(
self,
cancellation_event: Optional[asyncio.Event] = None,
):
"""
Subscribes to real-time trade transaction events.
Yields:
OnTradeTransactionData: Trade transaction data.
"""
Request message:
💬 Just the essentials¶
- What it is. Real-time stream of detailed trade transaction events (most comprehensive).
- Why you need it. Complete audit trail, detailed transaction logging, trade verification.
- Event-driven. Updates triggered immediately on every transaction event.
🎯 Purpose¶
Use it to:
- Build complete trade audit logs
- Track detailed transaction lifecycle
- Monitor order state changes in detail
- Verify trade execution
- Debug trading operations
- Implement transaction-based logic
- Create detailed trade journals
🔽 Input¶
| Parameter | Type | Description |
|---|---|---|
cancellation_event |
Optional[asyncio.Event] |
Event to cancel streaming (optional) |
⬆️ Output - Async Generator¶
Returns an async generator that yields OnTradeTransactionData objects.
OnTradeTransactionData fields:
| Field | Type | Description |
|---|---|---|
type |
int |
Transaction type |
trade_transaction |
MqlTradeTransaction |
Detailed transaction information |
trade_request |
MqlTradeRequest |
Original trade request |
trade_result |
MqlTradeResult |
Trade execution result |
account_info |
OnEventAccountInfo |
Current account state |
terminal_instance_guid_id |
str |
Terminal instance identifier |
Order type enum (SUB_ENUM_ORDER_TYPE):
| Name | Value | Description |
|---|---|---|
SUB_ORDER_TYPE_BUY |
0 | Market buy order |
SUB_ORDER_TYPE_SELL |
1 | Market sell order |
SUB_ORDER_TYPE_BUY_LIMIT |
2 | Buy limit pending order |
SUB_ORDER_TYPE_SELL_LIMIT |
3 | Sell limit pending order |
SUB_ORDER_TYPE_BUY_STOP |
4 | Buy stop pending order |
SUB_ORDER_TYPE_SELL_STOP |
5 | Sell stop pending order |
SUB_ORDER_TYPE_BUY_STOP_LIMIT |
6 | Buy stop limit pending order |
SUB_ORDER_TYPE_SELL_STOP_LIMIT |
7 | Sell stop limit pending order |
SUB_ORDER_TYPE_CLOSE_BY |
8 | Close by opposite position |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_ORDER_TYPE_BUY # = 0
Order state enum (SUB_ENUM_ORDER_STATE):
| Name | Value | Description |
|---|---|---|
SUB_ORDER_STATE_STARTED |
0 | Order checked, but not yet accepted |
SUB_ORDER_STATE_PLACED |
1 | Order accepted |
SUB_ORDER_STATE_CANCELED |
2 | Order canceled by client |
SUB_ORDER_STATE_PARTIAL |
3 | Order partially executed |
SUB_ORDER_STATE_FILLED |
4 | Order fully executed |
SUB_ORDER_STATE_REJECTED |
5 | Order rejected |
SUB_ORDER_STATE_EXPIRED |
6 | Order expired |
SUB_ORDER_STATE_REQUEST_ADD |
7 | Order being registered |
SUB_ORDER_STATE_REQUEST_MODIFY |
8 | Order being modified |
SUB_ORDER_STATE_REQUEST_CANCEL |
9 | Order being deleted |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_ORDER_STATE_FILLED # = 4
Deal type enum (SUB_ENUM_DEAL_TYPE):
| Name | Value | Description |
|---|---|---|
SUB_DEAL_TYPE_BUY |
0 | Buy deal |
SUB_DEAL_TYPE_SELL |
1 | Sell deal |
SUB_DEAL_TYPE_BALANCE |
2 | Balance operation |
SUB_DEAL_TYPE_CREDIT |
3 | Credit operation |
SUB_DEAL_TYPE_CHARGE |
4 | Additional charge |
SUB_DEAL_TYPE_CORRECTION |
5 | Correction |
SUB_DEAL_TYPE_BONUS |
6 | Bonus |
SUB_DEAL_TYPE_COMMISSION |
7 | Commission |
SUB_DEAL_TYPE_COMMISSION_DAILY |
8 | Daily commission |
SUB_DEAL_TYPE_COMMISSION_MONTHLY |
9 | Monthly commission |
SUB_DEAL_TYPE_COMMISSION_AGENT_DAILY |
10 | Agent daily commission |
SUB_DEAL_TYPE_COMMISSION_AGENT_MONTHLY |
11 | Agent monthly commission |
SUB_DEAL_TYPE_INTEREST |
12 | Interest rate |
SUB_DEAL_TYPE_BUY_CANCELED |
13 | Canceled buy deal |
SUB_DEAL_TYPE_SELL_CANCELED |
14 | Canceled sell deal |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_DEAL_TYPE_BUY # = 0
Deal entry enum (SUB_ENUM_DEAL_ENTRY):
| Name | Value | Description |
|---|---|---|
SUB_DEAL_ENTRY_IN |
0 | Entry into position |
SUB_DEAL_ENTRY_OUT |
1 | Exit from position |
SUB_DEAL_ENTRY_INOUT |
2 | Reverse of position |
SUB_DEAL_ENTRY_OUT_BY |
3 | Close by opposite position |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_DEAL_ENTRY_IN # = 0
Deal reason enum (SUB_ENUM_DEAL_REASON):
| Name | Value | Description |
|---|---|---|
SUB_DEAL_REASON_CLIENT |
0 | Deal placed from desktop terminal |
SUB_DEAL_REASON_MOBILE |
1 | Deal placed from mobile app |
SUB_DEAL_REASON_WEB |
2 | Deal placed from web terminal |
SUB_DEAL_REASON_EXPERT |
3 | Deal placed by Expert Advisor |
SUB_DEAL_REASON_SL |
4 | Deal due to Stop Loss activation |
SUB_DEAL_REASON_TP |
5 | Deal due to Take Profit activation |
SUB_DEAL_REASON_SO |
6 | Deal due to Stop Out |
SUB_DEAL_REASON_ROLLOVER |
7 | Deal due to rollover |
SUB_DEAL_REASON_VMARGIN |
8 | Deal due to variation margin |
SUB_DEAL_REASON_SPLIT |
9 | Deal due to split |
SUB_DEAL_REASON_CORPORATE_ACTION |
10 | Deal due to corporate action |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_DEAL_REASON_CLIENT # = 0
Order time type enum (SUB_ENUM_ORDER_TYPE_TIME):
| Name | Value | Description |
|---|---|---|
SUB_ORDER_TIME_GTC |
0 | Good till cancel |
SUB_ORDER_TIME_DAY |
1 | Good till current trading day |
SUB_ORDER_TIME_SPECIFIED |
2 | Good till specified date |
SUB_ORDER_TIME_SPECIFIED_DAY |
3 | Good till specified day |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_ORDER_TIME_GTC # = 0
Order filling type enum (SUB_ENUM_ORDER_TYPE_FILLING):
| Name | Value | Description |
|---|---|---|
SUB_ORDER_FILLING_FOK |
0 | Fill or Kill |
SUB_ORDER_FILLING_IOC |
1 | Immediate or Cancel |
SUB_ORDER_FILLING_BOC |
2 | Book or Cancel |
SUB_ORDER_FILLING_RETURN |
3 | Return |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_ORDER_FILLING_FOK # = 0
Order reason enum (SUB_ENUM_ORDER_REASON):
| Name | Value | Description |
|---|---|---|
SUB_ORDER_REASON_CLIENT |
0 | Order placed from desktop terminal |
SUB_ORDER_REASON_MOBILE |
2 | Order placed from mobile app |
SUB_ORDER_REASON_WEB |
3 | Order placed from web terminal |
SUB_ORDER_REASON_EXPERT |
4 | Order placed by Expert Advisor |
SUB_ORDER_REASON_SL |
5 | Order triggered by Stop Loss |
SUB_ORDER_REASON_TP |
6 | Order triggered by Take Profit |
SUB_ORDER_REASON_SO |
7 | Order triggered by Stop Out |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_ORDER_REASON_CLIENT # = 0
Position type enum (SUB_ENUM_POSITION_TYPE):
| Name | Value | Description |
|---|---|---|
SUB_POSITION_TYPE_BUY |
0 | Buy position |
SUB_POSITION_TYPE_SELL |
1 | Sell position |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_POSITION_TYPE_BUY # = 0
Position reason enum (SUB_ENUM_POSITION_REASON):
| Name | Value | Description |
|---|---|---|
SUB_POSITION_REASON_CLIENT |
0 | Position from desktop terminal |
SUB_POSITION_REASON_MOBILE |
2 | Position from mobile app |
SUB_POSITION_REASON_WEB |
3 | Position from web terminal |
SUB_POSITION_REASON_EXPERT |
4 | Position from Expert Advisor |
Usage:
import MetaRpcMT5.mt5_term_api_subscriptions_pb2 as subscriptions_pb2
subscriptions_pb2.SUB_POSITION_REASON_CLIENT # = 0
MqlTradeTransaction fields (partial list):
| Field | Type | Description |
|---|---|---|
deal_ticket |
uint64 |
Deal ticket number |
order_ticket |
uint64 |
Order ticket number |
symbol |
string |
Trading symbol |
type |
enum |
Transaction type |
order_type |
enum |
Order type (market, limit, stop, etc.) |
order_state |
enum |
Order state (started, placed, etc.) |
deal_type |
enum |
Deal type (buy, sell) |
order_time_type |
enum |
Order time type (GTC, DAY, etc.) |
price |
double |
Price |
price_stop_loss |
double |
Stop loss price |
price_take_profit |
double |
Take profit price |
volume |
double |
Volume |
position_ticket |
uint64 |
Position ticket |
MqlTradeRequest fields:
| Field | Type | Description |
|---|---|---|
action |
enum |
Trade operation type |
magic |
uint64 |
Expert Advisor ID |
order |
uint64 |
Order ticket |
symbol |
string |
Trading symbol |
volume |
double |
Requested volume |
price |
double |
Price |
MqlTradeResult fields:
| Field | Type | Description |
|---|---|---|
retcode |
uint32 |
Operation return code |
deal |
uint64 |
Deal ticket |
order |
uint64 |
Order ticket |
volume |
double |
Deal volume |
price |
double |
Deal price |
bid |
double |
Current Bid price |
ask |
double |
Current Ask price |
comment |
string |
Broker comment |
📚 Tutorial¶
For a detailed line-by-line explanation with examples, see: OnTradeTransaction - How it works
🧩 Notes & Tips¶
- Automatic reconnection: All
MT5Accountstreaming methods have built-in protection against transient gRPC errors with automatic reconnection viaexecute_stream_with_reconnect. - Async generator: The method returns an async generator - use
async forto consume data. - Most detailed: This is the most comprehensive transaction stream - includes full request/result data.
- No parameters: Subscribes to ALL trade transactions automatically.
- Event-driven: Updates arrive immediately when transactions occur (not periodic).
- Complete lifecycle: Tracks order placement, modification, execution, and cancellation.
- Includes failures: Receives events for both successful and failed transactions.
- Resource intensive: Most detailed stream - use
on_tradeif you don't need this level of detail.
🔗 Usage Examples¶
1) Basic transaction monitor¶
import asyncio
from MetaRpcMT5 import MT5Account
async def monitor_transactions():
account = MT5Account(
user=12345,
password="password",
grpc_server="mt5.mrpc.pro:443"
)
await account.connect_by_server_name(
server_name="YourBroker-Demo",
base_chart_symbol="EURUSD"
)
try:
async for tx_data in account.on_trade_transaction():
tx = tx_data.trade_transaction
print(f"\n[TRANSACTION EVENT]")
print(f" Type: {tx.type}")
print(f" Symbol: {tx.symbol}")
print(f" Order: #{tx.order_ticket}")
print(f" Deal: #{tx.deal_ticket}")
print(f" Position: #{tx.position_ticket}")
print(f" Price: {tx.price}")
print(f" Volume: {tx.volume}")
print(f" Order state: {tx.order_state}")
# Show request if available
if tx_data.trade_request and tx_data.trade_request.symbol:
req = tx_data.trade_request
print(f"\n Request:")
print(f" Symbol: {req.symbol}")
print(f" Volume: {req.volume}")
print(f" Price: {req.price}")
# Show result if available
if tx_data.trade_result and tx_data.trade_result.retcode != 0:
res = tx_data.trade_result
print(f"\n Result:")
print(f" Return code: {res.retcode}")
print(f" Comment: {res.comment}")
except KeyboardInterrupt:
print("\nStopping transaction monitor...")
finally:
await account.channel.close()
asyncio.run(monitor_transactions())
2) Complete transaction audit log¶
import asyncio
from datetime import datetime
from MetaRpcMT5 import MT5Account
async def audit_log():
account = MT5Account(
user=12345,
password="password",
grpc_server="mt5.mrpc.pro:443"
)
await account.connect_by_server_name(
server_name="YourBroker-Demo",
base_chart_symbol="EURUSD"
)
try:
with open('trade_audit.log', 'w') as logfile:
logfile.write("Timestamp,Type,Symbol,Order,Deal,Position,Price,Volume,State\n")
async for tx_data in account.on_trade_transaction():
tx = tx_data.trade_transaction
timestamp = datetime.now().isoformat()
# Write to audit log
logfile.write(
f"{timestamp},"
f"{tx.type},"
f"{tx.symbol},"
f"{tx.order_ticket},"
f"{tx.deal_ticket},"
f"{tx.position_ticket},"
f"{tx.price},"
f"{tx.volume},"
f"{tx.order_state}\n"
)
logfile.flush()
print(f"[{timestamp}] Logged transaction: "
f"Order #{tx.order_ticket}, Deal #{tx.deal_ticket}")
except KeyboardInterrupt:
print("\nStopping audit log...")
finally:
await account.channel.close()
asyncio.run(audit_log())
3) Order lifecycle tracker¶
import asyncio
from MetaRpcMT5 import MT5Account
async def track_order_lifecycle():
account = MT5Account(
user=12345,
password="password",
grpc_server="mt5.mrpc.pro:443"
)
await account.connect_by_server_name(
server_name="YourBroker-Demo",
base_chart_symbol="EURUSD"
)
order_states = {} # order_ticket -> list of states
try:
async for tx_data in account.on_trade_transaction():
tx = tx_data.trade_transaction
if tx.order_ticket > 0:
# Track order state changes
if tx.order_ticket not in order_states:
order_states[tx.order_ticket] = []
order_states[tx.order_ticket].append({
'state': tx.order_state,
'type': tx.type,
'price': tx.price,
'volume': tx.volume
})
print(f"\n[LIFECYCLE] Order #{tx.order_ticket}:")
for i, state in enumerate(order_states[tx.order_ticket], 1):
print(f" {i}. State: {state['state']}, "
f"Type: {state['type']}, "
f"Price: {state['price']}")
except KeyboardInterrupt:
print("\nStopping lifecycle tracker...")
print(f"\nTracked {len(order_states)} orders")
finally:
await account.channel.close()
asyncio.run(track_order_lifecycle())
4) Failed transaction monitor¶
import asyncio
from MetaRpcMT5 import MT5Account
async def monitor_failed_transactions():
account = MT5Account(
user=12345,
password="password",
grpc_server="mt5.mrpc.pro:443"
)
await account.connect_by_server_name(
server_name="YourBroker-Demo",
base_chart_symbol="EURUSD"
)
failed_count = 0
try:
async for tx_data in account.on_trade_transaction():
# Check for failed transactions
if tx_data.trade_result and tx_data.trade_result.retcode != 10009: # Not DONE
failed_count += 1
res = tx_data.trade_result
tx = tx_data.trade_transaction
print(f"\n[FAILED TRANSACTION #{failed_count}]")
print(f" Return code: {res.retcode}")
print(f" Comment: {res.comment}")
print(f" Symbol: {tx.symbol}")
print(f" Order: #{tx.order_ticket}")
print(f" Requested volume: {tx.volume}")
print(f" Price: {tx.price}")
if tx_data.trade_request:
req = tx_data.trade_request
print(f"\n Original request:")
print(f" Action: {req.action}")
print(f" Symbol: {req.symbol}")
print(f" Volume: {req.volume}")
except KeyboardInterrupt:
print(f"\n\nStopping monitor. Total failed: {failed_count}")
finally:
await account.channel.close()
asyncio.run(monitor_failed_transactions())
5) Transaction statistics collector¶
import asyncio
from collections import defaultdict
from MetaRpcMT5 import MT5Account
async def collect_transaction_stats(duration_seconds=300):
account = MT5Account(
user=12345,
password="password",
grpc_server="mt5.mrpc.pro:443"
)
await account.connect_by_server_name(
server_name="YourBroker-Demo",
base_chart_symbol="EURUSD"
)
stats = {
'total_transactions': 0,
'by_type': defaultdict(int),
'by_state': defaultdict(int),
'by_symbol': defaultdict(int),
'successful': 0,
'failed': 0,
}
cancel_event = asyncio.Event()
async def auto_stop():
await asyncio.sleep(duration_seconds)
cancel_event.set()
try:
stop_task = asyncio.create_task(auto_stop())
async for tx_data in account.on_trade_transaction(
cancellation_event=cancel_event
):
tx = tx_data.trade_transaction
stats['total_transactions'] += 1
stats['by_type'][tx.type] += 1
stats['by_state'][tx.order_state] += 1
if tx.symbol:
stats['by_symbol'][tx.symbol] += 1
# Check success/failure
if tx_data.trade_result:
if tx_data.trade_result.retcode == 10009: # DONE
stats['successful'] += 1
else:
stats['failed'] += 1
await stop_task
# Print statistics
print("\n" + "=" * 70)
print("TRANSACTION STATISTICS REPORT")
print("=" * 70)
print(f"Total transactions: {stats['total_transactions']:>6,}")
print(f"Successful: {stats['successful']:>6,}")
print(f"Failed: {stats['failed']:>6,}")
print("\nBy Type:")
for tx_type, count in sorted(stats['by_type'].items()):
print(f" Type {tx_type}: {count:>6,}")
print("\nBy State:")
for state, count in sorted(stats['by_state'].items()):
print(f" State {state}: {count:>6,}")
print("\nBy Symbol:")
for symbol, count in sorted(stats['by_symbol'].items(),
key=lambda x: x[1], reverse=True)[:10]:
print(f" {symbol}: {count:>6,}")
print("=" * 70)
finally:
await account.channel.close()
# Collect statistics for 5 minutes
asyncio.run(collect_transaction_stats(300))
6) Real-time trade journal¶
import asyncio
from datetime import datetime
from MetaRpcMT5 import MT5Account
async def trade_journal():
account = MT5Account(
user=12345,
password="password",
grpc_server="mt5.mrpc.pro:443"
)
await account.connect_by_server_name(
server_name="YourBroker-Demo",
base_chart_symbol="EURUSD"
)
journal_entries = []
try:
async for tx_data in account.on_trade_transaction():
tx = tx_data.trade_transaction
# Create journal entry
entry = {
'timestamp': datetime.now(),
'type': tx.type,
'symbol': tx.symbol,
'order_ticket': tx.order_ticket,
'deal_ticket': tx.deal_ticket,
'position_ticket': tx.position_ticket,
'price': tx.price,
'volume': tx.volume,
'order_state': tx.order_state,
'sl': tx.price_stop_loss,
'tp': tx.price_take_profit,
}
# Add result info if available
if tx_data.trade_result:
entry['retcode'] = tx_data.trade_result.retcode
entry['comment'] = tx_data.trade_result.comment
# Add account balance
if tx_data.account_info:
entry['balance'] = tx_data.account_info.balance
entry['equity'] = tx_data.account_info.equity
journal_entries.append(entry)
# Display entry
print(f"\n{'=' * 70}")
print(f"JOURNAL ENTRY #{len(journal_entries)}")
print(f"{'=' * 70}")
print(f"Time: {entry['timestamp'].strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Symbol: {entry['symbol']}")
print(f"Type: {entry['type']}")
print(f"Order: #{entry['order_ticket']}")
print(f"Deal: #{entry['deal_ticket']}")
print(f"Position: #{entry['position_ticket']}")
print(f"Price: {entry['price']}")
print(f"Volume: {entry['volume']}")
print(f"SL: {entry['sl']}")
print(f"TP: {entry['tp']}")
print(f"State: {entry['order_state']}")
if 'balance' in entry:
print(f"Balance: ${entry['balance']:.2f}")
print(f"Equity: ${entry['equity']:.2f}")
# Save journal periodically
if len(journal_entries) % 10 == 0:
print(f"\n[SAVED] {len(journal_entries)} entries to journal")
except KeyboardInterrupt:
print(f"\n\nStopping journal. Total entries: {len(journal_entries)}")
# Export journal
with open('trade_journal.txt', 'w') as f:
f.write("TRADE JOURNAL\n")
f.write("=" * 70 + "\n\n")
for i, entry in enumerate(journal_entries, 1):
f.write(f"Entry #{i}\n")
f.write(f" Time: {entry['timestamp']}\n")
f.write(f" Symbol: {entry['symbol']}\n")
f.write(f" Order: #{entry['order_ticket']}\n")
f.write(f" Price: {entry['price']}\n")
f.write("\n")
print(f"Journal exported to trade_journal.txt")
finally:
await account.channel.close()
asyncio.run(trade_journal())
📚 See also¶
- OnTrade - Simpler trade event stream (less detailed)
- OrderSend - Send trade orders
- OrderCheck - Verify order before sending
- OrderHistory - Get historical orders