Pine Script

DocumentationWriting strategies

Pine Script

A Pine Script v5 or v6 strategy from TradingView runs here as it is, on every plan. The engine compiles it to its own strategy format, so it backtests, optimizes over its inputs, runs on the server and goes to the marketplace like any other strategy, and it runs on the native core when that is switched on.

The rule the compiler keeps: a script either computes exactly what TradingView computes, within the engine's own fills, or it is refused with the line and the column of the reason. It never runs something close.

Bringing a script in

Open Build, press the plus in the Strategies rail, choose Pine script and paste your script over the template it starts with. Or use the import button in the rail: it takes a .pine or .txt file, or the script pasted in, names it after its strategy() title, and compiles it before anything is saved, so what it refuses is listed by line first. The editor highlights Pine, and Validate compiles it again after you change it.

What runs

  • strategy() and its title. Capital, commission, slippage and margin are the run's own settings here, and the compiler says so. default_qty_value with default_qty_type (strategy.fixed, strategy.cash or strategy.percent_of_equity) is the size of every entry without a qty.
  • input.int, input.float, input.bool and input(): each is a parameter, with minval, maxval and step as its range in the optimizer. input.source is fixed at its default price.
  • Declarations, var with := and +=, if, else if, else, the ternary, and, or, not, history with x[n], and your own functions.
  • Prices, bar_index, syminfo.mintick, and the bar's time: hour(time, "UTC") and its siblings are exact; bare hour reads UTC here where TradingView reads the exchange's zone, and the compiler notes it.
  • ta.sma, ema, rma, wma, rsi, atr, tr, stdev, cci, highest, lowest, change, mom, stoch, crossover, crossunder, cross, ta.macd, ta.bb and ta.dmi into three names, math.* and math.sum, na, nz.
  • strategy.entry long and short, strategy.close_all, strategy.close with one entry id or strategy.position_entry_name, and strategy.exit with profit and loss in ticks.
  • qty on strategy.entry, in units of the instrument, as on TradingView.
  • qty or qty_percent on strategy.close: part of the position, in units or as a percentage of it.
  • strategy.entry with limit= or stop=: an order that waits at that price under its id until it fills or strategy.cancel(id) or strategy.cancel_all() takes it back. The same id again moves it. oca_name with oca_type=strategy.oca.cancel cancels the rest of the group when one fills.
  • strategy.exit with trail_points and trail_offset: a trailing stop that starts once the position is trail_points ticks in profit and follows the best price trail_offset ticks behind.
  • switch, as a statement and as a value.
  • input.string (with options), input.session and input.timeframe, fixed at their defaults. Text compares with == and !=. input.color and colours are for drawing and are left out.
  • A session filter: not na(time(timeframe.period, "0800-1600", "UTC")). Without "UTC" the session is read in UTC here, where TradingView reads the exchange's zone, and the compiler notes it.
  • request.security(syminfo.tickerid, timeframe.period, expr) on the chart's own symbol and timeframe.
  • Plots, alert and alertcondition are left out of the run, with a note.

What is refused

Each by line and column: loops, libraries, methods and types, varip, request.* on another symbol or timeframe, drawings, arrays, strategy.order, limit and stop together, strategy.oca.reduce, trail_price and a trail with only one of its two settings, reads of the position (strategy.position_size and the rest), pyramiding, input.time and time as a value, an indicator inside an if body, and a declaration that reads a var set earlier on the same bar. TradingView skips bars in two more places, so they are refused too: an indicator in one side of ?: whose condition changes, and in version 6 an indicator on the right of and or or. Put it on a line of its own. The message says how to write each.

Where TradingView and this engine part

  • Fills. An entry fills at the next bar's open, stops and targets inside the bar by the accuracy tier you choose, and the size is qty or the default size, else the run's lot size. TradingView's broker emulator makes its own assumptions inside a bar.
  • A percentage of equity is a percentage of the balance here, measured when the entry is placed, without the open position's profit.
  • An entry at a price the way you already hold. TradingView ignores it; here it waits and can fill once the position is closed. Cancel it in the script where that matters.
  • A position opened at a price has its stop and target checked from the next bar here. TradingView can stop it out in the bar it filled.
  • Two orders on one bar. Two separate if blocks that both enter on the same bar are weighed as the engine weighs a bar's intents: from flat the first entry wins. TradingView places both, so the second reverses the first. Write the second as else if to be exact on both.

Taking it back out

Export to TradingView in the editor's toolbar prints the strategy back as Pine v6 in the subset that runs here, with its inputs at the values you tested. The same panel prints a MetaTrader 5 expert advisor and a cTrader cBot: both carry this engine's own indicators, checked against it value for value, and replay the history they have before their first order so the indicators start warm. Each file says in its header what differs on that platform. None of them installs itself: paste or compile it on the platform. A Python strategy that reads the order book exports to MetaTrader 5 and to cTrader, and is refused for TradingView, which has no order book to read.

A Python strategy exports to TradingView the same way when it keeps to what the engine can compile: parameters, state in self, ctx.sma and the other indicators, lists over ctx.bars(n) after if len(bars) < n: return, and ctx.signal. It is compiled, run beside the original on the same bars, and printed only when both traded alike; otherwise the panel names the line, or the trade where they parted.

OrionQuant