use crate::{
book::common::events::CancelCause,
book::protocol::{
command::{Persistence, Side},
reject::RejectReason,
},
types::*,
};
use std::collections::VecDeque;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
strum::Display,
strum::AsRefStr,
)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
pub enum BookMarketState {
Open,
Suspended,
Halted,
Closed,
Settled,
}
impl BookMarketState {
pub fn is_matchable(self) -> bool {
matches!(self, Self::Open)
}
pub fn is_terminal(self) -> bool {
matches!(self, Self::Closed | Self::Settled)
}
pub fn is_halted(self) -> bool {
matches!(self, Self::Halted)
}
}
pub fn ensure_phase_change(
current: MarketPhase,
to: MarketPhase,
) -> Result<(), crate::book::protocol::reject::RejectReason> {
if current == to {
return Err(crate::book::protocol::reject::RejectReason::NoChange);
}
Ok(())
}
pub fn ensure_can_accept_new_orders(state: BookMarketState) -> Result<(), RejectReason> {
if state.is_matchable() {
Ok(())
} else {
Err(RejectReason::MarketNotOpen)
}
}
pub fn ensure_state_change(
current: BookMarketState,
to: BookMarketState,
) -> Result<(), crate::book::protocol::reject::RejectReason> {
if current == to {
return Err(crate::book::protocol::reject::RejectReason::NoChange);
}
Ok(())
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum BatchProcessKind {
Close,
Cancel,
Lapse,
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct BatchProcessReason {
pub cancel_cause: CancelCause,
pub detail: Option<String>,
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum BatchProcessTarget {
AllLiveOrders,
LapseOrders,
FilteredCancel {
started_at_ms: i64,
from_created_at_inclusive_ms: Option<i64>,
to_created_at_inclusive_ms: Option<i64>,
account_filter: Option<AccountId>,
runner_filter: Option<RunnerId>,
final_event_metadata_json: Option<String>,
},
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum BatchProcessContext {
None,
Close { total_live_orders: u64 },
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct BatchProcessState {
pub kind: BatchProcessKind,
pub reason: BatchProcessReason,
pub batch_max_events: u16,
pub cursor_after: Option<OrderId>,
pub processed_total: u64,
pub chunks_done: u32,
pub target: BatchProcessTarget,
pub context: BatchProcessContext,
}
impl BatchProcessReason {
pub fn new(cancel_cause: CancelCause, detail: Option<String>) -> Self {
Self {
cancel_cause,
detail,
}
}
}
impl BatchProcessState {
pub fn close(batch_max_events: u16, total_live_orders: u64) -> Self {
Self {
kind: BatchProcessKind::Close,
reason: BatchProcessReason::new(CancelCause::CloseCancel, None),
batch_max_events,
cursor_after: None,
processed_total: 0,
chunks_done: 0,
target: BatchProcessTarget::AllLiveOrders,
context: BatchProcessContext::Close { total_live_orders },
}
}
#[allow(clippy::too_many_arguments)]
pub fn cancel(
batch_max_events: u16,
started_at_ms: i64,
from_created_at_inclusive_ms: Option<i64>,
to_created_at_inclusive_ms: Option<i64>,
account_filter: Option<AccountId>,
runner_filter: Option<RunnerId>,
detail: String,
final_event_metadata_json: Option<String>,
) -> Self {
Self {
kind: BatchProcessKind::Cancel,
reason: BatchProcessReason::new(CancelCause::BatchCancel, Some(detail)),
batch_max_events,
cursor_after: None,
processed_total: 0,
chunks_done: 0,
target: BatchProcessTarget::FilteredCancel {
started_at_ms,
from_created_at_inclusive_ms,
to_created_at_inclusive_ms,
account_filter,
runner_filter,
final_event_metadata_json,
},
context: BatchProcessContext::None,
}
}
pub fn lapse(batch_max_events: u16, cancel_cause: CancelCause) -> Self {
Self {
kind: BatchProcessKind::Lapse,
reason: BatchProcessReason::new(cancel_cause, None),
batch_max_events,
cursor_after: None,
processed_total: 0,
chunks_done: 0,
target: BatchProcessTarget::LapseOrders,
context: BatchProcessContext::None,
}
}
#[inline]
pub fn is_close(&self) -> bool {
self.kind == BatchProcessKind::Close
}
pub fn record_chunk(&mut self, cursor_after: Option<OrderId>, processed_count: u64) {
self.cursor_after = cursor_after;
self.processed_total = self.processed_total.saturating_add(processed_count);
self.chunks_done = self.chunks_done.saturating_add(1);
}
pub fn final_event_metadata_json(&self) -> Option<&str> {
match &self.target {
BatchProcessTarget::FilteredCancel {
final_event_metadata_json,
..
} => final_event_metadata_json.as_deref(),
_ => None,
}
}
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
strum::Display,
strum::AsRefStr,
)]
pub enum BookOrderState {
ExecutableUnmatched,
ExecutablePartiallyMatched,
ExecutionComplete,
Cancelled,
Lapsed,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub enum RunnerResult {
#[serde(rename = "1", alias = "Winner", alias = "Win")]
Win,
#[serde(rename = "0", alias = "Loser", alias = "Lose")]
Lose,
}
#[allow(non_upper_case_globals)]
impl RunnerResult {
pub const Winner: Self = Self::Win;
pub const Loser: Self = Self::Lose;
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Hash,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct RunnerSettlement {
pub runner_id: RunnerId,
pub result: RunnerResult,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub void_factor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deadheat_factor: Option<String>,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct SignedMoney(pub i64);
impl SignedMoney {
pub fn zero() -> Self {
Self(0)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BookOrderInfo {
pub order_id: OrderId,
pub account_id: AccountId,
pub correlation_id: Option<CorrelationId>,
pub side: Side,
pub state: BookOrderState,
pub created_at: DateTime,
pub last_updated_at: DateTime,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BookOrder {
pub info: BookOrderInfo,
pub runner_id: RunnerId,
pub price: OddsX10000,
pub stake: Money,
pub matched: Money,
pub persistence: Persistence,
}
impl BookOrder {
pub fn remaining(&self) -> Money {
Money(self.stake.0.saturating_sub(self.matched.0).max(0))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct PriceSize {
pub price: OddsX10000,
pub size: Money,
}
#[derive(Debug, Clone, Default)]
pub struct RunnerPrices {
pub runner_id: RunnerId,
pub available_to_back: Vec<PriceSize>,
pub available_to_lay: Vec<PriceSize>,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct BinaryPriceSize {
pub price_ticks: u16,
pub size_shares: u64,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BinaryDepth {
pub max_price_ticks: u16,
pub bids: Vec<BinaryPriceSize>,
pub asks: Vec<BinaryPriceSize>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PriceLevel {
pub fifo: VecDeque<OrderId>,
pub total_remaining: Money,
}
impl PriceLevel {
pub fn new() -> Self {
Self {
fifo: VecDeque::new(),
total_remaining: Money::zero(),
}
}
}
impl Default for PriceLevel {
fn default() -> Self {
Self::new()
}
}