Complete thin Dx-composition wrapper indicators with full test coverage: - PlusDi/MinusDi: Directional Indicator wrappers (DiPlus/DiMinus from Dx) - PlusDm/MinusDm: Directional Movement wrappers (DmPlus/DmMinus from Dx) - Individual validation tests per indicator directory (TALib, Skender, bounds) - Combined unit tests (DiDm.Tests.cs) and validation tests (DiDm.Validation.Tests.cs) - Quantower wrappers + tests for all 4 indicators - PineScript v6 implementations with compensated RMA - Normalized .md documentation for all indicators and categories - 182 tests passing, 0 failures
7.4 KiB
QUANTILE: Rolling Quantile
The quantile function is the inverse of the distribution function.
| Property | Value |
|---|---|
| Category | Statistic |
| Inputs | Source (close) |
| Parameters | period, quantileLevel (default 0.25) |
| Outputs | Single series (Quantile) |
| Output range | Varies (see docs) |
| Warmup | period bars |
| PineScript | quantile.pine |
- The Rolling Quantile computes the value below which a given fraction of observations fall within a sliding window.
- Parameterized by
period,quantilelevel(default 0.25). - Output range: Varies (see docs).
- Requires
periodbars of warmup before first valid output (IsHot = true). - Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Introduction
The Rolling Quantile computes the value below which a given fraction of observations fall within a sliding window. It is mathematically identical to Percentile but uses the statistician's convention of q ∈ [0, 1] instead of the analyst's p ∈ [0, 100]. When q=0.5, it returns the median; q=0 gives the minimum; q=1 gives the maximum. The linear interpolation method matches Excel's PERCENTILE.INC and PineScript's ta.percentile_linear_interpolation conventions (Hyndman-Fan Method 7).
Historical Context
Francis Galton introduced percentiles in 1885. The quantile formulation (0 to 1) gained dominance in mathematical statistics because it maps directly to cumulative distribution functions. In practice, the two are interchangeable: quantile q = percentile(100q). The choice between them is a matter of API convention, not mathematics. Trading platforms tend to use percentiles (0-100 range, more intuitive for non-statisticians); statistical libraries prefer quantiles (0-1 range, composable with CDFs and probability calculations).
Our implementation provides both: Percentile for the 0-100 convention, Quantile for the 0-1 convention. They share identical algorithms.
Architecture and Physics
1. Sorted Buffer Maintenance
Each Update call:
- Remove the oldest value from the sorted buffer (if window full): O(log N) search + O(N) shift.
- Insert the new value into sorted position: O(log N) search + O(N) shift.
- Compute the quantile via linear interpolation: O(1).
Total per-update cost: O(N) for the array shifts, dominated by the Array.Copy operations.
2. Linear Interpolation (Hyndman-Fan Method 7)
For sorted values x_0, x_1, \ldots, x_{n-1} and quantile level q \in [0, 1]:
\text{rank} = q \cdot (n - 1)
\text{result} = x_{\lfloor r \rfloor} + (r - \lfloor r \rfloor) \cdot (x_{\lceil r \rceil} - x_{\lfloor r \rfloor})
where r = \text{rank}.
Boundary cases:
q = 0: returnsx_0(minimum)q = 1: returnsx_{n-1}(maximum)n = 1: returns the single value regardless ofq
3. Bar Correction
State rollback uses _p_sortedBuffer backup arrays, identical to the Percentile, Median, and IQR pattern. When isNew=false, the sorted buffer is restored from the backup before applying the correction.
Mathematical Foundation
The quantile function Q(q) for a discrete sample using Hyndman-Fan Method 7:
Q(q) = (1 - g) \cdot x_j + g \cdot x_{j+1}
where:
j = \lfloor q \cdot (n-1) \rfloorg = q \cdot (n-1) - j(fractional part)
This is equivalent to the FMA form used in implementation:
Q(q) = \text{FMA}(g, x_{j+1} - x_j, x_j)
Relationship to Percentile: Q(q) = P(100q) where P is the percentile function.
Performance Profile
Operation Count (Streaming Mode)
Quantile uses the same sorted-buffer approach as Percentile, with fraction [0,1] mapped to sorted indices.
| Operation | Count | Cost (cycles) | Subtotal |
|---|---|---|---|
| Ring buffer evict oldest | 1 | 3 cy | ~3 cy |
| Binary search + array shift insert | log N + N/2 | 2 cy | ~N cy |
| Rank interpolation (linear) | 1 | 3 cy | ~3 cy |
| NaN guard + state update | 1 | 2 cy | ~2 cy |
| Total (N=20) | O(N) | — | ~28 cy |
O(N) per update. Linear interpolation between adjacent order statistics matches the standard R-7 quantile method used by NumPy and R by default.
| Operation | Cost | Notes |
|---|---|---|
| BinarySearch | O(log N) | Array.BinarySearch for insert/remove position |
| Array.Copy (shift) | O(N) | Dominates update cost |
| Interpolation | O(1) | Single FMA operation |
| Bar correction | O(N) | Array.Copy for buffer backup/restore |
| Memory | O(2N) | Sorted buffer + backup buffer |
| Quality | Score (1-10) |
|---|---|
| Precision | 10 — exact within IEEE 754 double precision |
| Latency | 7 — O(N) per update, fast for typical periods (5-50) |
| Memory | 8 — two double arrays + RingBuffer |
| Robustness | 9 — NaN/Infinity guarded, bar correction supported |
| SIMD applicability | 2 — comparison-heavy algorithm not vectorizable |
Validation
| Library | Match | Notes |
|---|---|---|
| PineScript | ✔️ | Source implementation, same linear interpolation |
| Excel PERCENTILE.INC | ✔️ | Same Method 7 interpolation (q = p/100) |
| QuanTAlib Percentile | ✔️ | Cross-validated, Quantile(q) == Percentile(q*100) |
| QuanTAlib Median (q=0.5) | ✔️ | Cross-validated, exact match |
| Wolfram Alpha | ≠ | Uses nearest-rank (Method 1), different by design |
Common Pitfalls
-
Parameter range confusion. Quantile uses q ∈ [0, 1], not [0, 100]. Passing 25 instead of 0.25 will throw
ArgumentException. UsePercentileif you prefer the 0-100 range. -
Interpolation method confusion. Wolfram Alpha, NumPy (
linear), and Excel (PERCENTILE.INC) all use slightly different conventions. Our implementation matches Excel/PineScript (Method 7). Do not validate against Wolfram's nearest-rank results. -
Period=1 edge case. A single value has a defined quantile (itself) for any q in [0, 1]. The implementation handles this correctly.
-
Window not full. Before reaching full period, the quantile is computed over the available values. This gives valid but potentially misleading results during warmup.
-
q=0.5 vs Median. For even-length windows, Quantile(q=0.5) uses linear interpolation which yields the average of two middle values — identical to Median. For odd-length windows, both return the middle value directly.
-
Floating-point accumulation. Since quantile uses direct sorted-buffer access (not running sums), there is no floating-point drift. The result is always computed fresh from the sorted values.
-
Large periods. For period > 256, the span batch implementation uses
ArrayPoolinstead ofstackallocto avoid stack overflow in chained indicator scenarios.
References
- Hyndman, R.J. and Fan, Y. (1996). "Sample Quantiles in Statistical Packages." The American Statistician, 50(4), 361-365.
- Galton, F. (1885). "Some Results of the Anthropometric Laboratory." Journal of the Anthropological Institute, 14, 275-287.
- Microsoft Excel Documentation: PERCENTILE.INC function
- TradingView PineScript Reference: ta.percentile_linear_interpolation