6.1 KiB
CMA: Cumulative Moving Average
The running average that never forgets. Every single tick you've ever fed it? Still in there, affecting the result. It's like the elephant of technical indicators.
| Property | Value |
|---|---|
| Category | Statistic |
| Inputs | Source (close) |
| Parameters | source |
| Outputs | Single series (CMA) |
| Output range | Varies (see docs) |
| Warmup | 1 bars |
| PineScript | cma.pine |
- The Cumulative Moving Average (CMA) calculates the arithmetic mean of ALL data points seen so far, not just a fixed window.
- Similar: SMA, EMA | Trading note: Cumulative Moving Average; running mean of all data points. Anchored VWAP without volume weighting.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Cumulative Moving Average (CMA) calculates the arithmetic mean of ALL data points seen so far, not just a fixed window. Unlike SMA or EMA which use a sliding window, CMA treats every historical value with equal weight. As the sample size grows, each new value has diminishing impact on the average.
Historical Context
The concept of a running mean is fundamental to statistics and was formalized by B. P. Welford in 1962 for numerically stable computation. Donald Knuth popularized it in The Art of Computer Programming. While not a traditional trading indicator, CMA is essential for scenarios requiring the true average of all observed data: calculating session VWAP from scratch, averaging tick counts, or computing lifetime average fill prices.
Architecture & Physics
The naive approach (sum all values, divide by count) works for small datasets but fails at scale. After millions of ticks, the running sum can overflow or lose precision.
Welford's Algorithm with FMA
QuanTAlib uses Welford's numerically stable update, enhanced with Fused Multiply-Add (FMA) for maximum precision:
M_n = M_{n-1} + \alpha \cdot (x_n - M_{n-1}) \quad \text{where } \alpha = \frac{1}{n}
Implemented as:
double alpha = 1.0 / n;
double delta = x - mean;
mean = Math.FusedMultiplyAdd(alpha, delta, mean);
This formulation:
- Keeps intermediate values near the scale of the actual mean (no overflow)
- Requires only O(1) memory (just count and mean)
- Achieves O(1) time complexity per update
- Uses FMA for single-rounding precision (avoids rounding
alpha * deltabefore adding tomean) - Is mathematically equivalent to
M_n = \frac{(n-1) \cdot M_{n-1} + x_n}{n}
Why Not Just Sum?
Consider averaging 10 million tick prices around 50,000 (a futures contract). The naive sum exceeds 5 \times 10^{11}, approaching the precision limits of double. Welford's algorithm keeps the working value around 50,000 throughout, maintaining full precision.
The Diminishing Return Problem
As n grows large, each new value contributes only \frac{1}{n} to the mean. After 1 million samples, a new tick moves the average by roughly 0.0001% of the difference from the current mean. This is mathematically correct but may not be what traders want for responsiveness (use EMA or SMA for that).
Mathematical Foundation
1. Incremental Update (Welford)
M_n = M_{n-1} + \frac{x_n - M_{n-1}}{n}
Where:
M_n= cumulative mean afternvaluesM_{n-1}= previous cumulative meanx_n= new valuen= total count of values
2. Algebraic Equivalence
M_n = \frac{1}{n} \sum_{i=1}^{n} x_i = \frac{(n-1) \cdot M_{n-1} + x_n}{n}
Performance Profile
Operation Count (Streaming Mode)
Cumulative Mean Arithmetic uses a simple running sum divided by count — no window, no buffer.
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| Add value to running sum | 1 | 1 cy | ~1 cy |
| Increment count | 1 | 1 cy | ~1 cy |
| Divide sum by count | 1 | 4 cy | ~4 cy |
| NaN guard + state update | 1 | 2 cy | ~2 cy |
| Total | O(1) | — | ~8 cy |
Cheapest mean variant — no buffer, no window management. Throughput limited by division latency (~4 cy on modern x86).
| Metric | Score | Notes |
|---|---|---|
| Throughput | ~5 ns/bar | Single division per update. |
| Allocations | 0 | Zero-allocation in hot paths. |
| Complexity | O(1) | Constant time regardless of history length. |
| Accuracy | 10 | Welford's algorithm ensures numerical stability. |
| Timeliness | 1 | Maximum lag; every historical value affects output. |
| Overshoot | 0 | Never overshoots the input data range. |
| Smoothness | 10 | Extremely smooth as n grows (almost constant). |
Validation
| Library | Status | Notes |
|---|---|---|
| TA-Lib | N/A | No CMA function. |
| Skender | N/A | No CMA function. |
| Tulip | N/A | No CMA function. |
| Mathematical | ✅ | Validated against known formulas. |
CMA is a fundamental statistical operation rather than a standard TA library indicator. QuanTAlib validates against mathematical proofs: arithmetic progressions, geometric series, and direct sum/count calculations.
Use Cases
- Session VWAP: Calculate volume-weighted average price from session start
- Lifetime Averages: Average fill price across all trades
- Quality Metrics: Average latency, slippage, or fill rate over time
- Baseline Comparison: Compare current price to "all-time average"
Common Pitfalls
- Responsiveness: CMA becomes nearly unresponsive after many values. For a reactive average, use SMA or EMA instead.
- Memory of Bad Data: A single extreme outlier early in the stream permanently affects the average. Consider filtering before feeding CMA.
- No Period Parameter: Unlike SMA/EMA, CMA has no period. It always includes all data. This is by design.
- Session Resets: If you need per-session averages, call
Reset()at session boundaries.