- Implemented ChopIndicator for Quantower with configurable period and cold value display. - Created Chop class for calculating the Choppiness Index with detailed documentation. - Added comprehensive unit tests for Chop functionality, covering various market conditions and edge cases. - Developed markdown documentation for CHOP, detailing its historical context, mathematical foundation, and usage examples. - Established a remediation plan for channel indicators documentation, identifying gaps and prioritizing updates.
4.0 KiB
CG: Center of Gravity
"The market's center of mass reveals where momentum shifts before price does."
The Center of Gravity (CG) oscillator identifies potential turning points in price action using the physics concept of weighted center of mass. Developed by John Ehlers, it measures where the "weight" of prices is concentrated within a lookback window, providing leading signals for trend reversals with minimal lag.
Historical Context
John Ehlers introduced the Center of Gravity oscillator in his 2002 book Cybernetic Analysis for Stocks and Futures. Ehlers, an electrical engineer turned trader, applied signal processing concepts to financial markets, focusing on creating indicators with minimal lag.
The CG oscillator draws directly from physics: just as the center of gravity of an object determines its balance point, the CG of price determines where momentum is balanced within a window. Ehlers designed it to be a leading indicator, contrasting it with the inherent lag of traditional moving averages.
Architecture & Physics
The indicator calculates a weighted center of mass that oscillates around zero using a sliding window.
1. Weighted Sum (Numerator)
Num = \sum_{i=1}^{n} i \cdot P_{t-n+i}
where i ranges from 1 (oldest) to n (newest), giving more weight to recent data.
2. Simple Sum (Denominator)
Den = \sum_{i=1}^{n} P_{t-n+i}
3. Center of Gravity
CG_t = \frac{Num}{Den} - \frac{n + 1}{2}
The term \frac{n + 1}{2} represents the geometric center of the window, centering the indicator around zero.
Performance Profile
Operation Count (Streaming Mode, per Bar)
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| ADD (running sum update) | 2 | 1 | 2 |
| SUB (oldest value removal) | 2 | 1 | 2 |
| MUL (weight × price) | 1 | 3 | 3 |
| DIV (Num / Den) | 1 | 15 | 15 |
| Total | 6 | — | ~22 cycles |
Complexity Analysis
- Streaming: O(1) per bar using running sums
- Memory: O(n) for RingBuffer storage
- Warmup: n bars required
Validation
| Library | Status | Notes |
|---|---|---|
| TA-Lib | N/A | Not standard in TA-Lib |
| Skender | N/A | Not standard in Skender.Stock.Indicators |
| PineScript | ✅ | Validated against ta.cg() |
Usage & Pitfalls
- Zero crossing signals shift in momentum balance—bullish when crossing up, bearish when crossing down
- Positive values indicate weight concentrated in recent prices (uptrend)
- Negative values indicate weight concentrated in older prices (downtrend)
- Period of 10 is standard—smaller periods increase noise, larger periods add lag
- Strong trends cause CG to hang at extremes; wait for zero crossing for reversal confirmation
- Pair with trigger line (1-bar delay or small SMA) to reduce whipsaws
API
classDiagram
class Cg {
+int Period
+double Value
+bool IsHot
+Cg(int period)
+Cg(ITValuePublisher source, int period)
+TValue Update(TValue input, bool isNew)
+void Reset()
}
Class: Cg
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
period |
int |
10 |
>0 |
Lookback window for CG calculation |
Properties
Value(double): The current CG value (oscillates around 0)IsHot(bool): Returnstruewhen warmup period is complete
Methods
Update(TValue input, bool isNew): Updates the indicator with a new data point
C# Example
using QuanTAlib;
// Create a 10-period CG indicator
var cg = new Cg(period: 10);
// Update with streaming data
foreach (var bar in quotes)
{
var result = cg.Update(new TValue(bar.Date, bar.Close));
if (cg.IsHot)
{
Console.WriteLine($"{bar.Date}: CG = {result.Value:F4}");
// Signal detection
if (result.Value > 0 && cg.Previous.Value <= 0)
Console.WriteLine(" → Bullish crossover");
}
}
// Batch calculation
var output = Cg.Calculate(sourceSeries, period: 10);