use rustrade_core::Side;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub enum SlippageModel {
#[default]
Zero,
FixedBps(f64),
}
impl SlippageModel {
pub fn apply(self, side: Side, reference_price: f64) -> f64 {
match self {
Self::Zero => reference_price,
Self::FixedBps(bps) => {
let factor = 1.0 + (bps / 10_000.0) * direction_sign(side);
reference_price * factor
}
}
}
}
fn direction_sign(side: Side) -> f64 {
match side {
Side::Buy => 1.0,
Side::Sell => -1.0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_returns_reference() {
assert_eq!(SlippageModel::Zero.apply(Side::Buy, 100.0), 100.0);
assert_eq!(SlippageModel::Zero.apply(Side::Sell, 100.0), 100.0);
}
#[test]
fn fixed_bps_buy_adds() {
let fill = SlippageModel::FixedBps(10.0).apply(Side::Buy, 100.0);
assert!((fill - 100.1).abs() < 1e-9);
}
#[test]
fn fixed_bps_sell_subtracts() {
let fill = SlippageModel::FixedBps(10.0).apply(Side::Sell, 100.0);
assert!((fill - 99.9).abs() < 1e-9);
}
#[test]
fn fixed_bps_symmetric_about_reference() {
let buy = SlippageModel::FixedBps(25.0).apply(Side::Buy, 1_000.0);
let sell = SlippageModel::FixedBps(25.0).apply(Side::Sell, 1_000.0);
assert!(((buy - 1_000.0) + (sell - 1_000.0)).abs() < 1e-9);
}
}