Skip to content

Complete Ehlers normalization for 2D timeseries #4

Description

@MoonFlowww

Motivation

Ehlers-based normalization (stochastic oscillator + Fisher transform) is standard in quantitative finance and signal processing for making non-stationary timeseries stationary before feeding them into a model. Nott already supports financial datasets (ETTh, PTBXL) and has FisherTransform / InverseFisher implemented, but the actual Ehlers oscillator loops for multi-feature ([T, F]) tensors are entirely missing. Users working with multivariate timeseries have no path to this normalization.

Current State

src/data/transform/normalization/ehlers.hpp:27 ends with:

/// TODO: Implement Ehler loops for 2d timeseries

Only FisherTransform and InverseFisher are present. The normalization registry (registry.hpp) cannot expose an Ehlers option because there is nothing to register.

Proposed Change

Implement EhlersNormalize and EhlersInverse operating on [T, F] tensors:

struct EhlersOptions {
    int period = 10;          // rolling window for stochastic oscillator
    double fisher_clamp = 0.999;
};

// Returns tensor in (-1, 1) after Fisher transform
at::Tensor EhlersNormalize(const at::Tensor& input, EhlersOptions opts = {});

// Inverts: Fisher inverse → rescale back to original range
at::Tensor EhlersInverse(const at::Tensor& normalized,
                          const at::Tensor& original,
                          EhlersOptions opts = {});

Algorithm per feature column:

  1. Compute rolling min and max over opts.period steps
  2. Stochastic: x = 2 * (val - min) / (max - min + eps) - 1 → clamp to (-clamp, clamp)
  3. Apply FisherTransform(x)

Wire into src/data/transform/normalization/registry.hpp alongside ZScore and PowerNorm.

Acceptance Criteria

  • EhlersNormalize handles [T, F] tensors (per-column rolling normalization)
  • EhlersInverse correctly reconstructs the original scale
  • EhlersOptions::period is configurable
  • Registered in the normalization registry and accessible via the public Data API
  • /// TODO comment removed
  • Works on both CPU and CUDA tensors

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions