mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-19 02:58:05 +00:00
- Implemented Sdchannel class for calculating standard deviation channels based on linear regression. - Added detailed documentation for SDCHANNEL, including overview, calculation methods, and interpretation. - Updated project files to include new numerics library components in Channels and Volatility projects.
225 lines
8.0 KiB
Markdown
225 lines
8.0 KiB
Markdown
# DECAYCHANNEL: Decay Min-Max Channel
|
||
|
||
> "Yesterday's high matters less today. Tomorrow, it matters even less. Decay channels know this."
|
||
|
||
Decay Min-Max Channel (DECAYCHANNEL) tracks the highest high and lowest low like Donchian, then applies exponential decay toward the midpoint. Fresh extremes snap the bands outward; time compresses them inward. The result: channels that respect recent price action while gradually forgetting stale levels. This implementation uses true half-life mathematics—50% convergence over the period length—ensuring predictable decay behavior across all timeframes.
|
||
|
||
## Historical Context
|
||
|
||
Traditional Donchian Channels treat all extremes within the lookback window equally. A high from 19 bars ago has the same influence as a high from 1 bar ago. This works for breakout detection but creates artificial support/resistance levels that persist until they mechanically exit the window.
|
||
|
||
Traders noticed this rigidity. A 20-day high from exactly 20 days ago shouldn't matter as much as one from 5 days ago. Various "adaptive channel" approaches emerged in the 1990s-2000s, but most used arbitrary decay rates or complex volatility weighting.
|
||
|
||
DECAYCHANNEL takes a simpler approach: pure exponential decay with mathematically defined half-life. The decay constant $\lambda = \ln(2) / \text{period}$ guarantees that bands converge 50% toward the midpoint over exactly one period. After two periods: 75%. After three: 87.5%. No tuning parameters, no volatility lookups—just consistent, predictable decay.
|
||
|
||
## Architecture & Physics
|
||
|
||
DECAYCHANNEL consists of four interconnected components that balance extreme tracking with temporal decay.
|
||
|
||
### 1. Extreme Tracking (Highest/Lowest)
|
||
|
||
Internal Highest and Lowest indicators maintain the actual max/min over the period:
|
||
|
||
$$
|
||
H_t^{raw} = \max_{i=0}^{n-1}(High_{t-i})
|
||
$$
|
||
|
||
$$
|
||
L_t^{raw} = \min_{i=0}^{n-1}(Low_{t-i})
|
||
$$
|
||
|
||
These raw values constrain the decayed bands—the upper band can never exceed the actual highest high, and the lower band can never go below the actual lowest low.
|
||
|
||
### 2. Decay Timers
|
||
|
||
Separate counters track how long since each band was reset by a new extreme:
|
||
|
||
$$
|
||
\tau_U = \text{bars since } High_t = H_t^{raw}
|
||
$$
|
||
|
||
$$
|
||
\tau_L = \text{bars since } Low_t = L_t^{raw}
|
||
$$
|
||
|
||
When price makes a new extreme, the corresponding timer resets to zero. Otherwise, it increments each bar.
|
||
|
||
### 3. Exponential Decay Engine
|
||
|
||
The decay rate uses the half-life formula:
|
||
|
||
$$
|
||
\lambda = \frac{\ln(2)}{\text{period}}
|
||
$$
|
||
|
||
For each bar, compute the decay factor based on elapsed time:
|
||
|
||
$$
|
||
d_U = 1 - e^{-\lambda \cdot \tau_U}
|
||
$$
|
||
|
||
$$
|
||
d_L = 1 - e^{-\lambda \cdot \tau_L}
|
||
$$
|
||
|
||
At $\tau = 0$ (new extreme), $d = 0$ (no decay). At $\tau = \text{period}$, $d = 0.5$ (half decayed).
|
||
|
||
### 4. Midpoint Convergence
|
||
|
||
Bands decay toward the current midpoint, not toward price:
|
||
|
||
$$
|
||
M_t = \frac{U_{t-1} + L_{t-1}}{2}
|
||
$$
|
||
|
||
$$
|
||
U_t = U_{t-1} - d_U \cdot (U_{t-1} - M_t)
|
||
$$
|
||
|
||
$$
|
||
L_t = L_{t-1} + d_L \cdot (M_t - L_{t-1})
|
||
$$
|
||
|
||
Finally, constrain to actual extremes:
|
||
|
||
$$
|
||
U_t = \max(U_t, H_t^{raw})
|
||
$$
|
||
|
||
$$
|
||
L_t = \min(L_t, L_t^{raw})
|
||
$$
|
||
|
||
## Mathematical Foundation
|
||
|
||
### Half-Life Derivation
|
||
|
||
Exponential decay follows:
|
||
|
||
$$
|
||
V(t) = V_0 \cdot e^{-\lambda t}
|
||
$$
|
||
|
||
For half-life $t_{1/2}$ where $V(t_{1/2}) = \frac{V_0}{2}$:
|
||
|
||
$$
|
||
\frac{V_0}{2} = V_0 \cdot e^{-\lambda t_{1/2}}
|
||
$$
|
||
|
||
$$
|
||
\lambda = \frac{\ln(2)}{t_{1/2}}
|
||
$$
|
||
|
||
Setting $t_{1/2} = \text{period}$ gives the implementation's decay constant.
|
||
|
||
### Convergence Schedule
|
||
|
||
| Elapsed Time | Decay Factor | Remaining Distance |
|
||
| :--- | :---: | :---: |
|
||
| 0 bars | 0% | 100% |
|
||
| period/2 bars | 29.3% | 70.7% |
|
||
| period bars | 50% | 50% |
|
||
| 2×period bars | 75% | 25% |
|
||
| 3×period bars | 87.5% | 12.5% |
|
||
|
||
### Middle Band Calculation
|
||
|
||
The output middle band is the average of the decayed upper and lower bands:
|
||
|
||
$$
|
||
Middle_t = \frac{U_t + L_t}{2}
|
||
$$
|
||
|
||
This differs from the convergence midpoint (which uses previous bar's values) to avoid feedback loops.
|
||
|
||
## Performance Profile
|
||
|
||
### Operation Count (Streaming Mode, Scalar)
|
||
|
||
Per-bar cost including internal Highest/Lowest updates:
|
||
|
||
| Operation | Count | Cost (cycles) | Subtotal |
|
||
| :--- | :---: | :---: | :---: |
|
||
| ADD/SUB | 8 | 1 | 8 |
|
||
| MUL | 4 | 3 | 12 |
|
||
| DIV | 1 | 15 | 15 |
|
||
| EXP | 2 | 50 | 100 |
|
||
| CMP/MAX/MIN | 6 | 1 | 6 |
|
||
| **Total** | **21** | — | **~141 cycles** |
|
||
|
||
**Breakdown:**
|
||
|
||
- Lambda: precomputed at construction (0 cycles per bar)
|
||
- Midpoint: 1 ADD + 1 DIV = 16 cycles
|
||
- Decay factors (×2): 2 MUL + 2 EXP + 2 SUB = 106 cycles
|
||
- Band updates: 2 MUL + 2 SUB = 8 cycles
|
||
- Constraint checks: 4 CMP = 4 cycles
|
||
- Internal Highest/Lowest: ~8 cycles (amortized O(1))
|
||
|
||
**Dominant cost:** EXP operations at 71% of total cycles.
|
||
|
||
### Batch Mode (512 values, SIMD/FMA)
|
||
|
||
| Operation | Scalar Ops | SIMD Benefit | Notes |
|
||
| :--- | :---: | :---: | :--- |
|
||
| Decay calculation | 2 | Limited | Sequential dependency on timers |
|
||
| Band update | 4 | 2× via FMA | `band - decay × (band - mid)` |
|
||
| Max/Min constraint | 4 | 1× | Comparison-based |
|
||
|
||
**Batch efficiency (512 bars):**
|
||
|
||
| Mode | Cycles/bar | Total (512 bars) | Improvement |
|
||
| :--- | :---: | :---: | :---: |
|
||
| Scalar streaming | 141 | 72,192 | — |
|
||
| FMA-optimized | ~135 | ~69,120 | **~4%** |
|
||
|
||
Limited improvement due to:
|
||
|
||
1. **EXP dominates**: 100 of 141 cycles are exponential operations (not SIMD-friendly in scalar mode)
|
||
2. **Timer dependency**: Each bar's decay factor depends on its timer value
|
||
3. **State coupling**: Upper/lower bands depend on previous bar's midpoint
|
||
|
||
### Quality Metrics
|
||
|
||
| Metric | Score | Notes |
|
||
| :--- | :---: | :--- |
|
||
| **Accuracy** | 10/10 | Mathematically exact exponential decay |
|
||
| **Timeliness** | 8/10 | Immediate response to new extremes |
|
||
| **Overshoot** | 6/10 | New extremes reset decay, can spike bands |
|
||
| **Smoothness** | 7/10 | Exponential decay provides smooth convergence between resets |
|
||
| **Adaptivity** | 8/10 | Channels naturally tighten during consolidation |
|
||
|
||
## Validation
|
||
|
||
| Library | Status | Notes |
|
||
| :--- | :---: | :--- |
|
||
| **TA-Lib** | N/A | Not implemented |
|
||
| **Skender** | N/A | Not implemented |
|
||
| **Tulip** | N/A | Not implemented |
|
||
| **Ooples** | N/A | Not implemented |
|
||
| **Internal** | ✅ | Four-mode consistency verified (streaming, batch, span, event) |
|
||
|
||
DECAYCHANNEL is a QuanTAlib-specific indicator with no external reference implementations.
|
||
|
||
## Common Pitfalls
|
||
|
||
1. **Decay Rate Confusion**: The period parameter controls half-life, not full decay. At period=100, bands are 50% decayed after 100 bars, not fully converged. For near-complete convergence (>95%), allow 4-5× the period.
|
||
|
||
2. **Constraint Snap-Back**: When the actual highest high drops (because an old extreme exits the Highest window), the upper band can snap downward even mid-decay. This is intentional—decayed bands never exceed actual extremes.
|
||
|
||
3. **Initialization Period**: DECAYCHANNEL needs `period` bars to establish meaningful extremes before decay becomes relevant. IsHot reflects this warmup requirement.
|
||
|
||
4. **Timer State Management**: Using `isNew=false` for bar correction requires restoring both the band values and the decay timers. The implementation handles this via state snapshots, but improper use corrupts both.
|
||
|
||
5. **Midpoint Targeting**: Bands decay toward the channel midpoint, not toward current price. In strong trends, this means the trailing band decays toward a point that may be far from price, creating asymmetric behavior.
|
||
|
||
6. **Memory Overhead**: Each instance maintains two Highest/Lowest indicators plus decay state. For period=100, budget ~1.6 KB per instance for the internal monotonic deques plus ~64 bytes for state.
|
||
|
||
7. **Exponential Sensitivity**: Small period values create aggressive decay. At period=10, bands are 50% converged after just 10 bars. For most applications, period≥50 provides more stable channels.
|
||
|
||
## References
|
||
|
||
- Murphy, J. J. (1999). *Technical Analysis of the Financial Markets*. New York Institute of Finance.
|
||
- Kaufman, P. J. (2013). *Trading Systems and Methods* (5th ed.). John Wiley & Sons.
|
||
- Press, W. H., et al. (2007). *Numerical Recipes: The Art of Scientific Computing* (3rd ed.). Cambridge University Press. [Exponential decay mathematics]
|