Files
QuanTAlib/lib/cycles/ebsw/ebsw.md
T
Miha Kralj 26280ce80b Add Choppiness Index (CHOP) implementation and tests
- Implemented ChopIndicator for Quantower with configurable period and cold value display.
- Created Chop class for calculating the Choppiness Index with detailed documentation.
- Added comprehensive unit tests for Chop functionality, covering various market conditions and edge cases.
- Developed markdown documentation for CHOP, detailing its historical context, mathematical foundation, and usage examples.
- Established a remediation plan for channel indicators documentation, identifying gaps and prioritizing updates.
2026-02-05 19:42:49 -08:00

4.5 KiB

EBSW: Ehlers Even Better Sinewave

"When you combine a high-pass filter with a super-smoother, you get cleaner cycles with automatic gain control."

The Even Better Sinewave (EBSW) indicator is a refined cycle oscillator developed by John Ehlers. It combines a high-pass filter (trend removal) with a Super-Smoother filter (noise removal) and Automatic Gain Control to produce an oscillator normalized between -1 and +1 that synthesizes a clean sine wave from price action.

Historical Context

Ehlers' original "Sinewave" indicator relied on the Hilbert Transform to extract phase. However, he found that direct Hilbert Transforms were often unstable on real market data. The "Even Better" Sinewave simplifies the approach: instead of complex phase math, it uses a tuned bandpass filter (High-Pass + Low-Pass) to isolate the wave, then normalizes it.

This resulted in a more robust tool for identifying turning points in both trending and ranging markets, first published in Cycle Analytics for Traders.

Architecture & Physics

The transformation pipeline consists of four distinct stages.

1. High-Pass Filter (Trend Removal)


\alpha_1 = \frac{1 - \sin(2\pi/HP)}{\cos(2\pi/HP)}

HP_t = 0.5 (1 + \alpha_1)(P_t - P_{t-1}) + \alpha_1 \cdot HP_{t-1}

2. Super-Smoother Filter (Noise Removal)


\alpha_2 = e^{-\sqrt{2}\pi / SSF}

Filt_t = \frac{1 - 2\alpha_2\cos(\sqrt{2}\pi/SSF) - \alpha_2^2}{2}(HP_t + HP_{t-1}) + 2\alpha_2\cos(\sqrt{2}\pi/SSF) \cdot Filt_{t-1} - \alpha_2^2 \cdot Filt_{t-2}

3. Wave & Power Calculation


Wave = \frac{Filt_t + Filt_{t-1} + Filt_{t-2}}{3}

Power = \frac{Filt_t^2 + Filt_{t-1}^2 + Filt_{t-2}^2}{3}

4. Normalization (AGC)


EBSW = \frac{Wave}{\sqrt{Power}}

Result is clamped to \pm 1.

Performance Profile

Operation Count (Streaming Mode, per Bar)

Operation Count Cost (cycles) Subtotal
FMA (filter updates) 4 4 16
MUL (power calc) 3 3 9
ADD/SUB 6 1 6
DIV 1 15 15
SQRT 1 12 12
Total 15 ~58 cycles

Complexity Analysis

  • Streaming: O(1) per bar—fixed cascaded IIR filters
  • Memory: O(1)—only filter state variables
  • Warmup: ~hpLength bars for HP filter convergence

Validation

Library Status Notes
TA-Lib N/A Not standard
Skender N/A Not standard
PineScript Matches ebsw script
Reference Matches Cycle Analytics for Traders logic

Usage & Pitfalls

  • Range is -1 to +1—zero crossings signal cycle phase changes
  • HP Length is critical—should match expected market cycle (e.g., 40 bars)
  • Too short HP Length filters out everything as "trend"
  • AGC amplifies noise in low volatility—verify with price action
  • Strong step moves cause railing at ±1 for extended periods
  • Buy at valley (EBSW turning up from -0.8), sell at peak (turning down from +0.8)

API

classDiagram
    class Ebsw {
        +int HpLength
        +int SsfLength
        +double Value
        +bool IsHot
        +Ebsw(int hpLength, int ssfLength)
        +Ebsw(ITValuePublisher source, int hpLength, int ssfLength)
        +TValue Update(TValue input, bool isNew)
        +void Reset()
    }

Class: Ebsw

Parameter Type Default Range Description
hpLength int 40 ≥1, ≠4 High-pass filter period (detrending)
ssfLength int 10 ≥1 Super-smoother filter period

Properties

  • Value (double): The current EBSW value (bounded -1 to +1)
  • IsHot (bool): Returns true when warmup is complete

Methods

  • Update(TValue input, bool isNew): Updates the indicator with a new data point

C# Example

using QuanTAlib;

// Create EBSW for 40-bar cycle with 10-bar smoothing
var ebsw = new Ebsw(hpLength: 40, ssfLength: 10);

// Update with streaming data
foreach (var bar in quotes)
{
    var result = ebsw.Update(new TValue(bar.Date, bar.Close));
    
    if (ebsw.IsHot)
    {
        Console.WriteLine($"{bar.Date}: EBSW = {result.Value:F4}");
        
        // Cycle turning point detection
        if (result.Value < -0.8 && result.Value > ebsw.Previous.Value)
            Console.WriteLine("  → Potential cycle bottom");
        else if (result.Value > 0.8 && result.Value < ebsw.Previous.Value)
            Console.WriteLine("  → Potential cycle top");
    }
}

// Batch calculation
var output = Ebsw.Calculate(sourceSeries, hpLength: 40, ssfLength: 10);