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

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);
```