Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
139 lines
3.4 KiB
Rust
139 lines
3.4 KiB
Rust
//! Custom assertions for domain-specific testing
|
|
|
|
/// Assert that a value is within a percentage tolerance
|
|
#[macro_export]
|
|
macro_rules! assert_approx_eq {
|
|
($left:expr, $right:expr, $tolerance:expr) => {
|
|
let diff = ($left - $right).abs();
|
|
let threshold = $right.abs() * $tolerance;
|
|
assert!(
|
|
diff <= threshold,
|
|
"assertion failed: `{} ≈ {}` (tolerance: {}%)\n left: {}\n right: {}\n diff: {} > {}",
|
|
stringify!($left),
|
|
stringify!($right),
|
|
$tolerance * 100.0,
|
|
$left,
|
|
$right,
|
|
diff,
|
|
threshold
|
|
);
|
|
};
|
|
}
|
|
|
|
/// Assert that a value is within an absolute tolerance
|
|
#[macro_export]
|
|
macro_rules! assert_within {
|
|
($value:expr, $target:expr, $tolerance:expr) => {
|
|
let diff = ($value - $target).abs();
|
|
assert!(
|
|
diff <= $tolerance,
|
|
"assertion failed: `{} within {} of {}`\n value: {}\n target: {}\n diff: {}",
|
|
stringify!($value),
|
|
$tolerance,
|
|
stringify!($target),
|
|
$value,
|
|
$target,
|
|
diff
|
|
);
|
|
};
|
|
}
|
|
|
|
/// Assert that VaR exceedances are within expected range
|
|
pub fn assert_var_exceedances(
|
|
returns: &[f64],
|
|
var: f64,
|
|
confidence: f64,
|
|
max_deviation: f64,
|
|
) {
|
|
let exceedances = returns.iter().filter(|&&r| r.abs() > var).count();
|
|
let expected_exceedances = (returns.len() as f64 * (1.0 - confidence)) as usize;
|
|
let max_allowed = (expected_exceedances as f64 * (1.0 + max_deviation)) as usize;
|
|
|
|
assert!(
|
|
exceedances <= max_allowed,
|
|
"VaR exceedances {} exceed maximum {} (expected: {}, confidence: {}, tolerance: {}%)",
|
|
exceedances,
|
|
max_allowed,
|
|
expected_exceedances,
|
|
confidence,
|
|
max_deviation * 100.0
|
|
);
|
|
}
|
|
|
|
/// Assert that OHLC relationships are valid
|
|
pub fn assert_ohlc_valid(open: f64, high: f64, low: f64, close: f64) {
|
|
assert!(
|
|
high >= open,
|
|
"High {} must be >= Open {}",
|
|
high,
|
|
open
|
|
);
|
|
assert!(
|
|
high >= close,
|
|
"High {} must be >= Close {}",
|
|
high,
|
|
close
|
|
);
|
|
assert!(
|
|
low <= open,
|
|
"Low {} must be <= Open {}",
|
|
low,
|
|
open
|
|
);
|
|
assert!(
|
|
low <= close,
|
|
"Low {} must be <= Close {}",
|
|
low,
|
|
close
|
|
);
|
|
}
|
|
|
|
/// Assert that a position's PnL is correct
|
|
pub fn assert_pnl(
|
|
entry_price: f64,
|
|
current_price: f64,
|
|
quantity: f64,
|
|
expected_pnl: f64,
|
|
tolerance: f64,
|
|
) {
|
|
let actual_pnl = (current_price - entry_price) * quantity;
|
|
assert_within!(actual_pnl, expected_pnl, tolerance);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_assert_approx_eq() {
|
|
assert_approx_eq!(100.0, 101.0, 0.02); // 2% tolerance
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_assert_approx_eq_fails() {
|
|
assert_approx_eq!(100.0, 105.0, 0.02); // Should fail
|
|
}
|
|
|
|
#[test]
|
|
fn test_assert_within() {
|
|
assert_within!(100.5, 100.0, 0.6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_assert_ohlc_valid() {
|
|
assert_ohlc_valid(100.0, 102.0, 98.0, 101.0);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_assert_ohlc_invalid() {
|
|
assert_ohlc_valid(100.0, 98.0, 99.0, 101.0); // High < Open
|
|
}
|
|
|
|
#[test]
|
|
fn test_assert_pnl() {
|
|
assert_pnl(100.0, 110.0, 50.0, 500.0, 0.01);
|
|
}
|
|
}
|