Files

124 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](cmo.pine) |
- The Chande Momentum Oscillator (CMO) is a momentum indicator developed by Tushar Chande.
- **Similar:** [RSI](../rsi/Rsi.md), [Stoch](../../oscillators/stoch/Stoch.md) | **Complementary:** Moving average crossover | **Trading note:** Chande Momentum Oscillator; like RSI but uses updown/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
```csharp
// 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
1. **Overbought/Oversold**
- CMO > +50: Overbought conditions
- CMO < -50: Oversold conditions
- Extreme readings (±70) suggest stronger signals
2. **Zero Line Crossings**
- Crossing above zero: Bullish momentum
- Crossing below zero: Bearish momentum
3. **Divergences**
- Price makes new high, CMO doesn't: Bearish divergence
- Price makes new low, CMO doesn't: Bullish divergence
4. **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](https://school.stockcharts.com/doku.php?id=technical_indicators:chande_momentum_oscillator)
## 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: 2832 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.