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:
Miha Kralj
2026-02-20 21:40:32 -08:00
parent cbeefc9d64
commit 90d5638008
121 changed files with 9595 additions and 6315 deletions
+134 -205
View File
@@ -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