mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-17 01:58:06 +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.
167 lines
5.8 KiB
Markdown
167 lines
5.8 KiB
Markdown
# SINE: Ehlers Sine Wave
|
|
|
|
> "The sine wave extraction reveals what moving averages obscure—the pure rhythmic heartbeat of price action."
|
|
|
|
The Ehlers Sine Wave extracts the dominant cycle from price data using cascaded signal processing: high-pass detrending, super-smoother noise reduction, and Hilbert Transform quadrature decomposition. Output oscillates between -1 and +1, representing the normalized position within the current cycle.
|
|
|
|
## Historical Context
|
|
|
|
John Ehlers introduced the Sine Wave indicator in *Cybernetic Analysis for Stocks and Futures* (2004) as a refined approach to cycle extraction. Unlike the HT_SINE which derives phase from raw Hilbert Transform output, this implementation adds explicit detrending and smoothing stages for cleaner cycle isolation.
|
|
|
|
The design philosophy separates three signal processing concerns: (1) trend removal via high-pass filtering, (2) aliasing prevention via super-smoothing, and (3) cycle extraction via Hilbert Transform. This staged approach produces cleaner output than attempting all three simultaneously.
|
|
|
|
The Sine Wave is particularly valuable in mean-reverting strategies. When the cycle position reaches extremes (-1 or +1), it suggests the cyclical component is stretched and likely to revert. Zero crossings indicate phase transitions—potential entry/exit points in the cycle.
|
|
|
|
## Architecture & Physics
|
|
|
|
The algorithm cascades three distinct filter stages with carefully tuned frequency responses.
|
|
|
|
**Step 1: High-Pass Filter (Detrending)**
|
|
|
|
A single-pole high-pass filter removes low-frequency trends below the cutoff period:
|
|
|
|
$$\alpha_{HP} = \frac{1 - \sin(2\pi/P_{HP})}{\cos(2\pi/P_{HP})}$$
|
|
|
|
$$HP_t = \frac{1 + \alpha_{HP}}{2}(P_t - P_{t-1}) + \alpha_{HP} \cdot HP_{t-1}$$
|
|
|
|
**Step 2: Super-Smoother Filter**
|
|
|
|
A 2-pole Butterworth low-pass filter removes high-frequency noise:
|
|
|
|
$$a = e^{-\sqrt{2}\pi/P_{SSF}}$$
|
|
$$b = 2a\cos(\sqrt{2}\pi/P_{SSF})$$
|
|
$$c_1 = 1 - b + a^2, \quad c_2 = b, \quad c_3 = -a^2$$
|
|
|
|
$$\text{Filt}_t = c_1 \cdot \frac{HP_t + HP_{t-1}}{2} + c_2 \cdot \text{Filt}_{t-1} + c_3 \cdot \text{Filt}_{t-2}$$
|
|
|
|
**Step 3: Hilbert Transform FIR**
|
|
|
|
Discrete Hilbert approximation extracts quadrature component:
|
|
|
|
$$Q_t = 0.0962 \cdot \text{Filt}_{t-3} + 0.5769 \cdot \text{Filt}_{t-1} - 0.5769 \cdot \text{Filt}_{t-5} - 0.0962 \cdot \text{Filt}_{t-7}$$
|
|
|
|
$$I_t = \text{Filt}_t$$
|
|
|
|
**Step 4: Power Normalization**
|
|
|
|
$$\text{Power}_t = I_t^2 + Q_t^2$$
|
|
|
|
$$\text{Sine}_t = \frac{I_t}{\sqrt{\text{Power}_t}}$$
|
|
|
|
## Performance Profile
|
|
|
|
### Operation Count (Streaming Mode, per Bar)
|
|
|
|
| Operation | Count | Cost (cycles) | Subtotal |
|
|
|-----------|------:|------:|------:|
|
|
| FMA | 6 | 5 | 30 |
|
|
| MUL | 8 | 4 | 32 |
|
|
| ADD/SUB | 12 | 1 | 12 |
|
|
| SQRT | 1 | 15 | 15 |
|
|
| Buffer access | 10 | 3 | 30 |
|
|
| **Total** | — | — | **~120** |
|
|
|
|
### Complexity Analysis
|
|
|
|
- **Time:** $O(1)$ per bar — fixed filter stages
|
|
- **Space:** $O(1)$ — ring buffers: 2 (src) + 2 (hp) + 8 (filt) = 12 elements
|
|
- **Latency:** max(hpPeriod, ssfPeriod) + 8 bars warmup
|
|
|
|
## Validation
|
|
|
|
| Library | Status | Notes |
|
|
|---------|--------|-------|
|
|
| Ehlers Reference | ✅ Match | *Cybernetic Analysis* algorithm verified |
|
|
| Synthetic Chirp | ✅ Pass | Locks onto dominant frequency in passband |
|
|
| Quantower | ✅ Match | `Sine.Quantower.Tests.cs` adapter tests |
|
|
|
|
## Usage & Pitfalls
|
|
|
|
- **Trending Markets:** Strong trends cause erratic output or extremum pegging
|
|
- **Period Tuning:** hpPeriod defines trend/cycle boundary; ssfPeriod removes aliasing noise
|
|
- **Ratio Rule:** Typically ssfPeriod = hpPeriod / 4 to hpPeriod / 2
|
|
- **Reversal Signals:** Extremes near ±1 often precede reversals in ranging markets
|
|
- **Zero Crossing:** Phase transition point—potential entry/exit signal
|
|
- **Single Output:** Unlike HT_SINE, provides only Sine (no LeadSine)
|
|
|
|
## API
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class AbstractBase {
|
|
<<abstract>>
|
|
+Name string
|
|
+WarmupPeriod int
|
|
+IsHot bool
|
|
+Last TValue
|
|
+Update(TValue input, bool isNew) TValue
|
|
+Reset() void
|
|
}
|
|
class Sine {
|
|
+HpPeriod int
|
|
+SsfPeriod int
|
|
+Sine(int hpPeriod, int ssfPeriod)
|
|
+Sine(ITValuePublisher source, int hpPeriod, int ssfPeriod)
|
|
+Update(TValue input, bool isNew) TValue
|
|
+Update(TSeries source) TSeries
|
|
+Prime(ReadOnlySpan~double~ source, TimeSpan? step) void
|
|
+Reset() void
|
|
+Calculate(TSeries source, int hpPeriod, int ssfPeriod)$ TSeries
|
|
}
|
|
AbstractBase <|-- Sine
|
|
```
|
|
|
|
### Class: `Sine`
|
|
|
|
Ehlers Sine Wave indicator with configurable filter periods.
|
|
|
|
### Properties
|
|
|
|
| Name | Type | Description |
|
|
|------|------|-------------|
|
|
| `HpPeriod` | `int` | High-pass filter cutoff period |
|
|
| `SsfPeriod` | `int` | Super-smoother filter period |
|
|
| `IsHot` | `bool` | True after warmup complete |
|
|
| `Last` | `TValue` | Most recent Sine output (-1 to +1) |
|
|
|
|
### Methods
|
|
|
|
| Name | Returns | Description |
|
|
|------|---------|-------------|
|
|
| `Update(TValue, bool)` | `TValue` | Updates state with new price value |
|
|
| `Calculate(TSeries, hp, ssf)` | `TSeries` | Static factory with custom periods |
|
|
| `Reset()` | `void` | Clears all filter state |
|
|
|
|
## C# Example
|
|
|
|
```csharp
|
|
using QuanTAlib;
|
|
|
|
// Create Sine indicator with default periods (40, 10)
|
|
var sine = new Sine(hpPeriod: 40, ssfPeriod: 10);
|
|
|
|
// Process price data
|
|
foreach (var bar in bars)
|
|
{
|
|
var result = sine.Update(new TValue(bar.Time, bar.Close));
|
|
|
|
if (sine.IsHot)
|
|
{
|
|
double sineValue = result.Value;
|
|
|
|
// Cycle position interpretation
|
|
// +1.0 = cycle peak (potential short)
|
|
// -1.0 = cycle trough (potential long)
|
|
// 0.0 = mid-cycle transition
|
|
|
|
if (sineValue > 0.9)
|
|
Console.WriteLine("Near cycle peak");
|
|
else if (sineValue < -0.9)
|
|
Console.WriteLine("Near cycle trough");
|
|
}
|
|
}
|
|
|
|
// Static calculation
|
|
var sineResults = Sine.Calculate(prices, hpPeriod: 48, ssfPeriod: 12);
|
|
```
|