betex 0.5.2

Betfair / Prediction Market Exchange
Documentation
use betex::{
    protocol::{
        command::{BinarySide, BinaryTimeInForce, MarketState, Persistence, Side, TimeInForce},
        response::{Response, TradeSummary},
    },
    snapshot::MarketSnapshot,
    types::{
        AccountId, CorrelationId, MarketId, MarketKind, MarketModel, OddsX10000, OrderId, RunnerId,
    },
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, strum::Display)]
#[serde(rename_all = "snake_case")]
pub enum MarketStatus {
    Active,
    Closing,
    Closed,
    Settled,
    Voided,
}

impl MarketStatus {
    pub fn is_terminal(&self) -> bool {
        matches!(self, Self::Settled | Self::Voided)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketInfo {
    pub market_id: MarketId,
    pub engine_id: Uuid,
    pub name: String,
    pub market_model: MarketModel,
    pub market_kind: MarketKind,
    pub runner_ids: Vec<RunnerId>,
    pub runner_labels: Vec<String>,
    pub status: MarketStatus,
    pub created_at: i64,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ClientMsg {
    // Admin commands
    SetMarketState {
        #[serde(default)]
        market_id: Option<MarketId>,
        state: MarketState,
    },
    CloseMarket {
        #[serde(default)]
        market_id: Option<MarketId>,
        batch_max_events: u16,
    },
    SettleMarket {
        #[serde(default)]
        market_id: Option<MarketId>,
        winner_runner_id: RunnerId,
    },
    VoidMarket {
        #[serde(default)]
        market_id: Option<MarketId>,
        reason: Option<String>,
    },
    HaltMarket {
        #[serde(default)]
        market_id: Option<MarketId>,
        reason: u32,
    },
    ResumeMarket {
        #[serde(default)]
        market_id: Option<MarketId>,
    },
    Ping,

    // Market lifecycle (control plane)
    CreateMarket {
        name: String,
        #[serde(default)]
        market_model: MarketModel,
        market_kind: MarketKind,
        runner_ids: Vec<RunnerId>,
        runner_labels: Vec<String>,
    },
    RemoveMarket {
        market_id: MarketId,
    },
    ListMarkets,
    Subscribe {
        market_id: MarketId,
    },

    // Trading commands
    PlaceOrder {
        correlation_id: CorrelationId,
        order_id: OrderId,
        market_id: MarketId,
        runner_id: RunnerId,
        account_id: AccountId,
        side: Side,
        odds: OddsX10000,
        stake: i64,
        persistence: Option<Persistence>,
        time_in_force: Option<TimeInForce>,
    },
    PlaceBinaryOrder {
        correlation_id: CorrelationId,
        order_id: OrderId,
        market_id: MarketId,
        account_id: AccountId,
        side: BinarySide,
        price_ticks: u16,
        qty_shares: u64,
        time_in_force: Option<BinaryTimeInForce>,
    },
    CancelOrder {
        correlation_id: CorrelationId,
        market_id: MarketId,
        account_id: AccountId,
        order_id: OrderId,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ServerMsg {
    Snapshot {
        snapshot: MarketSnapshot,
    },
    Status {
        global_seq: u64,
        market_count: usize,
    },
    Ack {
        ok: bool,
        response: Option<Response>,
        error: Option<String>,
    },
    OrderResponse {
        correlation_id: CorrelationId,
        ok: bool,
        order_id: Option<OrderId>,
        trades: Vec<TradeSummary>,
        error: Option<String>,
    },
    MarketCreated {
        market: MarketInfo,
    },
    MarketRemoved {
        market_id: MarketId,
    },
    MarketList {
        markets: Vec<MarketInfo>,
    },
    MarketStatusChanged {
        market_id: MarketId,
        status: MarketStatus,
    },
}

pub fn encode_msg<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, postcard::Error> {
    postcard::to_allocvec(value)
}

pub fn decode_msg<T>(bytes: &[u8]) -> Result<T, postcard::Error>
where
    T: for<'de> Deserialize<'de>,
{
    postcard::from_bytes(bytes)
}