Strategies overview

View as MarkdownOpen the editor

A strategy is a wrun indicator that places orders. The file declares strategy({ ... }) beside its outputs and calls strategy.long, strategy.exit, strategy.closeAll and the position getters inside onBar(); the chart's Strategy Tester runs it with the engine's own broker, and its trade list, equity curve and stats come out the same wherever it runs. This page is the map: what a strategy file is, what the Strategy Tester shows, the guarantees, and what each page in this section covers.

What a strategy file is

The smallest useful one: long when the fast EMA crosses above the slow one, flat when it crosses back under.

// Moving average cross as a strategy: long when the fast EMA crosses above the slow one, flat when it crosses back under.
strategy({ initialCapital: 10000, qtyType: "percentOfEquity", qtyValue: 50, commissionPercent: 0.05, slippageBps: 2 });
output("fast", line, overlay, { description: "9-period EMA of close" });
output("slow", line, overlay, { description: "21-period EMA of close" });

const fastEma = new Ema(9);
const slowEma = new Ema(21);
const cross = new Cross();

function onBar(): void {
  const fast = fastEma.update(bar.close());
  const slow = slowEma.update(bar.close());
  if (isNaN(fast) || isNaN(slow)) return;
  const crossed = cross.update(fast, slow);
  if (crossed == 1) strategy.long("L").send();
  if (crossed == -1) strategy.closeAll();
  out_fast(fast);
  out_slow(slow);
}
BTCUSDT perpetual on Binance, 1 hour bars, Aug 2 to Aug 18, 2026Real output from OpenMarket's engine

To try it, open the editor from the chart toolbar's Editor button, press New indicator in the Explorer, keep the //@lang=wrun-ts line at the top of the draft and replace the starter under it with this file, then press Backtest. A fuller version of this file is a starter in the editor's template picker: the Explorer's Templates icon ("Browse starter templates") lists it as Moving-Average Cross under Strategies (strategy-ma-cross), with Sma in place of Ema, a ribbon between the averages and a mark on each cross (Moving-average cross), commented throughout.

Three things make it a strategy rather than an indicator:

  • strategy({ ... }) at the top level is the broker: starting equity, sizing (50% of equity per entry), commission and slippage live in the file, so a published strategy carries its own assumptions. Every field is optional; a bare strategy() takes every default. Run derives a strategy section into the sheet, and that section is what makes the file a strategy: once it builds, the editor's toolbar shows a Strategy badge and the Run button reads Backtest.
  • strategy.long("L").send() in onBar() queues a market order under the id L. An order placed on a bar fills no earlier than the next bar's open; nothing in a run can act on information it did not have.
  • strategy.closeAll() is the signal exit. Orders and the position getters (strategy.positionSize() and the rest) both go in onBar(); a getter reads the position after the bar's fills.

The outputs stay outputs: fast and slow draw on the chart like any indicator's, and once the strategy is published they can carry alerts. A strategy adds four alert choices of its own, "Strategy order placed", "Strategy trade opened or closed", "Strategy position" and "Strategy equity" (Alerts), which is why strategy.position and strategy.equity are reserved: an output with either name is refused by name.

What the Strategy Tester shows

The file compiles in your browser, and Backtest puts the draft on the chart's price pane, where its trade markers land; a drawn output on lower gets a pane of its own below. The Strategy Tester docks under the chart with the engine's own result: a trades list (entries, exits, per-trade PnL, fees, exit reason), an equity curve with its drawdown, performance stats (net profit, win rate, profit factor, Sharpe, Sortino, max drawdown, exposure and the rest), and the open trades and pending orders as of the last bar, with a marker on the price series for every fill. Reading the Strategy Tester walks the panel.

The broker fills every order against the chart's own candles, which is why a strategy's price input reads the chart's ohlcv and carries no symbol or exchange pin (any other primary input is refused by name). The run covers the candles the chart has loaded and recomputes as you pan, so panning back tests more history.

Core guarantees

  • Deterministic. The same file over the same bars produces the same trades wherever it runs: the broker is the engine's own code, pinned to one engine version, run under the engine's own phase order.
  • Lookahead-free. Orders placed while bar N computes fill no earlier than bar N+1's open. The position a guard reads in onBar() is the position after that bar's fills, so a flat guard is already flat on the bar its exit filled.
  • Honest accounting. A bar that could have filled two levels settles by the declared fillModel and counts in ambiguousFillCount (the Tester's Model-settled fills); rejected orders (the pyramiding cap, a size the equity cannot fund, an order placed on a live chart's forming bar) are counted, never dropped. The chart attaches no finer-interval data, no order book and no funding data to a strategy run, and the Tester's Run details say so: "Fill precision: bar resolution" once the fill model has settled a fill, and "Funding data did not cover N bars" on a perps run that held a position under funding: "data".

What the broker is, and is not

A research tool: fast iteration and honest reporting over bar closes. It settles orders at the next open, resting limits and stops when touched, protective exits under the fill model, isolated margin, liquidation and maker or taker fees on perps. It does not model queue position, partial fills at a level, replenishment, latency, or venue fees beyond the declared commission or maker and taker rates, and on the chart it does not walk finer bars, price fills off a recorded book (slippageModel: "bookEstimate" is refused by name) or settle recorded funding (funding: "data" counts the bars it could not settle). Each is disclosed by name rather than approximated.

Section contents