mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18:05 +00:00
Add Choppiness Index (CHOP) implementation and tests
- 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.
This commit is contained in:
+123
-173
@@ -1,225 +1,175 @@
|
||||
# KCHANNEL: Keltner Channel
|
||||
|
||||
> "Chester Keltner understood that volatility defines opportunity—his channel shows where price *should* travel, not just where it has been."
|
||||
> "True Range reveals what close-to-close volatility hides—the overnight gaps."
|
||||
|
||||
Keltner Channel wraps an Exponential Moving Average (EMA) with bands based on Average True Range (ATR). The middle band tracks trend direction via EMA smoothing; the upper and lower bands expand and contract with market volatility. Unlike Bollinger Bands that use standard deviation (sensitive to outliers), Keltner uses ATR—a volatility measure designed specifically for price movement that includes gaps.
|
||||
Keltner Channels are volatility-based envelopes set above and below an **Exponential Moving Average (EMA)**. Unlike Bollinger Bands, which use Standard Deviation (statistical dispersion), Keltner Channels use **Average True Range (ATR)** (actual price range). This produces bands that are smoother and less prone to "sausage" effects (violent pinching/expanding) than Bollinger Bands, making them particularly effective for trend identification and gap handling in futures and gapping markets.
|
||||
|
||||
## Historical Context
|
||||
|
||||
Chester W. Keltner introduced the original Keltner Channel in his 1960 book "How to Make Money in Commodities." His version used a 10-period Simple Moving Average of the "typical price" (HLC/3) with bands at the 10-period average range.
|
||||
**Chester Keltner** introduced the original Keltner Channel in his 1960 book *How To Make Money in Commodities*. His version used a 10-day Simple Moving Average of "typical price" (High+Low+Close)/3 with bands at ±1× the 10-day SMA of the daily range (High-Low).
|
||||
|
||||
Linda Bradford Raschke modernized the formula in the 1980s, replacing SMA with EMA for smoother trend following and swapping average range for Average True Range to properly account for gaps. Most modern implementations—including this one—follow Raschke's formulation with a 20-period EMA and 2× ATR width.
|
||||
**Linda Bradford Raschke** modernized the indicator in the 1980s, replacing the SMA with an EMA for faster response and substituting the simple range with Wilder's Average True Range (ATR). ATR captures gap volatility that the simple High-Low range misses, making the channel more robust for markets that trade overnight or have limit moves.
|
||||
|
||||
The PineScript reference algorithm adds warmup compensation: instead of the traditional EMA formula that converges slowly from the first value, it tracks cumulative weighted sums to produce accurate values even during warmup. This implementation replicates that approach for both EMA and ATR (via RMA/Wilder smoothing).
|
||||
The modern Keltner Channel (EMA + ATR) gained popularity through Raschke's work and is now the standard implementation in most charting platforms. The indicator became central to the "TTM Squeeze" setup, which detects when Bollinger Bands nest inside Keltner Channels—a compression pattern often preceding explosive moves.
|
||||
|
||||
## Architecture & Physics
|
||||
|
||||
Keltner Channel consists of three interdependent components: the EMA middle band, the ATR volatility measure, and the upper/lower bands.
|
||||
The system relies on "True Range" volatility, which accounts for gaps between bars:
|
||||
|
||||
### 1. Exponential Moving Average (Middle Band)
|
||||
1. **Center of Gravity:** The middle line is an EMA, providing a more responsive center than the SMA used in Bollinger Bands.
|
||||
2. **Volatility Measure:** The width is determined by ATR (specifically, Wilder's RMA of True Range), which captures the typical "spatial volume" of price movement.
|
||||
3. **Envelope Logic:**
|
||||
- Price above the upper channel indicates strong momentum (breakout/trend).
|
||||
- Price below the lower channel indicates weakness.
|
||||
- Mean reversion is expected when price moves significantly outside the bands.
|
||||
|
||||
The middle band uses EMA with warmup compensation:
|
||||
### Calculation Steps
|
||||
|
||||
#### 1. True Range
|
||||
|
||||
$$
|
||||
\alpha = \frac{2}{\text{period} + 1}
|
||||
TR_t = \max(H_t - L_t, |H_t - C_{t-1}|, |L_t - C_{t-1}|)
|
||||
$$
|
||||
|
||||
Where $H$ = High, $L$ = Low, $C$ = Close.
|
||||
|
||||
#### 2. Average True Range (Wilder's Smoothing)
|
||||
|
||||
$$
|
||||
ATR_t = \frac{ATR_{t-1} \times (n-1) + TR_t}{n}
|
||||
$$
|
||||
|
||||
#### 3. Middle Band (EMA)
|
||||
|
||||
$$
|
||||
\alpha = \frac{2}{n + 1}
|
||||
$$
|
||||
|
||||
$$
|
||||
S_t = S_{t-1} \cdot (1 - \alpha) + P_t \cdot \alpha
|
||||
EMA_t = \alpha \times C_t + (1 - \alpha) \times EMA_{t-1}
|
||||
$$
|
||||
|
||||
#### 4. Channel Construction
|
||||
|
||||
$$
|
||||
\text{Upper}_t = EMA_t + (k \times ATR_t)
|
||||
$$
|
||||
|
||||
$$
|
||||
W_t = W_{t-1} \cdot (1 - \alpha) + \alpha
|
||||
\text{Lower}_t = EMA_t - (k \times ATR_t)
|
||||
$$
|
||||
|
||||
$$
|
||||
\text{EMA}_t = \frac{S_t}{W_t}
|
||||
$$
|
||||
|
||||
where $S$ is the cumulative weighted sum, $W$ is the cumulative weight, and $P$ is the close price. The division by $W_t$ compensates for the geometric decay during warmup, producing accurate values from the first bar rather than requiring period bars to converge.
|
||||
|
||||
### 2. True Range
|
||||
|
||||
True Range captures the full price movement including gaps:
|
||||
|
||||
$$
|
||||
\text{TR}_t = \max\begin{cases}
|
||||
H_t - L_t \\
|
||||
|H_t - C_{t-1}| \\
|
||||
|L_t - C_{t-1}|
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
where $H$ is high, $L$ is low, and $C$ is close. The first bar uses $H_0 - L_0$ (no previous close available).
|
||||
|
||||
### 3. Average True Range (via RMA)
|
||||
|
||||
ATR uses Wilder's RMA smoothing with warmup compensation:
|
||||
|
||||
$$
|
||||
\beta = \frac{1}{\text{period}}
|
||||
$$
|
||||
|
||||
$$
|
||||
\text{RawRMA}_t = \text{RawRMA}_{t-1} \cdot (1 - \beta) + \text{TR}_t \cdot \beta
|
||||
$$
|
||||
|
||||
$$
|
||||
E_t = E_{t-1} \cdot (1 - \beta)
|
||||
$$
|
||||
|
||||
$$
|
||||
\text{ATR}_t = \frac{\text{RawRMA}_t}{1 - E_t}
|
||||
$$
|
||||
|
||||
where $E$ is the exponential decay factor that converges to 0 as the series progresses. The division compensates for warmup bias.
|
||||
|
||||
### 4. Upper and Lower Bands
|
||||
|
||||
Bands are placed symmetrically around the EMA:
|
||||
|
||||
$$
|
||||
U_t = \text{EMA}_t + \text{mult} \cdot \text{ATR}_t
|
||||
$$
|
||||
|
||||
$$
|
||||
L_t = \text{EMA}_t - \text{mult} \cdot \text{ATR}_t
|
||||
$$
|
||||
|
||||
where mult is typically 2.0. The bands expand during volatile periods and contract during consolidation.
|
||||
|
||||
## Mathematical Foundation
|
||||
|
||||
### EMA Warmup Compensation
|
||||
|
||||
Traditional EMA initializes with the first price and decays toward the true average:
|
||||
|
||||
$$
|
||||
\text{EMA}_t = \alpha \cdot P_t + (1 - \alpha) \cdot \text{EMA}_{t-1}
|
||||
$$
|
||||
|
||||
This produces biased early values. The warmup-compensated version tracks:
|
||||
|
||||
$$
|
||||
S_t = \sum_{i=0}^{t} P_i \cdot \alpha \cdot (1-\alpha)^{t-i}
|
||||
$$
|
||||
|
||||
$$
|
||||
W_t = \sum_{i=0}^{t} \alpha \cdot (1-\alpha)^{t-i} = 1 - (1-\alpha)^{t+1}
|
||||
$$
|
||||
|
||||
Dividing $S_t / W_t$ normalizes by the actual accumulated weight rather than assuming unit weight.
|
||||
|
||||
### RMA (Wilder's Smoothing)
|
||||
|
||||
RMA uses $\alpha = 1/\text{period}$ compared to EMA's $\alpha = 2/(\text{period}+1)$:
|
||||
|
||||
| Period | EMA α | RMA α |
|
||||
| :---: | :---: | :---: |
|
||||
| 10 | 0.1818 | 0.10 |
|
||||
| 14 | 0.1333 | 0.0714 |
|
||||
| 20 | 0.0952 | 0.05 |
|
||||
|
||||
RMA is slower/smoother than EMA for the same period. An RMA(14) roughly matches an EMA(27) in smoothness.
|
||||
|
||||
### Band Width Interpretation
|
||||
|
||||
The ATR multiplier determines how many "volatility units" away the bands sit:
|
||||
|
||||
| Multiplier | Band Width | Usage |
|
||||
| :---: | :--- | :--- |
|
||||
| 1.0 | 1 ATR | Tight—frequent touches, aggressive trading |
|
||||
| 2.0 | 2 ATR | Standard—balanced signal frequency |
|
||||
| 3.0 | 3 ATR | Wide—rare touches, conservative entry |
|
||||
|
||||
Price spending extended time outside the bands indicates strong trend momentum (continuation) or potential exhaustion (reversal), depending on context.
|
||||
Where $n$ = period (default: 20), $k$ = multiplier (default: 2.0).
|
||||
|
||||
## Performance Profile
|
||||
|
||||
### Operation Count (Streaming Mode, Scalar)
|
||||
The calculation is highly efficient, relying on recursive O(1) formulas (EMA and RMA).
|
||||
|
||||
Per-bar cost for full Keltner Channel calculation:
|
||||
### Operation Count - Single value
|
||||
|
||||
| Operation | Count | Cost (cycles) | Subtotal |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| ADD/SUB | 8 | 1 | 8 |
|
||||
| MUL | 6 | 3 | 18 |
|
||||
| DIV | 2 | 15 | 30 |
|
||||
| MAX | 1 | 2 | 2 |
|
||||
| ABS | 2 | 1 | 2 |
|
||||
| ADD/SUB | 5 | 1 | 5 |
|
||||
| MUL | 4 | 3 | 12 |
|
||||
| DIV | 1 | 15 | 15 |
|
||||
| CMP/ABS | 4 | 1 | 4 |
|
||||
| FMA | 2 | 4 | 8 |
|
||||
| **Total** | **21** | — | **~68 cycles** |
|
||||
| **Total** | **16** | — | **~44 cycles** |
|
||||
|
||||
**Dominant cost**: Division operations (44% of total) for warmup compensation in both EMA and ATR.
|
||||
### Operation Count - Batch processing
|
||||
|
||||
### Batch Mode (512 values, SIMD/FMA)
|
||||
|
||||
Both EMA and RMA are recursive filters with sequential dependencies. SIMD applies only to independent operations:
|
||||
|
||||
| Operation | Scalar Ops | SIMD Ops (AVX2) | Speedup |
|
||||
| Operation | Scalar Ops | SIMD Ops (AVX/SSE) | Acceleration |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| True Range (max/abs) | 5 | 1 | 5× |
|
||||
| Band calculation (add/mul) | 4 | 1 | 4× |
|
||||
| EMA recursion | 4 | 4 | 1× |
|
||||
| ATR recursion | 4 | 4 | 1× |
|
||||
| TR calculation | 3N | 3N/8 | ~8× |
|
||||
| ATR (IIR) | N | N | 1× |
|
||||
| EMA (IIR) | N | N | 1× |
|
||||
| Band construction | 2N | 2N/8 | ~8× |
|
||||
|
||||
**Per-bar savings with FMA:**
|
||||
|
||||
| Optimization | Cycles Saved | New Total |
|
||||
| :--- | :---: | :---: |
|
||||
| EMA FMA (α×P + decay×S) | 2 | 66 |
|
||||
| RMA FMA (β×TR + decay×RMA) | 2 | 64 |
|
||||
| **Total FMA savings** | **~4 cycles** | **~64 cycles** |
|
||||
|
||||
**Batch efficiency (512 bars):**
|
||||
|
||||
| Mode | Cycles/bar | Total (512 bars) | Improvement |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Scalar streaming | 68 | 34,816 | — |
|
||||
| FMA streaming | 64 | 32,768 | **6%** |
|
||||
|
||||
Limited improvement due to IIR recursion dependencies.
|
||||
|
||||
### Quality Metrics
|
||||
|
||||
| Metric | Score | Notes |
|
||||
| :--- | :---: | :--- |
|
||||
| **Accuracy** | 9/10 | Warmup compensation provides early accuracy |
|
||||
| **Timeliness** | 7/10 | EMA responds faster than SMA; still lags trend changes |
|
||||
| **Overshoot** | 8/10 | ATR is stable; minimal overshoot vs std dev bands |
|
||||
| **Smoothness** | 8/10 | EMA + RMA produce smooth, continuous bands |
|
||||
*Note: Recursive filters (EMA, ATR) cannot be fully vectorized, but the final band projection and TR calculation benefit from SIMD.*
|
||||
|
||||
## Validation
|
||||
|
||||
| Library | Status | Notes |
|
||||
| :--- | :---: | :--- |
|
||||
| **TA-Lib** | N/A | No Keltner implementation |
|
||||
| **Skender** | ✅ | Structural match; minor divergence during warmup |
|
||||
| **Tulip** | N/A | No Keltner implementation |
|
||||
| **Ooples** | ❔ | Implementation exists; not fully validated |
|
||||
| **PineScript** | ✅ | Reference implementation match |
|
||||
| **TA-Lib** | N/A | No direct implementation |
|
||||
| **Skender** | ✅ | Matches `GetKeltnerChannels` |
|
||||
| **TradingView** | ✅ | Matches standard "Keltner Channels" indicator |
|
||||
| **Pandas-TA** | ✅ | Matches `ta.kc` |
|
||||
|
||||
Skender's implementation uses a different warmup approach (SMA seeding for initial values), causing 2-4% divergence during the first ~period bars. After warmup, values converge within floating-point tolerance.
|
||||
*Note: Minor startup divergence may occur due to different warmup seeding strategies.*
|
||||
|
||||
## Common Pitfalls
|
||||
## Usage & Pitfalls
|
||||
|
||||
1. **Warmup Period**: Keltner requires `period × 2` bars before `IsHot` becomes true. The ATR component needs its own warmup on top of the EMA warmup. Using the indicator before full warmup produces less accurate values (though warmup compensation minimizes this).
|
||||
- **TTM Squeeze**: When Bollinger Bands move inside Keltner Channels, volatility is compressed. Watch for the squeeze release.
|
||||
- **Trend Following**: In strong trends, use the middle band (EMA) as trailing support/resistance. Price respecting the EMA confirms trend continuation.
|
||||
- **EMA vs SMA**: The EMA middle band reacts faster than an SMA-based center. This reduces lag but may produce more whipsaws in choppy markets.
|
||||
- **ATR Warmup**: Wilder's smoothing has infinite memory—ATR requires significant warmup (~50+ bars) to fully stabilize. Early values may differ from other implementations.
|
||||
- **Multiplier Selection**: 2.0× ATR is standard for daily charts. Consider 1.5× for intraday or 2.5× for weekly timeframes.
|
||||
- **Gap Sensitivity**: ATR includes gaps, so a large overnight gap will widen the channel. This is feature, not bug—it reflects actual volatility.
|
||||
|
||||
2. **ATR vs. Standard Deviation**: Keltner uses ATR (absolute range including gaps); Bollinger uses standard deviation (statistical dispersion). They're not interchangeable—ATR is more stable for gap-heavy instruments like futures or weekend-gapping equities.
|
||||
## API
|
||||
|
||||
3. **RMA vs. EMA for ATR**: True ATR uses Wilder's RMA smoothing ($\alpha = 1/\text{period}$), not EMA ($\alpha = 2/(\text{period}+1)$). Using EMA for ATR produces faster-reacting but less smooth bands.
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Kchannel {
|
||||
+Name : string
|
||||
+WarmupPeriod : int
|
||||
+Upper : TValue
|
||||
+Lower : TValue
|
||||
+Last : TValue
|
||||
+IsHot : bool
|
||||
+Update(TBar bar) TValue
|
||||
+Update(TBarSeries source) TSeries
|
||||
}
|
||||
```
|
||||
|
||||
4. **Multiplier Sensitivity**: The default multiplier of 2.0 places bands at ±2 ATR. Changing to 1.5 or 3.0 dramatically alters signal frequency. Backtest your multiplier choice—don't assume the default is optimal.
|
||||
### Class: `Kchannel`
|
||||
|
||||
5. **Gap Handling**: ATR explicitly handles gaps via true range. On gap-up, TR includes $|H_t - C_{t-1}|$, expanding the channel. This is intentional—gaps represent volatility that SMA-based channels ignore.
|
||||
| Parameter | Type | Default | Range | Description |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| `period` | `int` | `20` | `>0` | Lookback for EMA and ATR. |
|
||||
| `multiplier` | `double` | `2.0` | `>0` | ATR multiplier for band width. |
|
||||
| `source` | `TBarSeries` | — | `any` | Initial input (optional). |
|
||||
|
||||
6. **Memory Footprint**: The implementation stores minimal state—just the running sums/weights for EMA and ATR. Approximately 64 bytes per instance. For 5,000 symbols, budget ~320 KB.
|
||||
### Properties
|
||||
|
||||
7. **Bar Correction (isNew=false)**: When correcting the current bar, the indicator restores the previous state and recalculates. State consists of 6 scalar values—efficient to copy and restore.
|
||||
- `Last` (`TValue`): The Middle Band (EMA) value.
|
||||
- `Upper` (`TValue`): The Upper Keltner Channel.
|
||||
- `Lower` (`TValue`): The Lower Keltner Channel.
|
||||
- `IsHot` (`bool`): Returns `true` after `period` bars.
|
||||
|
||||
### Methods
|
||||
|
||||
- `Update(TBar bar)`: Updates the indicator with OHLC data and returns the Middle band.
|
||||
- `Update(TBarSeries source)`: Batch processes a bar series.
|
||||
- `Reset()`: Clears all historical data.
|
||||
|
||||
## C# Example
|
||||
|
||||
```csharp
|
||||
using QuanTAlib;
|
||||
|
||||
// Initialize with standard settings (20, 2.0)
|
||||
var kchannel = new Kchannel(period: 20, multiplier: 2.0);
|
||||
|
||||
// Update Loop
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
var result = kchannel.Update(bar);
|
||||
|
||||
if (kchannel.IsHot)
|
||||
{
|
||||
Console.WriteLine($"{bar.Time}: Mid={result.Value:F2} Upper={kchannel.Upper.Value:F2} Lower={kchannel.Lower.Value:F2}");
|
||||
|
||||
// Trend Confirmation
|
||||
if (bar.Close > kchannel.Upper.Value)
|
||||
Console.WriteLine(" Strong Uptrend (Above Keltner)");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- Keltner, C. W. (1960). *How to Make Money in Commodities*. The Keltner Statistical Service.
|
||||
- Raschke, L. B. (1995). "Keltner Channel." *Technical Analysis of Stocks & Commodities*.
|
||||
- Wilder, J. W. (1978). *New Concepts in Technical Trading Systems*. Trend Research.
|
||||
- TradingView. (2024). "Keltner Channels." Pine Script Reference Manual.
|
||||
- Keltner, C. (1960). *How To Make Money in Commodities*. The Keltner Statistical Service.
|
||||
- Raschke, L.B. & Connors, L.A. (1995). *Street Smarts: High Probability Short-Term Trading Strategies*.
|
||||
- Wilder, J.W. (1978). *New Concepts in Technical Trading Systems*. Trend Research.
|
||||
|
||||
Reference in New Issue
Block a user