Files
QuanTAlib/lib/cycles/dsp/dsp.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

3.9 KiB
Raw Blame History

DSP: Detrended Synthetic Price

"Remove the trend, reveal the cycles."

The Detrended Synthetic Price (DSP) indicator creates a zero-centered oscillator by subtracting a half-cycle EMA from a quarter-cycle EMA. Developed by John Ehlers, this "synthetic" price highlights underlying cyclical movement, identifying momentum shifts when the faster EMA crosses the slower one.

Historical Context

John Ehlers introduced the DSP as part of his research into cycle analytics for traders. While many indicators (like MACD) use arbitrary periods (12/26), DSP is grounded in cycle theory. Ehlers posits that to effectively isolate a cycle, one should filter data based on the dominant cycle period.

The use of period/4 and period/2 roughly corresponds to extracting the cycle's momentum while cancelling out longer-term trends. This makes DSP particularly effective for cycle-based trading strategies.

Architecture & Physics

DSP utilizes a dual EMA architecture, calibrated to specific fractions of the cycle period.

1. Component Periods


P_{fast} = \max(2, \text{round}(P / 4))

P_{slow} = \max(3, \text{round}(P / 2))

2. Alpha Coefficients


\alpha_{fast} = \frac{2}{P_{fast} + 1}

\alpha_{slow} = \frac{2}{P_{slow} + 1}

3. EMA Updates (with Bias Correction)


EMA_{raw} = \alpha \cdot Price + (1 - \alpha) \cdot EMA_{raw\_prev}

EMA_{corrected} = \frac{EMA_{raw}}{1 - (1-\alpha)^n}

4. DSP Calculation


DSP = EMA_{fast} - EMA_{slow}

Performance Profile

Operation Count (Streaming Mode, per Bar)

Operation Count Cost (cycles) Subtotal
FMA (EMA updates) 2 4 8
MUL (decay factors) 2 3 6
DIV (bias correction) 2 15 30
SUB (DSP = fast - slow) 1 1 1
Total 7 ~45 cycles

Complexity Analysis

  • Streaming: O(1) per bar—fixed calculation depth
  • Memory: O(1)—only EMA state variables
  • Warmup: ~2 × slow period for convergence

Validation

Library Status Notes
TA-Lib N/A Not standard
Skender N/A Not standard
PineScript Matches Ehlers' reference logic

Usage & Pitfalls

  • Zero crossing indicates cycle phase change—above zero is bullish, below zero is bearish
  • Period should match market cycle—if market cycle is 20 bars, use period 20 not 40
  • Not normalized—amplitude reflects absolute price difference, varies by asset
  • Whipsaws occur in ranging markets with cycles shorter than the setting
  • Divergence (higher price highs with lower DSP highs) suggests cycle energy loss
  • Use FusedMultiplyAdd for optimal precision in EMA recursion

API

classDiagram
    class Dsp {
        +int Period
        +double Value
        +bool IsHot
        +Dsp(int period)
        +Dsp(ITValuePublisher source, int period)
        +TValue Update(TValue input, bool isNew)
        +void Reset()
    }

Class: Dsp

Parameter Type Default Range Description
period int 40 ≥4 Dominant cycle period

Properties

  • Value (double): The current DSP value (oscillates around 0)
  • 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 DSP for a 40-bar cycle
var dsp = new Dsp(period: 40);

// Update with streaming data
foreach (var bar in quotes)
{
    var result = dsp.Update(new TValue(bar.Date, bar.Close));
    
    if (dsp.IsHot)
    {
        Console.WriteLine($"{bar.Date}: DSP = {result.Value:F4}");
        
        // Cycle phase detection
        if (result.Value > 0)
            Console.WriteLine("  → Bullish cycle phase");
        else
            Console.WriteLine("  → Bearish cycle phase");
    }
}

// Batch calculation
var output = Dsp.Calculate(sourceSeries, period: 40);