use super::types::*;
use crate::{
book::protocol::command::{Persistence, Side, TimeInForce},
types::*,
};
use betex_macros::TaggedEnumBridge;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use smallvec::SmallVec;
use std::fmt;
pub type EventVec = SmallVec<[BookEventEnvelope; 4]>;
pub type EventMetadata = Option<Value>;
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct BookEventEnvelope {
pub market_id: MarketId,
pub market_name: String,
pub market_seq: u64,
#[rkyv(with = crate::types::DateTimeUtcAsUnixMillis)]
pub timestamp: DateTime,
#[rkyv(with = crate::types::JsonValueOptionAsStringOption)]
pub metadata: EventMetadata,
pub event: BookEvent,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CancelCause {
UserCancel,
Reduce,
IocRemainder,
FokRemainder,
RunnerRemoved,
Admin,
}
impl CancelCause {
pub(crate) fn detail(self) -> &'static str {
match self {
Self::UserCancel => "USER_CANCEL",
Self::Reduce => "REDUCE",
Self::IocRemainder => "IOC_REMAINDER",
Self::FokRemainder => "FOK_REMAINDER",
Self::RunnerRemoved => "RUNNER_REMOVED",
Self::Admin => "ADMIN",
}
}
}
pub(crate) fn time_in_force_remainder_cancel_cause(
time_in_force: TimeInForce,
has_remaining: bool,
) -> Option<CancelCause> {
if !has_remaining {
return None;
}
match time_in_force {
TimeInForce::ImmediateOrCancel => Some(CancelCause::IocRemainder),
TimeInForce::FillOrKill { .. } => Some(CancelCause::FokRemainder),
TimeInForce::Gtc => None,
}
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct CancelledOrderEntry {
pub order_id: OrderId,
pub account_id: AccountId,
pub correlation_id: Option<CorrelationId>,
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
pub struct OrderCancellationCursor {
pub order_id: OrderId,
pub account_id: AccountId,
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TradeRole {
Maker,
Taker,
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
TaggedEnumBridge,
Serialize,
Deserialize,
rkyv::Archive,
rkyv::Serialize,
rkyv::Deserialize,
)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum BookEvent {
MarketCreated {
correlation_id: Option<CorrelationId>,
name: String,
market_model: MarketModel,
book_type: Option<BookType>,
market_kind: MarketKind,
market_state: BookMarketState,
market_phase: MarketPhase,
runner_ids: Vec<RunnerId>,
runner_labels: Vec<String>,
},
RunnersAdded {
runner_ids: Vec<RunnerId>,
runner_labels: Vec<String>,
},
RunnersRemoved {
runner_ids: Vec<RunnerId>,
runner_labels: Vec<String>,
reduction_factor_bps: Option<u32>,
},
MarketStateChanged {
to: BookMarketState,
reason: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
close_batch_max_events: Option<u16>,
},
MarketPhaseChanged {
to: MarketPhase,
reason: String,
},
OrderAccepted {
correlation_id: Option<CorrelationId>,
order_id: OrderId,
account_id: AccountId,
runner_id: RunnerId,
runner_label: String,
side: Side,
price: OddsX10000,
stake: Money,
persistence: Persistence,
time_in_force: TimeInForce,
},
BinaryOrderAccepted {
correlation_id: Option<CorrelationId>,
order_id: OrderId,
account_id: AccountId,
side: Side,
price_ticks: u16,
qty_shares: u64,
time_in_force: TimeInForce,
},
OrderCancelled {
cancelled_order: CancelledOrderEntry,
cancel_cause: CancelCause,
cause_detail: Option<String>,
},
OrderCancelledBatched {
cancelled_orders: Vec<CancelledOrderEntry>,
cursor_after: Option<OrderCancellationCursor>,
batch_mode: BatchMode,
detail: Option<String>,
},
TradeMatched {
correlation_id: Option<CorrelationId>,
order_id: OrderId,
account_id: AccountId,
role: TradeRole,
runner_id: RunnerId,
runner_label: String,
side: Side,
market_phase: MarketPhase,
price: OddsX10000,
stake: Money,
counter_party: OrderId,
counter_party_account_id: AccountId,
remaining_stake: Money,
matched_delta: Money,
},
BinaryTradeMatched {
correlation_id: Option<CorrelationId>,
order_id: OrderId,
account_id: AccountId,
role: TradeRole,
side: Side,
market_phase: MarketPhase,
price_ticks: u16,
counter_party: OrderId,
counter_party_account_id: AccountId,
remaining_qty_shares: u64,
matched_delta_shares: u64,
},
VoidTrades {
market_phase: MarketPhase,
#[rkyv(with = crate::types::DateTimeUtcAsUnixMillis)]
start_time: DateTime,
#[rkyv(with = crate::types::DateTimeUtcAsUnixMillis)]
end_time: DateTime,
void_reason: String,
},
RunnerRemoved {
runner_id: RunnerId,
runner_label: String,
reduction_factor_bps: Option<u32>,
},
BatchProcessQueued {
batch_mode: BatchMode,
batch_max_events: u16,
target: BatchProcessTarget,
detail: Option<String>,
},
BatchProcessStarted {
batch_mode: BatchMode,
batch_max_events: u16,
target: BatchProcessTarget,
detail: Option<String>,
},
BatchProcessRetargeted {
from_mode: BatchMode,
to_mode: BatchMode,
batch_max_events: u16,
target: BatchProcessTarget,
detail: Option<String>,
abandoned_detail: Option<String>,
},
BatchProcessCompleted {
batch_mode: BatchMode,
},
MarketRemoved {
reason: String,
},
}
impl fmt::Display for BookEventEnvelope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"BOOK_EVENT_ENVELOPE market_id={:?} market_name={} timestamp_ms={} event={}",
self.market_id,
self.market_name,
self.timestamp.timestamp_millis(),
self.event
)
}
}
impl fmt::Display for BookEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let kind = match self {
BookEvent::MarketCreated { .. } => "MARKET_CREATED",
BookEvent::RunnersAdded { .. } => "RUNNERS_ADDED",
BookEvent::RunnersRemoved { .. } => "RUNNERS_REMOVED",
BookEvent::MarketStateChanged { .. } => "MARKET_STATE_CHANGED",
BookEvent::MarketPhaseChanged { .. } => "MARKET_PHASE_CHANGED",
BookEvent::OrderAccepted { .. } => "ORDER_ACCEPTED",
BookEvent::BinaryOrderAccepted { .. } => "BINARY_ORDER_ACCEPTED",
BookEvent::OrderCancelled { .. } => "ORDER_CANCELLED",
BookEvent::OrderCancelledBatched { .. } => "ORDER_CANCELLED_BATCHED",
BookEvent::TradeMatched { .. } => "TRADE_MATCHED",
BookEvent::BinaryTradeMatched { .. } => "BINARY_TRADE_MATCHED",
BookEvent::VoidTrades { .. } => "VOID_TRADES",
BookEvent::RunnerRemoved { .. } => "RUNNER_REMOVED",
BookEvent::BatchProcessQueued { .. } => "BATCH_PROCESS_QUEUED",
BookEvent::BatchProcessStarted { .. } => "BATCH_PROCESS_STARTED",
BookEvent::BatchProcessRetargeted { .. } => "BATCH_PROCESS_RETARGETED",
BookEvent::BatchProcessCompleted { .. } => "BATCH_PROCESS_COMPLETED",
BookEvent::MarketRemoved { .. } => "MARKET_REMOVED",
};
f.write_str(kind)
}
}