use crate::stats::{mean, variance};
pub fn alpha_beta(agent: &[f64], market: &[f64]) -> (f64, f64) {
let n = agent.len().min(market.len());
if n < 2 {
return (mean(agent), 0.0);
}
let a = &agent[..n];
let m = &market[..n];
let ma = mean(a);
let mm = mean(m);
let var_m = variance(m);
if var_m == 0.0 {
return (ma, 0.0);
}
let cov = a
.iter()
.zip(m.iter())
.map(|(x, y)| (x - ma) * (y - mm))
.sum::<f64>()
/ (n as f64 - 1.0);
let beta = cov / var_m;
let alpha = ma - beta * mm;
(alpha, beta)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pure_market_follower_has_zero_alpha() {
let market: Vec<f64> = (0..50).map(|i| 0.001 * (i as f64 * 0.3).sin()).collect();
let agent: Vec<f64> = market.iter().map(|m| 1.5 * m).collect();
let (alpha, beta) = alpha_beta(&agent, &market);
assert!((beta - 1.5).abs() < 1e-9, "beta={beta}");
assert!(alpha.abs() < 1e-9, "alpha={alpha}");
}
#[test]
fn constant_excess_is_pure_alpha() {
let market: Vec<f64> = (0..50).map(|i| 0.001 * (i as f64 * 0.3).sin()).collect();
let agent: Vec<f64> = market.iter().map(|m| m + 0.002).collect();
let (alpha, beta) = alpha_beta(&agent, &market);
assert!((beta - 1.0).abs() < 1e-9, "beta={beta}");
assert!((alpha - 0.002).abs() < 1e-9, "alpha={alpha}");
}
}