FlowscopeDocs

Orders

Place, modify and cancel simulated orders with strategy.entry, order, exit, close and cancel.

Order functions queue instructions for the broker emulator. They do not fill at once: the emulator works them from the next bar on (see Broker emulator). They run in handlers only and return nothing.

Overview

FunctionPurpose
strategy.entry(id, direction, qty?, limit:, stop:, ...)Open or add to a position; reverses an opposite one; respects pyramiding.
strategy.order(id, direction, qty?, limit:, stop:, ...)Raw buy or sell: no reversal, no pyramiding limit.
strategy.exit(id, fromEntry?, profit:, loss:, limit:, stop:, trailPoints:, trailOffset:, ...)Take-profit, stop-loss and trailing stop for an entry.
strategy.close(id, qty:, qtyPercent:, immediately:)Close an entry’s trades at market.
strategy.closeAll(immediately:)Close the whole position at market.
strategy.cancel(id)Cancel the pending orders with this id.
strategy.cancelAll()Cancel every pending order.

strategy.long and strategy.short are the two directions.

Order ids

Every order has a string id. Ids do three jobs:

  • Identity. Placing an order again with the id of a pending order modifies it instead of adding a second one.
  • Linking. strategy.exit("TP", "Long") and strategy.close("Long") refer to the entry with id Long.
  • Reporting. The id labels the marks on the chart and the Signal column of the list of trades, and is entryId / exitId in trade records.

Keep ids short and stable: an id built from a changing value creates a new order on every bar.

Order lifetime

OrderLifetime
Market (no limit, no stop)Fills at the next fill opportunity, then is gone.
Limit or stop entryPending across bars until it fills or is cancelled.
ExitPending while its entry is open; dropped when the position is flat and no entry is pending.

strategy.entry

Text
strategy.entry(id: string, direction: Direction, qty: float? = null,
               limit: float? = null, stop: float? = null, ocaName: string? = null,
               ocaType: OcaType? = null, comment: string? = null, alertMessage: string? = null)
  • Without limit and stop it is a market order; with limit a limit order, with stop a stop order, with both a stop-limit order.
  • Without qty the size comes from defaultQtyType and defaultQtyValue.
  • An entry against the current position closes it and opens the new direction with qty: long 1 and strategy.entry("S", strategy.short) with size 1 sells 2.
  • Entries in the direction of the position are limited by pyramiding.
  • alertMessage raises an alert when the order fills, with Pine’s placeholders filled in: {{ticker}}, {{strategy.order.action}} (buy or sell), {{strategy.order.contracts}}, {{strategy.order.price}}, {{strategy.order.id}} and {{strategy.order.comment}}. Fills already on the chart when the strategy is added are history and raise nothing. The other order calls take it the same way.
Flowscope Script
script "Breakout entries"

strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 0.1)

data chart = subscribe(data.ohlcv)

on chart.close {
  let hi = ta.highest(chart.high, 20)
  let lo = ta.lowest(chart.low, 20)
  if hi != null && lo != null && strategy.positionSize == 0.0 {
    // Re-placed every bar: the stop orders follow the channel.
    strategy.entry("Up", strategy.long, stop: hi + context.tickSize)
    strategy.entry("Down", strategy.short, stop: lo - context.tickSize)
  }
}

strategy.order

strategy.order buys or sells exactly qty and ignores pyramiding. An order against the position reduces it by its size and only reverses it when it is larger. Use it to scale in and out by hand.

Flowscope Script
script "Scale out"

strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 1.0)

data chart = subscribe(data.ohlcv)

on chart.close {
  let ema = ta.ema(chart.close, 50)
  if ema != null {
    if strategy.positionSize == 0.0 && ta.crossover(chart.close, ema) {
      strategy.order("Buy", strategy.long, 1.0)
    }
    // Sell half when price stretches 2 % above the average.
    if strategy.positionSize > 0.5 && chart.close > ema * 1.02 {
      strategy.order("Trim", strategy.short, 0.5)
    }
  }
}

strategy.exit

Text
strategy.exit(id: string, fromEntry: string? = null, qty: float? = null,
              qtyPercent: float? = null, profit: float? = null, limit: float? = null,
              loss: float? = null, stop: float? = null, trailPrice: float? = null,
              trailPoints: float? = null, trailOffset: float? = null, ocaName: string? = null,
              comment: string? = null, commentProfit: string? = null,
              commentLoss: string? = null, commentTrailing: string? = null,
              alertMessage: string? = null)
  • profit and loss are distances in ticks from the entry’s fill price; limit and stop are absolute prices. Give either form for each leg.
  • The trailing stop activates at trailPrice, or trailPoints ticks in profit, and then follows the best price trailOffset ticks behind.
  • Without fromEntry the exit applies to every open entry.
  • qty or qtyPercent (default 100) exits part of the entry.
  • The legs form one group: when one fills, the others are cancelled.
Flowscope Script
script "Bracket"

strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 1.0)

data chart = subscribe(data.ohlcv)

on chart.close {
  if ta.crossover(ta.ema(chart.close, 9), ta.ema(chart.close, 21)) {
    strategy.entry("Long", strategy.long)
    // 200 ticks target, 100 ticks stop.
    strategy.exit("TP/SL", "Long", profit: 200.0, loss: 100.0)
  }
}

A trailing stop that starts once the trade is 150 ticks in profit and trails 50 ticks behind:

Flowscope Script
script "Trailing stop"

strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 1.0)

data chart = subscribe(data.ohlcv)

on chart.close {
  if ta.crossover(chart.close, ta.sma(chart.close, 100)) {
    strategy.entry("Long", strategy.long)
    strategy.exit("Trail", "Long", trailPoints: 150.0, trailOffset: 50.0, loss: 300.0)
  }
}

strategy.close and strategy.closeAll

strategy.close("Long") closes the trades of entry Long with a market order at the next fill opportunity; qty or qtyPercent closes part of them. With immediately: true the order fills at once, at the current close. strategy.closeAll closes the whole position.

Flowscope Script
script "Time stop"

strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 1.0)

data chart = subscribe(data.ohlcv)

state barsIn = 0

on chart.close {
  if strategy.positionSize != 0.0 {
    barsIn += 1
  } else {
    barsIn = 0
  }
  if strategy.positionSize == 0.0 && ta.crossover(chart.close, ta.ema(chart.close, 20)) {
    strategy.entry("Long", strategy.long)
  }
  // Out after 10 bars whatever happens.
  if barsIn >= 10 {
    strategy.closeAll(comment: "Time stop")
  }
}

Cancelling

strategy.cancel("Up") removes the pending orders with id Up; strategy.cancelAll() removes every pending order. Filled orders cannot be cancelled; close the position instead.

One-cancels-all groups

Entries with the same ocaName form a group. With ocaType: strategy.oca.cancel, a fill cancels the other orders of the group; with strategy.oca.reduce it reduces them by the filled size. The breakout example above can use it so that only one of the two stop entries ever fills:

Flowscope Script
script "OCA breakout"

strategy(defaultQtyType: strategy.fixed, defaultQtyValue: 1.0)

data chart = subscribe(data.ohlcv)

on chart.close {
  let hi = ta.highest(chart.high, 20)
  let lo = ta.lowest(chart.low, 20)
  if hi != null && lo != null && strategy.positionSize == 0.0 {
    strategy.entry("Up", strategy.long, stop: hi, ocaName: "break", ocaType: strategy.oca.cancel)
    strategy.entry("Down", strategy.short, stop: lo, ocaName: "break", ocaType: strategy.oca.cancel)
  }
}