4.9 KiB
CMO (Chande Momentum Oscillator)
Chande's momentum oscillator compares the sum of gains to the sum of losses, yielding a symmetric measure of directional force.
| Property | Value |
|---|---|
| Category | Momentum |
| Inputs | Source (close) |
| Parameters | period (default 14) |
| Outputs | Single series (Cmo) |
| Output range | -100 to +100 |
| Warmup | period + 1 bars |
| PineScript | cmo.pine |
- The Chande Momentum Oscillator (CMO) is a momentum indicator developed by Tushar Chande.
- Similar: RSI, Stoch | Complementary: Moving average crossover | Trading note: Chande Momentum Oscillator; like RSI but uses up−down/up+down formula. Range ±100.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Chande Momentum Oscillator (CMO) is a momentum indicator developed by Tushar Chande. Unlike RSI which uses smoothed averages of gains and losses, CMO uses raw sums of up and down movements, making it more responsive to price changes. The indicator oscillates between -100 and +100.
Formula
CMO = 100 \times \frac{SumUp - SumDown}{SumUp + SumDown}
Where:
- SumUp = Sum of positive price changes over the period
- SumDown = Sum of absolute negative price changes over the period
Key Characteristics
| Property | Value |
|---|---|
| Output Range | -100 to +100 |
| Zero Line | Neutral momentum |
| Overbought | Above +50 |
| Oversold | Below -50 |
| Default Period | 14 |
Comparison with RSI
| Feature | CMO | RSI |
|---|---|---|
| Range | [-100, +100] | [0, 100] |
| Smoothing | None (raw sums) | RMA (exponential) |
| Sensitivity | Higher | Lower |
| Zero crossing | Valid signal | N/A (50 is neutral) |
Usage
// Create CMO indicator
var cmo = new Cmo(period: 14);
// Single value update
var result = cmo.Update(new TValue(time, price));
// Batch calculation
var results = Cmo.Batch(priceData, period: 14);
// Subscribe to source
var cmo = new Cmo(sourceIndicator, period: 14);
Interpretation
-
Overbought/Oversold
- CMO > +50: Overbought conditions
- CMO < -50: Oversold conditions
- Extreme readings (±70) suggest stronger signals
-
Zero Line Crossings
- Crossing above zero: Bullish momentum
- Crossing below zero: Bearish momentum
-
Divergences
- Price makes new high, CMO doesn't: Bearish divergence
- Price makes new low, CMO doesn't: Bullish divergence
-
Signal Line
- Some traders use a 9-period EMA of CMO as a signal line
Implementation Details
- O(1) streaming updates using circular buffers
- SIMD-optimized batch calculations
- Zero heap allocations in hot paths
- Handles NaN and edge cases gracefully
Sources
- Chande, Tushar S. "The New Technical Trader" (1994)
- Chande, Tushar S. & Kroll, Stanley. "Beyond Technical Analysis" (1997)
- StockCharts - CMO
Performance Profile
Operation Count (Streaming Mode)
CMO(N) maintains two ring buffers — _upBuffer (gains) and _downBuffer (losses) — and derives its value from the running sums already tracked by each buffer. The per-bar cost is dominated by the ring buffer updates and the single division.
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| Price delta (SUB) | 1 | 1 | ~1 |
| Up/down classification (branch) | 1 | 1 | ~1 |
| Ring buffer push × 2 (up + down) | 2 | 3 | ~6 |
| Running sum update × 2 (add evicted, add new) | 4 | 1 | ~4 |
| Sum subtraction (SumUp − SumDown) | 1 | 1 | ~1 |
| Sum addition (SumUp + SumDown) | 1 | 1 | ~1 |
| Scale (× 100) + division | 2 | 8 | ~16 |
| Total | 12 | — | ~30 cycles |
O(1) per bar. At N = 14 (default), WarmupPeriod = 15 bars (one extra for the initial delta). Typical measured cost: 28–32 cycles on a Zen 4 core with turbo.
Batch Mode (SIMD Analysis)
| Operation | Vectorizable? | Notes |
|---|---|---|
| Price delta series | Yes | VSUBPD across entire input span |
| Up/down split | Partial | VCMPPD + masked store; branching logic resists wide SIMD |
| Prefix-sum of up/down windows | Yes | scan-then-window via AVX2 prefix scan |
| Sliding window sum (subtract old, add new) | Yes | vectorizable once prefix sums are built |
| Final CMO formula (per bar) | Yes | VSUBPD, VADDPD, VDIVPD |
The classification branch (up vs. down) is the primary SIMD barrier. A branchless formulation using Vector.ConditionalSelect replaces the branch with a mask, enabling full vectorization. For N = 14, AVX2 processes 8 bars simultaneously after the prefix-sum setup.