mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-19 11:08:05 +00:00
- 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.
209 lines
8.9 KiB
Markdown
209 lines
8.9 KiB
Markdown
# MMCHANNEL: Min-Max Channel
|
||
|
||
> "The market's true range isn't about averages. It's about extremes—and who's winning."
|
||
|
||
Min-Max Channel (MMCHANNEL) tracks the highest high and lowest low over a lookback period, creating a pure price envelope without any midpoint calculation. Unlike Donchian Channels which include a middle band, MMCHANNEL delivers only the raw extremes—exactly what breakout traders and range analysis need. This implementation uses monotonic deques for O(1) amortized updates, making it suitable for high-frequency applications and long lookback periods.
|
||
|
||
## Historical Context
|
||
|
||
Min-Max channels represent the simplest form of price envelope analysis, predating most technical indicators. The concept is intuitive: track where price has been at its highest and lowest points over a defined period.
|
||
|
||
The approach gained prominence through Richard Donchian's work in the 1960s and later through the Turtle Trading system. While Donchian Channels include a midpoint average, MMCHANNEL strips this away, focusing purely on support and resistance levels defined by actual price extremes.
|
||
|
||
Most implementations suffer from O(n) complexity per update—scanning the entire window to find max/min values. For period=200 on tick data, this means 200 comparisons per tick. QuanTAlib uses monotonic deques that maintain sorted order implicitly, achieving O(1) amortized updates regardless of period length.
|
||
|
||
## Architecture & Physics
|
||
|
||
MMCHANNEL consists of two components: the upper band (highest high) and lower band (lowest low).
|
||
|
||
### 1. Upper Band (Highest High)
|
||
|
||
Tracks the maximum high price over the lookback window using a decreasing monotonic deque:
|
||
|
||
$$
|
||
U_t = \max_{i=0}^{n-1}(H_{t-i})
|
||
$$
|
||
|
||
where $H$ is the high price and $n$ is the period. New highs immediately update the upper band; the band only decreases when the previous maximum exits the lookback window.
|
||
|
||
**Monotonic deque invariant:** Elements are stored in decreasing order by value. The front element is always the maximum.
|
||
|
||
### 2. Lower Band (Lowest Low)
|
||
|
||
Tracks the minimum low price over the lookback window using an increasing monotonic deque:
|
||
|
||
$$
|
||
L_t = \min_{i=0}^{n-1}(L_{t-i})
|
||
$$
|
||
|
||
where $L$ is the low price. New lows immediately update the lower band; the band only increases when the previous minimum exits the window.
|
||
|
||
**Monotonic deque invariant:** Elements are stored in increasing order by value. The front element is always the minimum.
|
||
|
||
## Mathematical Foundation
|
||
|
||
### Monotonic Deque Algorithm
|
||
|
||
The key insight is maintaining sorted order without explicit sorting:
|
||
|
||
**For maximum (upper band):**
|
||
|
||
1. **Back removal:** Remove elements from the back that are ≤ the new value
|
||
2. **Insert:** Add the new (value, index) pair to the back
|
||
3. **Front expiry:** Remove elements from the front whose indices are outside the window
|
||
4. **Query:** The front element is always the maximum
|
||
|
||
**For minimum (lower band):**
|
||
|
||
1. **Back removal:** Remove elements from the back that are ≥ the new value
|
||
2. **Insert:** Add the new (value, index) pair to the back
|
||
3. **Front expiry:** Remove elements from the front whose indices are outside the window
|
||
4. **Query:** The front element is always the minimum
|
||
|
||
**Amortized Analysis:**
|
||
|
||
Each element enters the deque exactly once and leaves at most once (either from the back during insertion or from the front during expiry). Over $n$ operations, total work is $O(n)$, yielding $O(1)$ amortized per update.
|
||
|
||
### Channel Width
|
||
|
||
The distance between bands measures the price range:
|
||
|
||
$$
|
||
W_t = U_t - L_t
|
||
$$
|
||
|
||
Channel width indicates volatility: wider channels suggest larger price swings; narrower channels indicate consolidation.
|
||
|
||
## Performance Profile
|
||
|
||
### Operation Count (Streaming Mode, Scalar)
|
||
|
||
Per-bar cost using monotonic deque optimization:
|
||
|
||
| Operation | Count | Cost (cycles) | Subtotal |
|
||
| :--- | :---: | :---: | :---: |
|
||
| CMP (deque maintenance) | ~4 | 1 | ~4 |
|
||
| Memory access (deque) | ~4 | 3 | ~12 |
|
||
| **Total** | **~8** | — | **~16 cycles** |
|
||
|
||
**Complexity:** O(1) amortized per bar. Worst case O(n) occurs only when a monotonically increasing (for max) or decreasing (for min) sequence forces clearing the entire deque—rare in practice.
|
||
|
||
### Batch Mode (512 values, SIMD/FMA)
|
||
|
||
Sliding window max/min has limited SIMD benefit due to sequential dependency in deque operations:
|
||
|
||
| Operation | Scalar Ops | SIMD Benefit | Notes |
|
||
| :--- | :---: | :---: | :--- |
|
||
| Deque update | ~8 | 1× | Sequential by nature |
|
||
| Index comparison | 2 | 2× | SIMD possible for batch |
|
||
|
||
**Batch efficiency (512 bars):**
|
||
|
||
| Mode | Cycles/bar | Total (512 bars) | Improvement |
|
||
| :--- | :---: | :---: | :---: |
|
||
| Scalar streaming | 16 | 8,192 | — |
|
||
| Partial SIMD | ~14 | ~7,168 | **~12%** |
|
||
|
||
The monotonic deque algorithm is already highly efficient; SIMD provides marginal gains.
|
||
|
||
### Quality Metrics
|
||
|
||
| Metric | Score | Notes |
|
||
| :--- | :---: | :--- |
|
||
| **Accuracy** | 10/10 | Exact max/min calculation |
|
||
| **Timeliness** | 6/10 | Tracks past extremes, inherently lagging |
|
||
| **Overshoot** | 10/10 | No overshoot—bands are actual price levels |
|
||
| **Smoothness** | 4/10 | Bands move in discrete steps as extremes exit window |
|
||
|
||
## Validation
|
||
|
||
| Library | Status | Notes |
|
||
| :--- | :---: | :--- |
|
||
| **Dchannel** | ✅ | Exact match for upper/lower bands |
|
||
| **Skender** | ✅ | Exact match via Donchian upper/lower |
|
||
| **TA-Lib** | ✅ | Exact match via MAX/MIN functions |
|
||
| **Tulip** | ✅ | Exact match via max/min functions |
|
||
|
||
## Usage & Pitfalls
|
||
|
||
- **Stale Extremes:** The bands stay flat until a new extreme occurs or the old extreme exits the window. A band that hasn't moved in 15 bars isn't broken—it's waiting for price to exceed the current extreme or for that extreme to age out.
|
||
- **O(n) Implementation Trap:** Naive implementations rescan the window every bar. For period=200 on 60,000 bars/day, that's 12 million comparisons per symbol. The monotonic deque approach reduces this to ~120,000 operations.
|
||
- **Breakout vs. Touch:** Price touching the upper band differs from breaking out. True breakouts require closes above/below the band. Intrabar spikes that don't close outside the channel often reverse.
|
||
- **No Middle Band:** Unlike Donchian Channels, MMCHANNEL has no middle line. If you need a centerline, use Donchian or compute `(Upper + Lower) / 2` separately.
|
||
- **Asymmetric Movement:** Upper and lower bands move independently.
|
||
- **Gap Handling:** Overnight gaps immediately adjust the relevant band.
|
||
- **Memory Footprint:** The monotonic deque stores (value, index) pairs. Worst case is `2 * period` pairs per deque.
|
||
- **Bar Correction:** When `isNew=false`, the indicator must restore prior state before computing.
|
||
|
||
## API
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class Mmchannel {
|
||
+string Name
|
||
+int WarmupPeriod
|
||
+TValue Last
|
||
+TValue Upper
|
||
+TValue Lower
|
||
+bool IsHot
|
||
+Mmchannel(int period)
|
||
+Mmchannel(TBarSeries source, int period)
|
||
+TValue Update(TBar input, bool isNew)
|
||
+Tuple~TSeries,TSeries~ Update(TBarSeries source)
|
||
+void Prime(TBarSeries source)
|
||
+void Reset()
|
||
+static void Batch(ReadOnlySpan~double~ high, ReadOnlySpan~double~ low, Span~double~ upper, Span~double~ lower, int period)
|
||
+static Tuple~TSeries,TSeries~ Batch(TBarSeries source, int period)
|
||
+static Tuple~Tuple~TSeries,TSeries~,Mmchannel~ Calculate(TBarSeries source, int period)
|
||
}
|
||
```
|
||
|
||
### Class: `Mmchannel`
|
||
|
||
| Parameter | Type | Default | Range | Description |
|
||
| :--- | :--- | :--- | :--- | :--- |
|
||
| `period` | `int` | — | `>0` | Lookback period for highest high and lowest low. |
|
||
|
||
### Properties
|
||
|
||
- `Last` (`TValue`): Returns the upper band value (for single-value compatibility).
|
||
- `Upper` (`TValue`): The highest high over the lookback period.
|
||
- `Lower` (`TValue`): The lowest low over the lookback period.
|
||
- `IsHot` (`bool`): Returns `true` when warmup period is complete.
|
||
|
||
### Methods
|
||
|
||
- `Update(TBar input, bool isNew)`: Updates the indicator with a new bar and returns the result.
|
||
- `Update(TBarSeries source)`: Processes an entire bar series and returns (Upper, Lower) tuple of TSeries.
|
||
- `Prime(TBarSeries source)`: Initializes internal state from historical data.
|
||
- `Reset()`: Resets the indicator to its initial state.
|
||
- `Batch(...)`: Static method for zero-allocation span-based batch processing.
|
||
- `Calculate(TBarSeries source, int period)`: Static factory that returns results and indicator instance.
|
||
|
||
## C# Example
|
||
|
||
```csharp
|
||
using QuanTAlib;
|
||
|
||
// Initialize
|
||
var mmchannel = new Mmchannel(period: 20);
|
||
|
||
// Update Loop
|
||
foreach (var bar in quotes)
|
||
{
|
||
mmchannel.Update(bar, isNew: true);
|
||
|
||
// Use valid results
|
||
if (mmchannel.IsHot)
|
||
{
|
||
Console.WriteLine($"{bar.Time}: Upper={mmchannel.Upper.Value:F2}, Lower={mmchannel.Lower.Value:F2}");
|
||
}
|
||
}
|
||
```
|
||
|
||
## References
|
||
|
||
- Donchian, R. (1960). "High Finance in Copper." *Financial Analysts Journal*, 16(6), 133-142.
|
||
- Faith, C. (2007). *Way of the Turtle: The Secret Methods that Turned Ordinary People into Legendary Traders*. McGraw-Hill.
|
||
- Cormen, T. H., et al. (2009). *Introduction to Algorithms*, 3rd ed. MIT Press. (Monotonic deque analysis)
|