use crate::indicators::metadata::{IndicatorMetadata, ParamDef};
use crate::traits::Next;
use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct NoiseElimination {
length: usize,
window: VecDeque<f64>,
denom: f64,
}
impl NoiseElimination {
pub fn new(length: usize) -> Self {
let denom = 0.5 * (length as f64) * (length as f64 - 1.0);
Self {
length,
window: VecDeque::with_capacity(length),
denom,
}
}
}
impl Default for NoiseElimination {
fn default() -> Self {
Self::new(14)
}
}
impl Next<f64> for NoiseElimination {
type Output = f64;
fn next(&mut self, input: f64) -> Self::Output {
self.window.push_front(input);
if self.window.len() > self.length {
self.window.pop_back();
}
if self.window.len() < self.length {
return 0.0;
}
let mut num = 0.0;
for i in 1..self.length {
for j in 0..i {
let diff = self.window[i] - self.window[j];
if diff > 0.0 {
num -= 1.0;
} else if diff < 0.0 {
num += 1.0;
}
}
}
num / self.denom
}
}
pub const NOISE_ELIMINATION_METADATA: IndicatorMetadata = IndicatorMetadata {
name: "Noise Elimination Technology",
description: "Nonlinear noise removal using Kendall correlation against a straight line.",
params: &[ParamDef {
name: "length",
default: "14",
description: "Correlation length",
}],
formula_source: "https://github.com/lavs9/quantwave/blob/main/references/Ehlers%20Papers/Noise%20Elimination%20Technology.pdf",
formula_latex: r#"
\[
Num = \sum_{i=1}^{N-1} \sum_{j=0}^{i-1} -sgn(X_i - X_j)
\]
\[
Denom = \frac{N(N-1)}{2}
\]
\[
NET = \frac{Num}{Denom}
\]
"#,
gold_standard_file: "noise_elimination.json",
category: "Ehlers DSP",
};
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::Next;
use proptest::prelude::*;
#[test]
fn test_noise_elimination_basic() {
let mut net = NoiseElimination::new(14);
let inputs = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0];
let mut last_net = 0.0;
for input in inputs {
last_net = net.next(input);
}
assert_eq!(last_net, 1.0);
}
proptest! {
#[test]
fn test_noise_elimination_parity(
inputs in prop::collection::vec(-1.0..1.0, 20..100),
) {
let length = 14;
let mut net = NoiseElimination::new(length);
let streaming_results: Vec<f64> = inputs.iter().map(|&x| net.next(x)).collect();
let mut batch_results = Vec::with_capacity(inputs.len());
let denom = 0.5 * (length as f64) * (length as f64 - 1.0);
for i in 0..inputs.len() {
if i < length - 1 {
batch_results.push(0.0);
continue;
}
let mut num = 0.0;
for ii in 1..length {
for jj in 0..ii {
let diff = inputs[i - ii] - inputs[i - jj];
if diff > 0.0 {
num -= 1.0;
} else if diff < 0.0 {
num += 1.0;
}
}
}
batch_results.push(num / denom);
}
for (s, b) in streaming_results.iter().zip(batch_results.iter()) {
approx::assert_relative_eq!(s, b, epsilon = 1e-10);
}
}
}
}