mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-25 22:08:05 +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:
+63
-47
@@ -1,77 +1,93 @@
|
||||
# ADXR: Average Directional Movement Rating
|
||||
|
||||
> If ADX is the speedometer, ADXR is the cruise control setting. It smooths out the acceleration to tell you if the trend has staying power.
|
||||
|
||||
The Average Directional Movement Rating (ADXR) is a smoothed version of the ADX. It dampens the volatility of the ADX itself, providing a more stable—albeit significantly more lagging—measure of trend strength. It is primarily used to rate the efficacy of trend-following strategies before capital is committed.
|
||||
The Average Directional Movement Rating is a smoothed version of ADX that dampens short-term fluctuations in trend strength by averaging the current ADX with a historical ADX value. This creates a doubly-lagged metric that sacrifices all timing utility in exchange for stable regime classification. ADXR answers one question: does the current market environment reward trend-following strategies? If ADXR is high, deploy momentum logic. If low, deploy mean-reversion. It is a strategic filter, not a tactical signal.
|
||||
|
||||
## Historical Context
|
||||
|
||||
J. Welles Wilder Jr. introduced ADXR alongside ADX in *New Concepts in Technical Trading Systems* (1978). His goal was simple: ADX can be erratic. By averaging the current ADX with a past ADX, he created a metric that ignores short-term fluctuations in trend strength.
|
||||
|
||||
It is effectively a "momentum of momentum" indicator, smoothed to the point of geological stability.
|
||||
J. Welles Wilder Jr. introduced ADXR alongside ADX in *New Concepts in Technical Trading Systems* (1978). His reasoning was pragmatic: ADX itself can be erratic during transitions between trending and ranging regimes, producing whipsaw readings that confuse systematic allocation. By averaging the current ADX with its value from $N-1$ bars ago, Wilder created a "momentum of momentum" indicator smoothed to geological stability. The ADXR found its architectural niche not as a trading signal but as a capital allocation filter — determining whether a trend-following system should be active at all. Its double lag (ADX already lags price; ADXR lags ADX) makes it useless for entry timing by design.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
ADXR is a composite indicator. It does not interact with price directly; it interacts with the output of the ADX.
|
||||
### 1. ADX Dependency
|
||||
|
||||
1. **Dependency**: It instantiates and maintains a full `Adx` indicator internally.
|
||||
2. **History**: It maintains a circular buffer of historical ADX values.
|
||||
3. **Averaging**: It computes the arithmetic mean of the current ADX and the ADX from `Period - 1` bars ago.
|
||||
ADXR is a composite indicator that does not interact with price directly. It instantiates and maintains a full ADX pipeline internally:
|
||||
|
||||
### The Lag Trade-off
|
||||
$$\text{Price} \rightarrow \text{DM/TR} \rightarrow \text{RMA} \rightarrow \text{DI} \rightarrow \text{DX} \rightarrow \text{ADX} \rightarrow \text{ADXR}$$
|
||||
|
||||
ADXR is intentionally slow.
|
||||
### 2. Historical Buffer
|
||||
|
||||
* **ADX** lags price because of its multiple smoothing layers.
|
||||
* **ADXR** lags ADX because it averages the current value with a value from the distant past.
|
||||
A circular buffer of size $N$ stores historical ADX values, providing $O(1)$ access to the value from $N-1$ bars ago.
|
||||
|
||||
This double lag makes ADXR useless for entry timing. Its only valid architectural purpose is **regime filtering**: determining *if* a trend-following system should be active, not *when* it should trade.
|
||||
### 3. Rating Calculation
|
||||
|
||||
$$ADXR_t = \frac{ADX_t + ADX_{t-(N-1)}}{2}$$
|
||||
|
||||
The $N-1$ lag (rather than $N$) matches TA-Lib's reference implementation exactly.
|
||||
|
||||
### 4. Complexity
|
||||
|
||||
- **Time:** $O(1)$ per bar — ADX update plus one buffer lookup and one average
|
||||
- **Space:** $O(N)$ — circular buffer for ADX history
|
||||
- **Warmup:** $\approx 3N$ bars (ADX convergence + buffer fill)
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
The formula is deceptively simple, but relies on the complex ADX calculation underneath.
|
||||
### Parameters
|
||||
|
||||
$$ ADXR_t = \frac{ADX_t + ADX_{t-(n-1)}}{2} $$
|
||||
| Symbol | Parameter | Default | Constraint |
|
||||
|--------|-----------|---------|------------|
|
||||
| $N$ | period | 14 | $N \geq 2$ |
|
||||
|
||||
Where:
|
||||
The period controls both the internal ADX calculation and the historical lookback depth.
|
||||
|
||||
* $ADX_t$ is the current ADX value.
|
||||
* $n$ is the Period (typically 14).
|
||||
* $ADX_{t-(n-1)}$ is the ADX value from `n-1` periods ago.
|
||||
### Pseudo-code
|
||||
|
||||
*Note: The `n-1` lag is used to match TA-Lib's implementation exactly. Some sources cite `n`, but standard reference implementations use `n-1`.*
|
||||
```
|
||||
Initialize:
|
||||
adx = new Adx(period)
|
||||
adxBuffer = RingBuffer(period)
|
||||
bar_count = 0
|
||||
|
||||
## Performance Profile
|
||||
On each bar (high, low, close, isNew):
|
||||
if !isNew: restore previous state
|
||||
|
||||
The performance cost is dominated by the underlying ADX calculation. The ADXR step itself is trivial.
|
||||
// Full ADX pipeline
|
||||
adxValue = adx.Update(high, low, close, isNew)
|
||||
|
||||
### Zero-Allocation Design
|
||||
// Store in history
|
||||
adxBuffer.Add(adxValue)
|
||||
bar_count++
|
||||
|
||||
The implementation uses a circular buffer (`RingBuffer`) to store historical ADX values, ensuring O(1) access and zero heap allocations during the update cycle.
|
||||
// ADXR = average of current and (N-1)-lagged ADX
|
||||
if bar_count >= period:
|
||||
historicalAdx = adxBuffer[0] // oldest value in buffer
|
||||
ADXR = (adxValue + historicalAdx) / 2.0
|
||||
else:
|
||||
ADXR = adxValue // insufficient history
|
||||
|
||||
| Metric | Score | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| **Throughput** | 6ns | 6ns / bar (Apple M1 Max). |
|
||||
| **Allocations** | 0 | Hot path is allocation-free. |
|
||||
| **Complexity** | O(1) | Ring buffer access is constant time. |
|
||||
| **Accuracy** | 10/10 | Matches TA-Lib to 1e-9. |
|
||||
| **Timeliness** | 1/10 | Double lag (ADX + History). |
|
||||
| **Overshoot** | 10/10 | Extremely stable. |
|
||||
| **Smoothness** | 10/10 | Extremely stable trend rating. |
|
||||
output = ADXR
|
||||
```
|
||||
|
||||
## Validation
|
||||
### Lag Analysis
|
||||
|
||||
Validation is performed against industry-standard libraries.
|
||||
| Component | Lag Source |
|
||||
|-----------|-----------|
|
||||
| DM → RMA | $\approx N$ bars (Wilder smoothing) |
|
||||
| DX → ADX | $\approx N$ bars (second RMA) |
|
||||
| ADX → ADXR | $N-1$ bars (historical average) |
|
||||
| **Total effective lag** | $\approx 3N - 1$ bars |
|
||||
|
||||
| Library | Status | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| **QuanTAlib** | ✅ | Validated. |
|
||||
| **TA-Lib** | ✅ | Matches `TA_ADXR` to 1e-9. |
|
||||
| **Skender** | N/A | Not implemented in Skender. |
|
||||
| **Tulip** | ✅ | Matches `ti.adxr`. |
|
||||
| **Ooples** | N/A | Not implemented. |
|
||||
For the default period of 14, ADXR carries roughly 41 bars of effective lag. This is a feature, not a limitation — it ensures that only sustained regime changes register in the output.
|
||||
|
||||
### Common Pitfalls
|
||||
### Regime Classification
|
||||
|
||||
* **Using for Entries**: Do not use ADXR crossovers for entries. The signal is too late.
|
||||
* **Short Periods**: Using a short period (e.g., 3) defeats the purpose of ADXR. If you want responsiveness, use ADX. ADXR is for stability.
|
||||
| ADXR Value | Interpretation |
|
||||
|------------|----------------|
|
||||
| < 20 | Sustained range-bound; favor mean-reversion |
|
||||
| 20–25 | Ambiguous regime; reduce position sizing |
|
||||
| > 25 | Sustained trending; favor momentum strategies |
|
||||
|
||||
## Resources
|
||||
|
||||
- Wilder, J.W. — *New Concepts in Technical Trading Systems* (Trend Research, 1978)
|
||||
- PineScript reference: `adxr.pine` in indicator directory
|
||||
|
||||
Reference in New Issue
Block a user