FlowscopeDocs

Strategies & backtesting

Turn a script into a strategy that places simulated orders and backtests on the chart's history, TradingView style.

A strategy is a script that places orders as well as plotting. You decide when to enter and exit; Flowscope’s broker emulator decides how those orders would have filled, keeps the books (position, equity, commission, funding) and reports the result in the Strategy Tester.

The model follows TradingView’s Pine strategy(): one strategy(...) declaration, order functions such as strategy.entry and strategy.exit, and read-only values such as strategy.positionSize and strategy.equity, with the same names in camelCase. Everything else is the language you already use for indicators.

What changes when a script becomes a strategy

IndicatorStrategy
Plots, tables, drawings, alertsEverything an indicator can do
No notion of a positionA simulated account with position, equity and trades
Output lives on the chartOutput also feeds the Strategy Tester
—Entry and exit marks with order ids on the chart

A script is a strategy as soon as it contains a strategy(...) declaration. There is at most one, usually right after the script header.

A complete first strategy

The script trades a fast/slow EMA crossover in both directions and protects every entry with a stop two ATRs away from the fill.

ema-cross-atr-stop.fs
script "EMA cross with ATR stop"

strategy(
  initialCapital: 10000.0,
  defaultQtyType: strategy.percentOfEquity,
  defaultQtyValue: 25.0,
  commissionType: strategy.commission.percent,
  commissionValue: 0.04,
  slippage: 1
)

input (
  fastLen = input.int(12, title: "Fast EMA", min: 1, max: 200)
  slowLen = input.int(26, title: "Slow EMA", min: 2, max: 400)
  atrLen = input.int(14, title: "ATR length", min: 1, max: 100)
  stopAtr = input.float(2.0, title: "Stop distance (ATR)", min: 0.1)
)

data chart = subscribe(data.ohlcv)

plot (
  fastLine = plot.line(title: "Fast EMA", color: color.blue, width: 2)
  slowLine = plot.line(title: "Slow EMA", color: color.orange, width: 2)
)

on chart.close {
  let fast = ta.ema(chart.close, fastLen)
  let slow = ta.ema(chart.close, slowLen)
  let atr = chart.atr(atrLen)
  fastLine.plot(fast)
  slowLine.plot(slow)

  if atr != null {
    // The stop distance in ticks, fixed at signal time.
    let stopTicks = math.round(atr * stopAtr / context.tickSize)
    if ta.crossover(fast, slow) {
      strategy.entry("Long", strategy.long, comment: "EMA up")
      strategy.exit("Long SL", "Long", loss: stopTicks)
    }
    if ta.crossunder(fast, slow) {
      strategy.entry("Short", strategy.short, comment: "EMA down")
      strategy.exit("Short SL", "Short", loss: stopTicks)
    }
  }
}

Open the script editor, paste it, and press Add to chart. The Strategy Tester opens at the bottom of the window with the backtest.

What happens on each bar

  1. on chart.close runs once per confirmed bar of the loaded history, oldest first.
  2. On a bullish cross, strategy.entry("Long", strategy.long) queues a market order. If the strategy is short, the same order also closes the short: entries reverse.
  3. strategy.exit("Long SL", "Long", loss: ...) attaches a stop to the Long entry, loss ticks from the actual fill price.
  4. The emulator fills the market order at the next bar’s open plus one tick of slippage, charges 0.04 % commission, and then watches the stop on every bar.
Note

Orders placed at a bar’s close fill at the next bar’s open unless you declare processOrdersOnClose: true. You decide after the close, so you can only trade after it. See Broker emulator.

History and live bars

On a chart, a strategy runs over every loaded bar. When new bars arrive it runs again over the whole history (at most every 0.75 s while a bar is forming), so the Strategy Tester and the marks on the chart always show the backtest up to now. A forming bar is not traded: handlers in on chart.close see confirmed bars only.

Important

Strategies never send orders anywhere. There is no exchange connection in the strategy engine. To act on signals yourself, declare an alert and trigger it next to your order calls; alerts fire on live bars only.

Where to put order calls

Place order calls in on chart.close. Order functions run in handlers only; calling them from setup is a compile error.

Reading the result

The chart shows an arrow and label at each fill with the order id and size, a dotted line from entry to exit (green for a win, red for a loss), and a dashed line at the average entry price while a position is open. The Strategy Tester shows the P&L curve, the performance summary for all, long and short trades, and the list of trades.

During a bar replay the backtest stops at the replay bar and grows as the replay plays.

Next steps

For complete worked strategies, see the EMA and ATR strategy and the CVD breakout strategy examples.