use betex::{
book::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 {
SetMarketState {
#[serde(default)]
market_id: Option<MarketId>,
state: MarketState,
},
CloseMarket {
#[serde(default)]
market_id: Option<MarketId>,
batch_max_events: u16,
},
BatchCancelOrders {
#[serde(default)]
correlation_id: Option<CorrelationId>,
#[serde(default)]
market_id: Option<MarketId>,
#[serde(default)]
from_created_at_inclusive_ms: Option<i64>,
#[serde(default)]
to_created_at_inclusive_ms: Option<i64>,
#[serde(default)]
account_id: Option<AccountId>,
#[serde(default)]
runner_id: Option<RunnerId>,
batch_max_events: u16,
reason: String,
#[serde(default)]
metadata: Option<serde_json::Value>,
},
ContinueBatchCancelOrders {
#[serde(default)]
correlation_id: Option<CorrelationId>,
#[serde(default)]
market_id: Option<MarketId>,
},
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,
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,
},
PlaceOrder {
#[serde(default)]
correlation_id: Option<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 {
#[serde(default)]
correlation_id: Option<CorrelationId>,
order_id: OrderId,
market_id: MarketId,
account_id: AccountId,
side: BinarySide,
price_ticks: u16,
qty_shares: u64,
time_in_force: Option<BinaryTimeInForce>,
},
CancelOrder {
#[serde(default)]
correlation_id: Option<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: Option<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)
}