use rust_decimal::Decimal;
use crate::MetricsError;
pub fn var(returns: &[Decimal], confidence_pct: u32) -> Decimal {
match try_var(returns, confidence_pct) {
Ok(v) => v,
Err(_) => Decimal::ZERO,
}
}
pub fn try_var(returns: &[Decimal], confidence_pct: u32) -> Result<Decimal, MetricsError> {
if confidence_pct == 0 || confidence_pct >= 100 {
return Err(MetricsError::InvalidParameter(
"confidence_pct must be in (0, 100)".into(),
));
}
let n = returns.len();
let min_observations = (100 / (100 - confidence_pct as usize)).max(10);
if n < min_observations {
return Err(MetricsError::InsufficientData {
required: min_observations,
actual: n,
});
}
let mut sorted: Vec<Decimal> = returns.to_vec();
sorted.sort();
let index = ((100 - confidence_pct) as usize * n) / 100;
let index = index.min(n - 1);
Ok(sorted[index])
}
pub fn cvar(returns: &[Decimal], confidence_pct: u32) -> Decimal {
let n = returns.len();
if n == 0 || confidence_pct == 0 || confidence_pct >= 100 {
return Decimal::ZERO;
}
let mut sorted: Vec<Decimal> = returns.to_vec();
sorted.sort();
let cutoff = ((100 - confidence_pct) as usize * n) / 100;
let cutoff = cutoff.max(1).min(n);
let tail: &[Decimal] = &sorted[..cutoff];
let sum: Decimal = tail.iter().sum();
sum / Decimal::from(tail.len() as u32)
}
#[cfg(test)]
#[path = "risk_metrics_tests.rs"]
mod tests;