DSL capability-gap batch¶
Status: shipped to dev. Spec: docs/superpowers/specs/2026-06-21-dsl-capability-gaps-design.md.
Plan: docs/superpowers/plans/2026-06-21-dsl-capability-gaps.md.
Summary¶
Closes a batch of capability-gap issues filed by qkt-forge. Five were real engine gaps and
are now built; three were already expressible (forge's authoring grammar was stale) and are
proven with compile tests. Everything is deterministic and backtest=live, following the
existing FuncRegistry / IndicatorRegistry / multi-series patterns — no new AST node.
What's new¶
Scalar functions (FuncRegistry):
mod(a, b)— floored modulo; the round-number / grid-distance primitive.floor(x),ceil(x),round(x)— integer rounding (roundis half-to-even, the Money convention).
Rolling indicators (IndicatorRegistry):
percentile_rank(value, lookback)— distribution-free rolling rank, for bimodal regime gates.vwap_session(stream, anchorHour)— session-anchored volume-weighted average price.vwap_session_stdev(stream, anchorHour)— volume-weighted band stddev around the session VWAP.session_range_high(stream, sh, sm, eh, em)/session_range_low(...)— latched high/low of a prior completed UTC window, held as constant price levels.
Multi-series (compiler path, like resid):
confirm_ratio(signal, peer1, …, lookback)— fraction of peers whose return confirms the signal's direction; negate a peer for inverse-pair polarity.
Already expressible (no engine change)¶
- Minute-of-hour / top-of-hour gate →
NOW.MINUTE_UTC = 0/>= 4. - NR7 / inside-bar →
.high/.lowwithHIGHEST/LOWESTover a range expression and the built-in one-bar lag (HIGHEST(g.high, 1)= yesterday's high). - Rejection wick →
g.high - (CASE WHEN g.open > g.close THEN g.open ELSE g.close END). - Computed set-once entry sizing with a bracket (#818) → numeric sizing forms carry an
expression; compose bracket-derived inverse volatility and
CASEconviction tiers directly onBUY/SELL. - Extreme-reaction fade (#822) →
lag,abs,percentile_rank, andPOSITION.<stream>.holding_durationcompose into the executable fixed-elapsed-horizon recipe. Exact-tick execution correctly does not fill a close-derived signal at the already-completed signal-bar close. - Hour-drift continuation (#823) →
NOW.HOUR_UTC,lag, andPOSITION.<stream>.holding_durationcompose into the executable 30-minute recipe with the same post-close fill boundary. - Completed-session fades (#833, #835) →
session_range_high/session_range_loware evaluable inWHENexpressions after their one-day warmup. Replay tests exercise both directions against a completed 13:00–21:00 UTC range. An observed four trades in a short reproduction window is a research sample-size result, not an unsupported trigger. - Extreme-reaction fade (#834) → duplicate reproduction report for #822. The existing percentile-rank and fixed-elapsed-hold compile test remains the capability proof.
- Fixed-horizon ratio recipes (#836, #837, #838, #839) → cross-stream division feeds
zscoredirectly, signed thresholds select continuation or fade direction, andPOSITION.<stream>.holding_durationsupplies the 4-, 5-, or 12-bar elapsed exit. - Lower-tail return-shock fade (#840) → the relative return expression feeds
percentile_rankdirectly, and the same holding-duration accessor supplies the four-bar elapsed exit.
Compilation and trigger evaluation prove that qkt can represent and execute these recipes. They do not prove that a reported research edge survives the executable post-close fill model; trade count and PnL remain research-validation outcomes rather than DSL capability criteria.
Usage¶
See docs/reference/dsl/indicators.md (sections: Confirmation ratio, Session-anchored
indicators, Percentile rank, Math helpers) for worked, copy-pasteable examples.
Known limitations¶
- Session-VWAP needs per-bar volume. The MT5 gateway reports no volume for FX/metals, so a
session-VWAP strategy is backtest-faithful but inert live on MT5 until the gateway forwards
tick volume — the same limitation the existing
vwap/obvcarry. max(a, b)/min(a, b)are windowed aggregates, not scalar two-argument functions; use aCASEexpression for the larger/smaller of two values.- Session-range mid/width are not separate names — they compose:
(high+low)/2,high-low.
References¶
- Issues: #479, #501, #502, #503, #504, #505, #506, #507, #508, #818, #822, #823, #833, #834, #835, #836, #837, #838, #839, #840.
- Indicators:
PercentileRank,SessionVwap,SessionRange,ConfirmRatioundercom.qkt.indicators.catalog;MOD/FLOOR/CEIL/ROUNDincom.qkt.dsl.stdlib.FuncRegistry.