quantwave_core/indicators/
inverse_fisher.rs1use crate::indicators::metadata::IndicatorMetadata;
2use crate::traits::Next;
3
4#[derive(Debug, Clone, Default)]
13pub struct InverseFisherTransform;
14
15impl InverseFisherTransform {
16 pub fn new() -> Self {
17 Self
18 }
19}
20
21impl Next<f64> for InverseFisherTransform {
22 type Output = f64;
23
24 fn next(&mut self, input: f64) -> Self::Output {
25 input.tanh()
28 }
29}
30
31pub const INVERSE_FISHER_METADATA: IndicatorMetadata = IndicatorMetadata {
32 name: "Inverse Fisher Transform",
33 description: "A compressive transform that forces oscillator values towards +1 or -1, creating clear buy/sell signals.",
34 params: &[],
35 formula_source: "https://github.com/lavs9/quantwave/blob/main/references/Ehlers%20Papers/TheInverseFisherTransform.pdf",
36 formula_latex: r#"
37\[
38IFT(x) = \frac{e^{2x} - 1}{e^{2x} + 1} = \tanh(x)
39\]
40"#,
41 gold_standard_file: "inverse_fisher.json",
42 category: "Ehlers DSP",
43};
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::traits::Next;
49 use proptest::prelude::*;
50
51 #[test]
52 fn test_inverse_fisher_basic() {
53 let mut ift = InverseFisherTransform::new();
54 approx::assert_relative_eq!(ift.next(2.0), 0.96402758, epsilon = 1e-6);
56 approx::assert_relative_eq!(ift.next(5.0), 0.9999092, epsilon = 1e-6);
57 approx::assert_relative_eq!(ift.next(-2.0), -0.96402758, epsilon = 1e-6);
59 approx::assert_relative_eq!(ift.next(0.0), 0.0, epsilon = 1e-6);
61 }
62
63 proptest! {
64 #[test]
65 fn test_inverse_fisher_parity(input in prop::collection::vec(-5.0..5.0, 1..100)) {
66 let mut ift = InverseFisherTransform::new();
67 for &val in &input {
68 let s = ift.next(val);
69 let b = val.tanh();
70 approx::assert_relative_eq!(s, b, epsilon = 1e-10);
71 }
72 }
73 }
74}