Events
The client uses an event-driven architecture. Register handlers with @client.on() to receive real-time updates.
Registering Handlers
from ctrader_api_client.events import SpotEvent, ExecutionEvent
@client.on(SpotEvent, symbol_id=270) # Filter by symbol
async def on_spot(event: SpotEvent):
print(f"{event.bid}/{event.ask}")
@client.on(ExecutionEvent, account_id=12345) # Filter by account
async def on_execution(event: ExecutionEvent):
print(f"Order {event.order_id}: {event.execution_type}")
Filtering
Different events support different filters:
| Event | account_id |
symbol_id |
|---|---|---|
| SpotEvent | Yes | Yes |
| ExecutionEvent | Yes | Yes |
| DepthEvent | Yes | Yes |
| ReadyEvent | Yes | No |
| OrderErrorEvent | Yes | No |
| TraderUpdateEvent | Yes | No |
| MarginChangeEvent | Yes | No |
| ReconnectedEvent | No | No |
| ClientDisconnectEvent | No | No |
Using an unsupported filter raises ValueError at registration time.
Market Data Events
SpotEvent
Price tick event.
Emitted when bid/ask prices update for a subscribed symbol.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
symbol_id |
int
|
The symbol identifier. |
bid |
float | None
|
Current bid price |
ask |
float | None
|
Current ask price |
trendbar |
Trendbar | None
|
Live trend bar. Only populated if subscribed to trendbar updates via MarketDataAPI.subscribe_trendbars |
timestamp |
datetime
|
Server timestamp of the tick. |
SpotEvent contains live trendbar data when subscribed:
from ctrader_api_client.enums import TrendbarPeriod
# Subscribe to both spot prices and M1 trendbars
await client.market_data.subscribe_spots(account_id, [270])
await client.market_data.subscribe_trendbars(account_id, 270, TrendbarPeriod.M1)
@client.on(SpotEvent, symbol_id=270)
async def on_spot(event: SpotEvent):
# Prices are floats
print(f"Bid: {event.bid}, Ask: {event.ask}")
# Trendbar is included when subscribed
if event.trendbar:
bar = event.trendbar
print(f"Candle: O={bar.open} H={bar.high} L={bar.low} C={bar.close}")
DepthEvent
Market depth (order book) event.
Emitted when the order book updates for a subscribed symbol.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
symbol_id |
int
|
The symbol identifier. |
new_quotes |
tuple[DepthQuote, ...]
|
New or updated quotes. |
deleted_quote_ids |
tuple[int, ...]
|
IDs of quotes that were removed. |
DepthQuote
Single depth of market quote.
Attributes:
| Name | Type | Description |
|---|---|---|
quote_id |
int
|
Unique identifier for the quote (used for updates/deletions). |
price |
int
|
Quote price (raw integer, divide by 10^priceDigits). |
size |
int
|
Quote size (volume in cents). |
is_bid |
bool
|
True if this is a bid quote, False if it's an ask quote. |
Trading Events
ExecutionEvent
Order execution event.
Emitted when an order is accepted, filled, modified, canceled, etc.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
execution_type |
ExecutionType
|
Type of execution (fill, cancel, etc.). |
order_id |
int
|
The order identifier. |
position_id |
int | None
|
The position identifier (if applicable). |
symbol_id |
int
|
The symbol identifier. |
side |
OrderSide
|
Order side (buy/sell). |
filled_volume |
int | None
|
Volume filled in this execution (in cents). |
fill_price |
Decimal | None
|
Execution price (if filled). |
timestamp |
datetime
|
Server timestamp of the execution. |
is_server_event |
bool
|
True if generated by server (e.g., stop-out). |
error_code |
str | None
|
Error code if execution failed. |
OrderErrorEvent
Order error event.
Emitted when an order operation fails.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
order_id |
int | None
|
The order identifier (if available). |
position_id |
int | None
|
The position identifier (if available). |
error_code |
str
|
The error code from the server. |
description |
str
|
Human-readable error description. |
Account Events
ReadyEvent
Emitted when an account is authenticated and ready for use.
Fired after both initial authentication and reconnection re-authentication.
Use this to set up subscriptions that should persist across reconnections.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID that is now ready. |
is_reconnect |
bool
|
True if this follows a reconnection, False for initial auth. |
Use this to set up subscriptions that persist across reconnections:
@client.on(ReadyEvent)
async def on_ready(event: ReadyEvent):
await client.market_data.subscribe_spots(event.account_id, [270])
if event.is_reconnect:
print("Connection restored!")
TraderUpdateEvent
Trader account update event.
Emitted when account information changes (balance, leverage, etc.).
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
balance |
int
|
Current account balance (raw, divide by 10^moneyDigits). |
leverage_in_cents |
int | None
|
Account leverage in cents (5000 = 1:50). |
money_digits |
int
|
Exponent for monetary values. |
MarginChangeEvent
Position margin change event.
Emitted when the margin allocated to a position changes.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
position_id |
int
|
The position identifier. |
used_margin |
int
|
New margin value (raw, divide by 10^moneyDigits). |
money_digits |
int
|
Exponent for monetary values. |
MarginCallTriggerEvent
Margin call triggered event.
Emitted when account margin level reaches a configured threshold.
Sent at most once every 10 minutes per threshold to avoid spam.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
margin_call_type |
int
|
Type of margin call (1, 2, or 3 for different thresholds). |
margin_level_threshold |
Decimal
|
The threshold that was breached (percentage). |
PnLChangeEvent
Unrealized PnL change event.
Emitted when unrealized profit/loss changes due to market movement.
Requires subscription via ProtoOAv1PnLChangeSubscribeReq.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
gross_unrealized_pnl |
int
|
Gross unrealized PnL (raw, divide by 10^moneyDigits). |
net_unrealized_pnl |
int
|
Net unrealized PnL (raw, divide by 10^moneyDigits). |
money_digits |
int
|
Exponent for monetary values. |
TrailingStopChangedEvent
Trailing stop loss level changed event.
Emitted when a trailing stop loss price is updated due to
favorable price movement.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
position_id |
int
|
The position identifier. |
order_id |
int
|
The stop loss order identifier. |
stop_price |
Decimal
|
New stop loss price. |
timestamp |
datetime
|
Server timestamp of the update. |
Connection Events
ReconnectedEvent
Emitted after automatic reconnection and re-authentication.
When a connection is lost and automatically restored, the client
re-authenticates the app and all previously authenticated accounts.
Subscriptions (spots, trendbars, depth) are NOT automatically restored
and should be handled using ReadyEvent instead.
Use this event for any custom logic that depends on reconnection, such as logging or alerting.
Attributes:
| Name | Type | Description |
|---|---|---|
app_auth_restored |
bool
|
Whether app authentication succeeded. |
restored_accounts |
tuple[int, ...]
|
Account IDs that were successfully re-authenticated. |
failed_accounts |
tuple[tuple[int, str], ...]
|
Accounts that failed, as (account_id, error_message) tuples. |
Example:
@client.on(ReconnectedEvent)
async def on_reconnected(event: ReconnectedEvent):
print(f"Reconnected! App auth: {event.app_auth_restored}")
print(f"Restored accounts: {event.restored_accounts}")
if event.failed_accounts:
print(f"Failed accounts: {event.failed_accounts}")
ClientDisconnectEvent
Client disconnect event.
Emitted when the server terminates the client connection.
Attributes:
| Name | Type | Description |
|---|---|---|
reason |
str
|
Reason for disconnection. |
AccountDisconnectEvent
Account disconnect event.
Emitted when a specific account session is terminated.
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
TokenInvalidatedEvent
Token invalidated event.
Emitted when access tokens are revoked or expired.
Attributes:
| Name | Type | Description |
|---|---|---|
account_ids |
tuple[int, ...]
|
List of affected account IDs. |
reason |
str
|
Reason for invalidation. |
Symbol Events
SymbolChangedEvent
Symbol configuration changed event.
Emitted when a symbol's configuration is updated (trading hours,
margin requirements, spreads, etc.).
Attributes:
| Name | Type | Description |
|---|---|---|
account_id |
int
|
The cTID trader account ID. |
symbol_ids |
tuple[int, ...]
|
List of symbol IDs that changed. |