Wintrading

Navy: on the chart

Navy: what a script draws

The price chart or a pane of its own, plots and their styles, colors, levels and fills, backgrounds, markers, lines, boxes, labels and alerts.

On the price chart or in a pane

A sketched chart with every output a script can draw, each labelled with its call: plot, fill, line.new, label.new, box.new, marker.above, marker.below, bgcolor, hline, a columns plot and alert
Every output on one chart, with the call that draws it.

The declaration decides where a script draws.

A real chart with two scripts: overlay EMA on the price chart and its scale, with markers at the highs and lows; indicator RSI in a pane of its own with its own scale and marker.at dots on the line
overlay draws on the price chart; indicator in a pane of its own.
  • overlay('Title') — the price chart: averages, bands, levels, signals.
  • indicator('Title') — a pane below with its own scale: oscillators, volume, delta.
  • In a pane, mark a value with marker.at(cond ? value : na): marker.above/.below sit at the bar's high/low.
  • Up to 64 outputs; their title names them in the legend.
OptionWhat it doesDefault
precisionDigits after the point in the legend and on the scalethe symbol's
maxBarsBackHow far back x[k] with a variable k reaches at first (up to 20 000)500
fuelLoop iterations allowed per bar (up to 10 000 000)1 000 000
//@navy 2.0
indicator('RSI', { precision: 1 })

const r = ta.rsi(close, 14)
plot(r, { title: 'RSI', color: color.purple, width: 2 })
hline(50, { color: color.gray, style: 'dotted' })
// In its own pane the bar's high and low are not on this scale: mark the value itself
marker.at(ta.crossover(r, 50) ? r : na, '', { color: color.teal, size: 6 })

plot and its styles

plot(series, { title, color, width, style }) draws a value on every bar; na leaves a gap.

Seven real renders, one per plot style: line, step, histogram, columns, area with a base, circles and cross
The seven styles as the chart engine draws them.
  • color may change per bar: hist >= 0 ? color.teal : color.red; width, style — constants; no color → the palette's next.
  • base — where columns/area start; colorDown — area below it.
  • const p = plot(…) can be filled; Pine's linewidth, plot.style_stepline work.
//@navy 2.0
indicator('MACD in four styles')

const [macd, signal, hist] = ta.macd(close, 12, 26, 9)
const crossUp = ta.crossover(macd, signal)

plot(hist, { title: 'Histogram', style: 'columns', color: hist >= 0 ? color.teal : color.red })
plot(macd, { title: 'MACD', color: color.blue, width: 2 })
plot(signal, { title: 'Signal', color: color.orange, style: 'step' })
plot(crossUp ? macd : na, { title: 'Cross', style: 'circles', color: color.lime, width: 4 })
The example on a BTCUSDT 1m chart: a MACD pane with teal and red histogram columns, a blue MACD line, an orange step signal line and green dots on the crosses
The example: four styles in one MACD pane.

Colors

  • Named: color.red, green, blue, orange, yellow, purple, fuchsia, aqua, teal, lime, maroon, navy, olive, silver, gray, white, black.
  • Own: color.rgb(255, 152, 0, 0.5), '#ff9800', '#ff980080'; color.new(c, opacity).
  • Per bar: plot, fill, bgcolor, markers; constant: hline, a marker's text.
Two red gradient bars over a checkerboard: Navy counts opacity from 0 invisible to 1 solid, Pine counts transparency from 100 to 0; color.new(c, 0.15) and Pine color.new(c, 85) are the same color
Opacity from 0 to 1, not Pine's transparency: 0.15 here is 85 there.
  • Above 1 — read as Pine's transparency; color.new(c, 1) is solid.
//@navy 2.0
indicator('Volume by strength')

const rel = volume / ta.sma(volume, 20)        // 1 = an average bar
const strength = math.min(rel / 3, 1)          // 0 … 1, full at 3× the average
const base = close >= open ? color.teal : color.red

// Opacity per bar: quiet bars fade, heavy ones are solid
plot(volume, { title: 'Volume', style: 'columns', color: color.new(base, 0.25 + 0.75 * strength) })

Levels and fills

  • hline(70, { style }) — a constant level: 'solid', 'dashed' (default), 'dotted'.
  • fill(a, b, { color }) — between plots or hlines kept in constants (const a = plot(…)).
  • A cloud: the fill's color changes per bar; hide the edges with color.new(c, 0).
  • A gradient: topValue, bottomValue, topColor, bottomColor, as in Pine.
Four real renders: an EMA cloud that changes color, RSI levels with the zone between them filled, an RSI with gradient glows beyond 70 and 30, and a background colored by trend
Fills between plots and levels, a gradient, the bar background.
//@navy 2.0
indicator('RSI with zones', { precision: 1 })

const r = ta.rsi(close, input.int('Length', 14, { min: 2 }))
const top = hline(70, { title: 'Overbought', color: color.gray })
const bottom = hline(30, { title: 'Oversold', color: color.gray })
fill(top, bottom, { color: color.new(color.purple, 0.06) })

// A glow where the RSI leaves the zone: invisible edges held at 70 / 30, gradient fills
const hot = plot(math.max(r, 70), { title: 'Overbought edge', color: color.new(color.red, 0) })
const cold = plot(math.min(r, 30), { title: 'Oversold edge', color: color.new(color.teal, 0) })
fill(hot, top, { topValue: 85, bottomValue: 70, topColor: color.new(color.red, 0.8), bottomColor: color.new(color.red, 0) })
fill(bottom, cold, { topValue: 30, bottomValue: 15, topColor: color.new(color.teal, 0), bottomColor: color.new(color.teal, 0.8) })

plot(r, { title: 'RSI', color: color.purple, width: 2 })
The example on a BTCUSDT 1m chart: the RSI with its 70–30 zone; below 30 during a sell-off the RSI glows teal, more solid the deeper it goes
The example: the RSI zone and its glow beyond 30 during a sell-off.

Background and bar colors

  • bgcolor(color) — the full height of the script's pane behind a bar; na — clear. Opacity 0.05–0.2.
  • No barcolor: use a background, a marker or columns.
//@navy 2.0
indicator('Liquidation bars', { precision: 0 })

const longs = nz(liq.long)      // na on spot and before the loaded history → 0
const shorts = nz(liq.short)
const avg = ta.sma(longs + shorts, 50)
const spike = avg[1] > 0 && longs + shorts > avg[1] * 3    // against the previous bars' average

plot(longs, { title: 'Longs liquidated', style: 'columns', color: color.red })
plot(-shorts, { title: 'Shorts liquidated', style: 'columns', color: color.teal })
bgcolor(spike ? color.new(color.orange, 0.15) : na, { title: 'Spike' })
The example on a BTCUSDT 1m chart during a sell-off: long liquidations as red columns in a pane, the spikes on an orange background
The example: liquidations, spikes on an orange background (the liquidations here are sample data).

Markers

  • marker.above(cond, 'text', { shape, size, color }) — over the high; marker.below — under the low; marker.at(price) — at a price.
  • size: pixels or 'tiny' … 'huge'; color may change per bar.
  • The text is a constant: a price goes into label.new. Pine's shape.*, size.* work.
Two real renders: the ten marker shapes over the bars, each named; and marker.above over a high, marker.below under a low, marker.at on a level
The ten shapes; above, below, at a price.
//@navy 2.0
overlay('Engulfing candles')

const bull = close > open && close[1] < open[1] && close >= open[1] && open <= close[1]
const bear = close < open && close[1] > open[1] && close <= open[1] && open >= close[1]

marker.below(bull, 'Bull', { shape: 'label', color: color.teal, textColor: color.white, size: 'small' })
marker.above(bear, '', { shape: 'triangleDown', color: color.red, size: 'small' })

Lines, boxes and labels

Unlike plots, objects are created where something happens — in if, loops, functions — and stay until deleted.

The range-breakout example on a real chart: each broken range is a box.new from bar_index − 30 to bar_index − 1, each breakout a label.new at the bar's high; x is a bar index, y a price
x is a bar index, y a price: the boxes and labels of the breakout example.
  • line.new(x1, y1, x2, y2), box.new(left, top, right, bottom), label.new(x, y, text); bar_index + 10 is in the future.
  • Text from values: 'Breakout ' + str.tostring(top, format.mintick).
  • A handle in var to change it: lvl.setXY2(…), lvl.delete().
  • «Current state» goes in if (barstate.islast): redrawn on every update, not piled up.
  • 500 of each kind; past that the oldest goes.
Three real renders: lines in solid, dashed and dotted style and one extended to the right; labels in the six styles around one level; a box with text and a dashed box
Line styles, label styles, boxes.
//@navy 2.0
overlay('Range breakout')

const len = input.int('Range, bars', 30, { min: 5 })
const hh = ta.highest(high, len)
const ll = ta.lowest(low, len)
const top = hh[1]                 // the range of the previous len bars
const bottom = ll[1]
const breakUp = ta.crossover(close, top)

if (breakUp) {
  // The broken range as a box, the level written on the breakout bar
  box.new(bar_index - len, top, bar_index - 1, bottom, { color: color.new(color.blue, 0.1), borderColor: color.blue })
  label.new(bar_index, high, 'Breakout ' + str.tostring(top, format.mintick), { style: 'down', color: color.teal, textColor: color.white, size: 'small' })
}

// The current range: redrawn on every update of the last bar, never piled up
if (barstate.islast) {
  line.new(bar_index - len, top, bar_index + 10, top, { color: color.gray, style: 'dashed' })
  line.new(bar_index - len, bottom, bar_index + 10, bottom, { color: color.gray, style: 'dashed' })
}

alert(breakUp, '{{ticker}}: range breakout at {{close}}', { freq: 'close' })

Options and methods: Scripts → Lines, boxes and labels, the reference.

Alerts

alert(condition, 'message', { freq, title }) notifies you when the condition holds on a live bar; written at the top level.

Two live bars with the condition on each update and where each freq fires: bar on the first update where it holds, close once when the bar closes with it, all on every update where it holds
freq: 'bar' — the first update where it holds; 'close' — the bar's close; 'all' — every update.
  • 'bar' (default) — early; 'close' — no false alarms from a wick; 'all' — level touches.
  • Placeholders: {{ticker}}, {{exchange}}, {{interval}}, {{open}} … {{volume}}, {{time}}, {{plot_0}}, {{plot("RSI")}}.
  • An event found in a block: a top-level flag, set in the block (below).
  • Never on history or replay; alertcondition is silent for now.
//@navy 2.0
overlay('Price near a density')

const maxDist = input.float('Distance, %', 0.3, { min: 0.05, step: 0.05 })

// An event found inside a block: a top-level flag, set in the block
let near = false
if (barstate.islast) {
  for (let i = 0; i < walls.size(); i++) {
    if (walls.dist(i) <= maxDist) {
      near = true
      line.new(bar_index - 20, walls.price(i), bar_index + 5, walls.price(i), { color: walls.side(i) > 0 ? color.teal : color.red, width: 2 })
    }
  }
}

// Outputs stay at the top level and read the flag
alert(near, '{{ticker}}: the price is near a density', { title: 'Near a density' })
How a script's alert arrives: the alert call with its message, a pop-up, a sound or the voice, and the bell's list — while a chart with the script is open
Like the app's own alerts — while a chart with the script is open.

How alerts arrive and their sound: Scripts → Alerts.