betex 0.35.0

Betfair / Prediction Market Exchange
Documentation
//! # BetEx Engine
//!
//! A high-performance, event-sourced order matching engine for sports betting exchanges
//! and prediction markets.
//!
//! ## Event Sourcing Model
//!
//! The engine uses a command → events → WAL → handlers architecture:
//!
//! 1. **Commands** arrive via the [`Engine`] handler (e.g., PlaceOrder, CancelOrder)
//! 2. **Validation** happens in `EngineRoot::handle` against current state
//! 3. **Events** are emitted describing state changes (e.g., OrderAccepted, TradeMatched)
//! 4. **WAL** persists events durably before acknowledgment
//! 5. **Handlers** receive events via the disruptor for downstream processing
//!
//! ## Key Guarantees
//!
//! - **Durability**: Events are written to the WAL before response callbacks fire
//! - **Ordering**: Events are strictly ordered by sequence number
//! - **Determinism**: Replaying the same event sequence produces identical state
//! - **Atomicity**: Multi-event commands (trades) commit or rollback together
//!
//! ## Market Models
//!
//! The engine supports two market models:
//!
//! - **ExchangeOdds**: Traditional sports betting with BACK/LAY sides at decimal odds
//!   (e.g., 2.50 means "win $1.50 for every $1 staked")
//! - **BinaryYes**: Prediction markets with BUY/SELL sides at tick prices (0-10000 basis points)
//!
//! ## Quick Start
//!
//! ```ignore
//! use betex::prelude::*;
//! use betex::engine::consumers::ResponseCallbackHandler;
//!
//! // Create engine with markets
//! let config = Config::default();
//! let engine = EngineBuilder::with_markets(
//!     config,
//!     [MarketConfig::TwoRunner {
//!         market_id: MarketId(1),
//!         name: "Match Odds".to_string(),
//!         runner_a: RunnerId(1),
//!         runner_b: RunnerId(2),
//!         market_kind: MarketKind::InPlayCapable,
//!         market_state: BookMarketState::Open,
//!         market_phase: MarketPhase::Pre,
//!     }],
//!     "/tmp/wal",
//! )?
//! .register_handler(ResponseCallbackHandler::default(), HandlerRecovery::LiveOnly, None)
//! .build()?;
//!
//! // Submit orders
//! engine.submit(Command {
//!     correlation_id: Some(CorrelationId("11111111-1111-1111-1111-111111111111".into())),
//!     market_id: MarketId(1),
//!     kind: CommandKind::PlaceOrder {
//!         runner_id: RunnerId(1),
//!         account_id: AccountId::from(1),
//!         client_order_id: None,
//!         side: Side::Yes,
//!         odds: OddsX10000(20000), // 2.0
//!         stake: Money(10000),     // $1.00
//!         persistence: Persistence::Persist,
//!         time_in_force: TimeInForce::Gtc,
//!     },
//! })?;
//! # Ok::<(), anyhow::Error>(())
//! ```
//!
//! ## Module Overview
//!
//! - [`book`]: Order book implementations (two-runner, multi-runner, binary prediction)
//! - [`engine`]: Core engine and builder with WAL recovery
//! - [`cross_match`]: Multi-leg cross-matching for 3-runner markets
//! - [`types`]: Core domain types (Money, OrderId, OddsX10000, etc.)
//! - [`config`]: Engine and market configuration
//! - [`snapshot`]: Market state snapshots for external consumption

pub mod book;
pub mod config;
pub mod cross_match;
pub mod disruptor;
pub mod engine;
pub mod error;
pub mod idempotency;
pub mod metrics;
pub mod prelude;
pub mod snapshot;
pub mod types;

pub use crate::engine::state::MarketConfig;
pub use crate::engine::{
    core::{CommandLane, Engine, EngineBuilder, EngineEvent, HandlerRecovery},
    journal::{JournalEvent, JournalReader},
};
pub use crate::snapshot::{
    BinaryMarketSnapshot, ExchangeMarketSnapshot, MarketBookSnapshot, MarketSnapshot,
    RunnerRefSnapshot, RunnerSnapshot, SnapshotPriceSelection, SnapshotPriceSelections,
};