mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-23 04:58:08 +00:00
Add new moving average implementations: LTMA, MCNMA, NLMA, NMA, NYQMA, RAIN, and TRAMA
- LTMA (Linear Trend Moving Average): Introduces a predictive moving average using dual cascaded EMAs for trend estimation. - MCNMA (McNicholl EMA): Implements a zero-lag TEMA using a cascaded EMA structure for enhanced responsiveness. - NLMA (Non-Lag Moving Average): Utilizes a damped cosine kernel to achieve reduced lag in moving averages. - NMA (Natural Moving Average): Adapts smoothing based on volatility profiles using a square-root kernel. - NYQMA (Nyquist Moving Average): Applies the Nyquist-Shannon theorem to prevent aliasing in cascaded moving averages. - RAIN (Rainbow Moving Average): Combines multiple SMA layers with weighted averages for multi-scale smoothing. - TRAMA (Trend Regularity Adaptive Moving Average): Adapts smoothing based on the frequency of new highs and lows in price data.
This commit is contained in:
@@ -1,230 +1,159 @@
|
||||
# HT_TRENDMODE: Ehlers Hilbert Transform Trend vs Cycle Mode
|
||||
# HT_TRENDMODE: Hilbert Transform Trend vs Cycle Mode
|
||||
|
||||
The Hilbert Transform Trend Mode indicator is a binary regime classifier that determines whether price action is dominated by trending behavior (output = 1) or cyclical/mean-reverting behavior (output = 0). It uses the full Ehlers Hilbert Transform pipeline — 4-bar WMA smoothing, Hilbert FIR filters, homodyne discriminator for period estimation, DC phase extraction, and SineWave indicators — then applies four decision criteria to classify the current regime. The implementation follows TA-Lib's Ehlers-faithful algorithm from the February 2002 publication. Output is discrete {0, 1}, making it a direct strategy selector: deploy trend-following logic when mode = 1, and mean-reversion logic when mode = 0.
|
||||
|
||||
## Historical Context
|
||||
|
||||
The Hilbert Transform Trend Mode indicator was developed by **John Ehlers** as part of his cycle analysis toolkit. It uses the Hilbert Transform—a signal processing technique—to determine whether price action is dominated by **trending behavior** or **cyclical/mean-reverting behavior**.
|
||||
|
||||
This implementation follows **TA-Lib's Ehlers-faithful algorithm** from his February 2002 publication "The Instantaneous Trendline." The key insight: trend mode is detected via multiple criteria including SineWave crossings, phase rate analysis, and price-trendline deviation.
|
||||
John Ehlers developed the Trend Mode indicator as part of his cycle analysis toolkit, published in "The Instantaneous Trendline" (February 2002) and expanded in *MESA and Trading Market Cycles* (2002). Ehlers recognized that traders face two fundamentally different market regimes requiring opposite strategies. Applying a trend-following system to a cycling market produces losses, and applying a mean-reversion system to a trending market produces losses. The Hilbert Transform provides the mathematical machinery to distinguish these states by analyzing the phase behavior of the dominant cycle. When phase advances at a regular rate (consistent with a sinusoidal cycle), the market is in cycle mode. When phase rate becomes irregular or price deviates significantly from its trendline, the market is trending. The four-criteria decision logic prevents rapid mode flipping during transitional periods by requiring sustained evidence before declaring a regime change.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
### The Trend/Cycle Duality
|
||||
### 1. Hilbert Transform Core
|
||||
|
||||
Markets alternate between two fundamental states:
|
||||
The same pipeline as HT_DCPERIOD and HT_SINE:
|
||||
|
||||
| State | Characteristic | Strategy |
|
||||
|-------|---------------|----------|
|
||||
| **Trend Mode (1)** | Directional momentum | Trend-following |
|
||||
| **Cycle Mode (0)** | Mean-reverting oscillation | Range-trading |
|
||||
$$\text{smooth} = \frac{4P_t + 3P_{t-1} + 2P_{t-2} + P_{t-3}}{10}$$
|
||||
|
||||
The TA-Lib algorithm uses **four criteria** to determine trend mode:
|
||||
Hilbert FIR filters extract InPhase and Quadrature components, which feed the homodyne discriminator for period estimation:
|
||||
|
||||
1. **SineWave Crossings**: Reset trend counter when Sine crosses LeadSine
|
||||
2. **Days in Trend**: Must exceed half the smooth period
|
||||
3. **Phase Rate Check**: Normal phase change rate indicates cycle mode
|
||||
4. **Price-Trendline Deviation**: ≥1.5% deviation forces trend mode
|
||||
$$Re = 0.2(I_2 \cdot I_{2,t-1} + Q_2 \cdot Q_{2,t-1}) + 0.8 \cdot Re_{t-1}$$
|
||||
|
||||
$$Im = 0.2(I_2 \cdot Q_{2,t-1} - Q_2 \cdot I_{2,t-1}) + 0.8 \cdot Im_{t-1}$$
|
||||
|
||||
$$\text{period} = \frac{360}{\arctan(Im/Re) \times \frac{180}{\pi}}$$
|
||||
|
||||
$$\text{smoothPeriod} = 0.33 \times \text{period} + 0.67 \times \text{smoothPeriod}_{t-1}$$
|
||||
|
||||
### 2. DC Phase and SineWave
|
||||
|
||||
DFT accumulation over the dominant cycle period extracts the DC phase:
|
||||
|
||||
$$\text{dcPhase} = \arctan\!\left(\frac{\sum \sin(\omega i) \cdot \text{smooth}_i}{\sum \cos(\omega i) \cdot \text{smooth}_i}\right) + 90° + \text{lagComp}$$
|
||||
|
||||
$$\text{sine} = \sin(\text{dcPhase}), \quad \text{leadSine} = \sin(\text{dcPhase} + 45°)$$
|
||||
|
||||
### 3. Trendline
|
||||
|
||||
An SMA over the dominant cycle period, further smoothed with a 4-bar WMA:
|
||||
|
||||
$$\text{sma} = \text{Average}(\text{price}, \lfloor\text{dcPeriod}\rfloor)$$
|
||||
|
||||
$$\text{trendline} = \frac{4 \cdot \text{sma}_0 + 3 \cdot \text{sma}_1 + 2 \cdot \text{sma}_2 + \text{sma}_3}{10}$$
|
||||
|
||||
### 4. Four-Criteria Decision Logic
|
||||
|
||||
```
|
||||
trend = 1 (assume trend by default)
|
||||
|
||||
Criterion 1: SineWave crossing resets counter
|
||||
if sine crosses leadSine → daysInTrend = 0, trend = 0
|
||||
|
||||
Criterion 2: Duration threshold
|
||||
daysInTrend++
|
||||
if daysInTrend < 0.5 × smoothPeriod → trend = 0
|
||||
|
||||
Criterion 3: Phase rate check
|
||||
phaseChange = dcPhase - prevDcPhase
|
||||
expected = 360 / smoothPeriod
|
||||
if 0.67 × expected < phaseChange < 1.5 × expected → trend = 0
|
||||
|
||||
Criterion 4: Price deviation override
|
||||
if |smoothPrice - trendline| / trendline ≥ 0.015 → trend = 1
|
||||
```
|
||||
|
||||
### 5. Complexity
|
||||
|
||||
- **Time:** $O(P)$ per bar for the SMA over dominant cycle period; Hilbert pipeline is $O(1)$
|
||||
- **Space:** $O(P_{\max})$ — circular buffers for price history and Hilbert state ($P_{\max} = 50$)
|
||||
- **Warmup:** 63 bars (TA-Lib compatible)
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
### 1. Hilbert Transform Components
|
||||
### Parameters
|
||||
|
||||
The indicator uses the same Hilbert Transform core as HT_DCPERIOD:
|
||||
No user-configurable parameters. The algorithm self-tunes based on the detected dominant cycle period (clamped to 6-50 bars).
|
||||
|
||||
### Pseudo-code
|
||||
|
||||
```
|
||||
smooth_price = (4×P₀ + 3×P₁ + 2×P₂ + P₃) / 10
|
||||
Initialize:
|
||||
circBuffer = array for Hilbert state
|
||||
smoothPrice = priceHistory = arrays
|
||||
daysInTrend = 0
|
||||
smoothPeriod = 0
|
||||
prevDcPhase = 0
|
||||
bar_count = 0
|
||||
|
||||
detrender = FIR(smooth_price) × bandwidth
|
||||
Q1 = FIR(detrender) × bandwidth
|
||||
I1 = detrender[3]
|
||||
On each bar (price, isNew):
|
||||
if !isNew: restore previous state
|
||||
|
||||
// Phasor rotation
|
||||
I2 = I1 - jQ
|
||||
Q2 = Q1 + jI
|
||||
// Step 1: 4-bar WMA smooth
|
||||
smooth = (4×price[0] + 3×price[1] + 2×price[2] + price[3]) / 10
|
||||
|
||||
// Step 2: Hilbert Transform (FIR filters)
|
||||
detrender = HilbertFIR(smooth) × adjustedBandwidth
|
||||
Q1 = HilbertFIR(detrender) × adjustedBandwidth
|
||||
I1 = detrender[3]
|
||||
|
||||
// Step 3: Phasor rotation
|
||||
I2 = I1 - jQ_prev; Q2 = Q1 + jI_prev
|
||||
I2 = 0.2×I2 + 0.8×I2_prev; Q2 = 0.2×Q2 + 0.8×Q2_prev
|
||||
|
||||
// Step 4: Homodyne discriminator → period
|
||||
Re = 0.2×(I2×I2_prev + Q2×Q2_prev) + 0.8×Re_prev
|
||||
Im = 0.2×(I2×Q2_prev - Q2×I2_prev) + 0.8×Im_prev
|
||||
period = clamp(360 / (atan(Im/Re) × RAD2DEG), 6, 50)
|
||||
smoothPeriod = 0.33×period + 0.67×smoothPeriod_prev
|
||||
|
||||
// Step 5: DC Phase via DFT
|
||||
dcPeriodInt = floor(smoothPeriod + 0.5)
|
||||
realPart = Σ sin(i × 360/dcPeriodInt) × smooth[i] for i=0..dcPeriodInt-1
|
||||
imagPart = Σ cos(i × 360/dcPeriodInt) × smooth[i]
|
||||
dcPhase = atan(realPart/imagPart)×RAD2DEG + 90 + lagCompensation
|
||||
|
||||
// Step 6: SineWave indicators
|
||||
sine = sin(dcPhase × DEG2RAD)
|
||||
leadSine = sin((dcPhase + 45) × DEG2RAD)
|
||||
|
||||
// Step 7: Trendline (SMA smoothed with WMA)
|
||||
sma = average(price, dcPeriodInt)
|
||||
trendline = (4×sma[0] + 3×sma[1] + 2×sma[2] + sma[3]) / 10
|
||||
|
||||
// Step 8: Four-criteria trend decision
|
||||
trend = 1
|
||||
if sine crosses leadSine: daysInTrend = 0; trend = 0
|
||||
daysInTrend++
|
||||
if daysInTrend < 0.5 × smoothPeriod: trend = 0
|
||||
phaseChange = dcPhase - prevDcPhase
|
||||
expected = 360 / smoothPeriod
|
||||
if phaseChange > 0.67×expected AND phaseChange < 1.5×expected: trend = 0
|
||||
if |smooth - trendline| / trendline >= 0.015: trend = 1
|
||||
|
||||
prevDcPhase = dcPhase
|
||||
output = trend // 1 = trending, 0 = cycling
|
||||
```
|
||||
|
||||
### 2. Period and DC Phase
|
||||
### Decision Criteria Summary
|
||||
|
||||
```
|
||||
Re = 0.2×(I2×I2[1] + Q2×Q2[1]) + 0.8×Re[1]
|
||||
Im = 0.2×(I2×Q2[1] - Q2×I2[1]) + 0.8×Im[1]
|
||||
| Criterion | Purpose |
|
||||
|-----------|---------|
|
||||
| SineWave crossing | Resets trend counter — new cycle detected |
|
||||
| Duration threshold | Requires sustained trending before declaration |
|
||||
| Phase rate check | Normal phase advance indicates cycle mode |
|
||||
| Price deviation | Large deviation from trendline forces trend mode |
|
||||
|
||||
period = 360 / (atan(Im/Re) × RAD2DEG)
|
||||
smooth_period = 0.33×period + 0.67×smooth_period[1]
|
||||
### Mode Transition Patterns
|
||||
|
||||
// DC Phase calculation
|
||||
realPart = Σ sin(i × 360/dcPeriod) × smoothPrice[i]
|
||||
imagPart = Σ cos(i × 360/dcPeriod) × smoothPrice[i]
|
||||
dcPhase = atan(realPart/imagPart) × RAD2DEG + 90 + lag_compensation
|
||||
```
|
||||
| Pattern | Interpretation |
|
||||
|---------|---------------|
|
||||
| 0→1 after breakout | Trend confirmed; deploy momentum strategy |
|
||||
| 1→0 at extremes | Cycle started; switch to mean-reversion |
|
||||
| Long run of 1s | Strong, sustained trend |
|
||||
| Rapid 0/1 flipping | Transitional/choppy — reduce exposure |
|
||||
|
||||
### 3. SineWave Indicators
|
||||
## Resources
|
||||
|
||||
```
|
||||
sine = sin(dcPhase × DEG2RAD)
|
||||
leadSine = sin((dcPhase + 45) × DEG2RAD)
|
||||
```
|
||||
|
||||
### 4. Trendline Calculation
|
||||
|
||||
```
|
||||
// SMA over dominant cycle period
|
||||
sma = average(price, dcPeriodInt)
|
||||
|
||||
// WMA smoothing
|
||||
trendline = (4×sma₀ + 3×sma₁ + 2×sma₂ + sma₃) / 10
|
||||
```
|
||||
|
||||
### 5. Trend Mode Decision (TA-Lib Algorithm)
|
||||
|
||||
```
|
||||
trend = 1 // Assume trend by default
|
||||
|
||||
// Criterion 1: SineWave crossing resets counter
|
||||
if (sine crosses leadSine):
|
||||
daysInTrend = 0
|
||||
trend = 0
|
||||
|
||||
daysInTrend++
|
||||
|
||||
// Criterion 2: Must be trending for half a cycle
|
||||
if (daysInTrend < 0.5 × smoothPeriod):
|
||||
trend = 0
|
||||
|
||||
// Criterion 3: Normal phase rate → cycle mode
|
||||
phaseChange = dcPhase - prevDcPhase
|
||||
expectedChange = 360 / smoothPeriod
|
||||
if (phaseChange > 0.67×expectedChange AND phaseChange < 1.5×expectedChange):
|
||||
trend = 0
|
||||
|
||||
// Criterion 4: Price deviation override
|
||||
if (abs((smoothPrice - trendline) / trendline) >= 0.015):
|
||||
trend = 1
|
||||
```
|
||||
|
||||
## Performance Profile
|
||||
|
||||
- **Complexity**: O(1) per update
|
||||
- **Memory**: ~450 bytes state + circular buffers
|
||||
- **Lookback**: 63 bars (TA-Lib compatible)
|
||||
|
||||
### Zero-Allocation Design
|
||||
|
||||
```csharp
|
||||
[SkipLocalsInit]
|
||||
public sealed class HtTrendmode : AbstractBase
|
||||
{
|
||||
// All state in value types
|
||||
private State _state;
|
||||
private State _p_state;
|
||||
|
||||
// Pre-allocated buffers for Hilbert Transform
|
||||
private readonly double[] _circBuffer;
|
||||
private readonly double[] _smoothPrice;
|
||||
private readonly double[] _priceHistory;
|
||||
}
|
||||
```
|
||||
|
||||
### Bar Correction Pattern
|
||||
|
||||
Supports streaming updates with correction:
|
||||
|
||||
```csharp
|
||||
// New bar
|
||||
var result = indicator.Update(price, isNew: true);
|
||||
|
||||
// Same bar, corrected price
|
||||
var corrected = indicator.Update(newPrice, isNew: false);
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Streaming
|
||||
|
||||
```csharp
|
||||
var indicator = new HtTrendmode();
|
||||
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
var result = indicator.Update(bar.Close, isNew: true);
|
||||
|
||||
if (indicator.TrendMode == 1)
|
||||
{
|
||||
// Use trend-following strategy
|
||||
ApplyMomentumStrategy();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use mean-reversion strategy
|
||||
ApplyRangeStrategy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Batch
|
||||
|
||||
```csharp
|
||||
var result = HtTrendmode.Calculate(closePrices);
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `TrendMode` | int | Current mode: 1=trend, 0=cycle |
|
||||
| `SmoothPeriod` | double | Smoothed dominant cycle period [6-50] |
|
||||
| `InstPeriod` | double | Instantaneous (unsmoothed) period |
|
||||
| `DCPhase` | double | Dominant cycle phase in degrees |
|
||||
| `Trendline` | double | WMA-smoothed SMA over cycle period |
|
||||
| `DaysInTrend` | int | Days since last SineWave crossing |
|
||||
|
||||
## Interpretation
|
||||
|
||||
### Signal Interpretation
|
||||
|
||||
| Value | Mode | Interpretation |
|
||||
|-------|------|----------------|
|
||||
| **1** | Trend | Price is trending; momentum strategies preferred |
|
||||
| **0** | Cycle | Price is oscillating; mean-reversion preferred |
|
||||
|
||||
### Common Patterns
|
||||
|
||||
1. **Trend Confirmation**: When TrendMode flips from 0→1 after a breakout
|
||||
2. **Cycle Entry**: When TrendMode flips from 1→0 at potential reversal zones
|
||||
3. **Mode Persistence**: Long runs of 1s indicate strong trends
|
||||
4. **Mode Oscillation**: Rapid flipping indicates choppy markets
|
||||
|
||||
### Using Auxiliary Properties
|
||||
|
||||
```csharp
|
||||
// Access the trendline for support/resistance
|
||||
double trend = indicator.Trendline;
|
||||
|
||||
// Check how long in current trend
|
||||
int duration = indicator.DaysInTrend;
|
||||
|
||||
// Use phase for timing entries
|
||||
double phase = indicator.DCPhase;
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
### Cross-Library Comparison
|
||||
|
||||
| Library | Function | Notes |
|
||||
|---------|----------|-------|
|
||||
| TA-Lib | `HT_TRENDMODE` | Reference implementation (matched) |
|
||||
| TradingView | Built-in | PineScript version (differs) |
|
||||
|
||||
### Common Pitfalls
|
||||
|
||||
1. **Lag**: Hilbert Transform has inherent lag (~32-63 bars for reliable signal)
|
||||
2. **Whipsaws**: Mode can flip rapidly in transitional markets
|
||||
3. **Warmup**: Requires 63+ bars before valid output
|
||||
4. **Division Safety**: Use epsilon checks to avoid division by zero
|
||||
|
||||
## References
|
||||
|
||||
- Ehlers, J.F. "The Instantaneous Trendline" (February 2002)
|
||||
- Ehlers, J.F. "MESA and Trading Market Cycles" (2002)
|
||||
- Ehlers, J.F. "Rocket Science for Traders" (2001)
|
||||
- [TA-Lib HT_TRENDMODE Source](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_HT_TRENDMODE.c)
|
||||
- Ehlers, J.F. — "The Instantaneous Trendline" (February 2002)
|
||||
- Ehlers, J.F. — *MESA and Trading Market Cycles* (John Wiley & Sons, 2002)
|
||||
- Ehlers, J.F. — *Rocket Science for Traders* (John Wiley & Sons, 2001)
|
||||
- PineScript reference: `ht_trendmode.pine` in indicator directory
|
||||
|
||||
Reference in New Issue
Block a user