DSL reference¶
The qkt strategy language, explained one construct at a time. Each page covers a single concept — what it does, every variant the parser accepts, examples for each, and what it pairs with.
If you want the one-page cheat sheet, see DSL grammar (one-pager). If you want to learn it properly, start at the top of this page and work down.
Structure of a strategy file¶
-
The STRATEGY block
The outer envelope:
STRATEGY name VERSION n, the SYMBOLS / RULES sections, what's required and what's optional. -
Streams: SYMBOLS
Declaring which markets your strategy listens to. Broker prefixes, symbols, timeframes, multiple streams.
-
Synthetic series
Read-only engine state as candle streams, starting with account equity for indicator-based meta-risk filters.
-
Option chain analytics
Implied volatility and 25-delta skew of a stored option chain, at any tenor, as read-only streams.
-
Option structures
Multi-leg option positions chosen from the chain by delta and days to expiry, margined and unwound as one.
-
LET and DEFAULTS
Naming values so you can reuse them. The two ways:
LET(per-strategy aliases) andDEFAULTS(action defaults).
Conditions — when to act¶
-
The WHEN clause
The "if" half of every rule. Edge-triggered vs level-triggered, combining conditions with AND/OR/NOT.
-
Indicators
Every indicator the parser knows — ema, sma, rsi, atr, vwap, macd, bollinger, donchian (highest/lowest), and the math helpers.
-
Expressions
Arithmetic, comparisons, account references (
account.equity), position references (POSITION.stream). -
SEQUENCE
Ordered multi-stage setups with per-stage timeouts, snapshots, and a one-pass completion edge.
Actions — what to do¶
-
BUY / SELL / CLOSE / CANCEL / LOG
The action verbs. What each does, what they accept, how to combine them.
-
SIZING
Every way to size a position: fixed lots, percent of equity, fixed USD, risk-based, full-position close.
-
:material-bracket-arrow-down:{ .lg .middle } BRACKET
Atomic stop-loss + take-profit groups. Fixed prices, percent offsets, ATR-based, scale-out targets.
-
STACK pyramiding
Layered entries — one signal becomes N price-triggered orders. Time fences, custom per-layer sizing.
-
STACK_AT conditional stacks
Fire independent micro-trades when the primary leg's MFE crosses thresholds within time windows. Each stack tracks as its own leg with its own bracket.
-
TIMES repeated entries
Emit one entry N times in a single evaluation, each its own ticket and bracket. The count is an expression, so it can follow conviction, volatility, or the account balance.
-
EXIT AFTER timed exits
Close an entry a fixed time after it fills, checked on every tick rather than at bar close. Applies to its
STACK_ATlegs too. -
OTO one-triggers-other
Place child orders only when a parent fills (
ON_FILL). Children can be a different symbol, the opposite side, and priced relative to the parent fill viaentry— legged hedges, scale-ins, reversals.
Looping and composition¶
-
FOR EACH
Apply the same rule body to multiple streams. AST-level expansion at compile time.
-
PORTFOLIO files
Composing N strategies into a portfolio with regime-gated activation.
IMPORT,RUN,HOLD. -
BASKET synthetic instruments
Bind several streams into one tradeable pseudo-stream. An equal-weight log-return index you can read, and BUY/SELL/CLOSE that fan out to one equal-notional order per constituent.
How qkt parses your strategy¶
When you run qkt parse strategy.qkt, the compiler walks the file in this order:
- Header —
STRATEGY name VERSION norPORTFOLIO ... - DEFAULTS (optional) — captures default values for all later actions
- SYMBOLS — declares every stream the strategy listens to
- PARAM (optional) — number, boolean or string strategy inputs and their portfolio overrides
- LET (optional) — name-bound expressions for reuse
- SCHEDULE (optional) — clock-driven actions
- SEQUENCE (optional) — ordered setup state machines
- RULES — pairs of
WHEN <condition> THEN <action> - FOR EACH (optional, inside RULES, interleaved with
WHENrules) — macro expansion that emits one rule per listed stream
qkt parse then compiles the file, so an unknown indicator, function, stream alias or reference
is reported too — the same diagnostics the language server shows in an editor. Errors are
line/column tagged. A typo in WHEN or a missing THEN produces a clear error pointing to the
line, not a cryptic stack trace.
STRATEGY typed_params VERSION 1
SYMBOLS
btc = BACKTEST:BTCUSDT EVERY 1m
PARAM threshold = -0.5 -- number; a leading minus is part of the literal
PARAM armed = TRUE -- boolean
PARAM label = "breakout" -- string
RULES
WHEN armed AND btc.close - btc.open > threshold
THEN LOG "signal" kind=label
Quick legal/illegal¶
-- legal: minimum valid strategy
STRATEGY hello VERSION 1
SYMBOLS
btc = BACKTEST:BTCUSDT EVERY 1m
RULES
WHEN btc.close > 0
THEN LOG "tick received"
-- illegal: SYMBOLS must come before RULES
STRATEGY hello VERSION 1
RULES
WHEN btc.close > 0 THEN BUY btc
SYMBOLS
btc = BACKTEST:BTCUSDT EVERY 1m
-- parse error: SYMBOLS must come before RULES (line 5)
-- illegal: missing VERSION
STRATEGY hello
SYMBOLS ...
-- parse error: expected VERSION, got 'SYMBOLS' (line 2)
The parser is strict by design. A strategy file that compiles is one where the engine knows exactly what to do — there's no "interpret loosely and hope" mode.
See also¶
- Examples — every DSL feature used in a real strategy
- Recipes — task-oriented walkthroughs
- CLI commands —
qkt parse,qkt backtest, etc.