2026-01-18 19:02:03 -08:00
# DSP: Detrended Synthetic Price
2026-02-04 11:43:59 -08:00
> "Remove the trend, reveal the cycles."
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
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.
2026-01-18 19:02:03 -08:00
2026-02-04 11:43:59 -08:00
## Historical Context
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
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.
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
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.
2026-01-18 19:02:03 -08:00
2026-02-04 11:43:59 -08:00
## Architecture & Physics
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
DSP utilizes a dual EMA architecture, calibrated to specific fractions of the cycle period.
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
### 1. Component Periods
2026-01-18 19:02:03 -08:00
2026-02-04 11:43:59 -08:00
$$
2026-02-05 19:42:49 -08:00
P_{fast} = \max(2, \text{round}(P / 4))
2026-02-04 11:43:59 -08:00
$$
2026-01-18 19:02:03 -08:00
2026-02-04 11:43:59 -08:00
$$
2026-02-05 19:42:49 -08:00
P_{slow} = \max(3, \text{round}(P / 2))
2026-02-04 11:43:59 -08:00
$$
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
### 2. Alpha Coefficients
2026-01-18 19:02:03 -08:00
2026-02-04 11:43:59 -08:00
$$
2026-02-05 19:42:49 -08:00
\alpha_{fast} = \frac{2}{P_{fast} + 1}
2026-02-04 11:43:59 -08:00
$$
$$
2026-02-05 19:42:49 -08:00
\alpha_{slow} = \frac{2}{P_{slow} + 1}
2026-02-04 11:43:59 -08:00
$$
2026-02-05 19:42:49 -08:00
### 3. EMA Updates (with Bias Correction)
2026-02-04 11:43:59 -08:00
$$
2026-02-05 19:42:49 -08:00
EMA_{raw} = \alpha \cdot Price + (1 - \alpha) \cdot EMA_{raw\_prev}
2026-02-04 11:43:59 -08:00
$$
2026-02-05 19:42:49 -08:00
$$
EMA_{corrected} = \frac{EMA_{raw}}{1 - (1-\alpha)^n}
$$
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
### 4. DSP Calculation
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
$$
DSP = EMA_{fast} - EMA_{slow}
$$
2026-01-18 19:02:03 -08:00
## Performance Profile
2026-02-05 19:42:49 -08:00
### Operation Count (Streaming Mode, per Bar)
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
| 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** |
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
### Complexity Analysis
2026-01-18 19:02:03 -08:00
2026-02-05 19:42:49 -08:00
- **Streaming:** O(1) per bar—fixed calculation depth
- **Memory:** O(1)—only EMA state variables
- **Warmup:** ~2 × slow period for convergence
2026-02-04 11:43:59 -08:00
## Validation
| Library | Status | Notes |
2026-02-05 19:42:49 -08:00
| :--- | :---: | :--- |
| TA-Lib | N/A | Not standard |
| Skender | N/A | Not standard |
| PineScript | ✅ | Matches Ehlers' reference logic |
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
## Usage & Pitfalls
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
- **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
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
## API
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
```mermaid
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()
}
```
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
### Class: `Dsp`
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
| Parameter | Type | Default | Range | Description |
| :--- | :--- | :--- | :--- | :--- |
| `period` | `int` | `40` | `≥4` | Dominant cycle period |
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
### Properties
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
- `Value` (`double` ): The current DSP value (oscillates around 0)
- `IsHot` (`bool` ): Returns `true` when warmup is complete
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
### Methods
- `Update(TValue input, bool isNew)` : Updates the indicator with a new data point
## C# Example
2026-02-04 11:43:59 -08:00
```csharp
using QuanTAlib ;
2026-02-05 19:42:49 -08:00
// Create DSP for a 40-bar cycle
2026-02-04 11:43:59 -08:00
var dsp = new Dsp ( period : 40 );
2026-02-05 19:42:49 -08:00
// 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" );
}
}
2026-02-04 11:43:59 -08:00
2026-02-05 19:42:49 -08:00
// Batch calculation
var output = Dsp . Calculate ( sourceSeries , period : 40 );
2026-02-04 11:43:59 -08:00
```