SPB Git

spb/wp27_uqo

Public
4.0 KB · 134 lines python
1"""Core typed records for the exchange.23Conventions (exact integer arithmetic everywhere):4- prices are integers in *ticks* (tick_size in currency units is a property of the asset);5- quantities are integers in *lots*;6- cash is an integer in *cents* (1 currency unit = 100 cents); a trade's notional in cents is7  price_ticks * tick_cents * qty, where tick_cents = tick_size * 100 must be an integer.8"""9from __future__ import annotations1011from dataclasses import dataclass, field12from enum import Enum, IntEnum13from typing import Literal141516class Side(IntEnum):17    BUY = 118    SELL = -11920    @property21    def opposite(self) -> "Side":22        return Side.SELL if self is Side.BUY else Side.BUY2324    @property25    def label(self) -> str:26        return "buy" if self is Side.BUY else "sell"272829class OrderType(str, Enum):30    LIMIT = "limit"31    MARKET = "market"323334class OrderStatus(str, Enum):35    PENDING = "pending"      # in the latency queue, not yet at the exchange36    OPEN = "open"            # resting in the book (possibly partially filled)37    FILLED = "filled"38    CANCELLED = "cancelled"  # cancelled by the owner (possibly after partial fills)39    REJECTED = "rejected"    # never entered the book404142@dataclass(slots=True)43class Order:44    order_id: int45    agent_id: int46    asset: int47    side: Side48    order_type: OrderType49    qty: int                     # original quantity (lots)50    price: int | None            # limit price in ticks (None for market orders)51    submit_time: float           # agent decision time (ticks)52    arrival_time: float          # exchange arrival time = submit_time + latency53    remaining: int = 054    status: OrderStatus = OrderStatus.PENDING55    reserved_cash: int = 0       # cents reserved at acceptance (bids)56    reserved_qty: int = 0        # lots reserved at acceptance (asks)57    tag: str = ""                # free-form (policy name, 'liquidation', ...)58    reject_reason: str = ""5960    def __post_init__(self) -> None:61        if self.remaining == 0 and self.status is OrderStatus.PENDING:62            self.remaining = self.qty6364    @property65    def filled(self) -> int:66        return self.qty - self.remaining676869@dataclass(slots=True)70class Trade:71    trade_id: int72    time: float73    asset: int74    price: int                   # ticks (the resting order's price)75    qty: int76    buy_order_id: int77    sell_order_id: int78    buyer_id: int79    seller_id: int80    aggressor: Side              # side of the incoming (taker) order81    maker_fee: int               # cents paid by the maker (may be negative = rebate)82    taker_fee: int               # cents paid by the taker8384    @property85    def taker_id(self) -> int:86        return self.buyer_id if self.aggressor is Side.BUY else self.seller_id8788    @property89    def maker_id(self) -> int:90        return self.seller_id if self.aggressor is Side.BUY else self.buyer_id919293EventKind = Literal[94    "submit", "accept", "reject", "trade", "cancel", "expire", "halt", "resume",95    "dividend", "margin_call", "liquidate", "bankrupt", "tick", "auction",96]979899@dataclass(slots=True)100class Event:101    """One row of the event-sourced log. `payload` is a flat dict of scalars (parquet-friendly)."""102    seq: int103    time: float104    kind: str105    payload: dict = field(default_factory=dict)106107108@dataclass(slots=True)109class OrderIntent:110    """What an agent *wants* to do; converted to an Order by the exchange on arrival."""111    asset: int112    side: Side113    order_type: OrderType114    qty: int115    price: int | None = None     # ticks116    tag: str = ""117118119@dataclass(slots=True)120class CancelIntent:121    order_id: int122123124@dataclass(slots=True)125class Action:126    """A bundle of intents emitted by one agent at one decision time."""127    orders: list[OrderIntent] = field(default_factory=list)128    cancels: list[CancelIntent] = field(default_factory=list)129    meta: dict = field(default_factory=dict)   # e.g. LLM rationale/confidence/validity flags130131    @property132    def is_hold(self) -> bool:133        return not self.orders and not self.cancels134
Files · main
Menu

Theme