quantwave_core/indicators/
fisher.rs1use crate::indicators::metadata::IndicatorMetadata;
2use crate::traits::Next;
3
4#[derive(Debug, Clone, Default)]
12pub struct FisherTransform;
13
14impl FisherTransform {
15 pub fn new() -> Self {
16 Self
17 }
18}
19
20impl Next<f64> for FisherTransform {
21 type Output = f64;
22
23 fn next(&mut self, input: f64) -> Self::Output {
24 let x = input.clamp(-0.999, 0.999);
28 0.5 * ((1.0 + x) / (1.0 - x)).ln()
29 }
30}
31
32pub const FISHER_METADATA: IndicatorMetadata = IndicatorMetadata {
33 name: "Fisher Transform",
34 description: "Converts inputs to a nearly Gaussian probability distribution, creating sharp peaks at turning points.",
35 params: &[],
36 formula_source: "https://github.com/lavs9/quantwave/blob/main/references/Ehlers%20Papers/UsingTheFisherTransform.pdf",
37 formula_latex: r#"
38\[
39Fish(x) = 0.5 \times \ln\left(\frac{1 + x}{1 - x}\right) = \text{atanh}(x)
40\]
41"#,
42 gold_standard_file: "fisher.json",
43 category: "Ehlers DSP",
44};
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::traits::Next;
50 use proptest::prelude::*;
51
52 #[test]
53 fn test_fisher_basic() {
54 let mut fish = FisherTransform::new();
55 assert!(fish.next(0.9) > 1.0);
57 assert!(fish.next(-0.9) < -1.0);
59 approx::assert_relative_eq!(fish.next(0.0), 0.0, epsilon = 1e-6);
61 }
62
63 proptest! {
64 #[test]
65 fn test_fisher_parity(input in prop::collection::vec(-0.99..0.99, 1..100)) {
66 let mut fish = FisherTransform::new();
67 for &val in &input {
68 let s = fish.next(val);
69 let b = 0.5 * ((1.0 + val) / (1.0 - val)).ln();
70 approx::assert_relative_eq!(s, b, epsilon = 1e-10);
71 }
72 }
73 }
74}