Scripting: writing a strategy

A strategy script defines decide(bars, params, ctx, state) and returns one decision per closed bar. The decision drives the simulated evaluation account and nothing else.

Senzoukria · Documentation · Updated September 2026


Where to find it

Where
Scripting page → New strategy
Entry point
decide(bars, params, ctx, state), returning a decision or null
Cadence
Once per closed bar — the forming bar is never passed
Account
The simulated evaluation account on this machine; the platform routes no orders to a broker
Languages
JavaScript and Python share the same entry point and the same decision shape

What it does

A strategy is run through the same sandbox as an indicator. A short wrapper is appended after your code — after, so your line numbers stay the ones you see in the editor — which calls decide(), normalises what comes back and carries the state object to the next bar.

If decide() is not defined, the run fails with a message saying a strategy must define decide(bars, params, ctx, state). If it returns undefined or null, that is a valid answer meaning "no trade on this bar", and the run ribbon reports "No trade on this sample".

The context carries the simulated position, a flat flag and the tick size, so distances are expressed in ticks and the same script works on a different contract without editing.

The decision object

What decide() may return
FieldDefaultWhat it changes
action: "buy"Opens a long at market on the simulated account, subject to the account guards.
action: "sell"Opens a short at market, subject to the same guards.
action: "close"Flattens the open position; harmless when already flat.
qty1Contracts. Floored and clamped to at least 1.
stopTicksnoneLoss-side exit distance in ticks from the fill price.
targetTicksnoneProfit-side exit distance in ticks.
nullNo trade on this bar.
state{}Anything you store on the state object is handed back on the next bar; it is cleared when the script changes.

How to use it

  • Start from the Breakout strategy template: it buys a break of the N-bar high with a fixed stop and target, and shows how to use the carried state to count the bars a position has been held.
  • Run on the sample data first — a strategy run reports whether a decision was returned at all, which is the fastest way to find a condition that never fires.
  • Then mount it in Replay's Auto strategy panel to watch it decide bar by bar on real replayed data.
  • Express risk in ticks, not in prices: the tick size arrives in the context and the account translates the distance into an exit level.

Limits and pitfalls

  • The account guards are applied before any entry: a breached drawdown, a hit daily loss limit or an exceeded contract cap refuses it, and the panel shows the reason.
  • A return value that is not a recognised decision is reported as such, with the accepted shapes named.
  • Because decisions are taken on closed bars, a strategy cannot act on an intrabar move — this is what keeps a live sim run and a backtest of the same code comparable.
  • No script can reach a broker. A strategy here is a paper-trading strategy, and no result it produces is a promise about live trading.

This page in other languages

Frequently asked questions

Can a strategy be written in Python?
Yes. The wrapper exists in each language and is appended in the language of the script, so a Python strategy gets a Python wrapper. Write the decision keys as stopTicks and targetTicks: the Auto strategy panel reads those. The Python backtest loop also accepts stop_ticks and target_ticks.
How do I keep a value between bars?
Write it on the state object passed as the fourth argument. It is handed back on the next call and reset when you switch scripts.
Why was my buy refused?
Either an account guard blocked it — the reason is printed — or no live price was available for the symbol, in which case the entry waits rather than inventing a fill.

Keep reading