- Updated the name and description of the Hilbert Trendline (HTIT) to "Ehlers Hilbert Transform Instantaneous Trend (HTIT)". - Changed the name and description of the MESA Adaptive Moving Average (MAMA) to "Ehlers MESA Adaptive Moving Average". - Modified the Center of Gravity (CG) indicator to "Ehlers Center of Gravity (CG)". - Renamed the Detrended Synthetic Price (DSP) to "Ehlers Detrended Synthetic Price (DSP)". - Updated the Autocorrelation Periodogram (EACP) to "Ehlers Autocorrelation Periodogram (EACP)". - Changed the Homodyne Discriminator (HOMOD) to "Ehlers Homodyne Discriminator (HOMOD)". - Updated the Hilbert Transform Dominant Cycle Period and Phase indicators to include "Ehlers" in their names. - Renamed the Hilbert Transform Phasor Components to "Ehlers Hilbert Transform Phasor Components (HT_PHASOR)". - Updated the SineWave indicator to "Ehlers Hilbert Transform SineWave (HT_SINE)". - Changed the Phasor Analysis indicator to "Ehlers Hilbert Transform Phasor Components (HT_PHASOR)". - Updated the SSF-Based Detrended Synthetic Price to "Ehlers SSF Detrended Synthetic Price (SSFDSP)". - Renamed the Ultimate Channel to "Ehlers Ultimate Channel (UCHANNEL)". - Added new indicators: Moving Average Variable Period (MAVP), Ehlers Predictive Moving Average (PMA), Ehlers Reverse EMA (REVERSEEMA), and Ehlers Trendflex Indicator (TRENDFLEX). - Updated various SVG badges to reflect changes in classes, comments, source files, lines of code, methods, and public types.
4.4 KiB
HOMOD: Ehlers Homodyne Discriminator
"The homodyne discriminator reveals instantaneous frequency by multiplying a signal with its delayed self — the phase rotation between samples directly encodes the cycle period."
The Homodyne Discriminator (HOMOD) estimates the dominant cycle period of a market using homodyne mixing—multiplying the signal by a delayed version of itself. This technique exposes the angular phase change between bars, allowing calculation of the instantaneous period at every time step.
Historical Context
In Rocket Science for Traders and Cybernetic Analysis for Stocks and Futures, John Ehlers introduced signal processing concepts novel to technical analysis. The Homodyne Discriminator was presented as a superior alternative to the Hilbert Transform Discriminator for cycle measurement.
It offers better noise rejection and stability while maintaining reasonable responsiveness, making it practical for real-time trading applications.
Architecture & Physics
The algorithm is a complex pipeline of filters and transformations designed to isolate the analytic signal.
1. Pre-Processing (4-Bar WMA)
Smooth = \frac{4P_t + 3P_{t-1} + 2P_{t-2} + P_{t-3}}{10}
2. Analytic Signal Generation
In-Phase (I) and Quadrature (Q) components via Hilbert Transform:
I_2 = I_1 - JQ
Q_2 = Q_1 + JI
Smoothed with EMA (α = 0.2).
3. Homodyne Mixing
Multiplying complex signal z_t by its conjugate delayed by one bar:
Real = (I_2 \cdot I_{2,prev}) + (Q_2 \cdot Q_{2,prev})
Imag = (I_2 \cdot Q_{2,prev}) - (Q_2 \cdot I_{2,prev})
4. Period Extraction
\theta = \operatorname{atan2}(Imag, Real)
Period = \frac{2\pi}{\theta}
Clamped to [MinPeriod, MaxPeriod] and smoothed.
Performance Profile
Operation Count (Streaming Mode, per Bar)
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| MUL (Hilbert taps) | 14 | 3 | 42 |
| MUL (homodyne mix) | 4 | 3 | 12 |
| ADD/SUB | 20 | 1 | 20 |
| ATAN2 | 1 | 25 | 25 |
| DIV | 2 | 15 | 30 |
| Total | 41 | — | ~129 cycles |
Complexity Analysis
- Streaming: O(1) per bar—fixed filter depth
- Memory: O(1)—state struct with history variables
- Warmup: ~2 × MaxPeriod bars for convergence
Validation
| Library | Status | Notes |
|---|---|---|
| TA-Lib | N/A | Not implemented |
| Skender | N/A | Not implemented |
| PineScript | ✅ | Matches Ehlers' reference code |
Usage & Pitfalls
- Output is period in bars—not an oscillator like RSI, but a measurement like ATR
- Long settling time (~2 × MaxPeriod)—early values unreliable
- Trending markets make "cycle" ill-defined—period drifts to MaxPeriod
- Check for cycling (ADX or trend filter) before trusting period values
- High noise causes jitter—pre-smooth extremely noisy data
- Use for adaptive tuning:
Stochastic(length: homod.DominantCycle)
API
classDiagram
class Homod {
+double MinPeriod
+double MaxPeriod
+double DominantCycle
+bool IsHot
+Homod(double minPeriod, double maxPeriod)
+Homod(ITValuePublisher source, double minPeriod, double maxPeriod)
+TValue Update(TValue input, bool isNew)
+void Reset()
}
Class: Homod
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
minPeriod |
double |
6.0 |
>0 |
Minimum period to detect |
maxPeriod |
double |
50.0 |
>minPeriod |
Maximum period to detect |
Properties
DominantCycle(double): Current dominant cycle period in barsIsHot(bool): Returnstruewhen warmup is complete
Methods
Update(TValue input, bool isNew): Updates the indicator with a new data point
C# Example
using QuanTAlib;
// Configure for cycles between 6 and 50 bars
var homod = new Homod(minPeriod: 6, maxPeriod: 50);
// Update with streaming data
foreach (var bar in quotes)
{
var result = homod.Update(new TValue(bar.Date, bar.Close));
if (homod.IsHot)
{
double period = homod.DominantCycle;
Console.WriteLine($"{bar.Date}: Dominant Cycle = {period:F1} bars");
// Use cycle to tune Stochastic
int adaptiveLength = (int)Math.Round(period);
var adaptiveStoch = new Stochastic(adaptiveLength);
}
}
// Batch calculation
var output = Homod.Calculate(sourceSeries, minPeriod: 6, maxPeriod: 50);