- Tie-breaking: last occurrence wins (most recent bar, `<=` comparison).
- Cross-validation: `source[Minindex.Batch[i]] == Lowest.Batch[i]` for all bars after warmup.
MININDEX identifies the position of the minimum value within a rolling window. While LOWEST tells you the trough *value*, MININDEX tells you *where* that trough is relative to the current bar. This is essential for support analysis, timing studies, and detecting how "stale" a low is.
## Historical Context
The MININDEX function originates from TA-Lib (TA_MININDEX), used in quantitative trading systems to identify when the lowest price in a lookback window occurred. This timing information is critical for:
- **Support freshness**: A min at position 0 means support is being tested *now*; at position period-1, the low is stale and potentially irrelevant.
- **Pattern detection**: Identifying double bottoms, inverse head-and-shoulders, and other formations requires knowing *when* troughs occurred.
- **Exhaustion analysis**: The position of the low within the window indicates whether selling pressure is current or historical.
## Architecture & Physics
### 1. Streaming Mode — Bars-Ago Offset
In streaming mode, the output represents how many bars ago the minimum occurred:
$$
\text{Minindex}_t = t - \arg\min_{t-n+1 \leq k \leq t} V_k
$$
where $n$ is the lookback period. A value of 0 means the current bar is the minimum; a value of $n-1$ means the oldest bar in the window holds the minimum.
### 2. Batch Span Mode — Absolute Index
In the `Batch(ReadOnlySpan)` method, output is the absolute array index:
$$
\text{output}[i] = \arg\min_{i-n+1 \leq k \leq i} V_k
$$
This matches TA-Lib's MININDEX convention and enables direct array lookup: `source[output[i]]` yields the minimum value.
### 3. Tie-Breaking
When multiple values in the window are equal to the minimum, the **most recent** (rightmost) occurrence wins:
$$
\text{Minindex}_t = \max \{ k : V_k = \min(\text{window}) \}
$$
This is achieved using `<=` comparison, matching TA-Lib behavior.
### 4. Monotonic Deque (Batch Mode)
The batch span method uses the same O(n) monotonic deque algorithm as Lowest, but outputs the index stored at the deque head rather than the value at that index:
```
// Lowest: output[i] = values[deque.PeekHead()] → the VALUE
// Minindex: output[i] = deque.PeekHead() → the INDEX
```
### 5. Bar Correction via Rollback
When `isNew=false`, the indicator:
1. Restores previous state (`_state = _p_state`)
2. Replaces the last value in the buffer
3. Re-scans the buffer to find the new minimum position
## Mathematical Foundation
### Rolling Minimum Index Definition
$$
\text{Minindex}_t = \arg\min_{t-n+1 \leq k \leq t} V_k
$$
where $n$ is the lookback period and ties are broken in favor of the most recent occurrence.
### Partial Window Behavior
Before the window is full:
$$
\text{Minindex}_t = \arg\min_{0 \leq k \leq t} V_k \quad \text{for } t < n
$$
### Complexity Analysis
| Operation | Streaming | Batch (Deque) |
| :--- | :---: | :---: |
| Per-update (worst) | O(n) | O(n) |
| Per-update (amortized) | O(n) | O(1) |
| Total for N updates | O(N×n) | O(N) |
Streaming uses a linear scan of the RingBuffer, which is O(period) per bar — acceptable for typical periods (5–30). Batch mode uses the monotonic deque for O(1) amortized.
## Performance Profile
### Streaming Mode (Linear Scan)
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
| CMP (scan) | period | 1 | period |
| Array access | period | 3 | 3×period |
| Index arithmetic | 2 | 1 | 2 |
| **Total** | — | — | **~4×period cycles** |
### Batch Mode (Monotonic Deque)
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
| CMP (expired check) | 1 | 1 | 1 |
| CMP (monotonicity) | ~2 avg | 1 | 2 |
| Array access | 3 | 3 | 9 |
| Index arithmetic | 2 | 1 | 2 |
| **Total** | **~8** | — | **~14 cycles** |
### Quality Metrics
| Metric | Score | Notes |
| :--- | :---: | :--- |
| **Accuracy** | 10/10 | Exact index of minimum |
| **Timeliness** | 10/10 | Zero lag for index detection |