#[derive(Clone, Copy, Debug)]
pub struct CostModel {
pub fee_bps: f64,
pub slippage_bps: f64,
pub impact_bps: f64,
pub financing_bps: f64,
pub max_participation: f64,
}
impl Default for CostModel {
fn default() -> Self {
Self {
fee_bps: 2.0,
slippage_bps: 3.0,
impact_bps: 50.0,
financing_bps: 5.0,
max_participation: f64::INFINITY,
}
}
}
pub fn financing_cost_frac(financing_bps: f64, gross_exposure: f64) -> f64 {
financing_bps / 10_000.0 * (gross_exposure - 1.0).max(0.0)
}
pub fn liquidity_capped_delta(delta_value: f64, max_participation: f64, nav: f64) -> f64 {
if !max_participation.is_finite() {
return delta_value;
}
let cap = max_participation * nav.max(0.0);
delta_value.clamp(-cap, cap)
}
pub fn market_impact_frac(impact_bps: f64, participation: f64) -> f64 {
impact_bps / 10_000.0 * participation.max(0.0).sqrt()
}
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Rng(seed ^ 0xA5A5_5A5A_C3C3_3C3C)
}
fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub fn signed_unit(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / (1u64 << 53) as f64 * 2.0 - 1.0
}
pub fn unit(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn impact_grows_with_participation() {
let small = market_impact_frac(50.0, 0.01);
let big = market_impact_frac(50.0, 0.5);
assert!(big > small, "bigger trade should cost more");
assert!(market_impact_frac(50.0, 0.0).abs() < 1e-12);
}
#[test]
fn impact_is_concave() {
let a = market_impact_frac(50.0, 0.1);
let b = market_impact_frac(50.0, 0.2);
assert!(b < 2.0 * a, "impact must be concave in size");
}
#[test]
fn financing_only_bites_above_full_investment() {
assert_eq!(financing_cost_frac(50.0, 1.0), 0.0);
assert_eq!(financing_cost_frac(50.0, 0.5), 0.0);
assert!(financing_cost_frac(50.0, 2.0) > 0.0);
}
#[test]
fn liquidity_cap_clamps_large_trades() {
assert_eq!(liquidity_capped_delta(200.0, 0.05, 1000.0), 50.0);
assert_eq!(liquidity_capped_delta(-200.0, 0.05, 1000.0), -50.0);
assert_eq!(liquidity_capped_delta(30.0, 0.05, 1000.0), 30.0);
assert_eq!(liquidity_capped_delta(1e9, f64::INFINITY, 1000.0), 1e9);
}
}