# MMA: Modified Moving Average

> "MMA is a compromise: less lag than SMA, less overshoot than fully weighted filters. It's what you get when an SMA and a WMA have a carefully engineered offspring."

MMA (Modified Moving Average) uses a **simple mean** as a baseline, then adds a **weighted correction** based on the position of values within the buffer. The weighting tilts toward newer bars without fully discarding older ones, creating a filter that sits between SMA (equal weights) and WMA (linear weights) in both lag and smoothness characteristics.

## Historical Context

MMA is not a standardized textbook indicator. It appears in multiple custom trading systems, often labeled "modified," "balanced," or "adjusted" moving average. The specific formulation here follows the PineScript reference implementation (`mma.pine`), which uses a mathematically elegant correction term based on position-weighted deviations from the mean.

The design philosophy is pragmatic: SMA is too laggy, EMA can overshoot, and WMA is computationally expensive. MMA threads the needle by adding just enough recency bias to reduce lag while maintaining the smoothness benefits of averaging.

## Architecture & Physics

MMA operates in two conceptual stages that are combined into a single efficient calculation.

### 1. SMA Baseline

The foundation is a standard simple moving average:

$$\text{SMA}_t = \frac{1}{N}\sum_{i=0}^{N-1} x_{t-i}$$

This provides the smoothing benefit of equal-weighted averaging but introduces lag proportional to $(N-1)/2$ bars.

### 2. Weighted Correction Term

A position-weighted sum corrects the SMA toward recent values:

$$w_i = \frac{N - (2i + 1)}{2}$$

Where $i=0$ is the newest value and $i=N-1$ is the oldest. This creates weights that:
- Are positive for the newest half of the buffer
- Are negative for the oldest half
- Sum to zero (no DC bias)

The weighted sum:

$$W_t = \sum_{i=0}^{N-1} w_i \cdot x_{t-i}$$

### 3. Combined Output

The final MMA adds the scaled weighted correction to the SMA:

$$\text{MMA}_t = \text{SMA}_t + \frac{6W_t}{N(N+1)}$$

The scaling factor $\frac{6}{N(N+1)}$ normalizes the correction to produce appropriate lag reduction without excessive overshoot.

## Mathematical Foundation

### Weight Structure Analysis

For period $N$, the weights follow:

| Position $i$ | Weight $w_i = \frac{N-(2i+1)}{2}$ | Example (N=5) |
| :---: | :--- | :---: |
| 0 (newest) | $(N-1)/2$ | 2 |
| 1 | $(N-3)/2$ | 1 |
| 2 | $(N-5)/2$ | 0 |
| 3 | $(N-7)/2$ | -1 |
| 4 (oldest) | $(N-9)/2$ | -2 |

The weights are symmetric around zero, ensuring the correction term doesn't introduce DC bias.

### Effective Weight Interpretation

The combined MMA can be written as a single weighted average:

$$\text{MMA}_t = \sum_{i=0}^{N-1} \left(\frac{1}{N} + \frac{6w_i}{N(N+1)}\right) x_{t-i}$$

The effective weights emphasize recent values while still including all $N$ bars.

### Lag Analysis

- **SMA lag**: $(N-1)/2$ bars
- **MMA lag**: Approximately $(N-1)/3$ bars (varies with input characteristics)
- **Lag reduction**: ~33% compared to SMA

### Equivalence to Other Filters

MMA is closely related to the Linear Weighted Moving Average (LWMA), but with a different normalization that produces slightly different lag/smoothness characteristics.

## Performance Profile

### Operation Count (Streaming Mode)

| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
| Buffer update | 1 | 3 | 3 |
| Running sum update | 2 | 1 | 2 |
| Weighted sum pass | N | 4 | 4N |
| Final calculation | 3 | 3 | 9 |
| **Total** | **N+6** | — | **~4N+14 cycles** |

For typical $N=20$: approximately 94 cycles/bar.

### Batch Mode (SIMD Analysis)

The weighted sum computation is vectorizable:

| Optimization | Scalar Ops | SIMD Ops (AVX2) | Speedup |
| :--- | :---: | :---: | :---: |
| Weighted sum | N | N/8 | 8× |
| Sum reduction | N | log₂(8) | ~N/3× |

**With SIMD (N=20):**

| Mode | Cycles/bar |
| :--- | :---: |
| Scalar | ~94 |
| AVX2 | ~25 |
| **Improvement** | **~4×** |

### Quality Metrics

| Metric | Score | Notes |
| :--- | :---: | :--- |
| **Accuracy** | 8/10 | Good trend tracking with moderate lag |
| **Timeliness** | 6/10 | Faster than SMA, slower than EMA |
| **Overshoot** | 4/10 | Mild overshoot on sharp reversals |
| **Smoothness** | 7/10 | Smoother than EMA, rougher than SMA |

### Benchmark Results

| Metric | Value | Notes |
| :--- | :--- | :--- |
| **Throughput** | ~25 ns/bar | Period-dependent, N=20 |
| **Allocations** | 0 bytes | Circular buffer reuse |
| **Complexity** | O(N) | Weighted pass over buffer |
| **State Size** | 8N + 40 bytes | Buffer + metadata |

*Benchmarked on Intel i7-12700K @ 3.6 GHz, AVX2, .NET 10.0*

## 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 |
| **PineScript** | ✅ | Matches `lib/trends_IIR/mma/mma.pine` (tolerance: 1e-10) |

## Common Pitfalls

1. **O(N) Complexity**: Unlike EMA's O(1) update, MMA requires iterating over the entire buffer each update. For large periods (N > 100), this can become a bottleneck in high-frequency applications.

2. **Warmup Requirement**: The weighted correction produces unstable results until the buffer fills completely. Use `IsHot` to detect when $N$ bars have accumulated. Early outputs will be NaN or SMA approximations.

3. **Non-finite Input Handling**: NaN/Infinity values are replaced with the last valid value to prevent corruption of the running calculations. If no valid value exists yet, output is NaN.

4. **Memory Footprint**: MMA requires storing $N$ values in a circular buffer, unlike IIR filters (EMA, RMA) which need only constant state. For many parallel MMA instances, memory usage scales with $O(N \times \text{instances})$.

5. **Comparison with WMA**: MMA is not equivalent to WMA despite both using recency weighting. The mathematical relationship differs, and direct period comparisons will produce different results.

6. **Period Selection**: Due to the correction term, MMA(N) behaves more like SMA(N×0.7) in terms of lag. When migrating from SMA, consider increasing the period to maintain similar smoothness.

7. **Bar Correction**: Use `isNew=false` when correcting the current bar. The buffer must update correctly to maintain calculation integrity.

## References

- PineScript reference implementation: `lib/trends_IIR/mma/mma.pine`