2026-02-05 19:42:49 -08:00
# HT_SINE: Hilbert Transform SineWave
2026-02-04 20:58:05 -08:00
> "The Hilbert Transform gives us the phase of the dominant cycle—knowing when to buy and sell becomes a matter of trigonometry."
2026-02-05 19:42:49 -08:00
The Hilbert Transform SineWave extracts the dominant market cycle phase and outputs both sine and lead sine (45° phase advance) for cycle timing. The crossover of these two waves identifies turning points in ranging markets up to 1/8th of a cycle early.
2026-02-04 20:58:05 -08:00
## Historical Context
2026-02-05 19:42:49 -08:00
John Ehlers introduced the Hilbert Transform SineWave in *Rocket Science for Traders* (2001) as part of his comprehensive signal processing framework for financial markets. The indicator addresses a fundamental limitation of traditional oscillators—they respond to price amplitude rather than cycle phase.
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
The HT_SINE builds upon David Hilbert's 1905 mathematical transform, which creates a 90° phase-shifted (quadrature) version of a signal. In signal processing, this enables instantaneous frequency and phase extraction. Ehlers recognized that market cycles, though noisy and variable, could be analyzed using these same techniques.
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
Unlike momentum oscillators that lag price action, the HT_SINE theoretically provides zero-lag cycle detection by measuring phase directly. This makes it particularly valuable in ranging markets where cycles are well-defined. The dual output (Sine and LeadSine) creates a built-in early warning system for cycle reversals.
2026-02-04 20:58:05 -08:00
## Architecture & Physics
2026-02-05 19:42:49 -08:00
The algorithm implements a discrete approximation of the Hilbert Transform optimized for financial time series with adaptive period estimation.
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
**Step 1: WMA Smoothing**
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
A 4-bar weighted moving average removes Nyquist-frequency noise:
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
$$\bar{P}_t = \frac{4P_t + 3P_{t-1} + 2P_{t-2} + P_{t-3}}{10}$$
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
**Step 2: Hilbert Transform FIR**
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
The discrete Hilbert approximation generates quadrature components:
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
$$\text{Detrender}_t = 0.0962\bar{P}_t + 0.5769\bar{P}_{t-2} - 0.5769\bar{P}_{t-4} - 0.0962\bar{P}_{t-6}$$
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
**Step 3: I/Q Component Smoothing**
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
In-phase and quadrature components undergo exponential smoothing:
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
$$Q_t = 0.2(Q1_t + JI_t) + 0.8 Q_{t-1}$$
$$I_t = 0.2(I1_t - JQ_t) + 0.8 I_{t-1}$$
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
**Step 4: Homodyne Discriminator**
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
Period estimation uses phase rate of change:
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
$$Re_t = 0.2(I_t \cdot I_{t-1} + Q_t \cdot Q_{t-1}) + 0.8 Re_{t-1}$$
$$Im_t = 0.2(I_t \cdot Q_{t-1} - Q_t \cdot I_{t-1}) + 0.8 Im_{t-1}$$
$$\text{Period}_t = \frac{2\pi}{\arctan(Im_t / Re_t)}$$
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
**Step 5: DC Phase Calculation**
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
The dominant cycle phase sums weighted contributions:
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
$$\phi_t = \arctan\left(\frac{\sum_{i=0}^{P-1} \sin(2\pi i/P) \cdot \bar{P}_{t-i}}{\sum_{i=0}^{P-1} \cos(2\pi i/P) \cdot \bar{P}_{t-i}}\right)$$
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
**Step 6: Output Generation**
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
$$\text{Sine}_t = \sin(\phi_t)$$
$$\text{LeadSine}_t = \sin(\phi_t + 45°)$$
2026-02-04 20:58:05 -08:00
## Performance Profile
2026-02-05 19:42:49 -08:00
### Operation Count (Streaming Mode, per Bar)
2026-02-04 20:58:05 -08:00
| Operation | Count | Cost (cycles) | Subtotal |
2026-02-05 19:42:49 -08:00
|-----------|------:|------:|------:|
| FMA | 12 | 5 | 60 |
| MUL | 18 | 4 | 72 |
| ADD/SUB | 25 | 1 | 25 |
| DIV | 2 | 15 | 30 |
| sin/cos | 2P | 40 | ~80P |
| atan | 2 | 50 | 100 |
| Buffer access | 15 | 3 | 45 |
| **Total** | — | — | ** ~370** |
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
### Complexity Analysis
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
- **Time:** $O(P)$ per bar where P is smoothed period (typically 6-50)
- **Space:** $O(1)$ — fixed-size circular buffers (50 + 44 + 64 elements)
- **Latency:** 63 bars warmup (31 + 32 for TA-Lib compatibility)
2026-02-04 20:58:05 -08:00
## Validation
| Library | Status | Notes |
2026-02-05 19:42:49 -08:00
|---------|--------|-------|
| TA-Lib | ✅ Match | `TA_HT_SINE()` reference implementation |
| PineScript | ✅ Match | Custom `ht_sine.pine` validation script |
| Quantower | ✅ Match | `HtSine.Quantower.Tests.cs` adapter tests |
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
## Usage & Pitfalls
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
- **Trend Failure:** Crossover signals whipsaw in strong trends; parallel "snake" pattern indicates trending mode
- **Warmup Period:** Requires 63 bars before outputs stabilize
- **Phase Lag:** Despite "zero-lag" theory, smoothing introduces 4-6 bars practical lag
- **Range-Only:** Most effective in sideways/ranging markets with clear cyclical behavior
- **LeadSine First:** LeadSine turns before Sine at reversals—watch for divergence
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
## API
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
```mermaid
classDiagram
class AbstractBase {
<<abstract>>
+Name string
+WarmupPeriod int
+IsHot bool
+Last TValue
+Update(TValue input, bool isNew) TValue
+Reset() void
}
class HtSine {
+LeadSine double
+HtSine()
+HtSine(ITValuePublisher source)
+Update(TValue input, bool isNew) TValue
+Update(TSeries source) TSeries
+Prime(ReadOnlySpan~double~ source, TimeSpan? step) void
+Reset() void
+Calculate(TSeries source)$ TSeries
+Batch(ReadOnlySpan~double~ source, Span~double~ sine, Span~double~ leadSine)$ void
}
AbstractBase <|-- HtSine
```
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
### Class: `HtSine`
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
Hilbert Transform SineWave indicator with dual output.
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
### Properties
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
| Name | Type | Description |
|------|------|-------------|
| `LeadSine` | `double` | Current LeadSine value (45° phase lead) |
| `IsHot` | `bool` | True after 63 bars warmup |
| `Last` | `TValue` | Most recent Sine output |
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
### Methods
2026-02-04 20:58:05 -08:00
2026-02-05 19:42:49 -08:00
| Name | Returns | Description |
|------|---------|-------------|
| `Update(TValue, bool)` | `TValue` | Updates state with new price value |
| `Batch(source, sine, leadSine)` | `void` | Processes span with dual output spans |
| `Calculate(TSeries)` | `TSeries` | Static factory returning Sine series |
## C# Example
2026-02-04 20:58:05 -08:00
```csharp
2026-02-05 19:42:49 -08:00
using QuanTAlib ;
// Create HT_SINE indicator
2026-02-04 20:58:05 -08:00
var htSine = new HtSine ();
2026-02-05 19:42:49 -08:00
// Process price data
2026-02-04 20:58:05 -08:00
foreach ( var bar in bars )
{
2026-02-05 19:42:49 -08:00
var result = htSine . Update ( new TValue ( bar . Time , bar . Close ));
2026-02-04 20:58:05 -08:00
if ( htSine . IsHot )
{
double sine = result . Value ;
double leadSine = htSine . LeadSine ;
// Crossover detection
2026-02-05 19:42:49 -08:00
// Buy: Sine crosses above LeadSine
// Sell: Sine crosses below LeadSine
Console . WriteLine ( $"Sine: {sine:F4}, LeadSine: {leadSine:F4}" );
2026-02-04 20:58:05 -08:00
}
}
2026-02-05 19:42:49 -08:00
// Batch processing with dual outputs
Span < double > sineOut = stackalloc double [ prices . Length ];
Span < double > leadOut = stackalloc double [ prices . Length ];
HtSine . Batch ( prices , sineOut , leadOut );
2026-02-04 20:58:05 -08:00
```