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
| Function | Purpose |
|---|---|
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")andstrategy.close("Long")refer to the entry with idLong. - Reporting. The id labels the marks on the chart and the Signal column of the
list of trades, and is
entryId/exitIdin trade records.
Keep ids short and stable: an id built from a changing value creates a new order on every bar.
Order lifetime
| Order | Lifetime |
|---|---|
Market (no limit, no stop) | Fills at the next fill opportunity, then is gone. |
| Limit or stop entry | Pending across bars until it fills or is cancelled. |
| Exit | Pending while its entry is open; dropped when the position is flat and no entry is pending. |
strategy.entry
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
limitandstopit is a market order; withlimita limit order, withstopa stop order, with both a stop-limit order. - Without
qtythe size comes fromdefaultQtyTypeanddefaultQtyValue. - An entry against the current position closes it and opens the new direction with
qty: long 1 andstrategy.entry("S", strategy.short)with size 1 sells 2. - Entries in the direction of the position are limited by
pyramiding. alertMessageraises an alert when the order fills, with Pine’s placeholders filled in:{{ticker}},{{strategy.order.action}}(buyorsell),{{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.
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.
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
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)profitandlossare distances in ticks from the entry’s fill price;limitandstopare absolute prices. Give either form for each leg.- The trailing stop activates at
trailPrice, ortrailPointsticks in profit, and then follows the best pricetrailOffsetticks behind. - Without
fromEntrythe exit applies to every open entry. qtyorqtyPercent(default 100) exits part of the entry.- The legs form one group: when one fills, the others are cancelled.
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:
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.
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:
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)
}
}