9.0 KiB
PWMA: Parabolic Weighted Moving Average
Overview and Purpose
The Parabolic Weighted Moving Average (PWMA) is a technical indicator that applies parabolic weighting to price data, assigning significantly higher importance to the most recent observations. While the Weighted Moving Average (WMA) uses linear weighting (i), PWMA uses squared weighting (i^2), creating an even stronger recency bias. This results in an indicator that tracks price action with exceptional responsiveness, making it ideal for fast-moving markets and as a component in advanced momentum oscillators like Jurik's Velocity (VEL).
Core Concepts
- Parabolic weighting: Weights follow a squared progression (
1^2, 2^2, \dots, n^2), drastically emphasizing recent data over older points. - Reduced Lag: The aggressive weighting scheme minimizes lag significantly more than WMA or SMA, allowing for faster trend detection.
- O(1) Complexity: This implementation uses a triple running sum technique to ensure constant-time updates, regardless of the period length.
- Component Indicator: PWMA is a critical building block for other indicators, most notably serving as the "fast" component in the Velocity (VEL) indicator calculation (
VEL = PWMA - WMA).
The core innovation of PWMA is its use of squared weights, which shifts the center of gravity of the moving average much closer to the current price than linear methods. This makes it highly sensitive to recent price changes while still providing a smooth curve derived from the entire window.
Common Settings and Parameters
| Parameter | Default | Function | When to Adjust |
|---|---|---|---|
| Period | 14 | Controls the lookback period | Increase for smoother trends, decrease for ultra-fast responsiveness |
| Source | Close | Price data used for calculation | Consider using HLC3 for a more balanced price representation |
Pro Tip: Because PWMA is so responsive, it can be prone to overshooting in choppy markets. It is often best used in combination with a slower average (like WMA) to form a MACD-like oscillator or to identify rapid momentum shifts.
Calculation and Mathematical Foundation
Simplified explanation:
PWMA calculates a weighted average where the weight of each price is the square of its position in the window. For a 5-period PWMA, the weights would be 1, 4, 9, 16, 25 (for the oldest to newest prices respectively).
Technical formula:
PWMA = \frac{\sum_{i=1}^{n} i^2 \cdot P_i}{\sum_{i=1}^{n} i^2}
Where:
nis the period lengthP_iis the price at positioni(oldest to newest)i^2is the parabolic weight- Divisor
= \frac{n(n+1)(2n+1)}{6}(sum of squares of firstnintegers)
O(1) Optimization - Triple Running Sums:
To achieve constant-time updates, the algorithm maintains three running sums:
- S1 (Simple Sum):
\sum P_i - S2 (Linear Weighted Sum):
\sum i \cdot P_i - S3 (Parabolic Weighted Sum):
\sum i^2 \cdot P_i
The recurrence relations for updating these sums when the window slides are:
S_{1,new} = S_{1,old} - P_{oldest} + P_{new}
S_{2,new} = S_{2,old} - S_{1,old} + n \cdot P_{new}
S_{3,new} = S_{3,old} - 2 \cdot S_{2,old} + S_{1,old} + n^2 \cdot P_{new}
PWMA = \frac{S_{3,new}}{divisor}
This algebraic expansion allows the indicator to update in constant time (~12 operations) regardless of whether the period is 10 or 10,000.
C# Implementation
The library provides two implementations: a standard scalar version and a high-performance Span-based static version.
Single PWMA (Pwma)
The Pwma class calculates a single parabolic weighted moving average with O(1) update complexity.
using QuanTAlib;
// Initialize with period 14
var pwma = new Pwma(14);
// Streaming update
TValue result = pwma.Update(new TValue(time, price));
Console.WriteLine($"Current PWMA: {result.Value}");
// Access properties
Console.WriteLine($"Name: {pwma.Name}"); // "Pwma(14)"
Console.WriteLine($"IsHot: {pwma.IsHot}"); // true when buffer is full
// Batch calculation (TSeries API)
TSeries source = ...;
TSeries results = Pwma.Calculate(source, 14);
// High-performance Span API (zero allocation)
double[] prices = new double[10000];
double[] output = new double[10000];
Pwma.Calculate(prices.AsSpan(), output.AsSpan(), period: 14);
Zero-Allocation Span API
For performance-critical scenarios (backtesting, HFT), use the Span-based overload:
// Allocate buffers once, reuse across calculations
double[] source = new double[200000];
double[] pwmaOutput = new double[200000];
// Zero heap allocation during calculation
Pwma.Calculate(source.AsSpan(), pwmaOutput.AsSpan(), period: 100);
// Results are written directly to output buffer
Console.WriteLine($"Last PWMA: {pwmaOutput[^1]}");
Benefits:
- Zero allocation: No GC pressure during calculation
- Cache-friendly: Sequential memory access patterns
- O(1) per-bar via triple running sums
- Compatible with
ArrayPool<T>for buffer management
Bar Correction (isNew Parameter)
Pwma supports intra-bar updates for real-time trading systems:
var pwma = new Pwma(14);
// Process historical bars
for (int i = 0; i < historicalBars.Count; i++)
{
pwma.Update(historicalBars[i], isNew: true);
}
// Real-time: receive initial tick for new bar
pwma.Update(new TValue(time, 100.5), isNew: true);
// Real-time: price updates within same bar
pwma.Update(new TValue(time, 101.0), isNew: false); // O(1) correction
pwma.Update(new TValue(time, 100.8), isNew: false); // O(1) correction
// Bar closes, next bar starts
pwma.Update(new TValue(time + 1, 101.2), isNew: true);
Implementation detail: Bar correction is O(1) using scalar state save/restore.
Eventing and Reactive Support
This indicator implements the ITValuePublisher interface, enabling event-driven and reactive workflows.
- Subscription: Can be constructed with an
ITValuePublisher(e.g.,TSeries) to automatically update when the source emits a new value. - Publication: Emits a
Pubevent with the newTValuewhenever it is updated.
using QuanTAlib;
// 1. Setup a source (publisher)
var source = new TSeries();
// 2. Create indicator subscribed to source
var pwma = new Pwma(source, period: 14);
// 3. Optional: Subscribe to indicator's output
pwma.Pub += (item) => Console.WriteLine($"PWMA Updated: {item.Value}");
// 4. Ingest data into source
source.Add(new TValue(DateTime.Now, 100));
Handling Invalid Values (NaN/Infinity)
Pwma uses last-value substitution for handling invalid inputs:
var pwma = new Pwma(14);
// Valid values establish baseline
pwma.Update(new TValue(time, 100));
// NaN or Infinity inputs are replaced with last valid value
var result = pwma.Update(new TValue(time, double.NaN));
Console.WriteLine(double.IsFinite(result.Value)); // true
Behavior:
- When
NaN,PositiveInfinity, orNegativeInfinityis encountered, the last valid value is substituted - This provides output continuity instead of propagating invalid values
Reset()clears the last valid value, so the next valid input establishes a new baseline
Performance Characteristics
| Operation | Complexity | Notes |
|---|---|---|
| Update (isNew=true) | O(1) | Triple running sums logic |
| Update (isNew=false) | O(1) | Scalar state restore + recalculate |
| Batch processing | O(n) | Where n is series length |
| Memory (single) | O(period) | One RingBuffer for values |
| Memory (state) | O(1) | Scalar state struct |
The implementation uses:
- Triple running sums for O(1) parabolic weighted average calculation
- Scalar state save/restore for O(1) bar correction
- Pinned memory in RingBuffer for cache-friendly access
- Periodic Resync (every 1000 ticks) to prevent floating-point drift
Interpretation Details
PWMA is primarily used for:
- High-Speed Trend Detection: Its low lag makes it excellent for catching trends early.
- Velocity Calculation: Used in conjunction with WMA to calculate Velocity (
VEL = PWMA - WMA). - Dynamic Support/Resistance: Acts as a tighter support/resistance level than SMA or WMA in strong trends.
PWMA vs WMA vs SMA Comparison
| Aspect | PWMA | WMA | SMA |
|---|---|---|---|
| Weighting | Parabolic (i^2) |
Linear (i) |
Equal (1) |
| Lag | Lowest | Low | High |
| Sensitivity | Highest | High | Low |
| Noise filtering | Low | Good | Best |
| Best use | Momentum, Velocity | General Trend | Long-term Trend |
Limitations and Considerations
- Overshoot: Due to the aggressive weighting, PWMA can overshoot price targets during sudden reversals.
- Noise Sensitivity: It is more sensitive to market noise than WMA or SMA.
- Drift: The complex running sum algorithm requires periodic resynchronization (handled internally) to maintain precision over millions of updates.
References
- Jurik Research (concept of parabolic weighting in Velocity)
- Colby, Robert W. "The Encyclopedia of Technical Market Indicators." McGraw-Hill, 2002