mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 01:37:43 +00:00
86fe32a682
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: aider (openrouter/anthropic/claude-sonnet-4) <aider@aider.chat> Co-authored-by: Warp <agent@warp.dev>
142 lines
4.7 KiB
Markdown
142 lines
4.7 KiB
Markdown
# MAPD: Mean Absolute Percentage Deviation
|
|
|
|
> "Like MAPE, but divides by what you predicted instead of what actually happened."
|
|
|
|
Mean Absolute Percentage Deviation (MAPD) measures the average absolute percentage difference between actual and predicted values, using the predicted value as the denominator. This is the key difference from MAPE, which uses the actual value.
|
|
|
|
## Historical Context
|
|
|
|
MAPD emerged as an alternative to MAPE when analysts needed a metric that was more stable when actual values had high variance or approached zero. By using the predicted value as the denominator, MAPD provides different asymmetry characteristics than MAPE.
|
|
|
|
## Architecture & Physics
|
|
|
|
MAPD divides each absolute error by the predicted value instead of the actual value. This choice affects the asymmetry of the metric: MAPD penalizes under-prediction more heavily than over-prediction (opposite of MAPE).
|
|
|
|
### Properties
|
|
|
|
* **Scale-independent**: Expressed as percentage
|
|
* **Asymmetric**: Penalizes under-prediction more than over-prediction
|
|
* **Undefined at zero**: Cannot compute when predicted value is zero
|
|
* **Non-negative**: MAPD ≥ 0, with 0 indicating perfect prediction
|
|
* **Opposite bias to MAPE**: Favors over-prediction
|
|
|
|
## Mathematical Foundation
|
|
|
|
### 1. Percentage Deviation
|
|
|
|
For each observation, calculate the absolute percentage deviation:
|
|
|
|
$$APD_i = 100 \times \left| \frac{y_i - \hat{y}_i}{\hat{y}_i} \right|$$
|
|
|
|
Where:
|
|
|
|
* $y_i$ = actual value
|
|
* $\hat{y}_i$ = predicted value
|
|
|
|
### 2. Mean Calculation
|
|
|
|
Average the absolute percentage deviations over the period:
|
|
|
|
$$MAPD = \frac{100}{n} \sum_{i=1}^{n} \left| \frac{y_i - \hat{y}_i}{\hat{y}_i} \right|$$
|
|
|
|
### 3. Running Update (O(1))
|
|
|
|
QuanTAlib uses a ring buffer with running sum for O(1) updates:
|
|
|
|
$$S_{new} = S_{old} - APD_{oldest} + APD_{newest}$$
|
|
|
|
$$MAPD = \frac{S_{new}}{n}$$
|
|
|
|
## Implementation Details
|
|
|
|
### Usage Patterns
|
|
|
|
```csharp
|
|
// Streaming mode - update with each new observation
|
|
var mapd = new Mapd(period: 20);
|
|
var result = mapd.Update(actualValue, predictedValue);
|
|
|
|
// Batch mode - calculate for entire series
|
|
var results = Mapd.Calculate(actualSeries, predictedSeries, period: 20);
|
|
|
|
// Span mode - zero-allocation for high performance
|
|
Mapd.Batch(actualSpan, predictedSpan, outputSpan, period: 20);
|
|
```
|
|
|
|
### Parameters
|
|
|
|
| Parameter | Type | Description |
|
|
| :--- | :--- | :--- |
|
|
| **period** | int | Lookback window for averaging (must be > 0) |
|
|
|
|
### Properties
|
|
|
|
| Property | Type | Description |
|
|
| :--- | :--- | :--- |
|
|
| **Last** | TValue | Most recent MAPD value (as percentage) |
|
|
| **IsHot** | bool | True when buffer is full |
|
|
| **Name** | string | Indicator name (e.g., "Mapd(20)") |
|
|
| **WarmupPeriod** | int | Number of periods before valid output |
|
|
|
|
## Performance Profile
|
|
|
|
| Metric | Score | Notes |
|
|
| :--- | :--- | :--- |
|
|
| **Throughput** | ~12 ns/bar | O(1) update complexity |
|
|
| **Allocations** | 0 | Uses pre-allocated ring buffer |
|
|
| **Complexity** | O(1) | Constant time per update |
|
|
| **Accuracy** | 10/10 | Exact calculation |
|
|
| **Timeliness** | 9/10 | No lag beyond the period |
|
|
| **Smoothness** | 7/10 | Moderate smoothing |
|
|
|
|
## MAPE vs MAPD Comparison
|
|
|
|
```csharp
|
|
var mape = new Mape(1);
|
|
var mapd = new Mapd(1);
|
|
|
|
// actual=100, predicted=200
|
|
mape.Update(100, 200); // |100-200|/100 = 100%
|
|
mapd.Update(100, 200); // |100-200|/200 = 50%
|
|
|
|
// actual=200, predicted=100
|
|
mape.Update(200, 100); // |200-100|/200 = 50%
|
|
mapd.Update(200, 100); // |200-100|/100 = 100%
|
|
```
|
|
|
|
| Scenario | MAPE | MAPD |
|
|
| :--- | :--- | :--- |
|
|
| **Over-prediction** | Lower | Higher |
|
|
| **Under-prediction** | Higher | Lower |
|
|
|
|
## Comparison with Other Metrics
|
|
|
|
| Metric | Denominator | Bias |
|
|
| :--- | :--- | :--- |
|
|
| **MAPE** | Actual | Favors under-prediction |
|
|
| **MAPD** | Predicted | Favors over-prediction |
|
|
| **SMAPE** | (Actual + Predicted)/2 | Symmetric |
|
|
| **MAE** | None | No percentage conversion |
|
|
|
|
## Common Use Cases
|
|
|
|
1. **Forecast Validation**: When predicted values are more reliable than actuals
|
|
2. **Model Comparison**: Alternative perspective to MAPE
|
|
3. **Budgeting**: When comparing actuals to budget (predicted)
|
|
4. **Quality Control**: When predictions are the reference standard
|
|
|
|
## Edge Cases
|
|
|
|
* **Identical Values**: Returns 0% when actual equals predicted
|
|
* **Zero Predicted**: Uses epsilon (1e-10) to avoid division by zero
|
|
* **NaN Handling**: Uses last valid value substitution
|
|
* **Single Input**: Not supported (requires two series)
|
|
* **Period = 1**: Returns current percentage deviation
|
|
|
|
## Related Indicators
|
|
|
|
* [MAPE](../mape/Mape.md) - Mean Absolute Percentage Error (divides by actual)
|
|
* [SMAPE](../smape/Smape.md) - Symmetric Mean Absolute Percentage Error
|
|
* [MPE](../mpe/Mpe.md) - Mean Percentage Error (signed)
|
|
* [MAE](../mae/Mae.md) - Mean Absolute Error (same units)
|