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 usage: "Apply to RSI or other oscillators to rescale them to a ±1 range with sharp threshold behaviour. Values near ±1 indicate high-confidence overbought/oversold conditions.",
35 keywords: &["oscillator", "ehlers", "normalization", "momentum"],
36 ehlers_summary: "The Inverse Fisher Transform maps input values to (-1, +1) via a hyperbolic tangent function. Ehlers uses it in Cybernetic Analysis to create oscillators whose output clusters near the extremes, making crossovers of fixed thresholds reliable trading signals.",
37 params: &[],
38 formula_source: "https://github.com/lavs9/quantwave/blob/main/references/Ehlers%20Papers/TheInverseFisherTransform.pdf",
39 formula_latex: r#"
40\[
41IFT(x) = \frac{e^{2x} - 1}{e^{2x} + 1} = \tanh(x)
42\]
43"#,
44 gold_standard_file: "inverse_fisher.json",
45 category: "Ehlers DSP",
46};
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::traits::Next;
52 use proptest::prelude::*;
53
54 #[test]
55 fn test_inverse_fisher_basic() {
56 let mut ift = InverseFisherTransform::new();
57 approx::assert_relative_eq!(ift.next(2.0), 0.96402758, epsilon = 1e-6);
59 approx::assert_relative_eq!(ift.next(5.0), 0.9999092, epsilon = 1e-6);
60 approx::assert_relative_eq!(ift.next(-2.0), -0.96402758, epsilon = 1e-6);
62 approx::assert_relative_eq!(ift.next(0.0), 0.0, epsilon = 1e-6);
64 }
65
66 proptest! {
67 #[test]
68 fn test_inverse_fisher_parity(input in prop::collection::vec(-5.0..5.0, 1..100)) {
69 let mut ift = InverseFisherTransform::new();
70 for &val in &input {
71 let s = ift.next(val);
72 let b = val.tanh();
73 approx::assert_relative_eq!(s, b, epsilon = 1e-10);
74 }
75 }
76 }
77}