A strategy is a spec — a structured, declarative description with no code in it. You can write that description in plain language and let the platform compile it, or edit the spec directly. Either way you confirm the interpreted spec before anything runs.
The five parts of a spec
- Name — what the strategy is called.
- Universe— the symbol or symbols (up to five, from any mix of markets; one per audit on Free), the timeframe, and the date range. Also the data source and cost class, which the platform fills in from the symbols: each symbol keeps its own market’s data and costs, and each one is its own trial.
- Signals — the indicators the strategy uses, declared once by name, and the per-bar conditions for entering long or short, and optionally for exiting each side.
- Geometry — the stop, the target, the position size, optional rules that move the stop after entry, and optional partial take-profits. Considered separately from the entry: the same entry with a different stop is a different strategy.
- Pre-registration — the floors the run is judged against, fixed before it starts.
Entry and exit conditions
A condition is a per-bar boolean expression over indicator outputs and bar fields (open, high, low, close, volume). The vocabulary is fixed: arithmetic and comparisons; and / or / not; crosses_above / crosses_below; “X for at least N bars”, “X within last N bars”, “bars since X between M and N”; and shift(series, N) for an N-bar-lagged value, with N forced to be zero or positive.
An exit condition closes an open trade on its side. It is checked at each bar’s close and filled at the next bar’s open, never at the close that triggered it. If an entry signal fires on that same bar, the next trade opens at that same open, so a stop-and-reverse system runs as described.
Deliberately absent: variable assignment, if/then/else, function definitions, loops, and indexing a specific past pivot by position. These all tend to encode sequential state, and a language that allows them cannot make the leak audit’s guarantee.
Geometry
Stops can be a fixed distance, a multiple of ATR at entry, or read from a series (a swing level, with an optional buffer and a minimum distance). Targets are usually an R-multiple — a multiple of the actual stop distance — or a fixed distance, an ATR multiple, a structural level, or a value snapshotted at entry. Sizing is a fixed risk of N R per trade by default, where 1 R is the stop distance. An optional timeout force-closes a trade after N bars. A strategy can run without a target when a timeout, a trailing stop or an exit condition closes its winners. Partial take-profits don’t count: they never close the whole position.
Stop management moves the stop after entry: to breakeven (plus an optional offset) once the trade is N R in profit, and/or trailing it — a multiple of ATR behind the best price, along an indicator line, or up in fixed R steps. Each rule is checked at a bar’s close and applied from the next bar, a stop only ever tightens, and R is always measured from the original stop. A trade stopped out after a move is reported separately from an ordinary stop-out.
Scaling out
Partial take-profits close part of a position early, at a price level, and let the rest run. A strategy takes up to three parts, listed nearest first. Each closes a whole percent of the original position (50 is half) at a level written like a target: an R-multiple, a fixed distance, an ATR multiple, a structural level, or a value snapshotted at entry. The parts add up to 99% at most, so the rest still closes on the target, the stop, a trailing stop, an exit condition or the timeout.
- A part fills only when price trades through its level by at least one price increment, never on a touch, and it fills at its level, never better, even when a bar opens past it. The target keeps its own rule: it fills on a touch.
- The increment is the market’s own tick: 0.25 points on
NQ1!, the exchange tick in force on the bar’s date for a crypto perpetual, and the broker’s quote step for FX and CFDs. For US stocks it is one real tick in split-adjusted prices: one cent where no split has happened since, the larger step older prices move in before a reverse split (one real cent shows up as several cents), and never less than a cent at $1 or more. Below $1 it is the step older prices move in, even when a forward split makes that finer than 0.0001. Where the platform doesn’t know an FX or CFD instrument’s tick, it uses 0.2 basis points of the level, which can be less than one real tick. The readback names the increment that applies. - On US stocks, a strategy with partial take-profits must start on or after 2001-04-09. Before that date stocks were quoted in fractions of a dollar, not cents, so no part can be held to one real tick. The same goes for a crypto perpetual the platform has no tick history for (one listed since its tick table was last refreshed): partial take-profits on it are refused until the table is refreshed.
- On a bar that also touches the stop, the stop comes first and no part fills.
- On a trade where a part’s level can’t be computed, isn’t beyond the entry, or isn’t before the next part’s level or the target, that part is skipped and its size stays with the rest. A part never cancels a trade.
- It is still one trade: its R is each piece’s R weighted by its size, measured from the original stop.
Stop management can wait for a part: move the stop to breakeven, or start a trail, once part N has filled. The move applies from the next bar, and on a trade where that part never fills, it never starts.
Pre-registration
Floors are either platform default— derived from the geometry’s implied chance baseline, the sample size, and standard significance thresholds — or user-committed, where you provide them explicitly (Pro). Whichever you choose is recorded with the run and cannot be changed once it starts. See run history & trials.
The three test strategies the DSL is built to express — a trend-follower, a Bollinger-band mean-reversion with a structural stop, and one strategy built on a custom indicator — are the concrete guide to what fits. If a strategy needs something none of them do, the answer is a richer library, not a more permissive DSL.