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
| Field | Default | What 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. |
| qty | 1 | Contracts. Floored and clamped to at least 1. |
| stopTicks | none | Loss-side exit distance in ticks from the fill price. |
| targetTicks | none | Profit-side exit distance in ticks. |
| null | — | No 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.
Related pages
- Running a strategy on the simulated account
- Script template gallery
- Python scripts (Pyodide engine)
- Backtest
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.