use deadeye_collateral::{MinimizationPolicy, lambda, normal_collateral};
use deadeye_core::{NormalDistribution, Sq128};
const N_SIGMA_SAMPLES: u32 = 50;
const N_MEAN_SAMPLES: u32 = 50;
const DEFAULT_MAX_SIGMA_RATIO: f64 = 4.0_f64;
const DEFAULT_MAX_MEAN_SEP_SIGMAS: f64 = 4.0_f64;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct OptimizerConstraints {
pub max_sigma_ratio: f64,
pub max_mean_sep_sigmas: f64,
}
impl Default for OptimizerConstraints {
fn default() -> Self {
Self {
max_sigma_ratio: DEFAULT_MAX_SIGMA_RATIO,
max_mean_sep_sigmas: DEFAULT_MAX_MEAN_SEP_SIGMAS,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NormalOptimizationInput {
pub budget: f64,
pub belief_mean: f64,
pub belief_sigma: f64,
pub market_mean: f64,
pub market_sigma: f64,
pub effective_k: f64,
pub payout_amplifier: f64,
pub constraints: OptimizerConstraints,
}
impl NormalOptimizationInput {
#[must_use]
pub fn new(
budget: f64,
belief_mean: f64,
belief_sigma: f64,
market_mean: f64,
market_sigma: f64,
effective_k: f64,
) -> Self {
Self {
budget,
belief_mean,
belief_sigma,
market_mean,
market_sigma,
effective_k,
payout_amplifier: 1.0,
constraints: OptimizerConstraints::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NormalOptimizationResult {
pub optimized_mean: f64,
pub optimized_sigma: f64,
pub optimized_variance: f64,
pub collateral_required: f64,
pub expected_value: f64,
pub belief_utilization: f64,
pub is_budget_sufficient: bool,
pub budget_surplus: f64,
pub roi: f64,
}
fn gaussian_product_integral(mu1: f64, sigma1: f64, mu2: f64, sigma2: f64) -> f64 {
let sum_var = sigma1.mul_add(sigma1, sigma2 * sigma2);
if sum_var <= 0.0 {
return 0.0;
}
let diff_mu = mu1 - mu2;
(1.0 / (2.0 * core::f64::consts::PI * sum_var).sqrt())
* (-(diff_mu * diff_mu) / (2.0 * sum_var)).exp()
}
fn collateral_number(mu_f: f64, sigma_f: f64, mu_g: f64, sigma_g: f64, k: f64) -> f64 {
if (mu_f - mu_g).abs() < 1e-12_f64 && (sigma_f - sigma_g).abs() < 1e-12_f64 {
return 0.0;
}
let Ok(mean_f) = Sq128::from_f64(mu_f) else {
return f64::INFINITY;
};
let Ok(var_f) = Sq128::from_f64(sigma_f * sigma_f) else {
return f64::INFINITY;
};
let Ok(mean_g) = Sq128::from_f64(mu_g) else {
return f64::INFINITY;
};
let Ok(var_g) = Sq128::from_f64(sigma_g * sigma_g) else {
return f64::INFINITY;
};
let Ok(f) = NormalDistribution::from_variance(mean_f, var_f) else {
return f64::INFINITY;
};
let Ok(g) = NormalDistribution::from_variance(mean_g, var_g) else {
return f64::INFINITY;
};
let _ = k;
match normal_collateral(&f, &g, MinimizationPolicy::unrestricted()) {
Ok(verified) if verified.collateral.is_finite() => verified.collateral.max(0.0),
_ => f64::INFINITY,
}
}
#[expect(
clippy::too_many_arguments,
reason = "EV depends on 8 distinct numeric inputs; bundling them adds churn without value"
)]
fn expected_value(
mu_f: f64,
sigma_f: f64,
mu_g: f64,
sigma_g: f64,
k: f64,
belief_mu: f64,
belief_sigma: f64,
amplifier: f64,
) -> f64 {
let lam_g = lambda(sigma_g, k);
let lam_f = lambda(sigma_f, k);
let raw_ev = lam_g.mul_add(
gaussian_product_integral(mu_g, sigma_g, belief_mu, belief_sigma),
-(lam_f * gaussian_product_integral(mu_f, sigma_f, belief_mu, belief_sigma)),
);
amplifier * raw_ev
}
#[must_use]
pub fn optimize_normal_trade(input: NormalOptimizationInput) -> NormalOptimizationResult {
let no_trade = NormalOptimizationResult {
optimized_mean: input.market_mean,
optimized_sigma: input.market_sigma,
optimized_variance: input.market_sigma * input.market_sigma,
collateral_required: 0.0,
expected_value: 0.0,
belief_utilization: 0.0,
is_budget_sufficient: false,
budget_surplus: input.budget,
roi: 0.0,
};
if input.budget <= 0.0 || input.market_sigma <= 0.0 || input.effective_k <= 0.0 {
return no_trade;
}
let sigma_min = (input.market_sigma / input.constraints.max_sigma_ratio).max(1e-6_f64);
let sigma_max = input.market_sigma * input.constraints.max_sigma_ratio;
let sigma_step = (sigma_max - sigma_min) / f64::from(N_SIGMA_SAMPLES);
let mean_dir = if input.belief_mean >= input.market_mean {
1.0_f64
} else {
-1.0_f64
};
let max_shift = input.constraints.max_mean_sep_sigmas * input.market_sigma;
let mut best_net = f64::NEG_INFINITY;
let mut best_mu = input.market_mean;
let mut best_sigma = input.market_sigma;
let mut best_coll = 0.0_f64;
let mut best_ev = 0.0_f64;
for i in 0..=N_SIGMA_SAMPLES {
let cand_sigma = f64::from(i).mul_add(sigma_step, sigma_min);
for j in 0..=N_MEAN_SAMPLES {
let shift = (f64::from(j) / f64::from(N_MEAN_SAMPLES)) * max_shift;
let cand_mu = mean_dir.mul_add(shift, input.market_mean);
let coll = collateral_number(
input.market_mean,
input.market_sigma,
cand_mu,
cand_sigma,
input.effective_k,
);
if coll < 0.0 || coll > input.budget {
continue;
}
let ev = expected_value(
input.market_mean,
input.market_sigma,
cand_mu,
cand_sigma,
input.effective_k,
input.belief_mean,
input.belief_sigma,
input.payout_amplifier,
);
let net = ev - coll;
if net > best_net {
best_net = net;
best_mu = cand_mu;
best_sigma = cand_sigma;
best_coll = coll;
best_ev = ev;
}
}
}
if best_net <= 0.0 || best_coll <= 0.0 {
return no_trade;
}
let full_shift = (input.belief_mean - input.market_mean).abs();
let achieved_shift = (best_mu - input.market_mean).abs();
let utilization = if full_shift > 1e-6_f64 {
(achieved_shift / full_shift).min(1.0)
} else {
1.0
};
NormalOptimizationResult {
optimized_mean: best_mu,
optimized_sigma: best_sigma,
optimized_variance: best_sigma * best_sigma,
collateral_required: best_coll,
expected_value: best_ev,
belief_utilization: utilization,
is_budget_sufficient: (best_mu - input.belief_mean).abs() < sigma_step * 0.5
&& (best_sigma - input.belief_sigma).abs() < sigma_step * 0.5,
budget_surplus: input.budget - best_coll,
roi: best_net / best_coll,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_budget_yields_no_trade() {
let input = NormalOptimizationInput::new(0.0, 105.0, 1.0, 100.0, 2.0, 50.0);
let r = optimize_normal_trade(input);
assert!(r.collateral_required.abs() < 1e-12);
assert!((r.optimized_mean - 100.0).abs() < 1e-12);
}
#[test]
fn zero_market_sigma_yields_no_trade() {
let input = NormalOptimizationInput::new(100.0, 105.0, 1.0, 100.0, 0.0, 50.0);
let r = optimize_normal_trade(input);
assert!(r.collateral_required.abs() < 1e-12);
}
#[test]
fn picks_a_trade_under_reasonable_budget() {
let input = NormalOptimizationInput::new(100.0, 105.0, 1.0, 100.0, 2.0, 50.0);
let r = optimize_normal_trade(input);
assert!(r.collateral_required >= 0.0);
assert!(r.budget_surplus <= input.budget);
assert!(r.optimized_mean >= input.market_mean);
}
#[test]
fn sigma_arb_with_equal_mu_finds_positive_ev_trade() {
let input = NormalOptimizationInput::new(
50.0, 4.3274, 0.2143, 4.2900, 0.3500, 75.07, );
let r = optimize_normal_trade(input);
assert!(
r.collateral_required > 0.0 && r.collateral_required < 50.0,
"σ-arb: expected positive in-budget collateral, got {}",
r.collateral_required
);
assert!(
r.expected_value > r.collateral_required,
"σ-arb: expected positive net EV, got ev={} cost={}",
r.expected_value,
r.collateral_required
);
assert!(
r.optimized_sigma < input.market_sigma * 0.9,
"σ-arb: expected tightened σ_g, got {}",
r.optimized_sigma
);
}
#[test]
fn pure_sigma_arb_finds_trade() {
let input = NormalOptimizationInput::new(50.0, 4.29, 0.21, 4.29, 0.35, 75.07);
let r = optimize_normal_trade(input);
assert!(r.collateral_required > 0.0);
assert!(r.expected_value > r.collateral_required);
}
}