6.8 KiB
MPE: Mean Percentage Error
MAPE tells you how wrong you are; MPE tells you which direction you're wrong in.
| Property | Value |
|---|---|
| Category | Error Metric |
| Inputs | Actual, Predicted (dual series) |
| Parameters | period |
| Outputs | Single series (MPE) |
| Output range | Any (positive or negative) |
| Warmup | period bars |
| PineScript | mpe.pine |
- Mean Percentage Error measures the average percentage difference between actual and predicted values while preserving the sign.
- Similar: ME, MAPE | Trading note: Mean Percentage Error; reveals directional bias as percentage. Positive/negative cancellation is a feature.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Mean Percentage Error measures the average percentage difference between actual and predicted values while preserving the sign. Unlike MAPE, which takes absolute values, MPE reveals systematic bias in predictions—whether a model consistently over-predicts or under-predicts.
Architecture & Physics
MPE computes the signed percentage error for each data point and averages over a rolling window:
\text{MPE} = \frac{100}{n} \sum_{i=1}^{n} \frac{(\text{actual}_i - \text{predicted}_i)}{\text{actual}_i}
The sign preservation makes MPE invaluable for bias detection:
- Positive MPE: Model systematically under-predicts (actual > predicted)
- Negative MPE: Model systematically over-predicts (actual < predicted)
- MPE near zero: No systematic bias (though individual errors may be large)
Bias Detection
Consider a weather forecasting model:
- If MPE = +15%, the model consistently predicts temperatures 15% lower than actual
- If MPE = -10%, the model consistently predicts temperatures 10% higher than actual
- If MPE ≈ 0% but MAPE = 20%, errors cancel out (no bias) but magnitude is still significant
Mathematical Foundation
1. Point-wise Percentage Error
For each observation:
e_i = 100 \times \frac{\text{actual}_i - \text{predicted}_i}{\text{actual}_i}
2. Rolling Average
Over a period n:
\text{MPE}_t = \frac{1}{n} \sum_{i=t-n+1}^{t} e_i
3. Relationship to MAPE
\text{MAPE} = \frac{100}{n} \sum |e_i / 100|
\text{MPE} = \frac{100}{n} \sum (e_i / 100)
When errors are consistently in one direction: |\text{MPE}| \approx \text{MAPE}
When errors alternate: |\text{MPE}| < \text{MAPE}
Performance Profile
Operation Count (Streaming Mode)
O(1) per bar. Single-pass scalar transformation of (actual, forecast) pair; no lookback window required.
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| Error computation (subtract, abs/square/log) | 1-3 | ~3-8 cy | ~5-15 cy |
| Running accumulator update (EMA or sum) | 1 | ~4 cy | ~4 cy |
| Total | 2-4 | — | ~9-19 cycles |
Streaming update requires only the current actual/forecast pair and running state. ~10-15 cycles/bar typical.
Batch Mode (SIMD Analysis)
| Operation | Vectorizable? | Notes |
|---|---|---|
| Element-wise error computation | Yes | Independent per bar; fully vectorizable with Vector<double> |
| Reduction (sum/mean) | Yes | Parallel reduction; AVX2 gives 4x speedup |
| Log/exp components | Partial | Transcendental ops; polynomial approx for SIMD |
Batch SIMD: 4x-8x speedup for large windows. ~3-5 cy/bar amortized in vectorized batch mode.
| Metric | Score | Notes |
|---|---|---|
| Throughput | 15 ns/bar | O(1) via running sum |
| Allocations | 0 | Zero-allocation hot path |
| Complexity | O(1) | Constant per update |
| Bias Detection | 10/10 | Primary strength |
| Magnitude Info | 3/10 | Errors can cancel |
| Scale Independence | 9/10 | Percentage-based |
| Outlier Sensitivity | 5/10 | Linear in error magnitude |
Usage
// Streaming mode - bias detection in real-time
var mpe = new Mpe(20);
// Actual values consistently higher than predictions
mpe.Update(actual: 105.0, predicted: 100.0); // +5%
mpe.Update(actual: 110.0, predicted: 100.0); // +10%
// MPE will be positive, indicating under-prediction bias
double currentBias = mpe.Last.Value;
if (currentBias > 5.0)
Console.WriteLine("Model is under-predicting by {0:F1}%", currentBias);
else if (currentBias < -5.0)
Console.WriteLine("Model is over-predicting by {0:F1}%", Math.Abs(currentBias));
else
Console.WriteLine("Model shows no significant bias");
// Batch mode - analyze historical predictions
var actual = new TSeries { 100, 105, 98, 102, 101 };
var predicted = new TSeries { 95, 100, 95, 100, 100 };
var results = Mpe.Calculate(actual, predicted, period: 3);
// Span mode - zero-allocation bulk processing
Span<double> output = stackalloc double[1000];
Mpe.Batch(actualSpan, predictedSpan, output, period: 20);
Interpretation Guide
| MPE Value | Interpretation | Action |
|---|---|---|
| > +10% | Severe under-prediction | Add positive bias correction |
| +5% to +10% | Moderate under-prediction | Consider model recalibration |
| -5% to +5% | Acceptable bias range | Monitor for drift |
| -10% to -5% | Moderate over-prediction | Consider model recalibration |
| < -10% | Severe over-prediction | Add negative bias correction |
Comparison with Related Metrics
| Metric | Formula | Preserves Sign | Use Case |
|---|---|---|---|
| MPE | 100 × (A-P)/A | ✓ | Bias detection |
| MAPE | 100 × |A-P|/A | ✗ | Magnitude only |
| ME | A - P | ✓ | Absolute bias |
| MAE | |A - P| | ✗ | Absolute magnitude |
Common Pitfalls
1. Zero Actuals
MPE is undefined when actual = 0. The implementation uses epsilon fallback:
double divisor = Math.Abs(actual) < 1e-10 ? 1e-10 : actual;
2. Cancellation Effect
Errors of opposite signs cancel out. A model alternating between +50% and -50% errors would show MPE ≈ 0%, masking severe inaccuracy.
Solution: Use MPE alongside MAPE:
- Low MAPE + Low |MPE|: Good model
- Low MAPE + High |MPE|: Unlikely (mathematically constrained)
- High MAPE + Low |MPE|: High variance, no bias
- High MAPE + High |MPE|: High variance with bias
3. Asymmetric Bounds
Unlike MAPE (bounded at 0% to ∞), MPE can range from -∞ to +100%:
- Maximum positive: actual = 100, predicted = 0 → MPE = +100%
- No upper bound on negative: actual = 100, predicted = 1000 → MPE = -900%