use crate::zigzag::types::{BaseClass, HighClass};
#[inline]
pub fn classify_base_class(l0_price: i64, l2_price: i64, epsilon: i64) -> BaseClass {
let diff = l2_price - l0_price;
if diff.abs() <= epsilon {
BaseClass::EL
} else if diff > 0 {
BaseClass::HL
} else {
BaseClass::LL
}
}
#[inline]
pub fn compute_z(l0_price: i64, h1_price: i64, l2_price: i64) -> Option<f64> {
let segment_size = h1_price - l0_price;
if segment_size == 0 {
return None;
}
Some((l2_price - l0_price) as f64 / segment_size as f64)
}
#[inline]
pub fn classify_high_class(h1_price: i64, h3_price: i64, epsilon: i64) -> HighClass {
let diff = h3_price - h1_price;
if diff.abs() <= epsilon {
HighClass::EH
} else if diff > 0 {
HighClass::HH
} else {
HighClass::LH
}
}
#[inline]
pub fn compute_z_high(h1_price: i64, l2_price: i64, h3_price: i64) -> Option<f64> {
let second_leg = h1_price - l2_price;
if second_leg == 0 {
return None;
}
Some((h3_price - h1_price) as f64 / second_leg as f64)
}
#[cfg(test)]
mod tests {
use super::*;
const SCALE: i64 = 100_000_000;
fn fp(value: f64) -> i64 {
(value * SCALE as f64).round() as i64
}
#[test]
fn test_higher_low() {
assert_eq!(
classify_base_class(fp(100.0), fp(102.0), fp(0.5)),
BaseClass::HL
);
}
#[test]
fn test_equal_low() {
assert_eq!(
classify_base_class(fp(100.0), fp(100.3), fp(0.5)),
BaseClass::EL
);
}
#[test]
fn test_equal_low_negative_diff() {
assert_eq!(
classify_base_class(fp(100.0), fp(99.8), fp(0.5)),
BaseClass::EL
);
}
#[test]
fn test_lower_low() {
assert_eq!(
classify_base_class(fp(100.0), fp(98.0), fp(0.5)),
BaseClass::LL
);
}
#[test]
fn test_boundary_equal() {
let l0 = fp(100.0);
let epsilon = fp(1.0);
assert_eq!(
classify_base_class(l0, l0 + epsilon, epsilon),
BaseClass::EL
);
assert_eq!(
classify_base_class(l0, l0 - epsilon, epsilon),
BaseClass::EL
);
}
#[test]
fn test_compute_z_higher_low() {
let z = compute_z(fp(100.0), fp(110.0), fp(105.0)).unwrap();
assert!((z - 0.5).abs() < 1e-10);
}
#[test]
fn test_compute_z_equal_low() {
let z = compute_z(fp(100.0), fp(110.0), fp(100.0)).unwrap();
assert!(z.abs() < 1e-10);
}
#[test]
fn test_compute_z_lower_low() {
let z = compute_z(fp(100.0), fp(110.0), fp(95.0)).unwrap();
assert!((z - (-0.5)).abs() < 1e-10);
}
#[test]
fn test_compute_z_degenerate() {
assert!(compute_z(fp(100.0), fp(100.0), fp(100.0)).is_none());
}
#[test]
fn test_higher_high() {
assert_eq!(
classify_high_class(fp(100.0), fp(102.0), fp(0.5)),
HighClass::HH
);
}
#[test]
fn test_equal_high() {
assert_eq!(
classify_high_class(fp(100.0), fp(100.3), fp(0.5)),
HighClass::EH
);
}
#[test]
fn test_equal_high_negative_diff() {
assert_eq!(
classify_high_class(fp(100.0), fp(99.8), fp(0.5)),
HighClass::EH
);
}
#[test]
fn test_lower_high() {
assert_eq!(
classify_high_class(fp(100.0), fp(98.0), fp(0.5)),
HighClass::LH
);
}
#[test]
fn test_boundary_equal_high() {
let h1 = fp(100.0);
let epsilon = fp(1.0);
assert_eq!(
classify_high_class(h1, h1 + epsilon, epsilon),
HighClass::EH
);
assert_eq!(
classify_high_class(h1, h1 - epsilon, epsilon),
HighClass::EH
);
assert_eq!(
classify_high_class(h1, h1 + epsilon + 1, epsilon),
HighClass::HH
);
assert_eq!(
classify_high_class(h1, h1 - epsilon - 1, epsilon),
HighClass::LH
);
}
#[test]
fn test_compute_z_high_higher() {
let z = compute_z_high(fp(100.0), fp(90.0), fp(110.0)).unwrap();
assert!((z - 1.0).abs() < 1e-10);
}
#[test]
fn test_compute_z_high_lower() {
let z = compute_z_high(fp(100.0), fp(90.0), fp(95.0)).unwrap();
assert!((z - (-0.5)).abs() < 1e-10);
}
#[test]
fn test_compute_z_high_degenerate() {
assert!(compute_z_high(fp(100.0), fp(100.0), fp(105.0)).is_none());
}
}