mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-18 10:38:05 +00:00
- 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.
156 lines
4.4 KiB
Markdown
156 lines
4.4 KiB
Markdown
# HOMOD: 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
|
||
|
||
```mermaid
|
||
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 bars
|
||
- `IsHot` (`bool`): Returns `true` when warmup is complete
|
||
|
||
### Methods
|
||
|
||
- `Update(TValue input, bool isNew)`: Updates the indicator with a new data point
|
||
|
||
## C# Example
|
||
|
||
```csharp
|
||
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);
|
||
```
|