7.2 KiB
PMO: Price Momentum Oscillator
Double-smooth the rate of change and you get something that actually tells you where momentum is headed, not where it was five bars ago.
| Property | Value |
|---|---|
| Category | Momentum |
| Inputs | Source (close) |
| Parameters | timePeriods (default DefaultTimePeriods), smoothPeriods (default DefaultSmoothPeriods), signalPeriods (default DefaultSignalPeriods) |
| Outputs | Single series (Pmo) |
| Output range | Varies (see docs) |
| Warmup | timePeriods + smoothPeriods bars |
| PineScript | pmo.pine |
- PMO (Price Momentum Oscillator), developed by Carl Swenlin at DecisionPoint, is a double-smoothed 1-bar rate of change.
- Similar: MACD, TSI | Complementary: Signal line for crossovers | Trading note: Price Momentum Oscillator; double-smoothed ROC. Decisionpoint.com creation.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
PMO (Price Momentum Oscillator), developed by Carl Swenlin at DecisionPoint, is a double-smoothed 1-bar rate of change. It applies two custom EMA passes to a percentage ROC, producing a momentum oscillator that is smoother than raw ROC yet more responsive than triple-smoothed alternatives like TRIX. The custom EMA uses \alpha = 2/N rather than the standard 2/(N+1), and seeds with the SMA of the first N values. PMO oscillates around zero: positive values indicate upward momentum, negative values indicate downward momentum.
Historical Context
Carl Swenlin introduced PMO through DecisionPoint.com as a refinement of the standard rate of change. The insight was that raw ROC (percentage change) is too noisy for reliable signal generation, but standard smoothing methods introduce too much lag. Swenlin's solution was a two-stage custom EMA pipeline applied to a 1-bar ROC, scaled by a factor of 10 after the first smoothing stage.
The implementation details matter: Swenlin specified \alpha = 2/N, not the standard EMA formula 2/(N+1). This subtle difference produces a slightly more responsive filter. Both Skender.Stock.Indicators and OoplesFinance implement this custom alpha, confirming the specification.
PMO is frequently used with a signal line (an EMA of the PMO itself) to generate crossover signals, similar to MACD. The default parameters (35, 20, 10) provide a balance between responsiveness and smoothness on daily charts.
Architecture & Physics
1. One-Bar Percentage ROC
\text{ROC}_t = \left(\frac{P_t}{P_{t-1}} - 1\right) \times 100
This is always a 1-bar lookback regardless of parameters. The percentage form normalizes across price levels.
2. First Custom EMA (ROC Smoothing)
\text{RocEma}_t = \text{CustomEMA}(\text{ROC}, \text{timePeriods}) \times 10
The custom EMA uses \alpha_1 = 2 / \text{timePeriods} and is seeded with the SMA of the first N ROC values. The \times 10 scaling amplifies the signal to a more readable range.
3. Second Custom EMA (PMO Smoothing)
\text{PMO}_t = \text{CustomEMA}(\text{RocEma}, \text{smoothPeriods})
The second pass uses \alpha_2 = 2 / \text{smoothPeriods}, also seeded with SMA. This produces the final PMO value.
4. State Management
The indicator uses record struct State with 12 fields tracking both EMA pipelines, seeding progress, and bar correction state. The _state / _p_state pattern enables rollback for streaming bar corrections.
Mathematical Foundation
Core Formulas
Step 1 - Percentage ROC (1-bar):
\text{ROC}_t = \left(\frac{P_t}{P_{t-1}} - 1\right) \times 100
Step 2 - Custom EMA smoothing:
The custom EMA differs from standard EMA:
| Property | Standard EMA | Custom EMA (PMO) |
|---|---|---|
| Alpha | \frac{2}{N+1} |
\frac{2}{N} |
| Seed | First value | SMA of first N values |
\text{CustomEMA}_t = \alpha \cdot x_t + (1 - \alpha) \cdot \text{CustomEMA}_{t-1}
Step 3 - Scale and second smooth:
\text{RocEma}_t = \text{CustomEMA}_1(\text{ROC}_t) \times 10
\text{PMO}_t = \text{CustomEMA}_2(\text{RocEma}_t)
Default Parameters
| Parameter | Default | Purpose |
|---|---|---|
| timePeriods | 35 | First EMA smoothing of 1-bar ROC |
| smoothPeriods | 20 | Second EMA smoothing for PMO |
| signalPeriods | 10 | Signal line EMA (future use) |
Warmup
\text{WarmupPeriod} = \text{timePeriods} + \text{smoothPeriods}
Performance Profile
Operation Count (Streaming Mode)
| Operation | Count | Notes |
|---|---|---|
| DIV | 1 | ROC percentage calculation |
| MUL | 3 | alpha multiplications + scale |
| ADD/SUB | 4 | EMA updates + ROC |
| State copy | 1 | rollback support |
| Total | ~9 ops | Lightweight double-EMA |
Batch Mode (Span-based)
| Operation | Complexity | Notes |
|---|---|---|
| Per-element | O(1) | Fixed operations per bar |
| Total | O(n) | Linear scan |
| Memory | O(1) | No additional allocation beyond state |
Quality Metrics
| Metric | Score | Notes |
|---|---|---|
| Accuracy | 9/10 | Custom EMA matches DecisionPoint spec |
| Timeliness | 7/10 | Double smoothing adds moderate lag |
| Smoothness | 8/10 | Substantially smoother than raw ROC |
| Simplicity | 6/10 | Two-stage pipeline with custom alpha |
Validation
| Library | Status | Notes |
|---|---|---|
| Skender | ✅ | Matches within 1e-9 tolerance |
| TA-Lib | N/A | No PMO function |
| Tulip | N/A | No PMO function |
| Ooples | ✅ | Matches within 1e-6 tolerance |
Common Pitfalls
-
Custom alpha confusion: PMO uses
\alpha = 2/N, not the standard2/(N+1). Using standard EMA alpha produces different results that do not match the DecisionPoint specification. -
SMA seeding: The custom EMA must be seeded with the SMA of the first N values, not with the first value. This affects the convergence behavior during warmup.
-
Scale factor: The
\times 10multiplier is applied after the first EMA pass, not before. Misplacing this scaling produces values off by an order of magnitude. -
1-bar ROC only: PMO always uses a 1-bar ROC regardless of the timePeriods parameter. The timePeriods parameter controls only the first EMA smoothing length.
-
Division by zero: When
PrevCloseis zero, the ROC calculation would produce Infinity. The implementation guards against this with last-valid-value substitution. -
Warmup length: PMO requires
timePeriods + smoothPeriodsbars before producing stable values. Early values are heavily influenced by the SMA seed. -
Signal line: The signalPeriods parameter is reserved for future signal line implementation. Currently only the PMO line is computed.
References
- Swenlin, C. "DecisionPoint Price Momentum Oscillator (PMO)." DecisionPoint.com.
- StockCharts.com: "DecisionPoint Price Momentum Oscillator (PMO)" Technical Analysis documentation.
- Murphy, J. J. (1999). "Technical Analysis of the Financial Markets." New York Institute of Finance.