251 lines
9.7 KiB
Python
251 lines
9.7 KiB
Python
"""
|
|
Event definitions for the Exchange Kernel.
|
|
|
|
All events that can occur during the order lifecycle, position management,
|
|
and account updates.
|
|
"""
|
|
|
|
from enum import StrEnum
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from ..schema.order_spec import Float, Uint64
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Base Event Classes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class EventType(StrEnum):
|
|
"""Types of events emitted by the exchange kernel"""
|
|
# Order lifecycle
|
|
ORDER_SUBMITTED = "ORDER_SUBMITTED"
|
|
ORDER_ACCEPTED = "ORDER_ACCEPTED"
|
|
ORDER_REJECTED = "ORDER_REJECTED"
|
|
ORDER_PARTIALLY_FILLED = "ORDER_PARTIALLY_FILLED"
|
|
ORDER_FILLED = "ORDER_FILLED"
|
|
ORDER_CANCELED = "ORDER_CANCELED"
|
|
ORDER_MODIFIED = "ORDER_MODIFIED"
|
|
ORDER_EXPIRED = "ORDER_EXPIRED"
|
|
|
|
# Position events
|
|
POSITION_OPENED = "POSITION_OPENED"
|
|
POSITION_MODIFIED = "POSITION_MODIFIED"
|
|
POSITION_CLOSED = "POSITION_CLOSED"
|
|
|
|
# Account events
|
|
ACCOUNT_BALANCE_UPDATED = "ACCOUNT_BALANCE_UPDATED"
|
|
MARGIN_CALL_WARNING = "MARGIN_CALL_WARNING"
|
|
|
|
# System events
|
|
RECONCILIATION_FAILED = "RECONCILIATION_FAILED"
|
|
CONNECTION_LOST = "CONNECTION_LOST"
|
|
CONNECTION_RESTORED = "CONNECTION_RESTORED"
|
|
|
|
|
|
class BaseEvent(BaseModel):
|
|
"""Base class for all exchange kernel events"""
|
|
|
|
model_config = {"extra": "forbid"}
|
|
|
|
event_type: EventType = Field(description="Type of event")
|
|
timestamp: Uint64 = Field(description="Event timestamp (Unix milliseconds)")
|
|
exchange: str = Field(description="Exchange identifier")
|
|
metadata: dict[str, Any] = Field(default_factory=dict, description="Additional event data")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Order Events
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class OrderEvent(BaseEvent):
|
|
"""Base class for order-related events"""
|
|
|
|
intent_id: str = Field(description="Order intent ID")
|
|
order_id: str | None = Field(default=None, description="Exchange order ID (if assigned)")
|
|
symbol_id: str = Field(description="Symbol being traded")
|
|
|
|
|
|
class OrderSubmitted(OrderEvent):
|
|
"""Order has been submitted to the exchange"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_SUBMITTED)
|
|
client_order_id: str | None = Field(default=None, description="Client-assigned order ID")
|
|
|
|
|
|
class OrderAccepted(OrderEvent):
|
|
"""Order has been accepted by the exchange"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_ACCEPTED)
|
|
order_id: str = Field(description="Exchange-assigned order ID")
|
|
accepted_at: Uint64 = Field(description="Exchange acceptance timestamp")
|
|
|
|
|
|
class OrderRejected(OrderEvent):
|
|
"""Order was rejected by the exchange"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_REJECTED)
|
|
reason: str = Field(description="Rejection reason")
|
|
error_code: str | None = Field(default=None, description="Exchange error code")
|
|
|
|
|
|
class OrderPartiallyFilled(OrderEvent):
|
|
"""Order was partially filled"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_PARTIALLY_FILLED)
|
|
order_id: str = Field(description="Exchange order ID")
|
|
fill_price: Float = Field(description="Fill price for this execution")
|
|
fill_quantity: Float = Field(description="Quantity filled in this execution")
|
|
total_filled: Float = Field(description="Total quantity filled so far")
|
|
remaining_quantity: Float = Field(description="Remaining quantity to fill")
|
|
commission: Float = Field(default=0.0, description="Commission/fee for this fill")
|
|
commission_asset: str | None = Field(default=None, description="Asset used for commission")
|
|
trade_id: str | None = Field(default=None, description="Exchange trade ID")
|
|
|
|
|
|
class OrderFilled(OrderEvent):
|
|
"""Order was completely filled"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_FILLED)
|
|
order_id: str = Field(description="Exchange order ID")
|
|
average_fill_price: Float = Field(description="Average execution price")
|
|
total_quantity: Float = Field(description="Total quantity filled")
|
|
total_commission: Float = Field(default=0.0, description="Total commission/fees")
|
|
commission_asset: str | None = Field(default=None, description="Asset used for commission")
|
|
completed_at: Uint64 = Field(description="Completion timestamp")
|
|
|
|
|
|
class OrderCanceled(OrderEvent):
|
|
"""Order was canceled"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_CANCELED)
|
|
order_id: str = Field(description="Exchange order ID")
|
|
reason: str = Field(description="Cancellation reason")
|
|
filled_quantity: Float = Field(default=0.0, description="Quantity filled before cancellation")
|
|
canceled_at: Uint64 = Field(description="Cancellation timestamp")
|
|
|
|
|
|
class OrderModified(OrderEvent):
|
|
"""Order was modified (price, quantity, etc.)"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_MODIFIED)
|
|
order_id: str = Field(description="Exchange order ID")
|
|
old_price: Float | None = Field(default=None, description="Previous price")
|
|
new_price: Float | None = Field(default=None, description="New price")
|
|
old_quantity: Float | None = Field(default=None, description="Previous quantity")
|
|
new_quantity: Float | None = Field(default=None, description="New quantity")
|
|
modified_at: Uint64 = Field(description="Modification timestamp")
|
|
|
|
|
|
class OrderExpired(OrderEvent):
|
|
"""Order expired (GTD, DAY orders)"""
|
|
|
|
event_type: EventType = Field(default=EventType.ORDER_EXPIRED)
|
|
order_id: str = Field(description="Exchange order ID")
|
|
filled_quantity: Float = Field(default=0.0, description="Quantity filled before expiration")
|
|
expired_at: Uint64 = Field(description="Expiration timestamp")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Position Events
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class PositionEvent(BaseEvent):
|
|
"""Base class for position-related events"""
|
|
|
|
position_id: str = Field(description="Position identifier")
|
|
symbol_id: str = Field(description="Symbol identifier")
|
|
asset_id: str = Field(description="Asset identifier")
|
|
|
|
|
|
class PositionOpened(PositionEvent):
|
|
"""New position was opened"""
|
|
|
|
event_type: EventType = Field(default=EventType.POSITION_OPENED)
|
|
quantity: Float = Field(description="Position quantity")
|
|
entry_price: Float = Field(description="Entry price")
|
|
side: str = Field(description="LONG or SHORT")
|
|
leverage: Float | None = Field(default=None, description="Leverage")
|
|
|
|
|
|
class PositionModified(PositionEvent):
|
|
"""Existing position was modified (size change, etc.)"""
|
|
|
|
event_type: EventType = Field(default=EventType.POSITION_MODIFIED)
|
|
old_quantity: Float = Field(description="Previous quantity")
|
|
new_quantity: Float = Field(description="New quantity")
|
|
average_entry_price: Float = Field(description="Updated average entry price")
|
|
unrealized_pnl: Float | None = Field(default=None, description="Current unrealized P&L")
|
|
|
|
|
|
class PositionClosed(PositionEvent):
|
|
"""Position was closed"""
|
|
|
|
event_type: EventType = Field(default=EventType.POSITION_CLOSED)
|
|
exit_price: Float = Field(description="Exit price")
|
|
realized_pnl: Float = Field(description="Realized profit/loss")
|
|
closed_at: Uint64 = Field(description="Closure timestamp")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Account Events
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class AccountEvent(BaseEvent):
|
|
"""Base class for account-related events"""
|
|
|
|
account_id: str = Field(description="Account identifier")
|
|
|
|
|
|
class AccountBalanceUpdated(AccountEvent):
|
|
"""Account balance was updated"""
|
|
|
|
event_type: EventType = Field(default=EventType.ACCOUNT_BALANCE_UPDATED)
|
|
asset_id: str = Field(description="Asset that changed")
|
|
old_balance: Float = Field(description="Previous balance")
|
|
new_balance: Float = Field(description="New balance")
|
|
old_available: Float = Field(description="Previous available")
|
|
new_available: Float = Field(description="New available")
|
|
change_reason: str = Field(description="Why balance changed (TRADE, DEPOSIT, WITHDRAWAL, etc.)")
|
|
|
|
|
|
class MarginCallWarning(AccountEvent):
|
|
"""Margin level is approaching liquidation threshold"""
|
|
|
|
event_type: EventType = Field(default=EventType.MARGIN_CALL_WARNING)
|
|
margin_level: Float = Field(description="Current margin level")
|
|
liquidation_threshold: Float = Field(description="Liquidation threshold")
|
|
required_action: str = Field(description="Required action to avoid liquidation")
|
|
estimated_liquidation_price: Float | None = Field(
|
|
default=None,
|
|
description="Estimated liquidation price for positions"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# System Events
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class ReconciliationFailed(BaseEvent):
|
|
"""Failed to reconcile intent with actual state"""
|
|
|
|
event_type: EventType = Field(default=EventType.RECONCILIATION_FAILED)
|
|
intent_id: str = Field(description="Order intent ID")
|
|
error_message: str = Field(description="Error details")
|
|
retry_count: int = Field(description="Number of retry attempts")
|
|
|
|
|
|
class ConnectionLost(BaseEvent):
|
|
"""Connection to exchange was lost"""
|
|
|
|
event_type: EventType = Field(default=EventType.CONNECTION_LOST)
|
|
reason: str = Field(description="Disconnection reason")
|
|
|
|
|
|
class ConnectionRestored(BaseEvent):
|
|
"""Connection to exchange was restored"""
|
|
|
|
event_type: EventType = Field(default=EventType.CONNECTION_RESTORED)
|
|
downtime_duration: int = Field(description="Duration of downtime in milliseconds")
|