use crate::Decimal;
use crate::execution::Side;
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use super::data::MarketTick;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SimulatedOrder {
pub side: Side,
pub price: Decimal,
pub quantity: Decimal,
pub submitted_at: u64,
}
impl SimulatedOrder {
#[must_use]
pub fn new(side: Side, price: Decimal, quantity: Decimal, submitted_at: u64) -> Self {
Self {
side,
price,
quantity,
submitted_at,
}
}
#[must_use]
pub fn notional(&self) -> Decimal {
self.price * self.quantity
}
#[must_use]
pub fn is_buy(&self) -> bool {
self.side == Side::Buy
}
#[must_use]
pub fn is_sell(&self) -> bool {
self.side == Side::Sell
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Default)]
pub enum FillResult {
#[default]
NoFill,
PartialFill {
filled_quantity: Decimal,
fill_price: Decimal,
},
FullFill {
fill_price: Decimal,
},
}
impl FillResult {
#[must_use]
pub fn is_filled(&self) -> bool {
!matches!(self, FillResult::NoFill)
}
#[must_use]
pub fn is_full_fill(&self) -> bool {
matches!(self, FillResult::FullFill { .. })
}
#[must_use]
pub fn is_partial_fill(&self) -> bool {
matches!(self, FillResult::PartialFill { .. })
}
#[must_use]
pub fn fill_price(&self) -> Option<Decimal> {
match self {
FillResult::NoFill => None,
FillResult::PartialFill { fill_price, .. } => Some(*fill_price),
FillResult::FullFill { fill_price } => Some(*fill_price),
}
}
#[must_use]
pub fn filled_quantity(&self, order_quantity: Decimal) -> Decimal {
match self {
FillResult::NoFill => Decimal::ZERO,
FillResult::PartialFill {
filled_quantity, ..
} => *filled_quantity,
FillResult::FullFill { .. } => order_quantity,
}
}
}
pub trait FillModel: Send + Sync {
fn simulate_fill(
&self,
order: &SimulatedOrder,
tick: &MarketTick,
time_in_queue_ms: u64,
) -> FillResult;
fn reset(&mut self);
fn name(&self) -> &'static str;
}
#[derive(Debug, Clone, Default)]
pub struct ImmediateFillModel;
impl ImmediateFillModel {
#[must_use]
pub fn new() -> Self {
Self
}
}
impl FillModel for ImmediateFillModel {
fn simulate_fill(
&self,
order: &SimulatedOrder,
tick: &MarketTick,
_time_in_queue_ms: u64,
) -> FillResult {
match order.side {
Side::Buy => {
if tick.ask_price <= order.price {
FillResult::FullFill {
fill_price: order.price,
}
} else {
FillResult::NoFill
}
}
Side::Sell => {
if tick.bid_price >= order.price {
FillResult::FullFill {
fill_price: order.price,
}
} else {
FillResult::NoFill
}
}
}
}
fn reset(&mut self) {}
fn name(&self) -> &'static str {
"ImmediateFill"
}
}
#[derive(Debug, Clone)]
pub struct QueuePositionFillModel {
queue_depth: HashMap<String, Decimal>,
fill_rate: Decimal,
min_queue_time_ms: u64,
}
impl QueuePositionFillModel {
#[must_use]
pub fn new(fill_rate: Decimal) -> Self {
Self {
queue_depth: HashMap::new(),
fill_rate,
min_queue_time_ms: 0,
}
}
#[must_use]
pub fn with_min_queue_time(fill_rate: Decimal, min_queue_time_ms: u64) -> Self {
Self {
queue_depth: HashMap::new(),
fill_rate,
min_queue_time_ms,
}
}
pub fn update_queue(&mut self, tick: &MarketTick) {
let bid_key = tick.bid_price.to_string();
let ask_key = tick.ask_price.to_string();
self.queue_depth.insert(bid_key, tick.bid_size);
self.queue_depth.insert(ask_key, tick.ask_size);
}
#[must_use]
pub fn get_queue_depth(&self, price: Decimal) -> Decimal {
self.queue_depth
.get(&price.to_string())
.copied()
.unwrap_or(Decimal::ZERO)
}
fn volume_cleared(&self, time_in_queue_ms: u64) -> Decimal {
self.fill_rate * Decimal::from(time_in_queue_ms)
}
}
impl FillModel for QueuePositionFillModel {
fn simulate_fill(
&self,
order: &SimulatedOrder,
tick: &MarketTick,
time_in_queue_ms: u64,
) -> FillResult {
if time_in_queue_ms < self.min_queue_time_ms {
return FillResult::NoFill;
}
let market_crossed = match order.side {
Side::Buy => tick.ask_price <= order.price,
Side::Sell => tick.bid_price >= order.price,
};
if !market_crossed {
return FillResult::NoFill;
}
let queue_ahead = self.get_queue_depth(order.price);
let cleared = self.volume_cleared(time_in_queue_ms);
if cleared >= queue_ahead + order.quantity {
FillResult::FullFill {
fill_price: order.price,
}
} else if cleared > queue_ahead {
let filled = cleared - queue_ahead;
if filled > Decimal::ZERO {
FillResult::PartialFill {
filled_quantity: filled.min(order.quantity),
fill_price: order.price,
}
} else {
FillResult::NoFill
}
} else {
FillResult::NoFill
}
}
fn reset(&mut self) {
self.queue_depth.clear();
}
fn name(&self) -> &'static str {
"QueuePosition"
}
}
impl Default for QueuePositionFillModel {
fn default() -> Self {
Self::new(Decimal::from_str_exact("0.01").unwrap())
}
}
#[derive(Debug)]
pub struct ProbabilisticFillModel {
base_probability: Decimal,
depth_factor: Decimal,
time_factor: Decimal,
seed: u64,
state: AtomicU64,
}
impl ProbabilisticFillModel {
#[must_use]
pub fn new(
base_probability: Decimal,
depth_factor: Decimal,
time_factor: Decimal,
seed: u64,
) -> Self {
Self {
base_probability,
depth_factor,
time_factor,
seed,
state: AtomicU64::new(seed),
}
}
#[must_use]
pub fn calculate_probability(
&self,
order: &SimulatedOrder,
tick: &MarketTick,
time_in_queue_ms: u64,
) -> Decimal {
let available_liquidity = match order.side {
Side::Buy => tick.ask_size,
Side::Sell => tick.bid_size,
};
let relative_depth = if available_liquidity > Decimal::ZERO {
order.quantity / available_liquidity
} else {
Decimal::ONE };
let depth_component = Decimal::ONE / (Decimal::ONE + self.depth_factor * relative_depth);
let time_component = (self.time_factor * Decimal::from(time_in_queue_ms)).min(Decimal::ONE);
self.base_probability * depth_component * time_component
}
fn next_random(&self) -> Decimal {
let current = self.state.load(Ordering::Relaxed);
let next = current.wrapping_mul(6364136223846793005).wrapping_add(1);
self.state.store(next, Ordering::Relaxed);
let value = (next >> 33) as u32;
Decimal::from(value) / Decimal::from(u32::MAX)
}
}
impl FillModel for ProbabilisticFillModel {
fn simulate_fill(
&self,
order: &SimulatedOrder,
tick: &MarketTick,
time_in_queue_ms: u64,
) -> FillResult {
let market_crossed = match order.side {
Side::Buy => tick.ask_price <= order.price,
Side::Sell => tick.bid_price >= order.price,
};
if !market_crossed {
return FillResult::NoFill;
}
let prob = self.calculate_probability(order, tick, time_in_queue_ms);
let random = self.next_random();
if random < prob {
FillResult::FullFill {
fill_price: order.price,
}
} else {
FillResult::NoFill
}
}
fn reset(&mut self) {
self.state.store(self.seed, Ordering::Relaxed);
}
fn name(&self) -> &'static str {
"Probabilistic"
}
}
impl Default for ProbabilisticFillModel {
fn default() -> Self {
Self::new(
Decimal::from_str_exact("0.5").unwrap(),
Decimal::ONE,
Decimal::from_str_exact("0.001").unwrap(),
42,
)
}
}
#[derive(Debug, Clone)]
pub struct MarketImpactFillModel<M: FillModel> {
impact_coefficient: Decimal,
average_daily_volume: Decimal,
base_model: M,
}
impl<M: FillModel> MarketImpactFillModel<M> {
#[must_use]
pub fn new(impact_coefficient: Decimal, average_daily_volume: Decimal, base_model: M) -> Self {
Self {
impact_coefficient,
average_daily_volume,
base_model,
}
}
#[must_use]
pub fn calculate_impact(&self, size: Decimal) -> Decimal {
if self.average_daily_volume <= Decimal::ZERO {
return Decimal::ZERO;
}
let size_ratio = size / self.average_daily_volume;
let sqrt_ratio = decimal_sqrt(size_ratio).unwrap_or(Decimal::ZERO);
self.impact_coefficient * sqrt_ratio
}
#[must_use]
pub fn base_model(&self) -> &M {
&self.base_model
}
pub fn base_model_mut(&mut self) -> &mut M {
&mut self.base_model
}
}
impl<M: FillModel> FillModel for MarketImpactFillModel<M> {
fn simulate_fill(
&self,
order: &SimulatedOrder,
tick: &MarketTick,
time_in_queue_ms: u64,
) -> FillResult {
let base_result = self.base_model.simulate_fill(order, tick, time_in_queue_ms);
match base_result {
FillResult::NoFill => FillResult::NoFill,
FillResult::PartialFill {
filled_quantity,
fill_price,
} => {
let impact = self.calculate_impact(filled_quantity);
let adjusted_price = match order.side {
Side::Buy => fill_price + impact,
Side::Sell => fill_price - impact,
};
FillResult::PartialFill {
filled_quantity,
fill_price: adjusted_price,
}
}
FillResult::FullFill { fill_price } => {
let impact = self.calculate_impact(order.quantity);
let adjusted_price = match order.side {
Side::Buy => fill_price + impact,
Side::Sell => fill_price - impact,
};
FillResult::FullFill {
fill_price: adjusted_price,
}
}
}
}
fn reset(&mut self) {
self.base_model.reset();
}
fn name(&self) -> &'static str {
"MarketImpact"
}
}
fn decimal_sqrt(n: Decimal) -> Option<Decimal> {
if n < Decimal::ZERO {
return None;
}
if n == Decimal::ZERO {
return Some(Decimal::ZERO);
}
let mut x = n;
let two = Decimal::TWO;
for _ in 0..20 {
let next = (x + n / x) / two;
if (next - x).abs() < Decimal::from_str_exact("0.0000001").unwrap() {
return Some(next);
}
x = next;
}
Some(x)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dec;
fn create_test_tick(bid: Decimal, ask: Decimal) -> MarketTick {
MarketTick::new(1000, bid, dec!(10.0), ask, dec!(10.0))
}
#[test]
fn test_simulated_order_new() {
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
assert_eq!(order.side, Side::Buy);
assert_eq!(order.price, dec!(100.0));
assert_eq!(order.quantity, dec!(1.0));
assert_eq!(order.submitted_at, 1000);
}
#[test]
fn test_simulated_order_notional() {
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(2.5), 1000);
assert_eq!(order.notional(), dec!(250.0));
}
#[test]
fn test_simulated_order_is_buy_sell() {
let buy = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let sell = SimulatedOrder::new(Side::Sell, dec!(100.0), dec!(1.0), 1000);
assert!(buy.is_buy());
assert!(!buy.is_sell());
assert!(!sell.is_buy());
assert!(sell.is_sell());
}
#[test]
fn test_fill_result_no_fill() {
let result = FillResult::NoFill;
assert!(!result.is_filled());
assert!(!result.is_full_fill());
assert!(!result.is_partial_fill());
assert!(result.fill_price().is_none());
assert_eq!(result.filled_quantity(dec!(1.0)), Decimal::ZERO);
}
#[test]
fn test_fill_result_full_fill() {
let result = FillResult::FullFill {
fill_price: dec!(100.0),
};
assert!(result.is_filled());
assert!(result.is_full_fill());
assert!(!result.is_partial_fill());
assert_eq!(result.fill_price(), Some(dec!(100.0)));
assert_eq!(result.filled_quantity(dec!(5.0)), dec!(5.0));
}
#[test]
fn test_fill_result_partial_fill() {
let result = FillResult::PartialFill {
filled_quantity: dec!(0.5),
fill_price: dec!(100.0),
};
assert!(result.is_filled());
assert!(!result.is_full_fill());
assert!(result.is_partial_fill());
assert_eq!(result.fill_price(), Some(dec!(100.0)));
assert_eq!(result.filled_quantity(dec!(1.0)), dec!(0.5));
}
#[test]
fn test_immediate_fill_buy_fills() {
let model = ImmediateFillModel::new();
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.8), dec!(99.9));
let result = model.simulate_fill(&order, &tick, 0);
assert!(result.is_full_fill());
assert_eq!(result.fill_price(), Some(dec!(100.0)));
}
#[test]
fn test_immediate_fill_buy_no_fill() {
let model = ImmediateFillModel::new();
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(100.0), dec!(100.1));
let result = model.simulate_fill(&order, &tick, 0);
assert!(!result.is_filled());
}
#[test]
fn test_immediate_fill_sell_fills() {
let model = ImmediateFillModel::new();
let order = SimulatedOrder::new(Side::Sell, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(100.1), dec!(100.2));
let result = model.simulate_fill(&order, &tick, 0);
assert!(result.is_full_fill());
assert_eq!(result.fill_price(), Some(dec!(100.0)));
}
#[test]
fn test_immediate_fill_sell_no_fill() {
let model = ImmediateFillModel::new();
let order = SimulatedOrder::new(Side::Sell, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(100.1));
let result = model.simulate_fill(&order, &tick, 0);
assert!(!result.is_filled());
}
#[test]
fn test_immediate_fill_name() {
let model = ImmediateFillModel::new();
assert_eq!(model.name(), "ImmediateFill");
}
#[test]
fn test_queue_position_new() {
let model = QueuePositionFillModel::new(dec!(0.1));
assert_eq!(model.fill_rate, dec!(0.1));
}
#[test]
fn test_queue_position_update_queue() {
let mut model = QueuePositionFillModel::new(dec!(0.1));
let tick = MarketTick::new(1000, dec!(100.0), dec!(50.0), dec!(100.1), dec!(30.0));
model.update_queue(&tick);
assert_eq!(model.get_queue_depth(dec!(100.0)), dec!(50.0));
assert_eq!(model.get_queue_depth(dec!(100.1)), dec!(30.0));
}
#[test]
fn test_queue_position_no_fill_market_not_crossed() {
let model = QueuePositionFillModel::new(dec!(0.1));
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(100.1));
let result = model.simulate_fill(&order, &tick, 1000);
assert!(!result.is_filled());
}
#[test]
fn test_queue_position_fill_after_queue_clears() {
let mut model = QueuePositionFillModel::new(dec!(0.1)); let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = MarketTick::new(1000, dec!(99.9), dec!(10.0), dec!(100.0), dec!(5.0));
model.update_queue(&tick);
let result = model.simulate_fill(&order, &tick, 60);
assert!(result.is_full_fill());
}
#[test]
fn test_queue_position_partial_fill() {
let mut model = QueuePositionFillModel::new(dec!(0.1)); let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(2.0), 1000);
let tick = MarketTick::new(1000, dec!(99.9), dec!(10.0), dec!(100.0), dec!(5.0));
model.update_queue(&tick);
let result = model.simulate_fill(&order, &tick, 60);
assert!(result.is_partial_fill());
assert_eq!(result.filled_quantity(dec!(2.0)), dec!(1.0));
}
#[test]
fn test_queue_position_min_queue_time() {
let model = QueuePositionFillModel::with_min_queue_time(dec!(0.1), 100);
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(99.9));
let result = model.simulate_fill(&order, &tick, 50);
assert!(!result.is_filled());
let _result = model.simulate_fill(&order, &tick, 100);
}
#[test]
fn test_queue_position_reset() {
let mut model = QueuePositionFillModel::new(dec!(0.1));
let tick = create_test_tick(dec!(100.0), dec!(100.1));
model.update_queue(&tick);
assert!(model.get_queue_depth(dec!(100.0)) > Decimal::ZERO);
model.reset();
assert_eq!(model.get_queue_depth(dec!(100.0)), Decimal::ZERO);
}
#[test]
fn test_probabilistic_new() {
let model = ProbabilisticFillModel::new(dec!(0.5), dec!(1.0), dec!(0.001), 42);
assert_eq!(model.base_probability, dec!(0.5));
assert_eq!(model.depth_factor, dec!(1.0));
assert_eq!(model.seed, 42);
}
#[test]
fn test_probabilistic_calculate_probability() {
let model = ProbabilisticFillModel::new(dec!(0.5), dec!(1.0), dec!(0.01), 42);
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = MarketTick::new(1000, dec!(99.9), dec!(10.0), dec!(100.0), dec!(10.0));
let prob = model.calculate_probability(&order, &tick, 100);
assert!(prob >= Decimal::ZERO);
assert!(prob <= dec!(0.5));
}
#[test]
fn test_probabilistic_probability_increases_with_time() {
let model = ProbabilisticFillModel::new(dec!(0.5), dec!(1.0), dec!(0.01), 42);
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = MarketTick::new(1000, dec!(99.9), dec!(10.0), dec!(100.0), dec!(10.0));
let prob_early = model.calculate_probability(&order, &tick, 10);
let prob_late = model.calculate_probability(&order, &tick, 100);
assert!(prob_late > prob_early);
}
#[test]
fn test_probabilistic_deterministic() {
let model1 = ProbabilisticFillModel::new(dec!(0.5), dec!(1.0), dec!(0.01), 42);
let model2 = ProbabilisticFillModel::new(dec!(0.5), dec!(1.0), dec!(0.01), 42);
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(99.9));
let result1 = model1.simulate_fill(&order, &tick, 100);
let result2 = model2.simulate_fill(&order, &tick, 100);
assert_eq!(result1, result2);
}
#[test]
fn test_probabilistic_reset() {
let mut model = ProbabilisticFillModel::new(dec!(0.5), dec!(1.0), dec!(0.01), 42);
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(99.9));
let result1 = model.simulate_fill(&order, &tick, 100);
model.reset();
let result2 = model.simulate_fill(&order, &tick, 100);
assert_eq!(result1, result2);
}
#[test]
fn test_market_impact_calculate_impact() {
let model =
MarketImpactFillModel::new(dec!(0.1), dec!(1000000.0), ImmediateFillModel::new());
let impact = model.calculate_impact(dec!(100.0));
assert!(impact > Decimal::ZERO);
assert!(impact < dec!(0.01));
}
#[test]
fn test_market_impact_buy_increases_price() {
let model = MarketImpactFillModel::new(dec!(1.0), dec!(1000.0), ImmediateFillModel::new());
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(100.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(99.9));
let result = model.simulate_fill(&order, &tick, 0);
if let FillResult::FullFill { fill_price } = result {
assert!(fill_price > dec!(100.0));
} else {
panic!("Expected full fill");
}
}
#[test]
fn test_market_impact_sell_decreases_price() {
let model = MarketImpactFillModel::new(dec!(1.0), dec!(1000.0), ImmediateFillModel::new());
let order = SimulatedOrder::new(Side::Sell, dec!(100.0), dec!(100.0), 1000);
let tick = create_test_tick(dec!(100.1), dec!(100.2));
let result = model.simulate_fill(&order, &tick, 0);
if let FillResult::FullFill { fill_price } = result {
assert!(fill_price < dec!(100.0));
} else {
panic!("Expected full fill");
}
}
#[test]
fn test_market_impact_no_fill_passes_through() {
let model =
MarketImpactFillModel::new(dec!(0.1), dec!(1000000.0), ImmediateFillModel::new());
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let tick = create_test_tick(dec!(99.9), dec!(100.1));
let result = model.simulate_fill(&order, &tick, 0);
assert!(!result.is_filled());
}
#[test]
fn test_market_impact_reset() {
let mut model =
MarketImpactFillModel::new(dec!(0.1), dec!(1000000.0), ImmediateFillModel::new());
model.reset(); assert_eq!(model.name(), "MarketImpact");
}
#[test]
fn test_decimal_sqrt_positive() {
let result = decimal_sqrt(dec!(4.0)).unwrap();
assert!((result - dec!(2.0)).abs() < dec!(0.0001));
}
#[test]
fn test_decimal_sqrt_zero() {
assert_eq!(decimal_sqrt(Decimal::ZERO), Some(Decimal::ZERO));
}
#[test]
fn test_decimal_sqrt_negative() {
assert!(decimal_sqrt(dec!(-1.0)).is_none());
}
#[cfg(feature = "serde")]
#[test]
fn test_simulated_order_serialization() {
let order = SimulatedOrder::new(Side::Buy, dec!(100.0), dec!(1.0), 1000);
let json = serde_json::to_string(&order).unwrap();
let deserialized: SimulatedOrder = serde_json::from_str(&json).unwrap();
assert_eq!(order, deserialized);
}
#[cfg(feature = "serde")]
#[test]
fn test_fill_result_serialization() {
let result = FillResult::FullFill {
fill_price: dec!(100.0),
};
let json = serde_json::to_string(&result).unwrap();
let deserialized: FillResult = serde_json::from_str(&json).unwrap();
assert_eq!(result, deserialized);
}
}