mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-21 12:08:05 +00:00
181 lines
9.8 KiB
Markdown
181 lines
9.8 KiB
Markdown
# NYQMA: Nyquist Moving Average
|
|
|
|
> *Manfred Dürschner applied the Nyquist-Shannon sampling theorem to cascaded moving averages: the second smoothing period must not exceed half the first, or you get aliasing artifacts. Respect the theorem and the ghost signals disappear.*
|
|
|
|
| Property | Value |
|
|
| ---------------- | -------------------------------- |
|
|
| **Category** | Trend (FIR MA) |
|
|
| **Inputs** | Source (close) |
|
|
| **Parameters** | `period` (default 89), `nyquistPeriod` (default 21) |
|
|
| **Outputs** | Single series (Nyqma) |
|
|
| **Output range** | Tracks input |
|
|
| **Warmup** | 1 bar |
|
|
| **PineScript** | [nyqma.pine](nyqma.pine) |
|
|
| **Signature** | [nyqma_signature](nyqma_signature.md) |
|
|
|
|
- NYQMA combines a primary LWMA (Linear Weighted Moving Average) with a secondary LWMA applied to the first, using lag-compensating extrapolation: $\...
|
|
- **Similar:** [Lanczos](../lanczos/Lanczos.md), [SinEma](../sinema/sinema.md) | **Complementary:** Cycle detection | **Trading note:** Nyquist MA; designed around Nyquist frequency. Optimal for eliminating aliased cycles.
|
|
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
|
|
|
|
NYQMA combines a primary LWMA (Linear Weighted Moving Average) with a secondary LWMA applied to the first, using lag-compensating extrapolation: $\text{NYQMA} = (1+\alpha) \cdot \text{MA}_1 - \alpha \cdot \text{MA}_2$, where $\alpha = N_2 / (N_1 - N_2)$. The Nyquist constraint $N_2 \leq \lfloor N_1/2 \rfloor$ ensures the second smoothing does not introduce aliasing artifacts into the output. This produces a lag-reduced moving average grounded in sampling theory rather than ad-hoc coefficient tuning. Streaming update is O(1) per bar via composed Wma instances; batch mode uses stackalloc/ArrayPool with FMA in the extrapolation loop.
|
|
|
|
## Historical Context
|
|
|
|
Dr. Manfred G. Dürschner published NYQMA in *Gleitende Durchschnitte 3.0* ("Moving Averages 3.0"), a German-language work that applies rigorous signal-processing theory to financial moving average design. His key insight: cascading two smoothing operations is mathematically equivalent to sampling a continuous signal at two rates. The Nyquist-Shannon sampling theorem (Shannon 1949, Nyquist 1928) dictates that the second rate cannot exceed half the first without introducing aliasing.
|
|
|
|
The lag compensation formula $(1+\alpha) \cdot \text{MA}_1 - \alpha \cdot \text{MA}_2$ is structurally identical to DEMA and GDEMA, but with two critical distinctions: (a) both constituent MAs are LWMAs, not EMAs, giving the filter a finite impulse response with bounded memory; (b) the gain factor $\alpha$ is derived from the period ratio $N_2/(N_1 - N_2)$ rather than being a free parameter, grounding the extrapolation strength in sampling theory.
|
|
|
|
Most double-smoothed moving averages (DEMA, TEMA, T3) use the same MA period for both stages. Dürschner's contribution is recognizing that the second stage period is a free design parameter, but one constrained by the Nyquist limit. When $N_2 = N_1$ (the DEMA case), the constraint is violated by definition. When $N_2 = \lfloor N_1/2 \rfloor$ (the maximum legal Nyquist value), lag cancellation is maximized without aliasing. Smaller $N_2$ values trade less lag reduction for more conservative anti-aliasing margin.
|
|
|
|
## Architecture and Physics
|
|
|
|
### 1. Primary LWMA (period $N_1$)
|
|
|
|
A standard Linear Weighted Moving Average with period $N_1$:
|
|
|
|
$$
|
|
\text{MA}_1 = \text{WMA}(x, N_1)
|
|
$$
|
|
|
|
Implemented via the composable `Wma` class with O(1) streaming update (running weighted sum + running sum via circular buffer).
|
|
|
|
### 2. Secondary LWMA (period $N_2$, Nyquist-constrained)
|
|
|
|
A LWMA applied to $\text{MA}_1$ with Nyquist-constrained period $N_2 \leq \lfloor N_1/2 \rfloor$:
|
|
|
|
$$
|
|
\text{MA}_2 = \text{WMA}(\text{MA}_1, N_2)
|
|
$$
|
|
|
|
The implementation clamps: $N_2 = \text{clamp}(N_2, 1, \lfloor N_1/2 \rfloor)$.
|
|
|
|
### 3. Lag-Compensating Extrapolation
|
|
|
|
$$
|
|
\alpha = \frac{N_2}{N_1 - N_2}
|
|
$$
|
|
|
|
$$
|
|
\text{NYQMA} = (1 + \alpha) \cdot \text{MA}_1 - \alpha \cdot \text{MA}_2
|
|
$$
|
|
|
|
Implemented with `Math.FusedMultiplyAdd` for the final combination:
|
|
|
|
```csharp
|
|
result = Math.FusedMultiplyAdd(1.0 + alpha, wma1, -alpha * wma2);
|
|
```
|
|
|
|
### 4. Warmup Period
|
|
|
|
$$
|
|
\text{WarmupPeriod} = N_1 + N_2 - 1
|
|
$$
|
|
|
|
The primary WMA needs $N_1$ bars to fill; the secondary WMA then needs $N_2$ bars of valid WMA1 output, but the first WMA1 output arrives at bar 1, so the total warmup is $N_1 + N_2 - 1$.
|
|
|
|
## Mathematical Foundation
|
|
|
|
**LWMA (period $N$):**
|
|
|
|
$$
|
|
\text{WMA}(x, N) = \frac{\sum_{i=0}^{N-1} (N-i) \cdot x_{t-i}}{N(N+1)/2}
|
|
$$
|
|
|
|
**Lag compensation coefficient:**
|
|
|
|
$$
|
|
\alpha = \frac{N_2}{N_1 - N_2}
|
|
$$
|
|
|
|
When $N_2 = \lfloor N_1/2 \rfloor$: $\alpha \approx 1$ (maximum compensation).
|
|
When $N_2 = 1$: $\alpha = 1/(N_1 - 1) \approx 0$ (minimal compensation).
|
|
|
|
**Output:**
|
|
|
|
$$
|
|
\text{NYQMA} = (1 + \alpha) \cdot \text{WMA}(x, N_1) - \alpha \cdot \text{WMA}(\text{WMA}(x, N_1), N_2)
|
|
$$
|
|
|
|
**Nyquist constraint (hard rule):**
|
|
|
|
$$
|
|
N_2 \leq \left\lfloor \frac{N_1}{2} \right\rfloor
|
|
$$
|
|
|
|
**Lag analysis:** WMA has group delay $(N-1)/3$. The extrapolation compensates a fraction $\alpha/(1+\alpha) = N_2/N_1$ of the primary MA's lag.
|
|
|
|
**Default parameters:** `period = 89` ($N_1$), `nyquistPeriod = 21` ($N_2$).
|
|
|
|
## Performance Profile
|
|
|
|
### Operation Count (Streaming Mode, Scalar)
|
|
|
|
| Operation | Count per bar |
|
|
|-----------|--------------|
|
|
| WMA1 Update (O(1)) | 1 |
|
|
| WMA2 Update (O(1)) | 1 |
|
|
| FMA extrapolation | 1 |
|
|
| Multiply ($-\alpha \cdot w_2$) | 1 |
|
|
| Sample count increment | 1 |
|
|
| **Total** | **~5 FP ops** |
|
|
|
|
### Batch Mode
|
|
|
|
| Step | Complexity |
|
|
|------|-----------|
|
|
| WMA1 span batch | O(n) |
|
|
| WMA2 span batch | O(n) |
|
|
| FMA extrapolation loop | O(n) |
|
|
| **Total** | **O(n)** |
|
|
|
|
Memory: stackalloc for $n \leq 1024$ (two buffers of 8 KB each); ArrayPool beyond that.
|
|
|
|
### Quality Metrics
|
|
|
|
| Metric | Score (1-10) | Notes |
|
|
|--------|-------------|-------|
|
|
| Lag reduction | 7 | Significant lag reduction vs raw WMA; bounded by Nyquist constraint |
|
|
| Smoothness | 7 | Inherits WMA smoothness; extrapolation adds minor overshoot |
|
|
| Overshoot | 5 | More than WMA, less than DEMA; controlled by $\alpha$ |
|
|
| Noise rejection | 7 | FIR nature ensures bounded impulse response |
|
|
| Computational cost | 9 | Two O(1) WMA updates + one FMA |
|
|
| Parameter sensitivity | 8 | Two intuitive parameters with physical meaning |
|
|
|
|
## Validation
|
|
|
|
NYQMA has no direct equivalent in external libraries (TA-Lib, Skender, Tulip, Ooples). Validation is performed via component consistency: verifying that the composed output matches the explicit formula.
|
|
|
|
| Test | Method | Tolerance | Result |
|
|
|------|--------|-----------|--------|
|
|
| Component formula (batch) | $\text{NYQMA} = (1+\alpha) \cdot \text{WMA}_1 - \alpha \cdot \text{WMA}_2$ | $1 \times 10^{-9}$ | PASS |
|
|
| Component formula (streaming) | Same formula, bar-by-bar | $1 \times 10^{-9}$ | PASS |
|
|
| Component formula (span) | Same formula, span API | $1 \times 10^{-9}$ | PASS |
|
|
| Batch vs streaming | Last value agreement | $1 \times 10^{-6}$ | PASS |
|
|
| Batch vs span | Last value agreement | $1 \times 10^{-9}$ | PASS |
|
|
| Constant convergence | Multiple period combos | $1 \times 10^{-6}$ | PASS |
|
|
| 4-mode consistency | Batch = Streaming = Span = Eventing | $1 \times 10^{-9}$ | PASS |
|
|
|
|
The $1 \times 10^{-6}$ tolerance for batch vs streaming reflects expected floating-point divergence from different evaluation order (span-based batch vs composed instance streaming).
|
|
|
|
## Common Pitfalls
|
|
|
|
1. **Integer division in alpha calculation.** The original PineScript used integer types for `n2 / (period - n2)`, yielding 0 for all typical parameters (e.g., `21/68 = 0` in integer arithmetic). The fix: cast to float before division. Impact: complete failure of lag compensation when alpha truncates to zero.
|
|
|
|
2. **Nyquist period exceeding half the primary period.** Violates the sampling theorem. The implementation clamps silently, but users who set $N_2 > N_1/2$ and expect that exact value will get a different (clamped) indicator. Document the clamping behavior.
|
|
|
|
3. **Overshoot on sharp reversals.** The $(1+\alpha)$ gain factor amplifies the primary WMA relative to the smoothed secondary. On V-shaped reversals, NYQMA will overshoot more than a raw WMA. For $\alpha \approx 1$ (maximum Nyquist), the overshoot approaches DEMA levels.
|
|
|
|
4. **Warmup period miscalculation.** The total warmup is $N_1 + N_2 - 1$, not $\max(N_1, N_2)$ or $N_1 \cdot N_2$. Before warmup completion, the filter output is valid but numerically colder (reduced effective smoothing).
|
|
|
|
5. **Confusing NYQMA with PMA.** PMA uses $\text{WMA}(\text{src}, N)$ and $\text{WMA}(\text{WMA}(\text{src}, N), N)$ (same period twice) with $\alpha = 1$ fixed. NYQMA uses different periods and derives $\alpha$ from the period ratio. They converge only when $N_2 = N_1/2$ and rounding is ignored.
|
|
|
|
6. **Batch vs streaming floating-point divergence.** The batch path computes WMA1 and WMA2 as separate span passes, while streaming composes two Wma instances feeding one into the other. Different accumulation order produces divergence around $10^{-8}$ to $10^{-6}$ on 500-bar series. This is expected and not a bug.
|
|
|
|
7. **Using System.Random for test data.** Per project protocol, test data must use GBM (Geometric Brownian Motion) helpers. Random walk paths from System.Random lack the drift and volatility characteristics of financial time series.
|
|
|
|
## References
|
|
|
|
- Dürschner, M.G. *Gleitende Durchschnitte 3.0*. (Original NYQMA publication, German language.)
|
|
- Shannon, C.E. (1949). "Communication in the Presence of Noise." *Proceedings of the IRE*, 37(1), 10-21.
|
|
- Nyquist, H. (1928). "Certain Topics in Telegraph Transmission Theory." *Transactions of the AIEE*, 47(2), 617-644.
|
|
- Ehlers, J.F. (2001). *Rocket Science for Traders*. Wiley. (PMA reference for comparison.) |