use crate::indicators::metadata::IndicatorMetadata;
use crate::traits::Next;
#[derive(Debug, Clone, Default)]
pub struct InverseFisherTransform;
impl InverseFisherTransform {
pub fn new() -> Self {
Self
}
}
impl Next<f64> for InverseFisherTransform {
type Output = f64;
fn next(&mut self, input: f64) -> Self::Output {
input.tanh()
}
}
pub const INVERSE_FISHER_METADATA: IndicatorMetadata = IndicatorMetadata {
name: "Inverse Fisher Transform",
description: "A compressive transform that forces oscillator values towards +1 or -1, creating clear buy/sell signals.",
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.",
keywords: &["oscillator", "ehlers", "normalization", "momentum"],
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.",
params: &[],
formula_source: "https://github.com/lavs9/quantwave/blob/main/references/Ehlers%20Papers/TheInverseFisherTransform.pdf",
formula_latex: r#"
\[
IFT(x) = \frac{e^{2x} - 1}{e^{2x} + 1} = \tanh(x)
\]
"#,
gold_standard_file: "inverse_fisher.json",
category: "Ehlers DSP",
};
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::Next;
use proptest::prelude::*;
#[test]
fn test_inverse_fisher_basic() {
let mut ift = InverseFisherTransform::new();
approx::assert_relative_eq!(ift.next(2.0), 0.96402758, epsilon = 1e-6);
approx::assert_relative_eq!(ift.next(5.0), 0.9999092, epsilon = 1e-6);
approx::assert_relative_eq!(ift.next(-2.0), -0.96402758, epsilon = 1e-6);
approx::assert_relative_eq!(ift.next(0.0), 0.0, epsilon = 1e-6);
}
proptest! {
#[test]
fn test_inverse_fisher_parity(input in prop::collection::vec(-5.0..5.0, 1..100)) {
let mut ift = InverseFisherTransform::new();
for &val in &input {
let s = ift.next(val);
let b = val.tanh();
approx::assert_relative_eq!(s, b, epsilon = 1e-10);
}
}
}
}