millionchart
فارسی

Lesson 8: Labels, lines and boxes

Place labels, lines and boxes at a bar and a price, move or delete a drawing you keep, draw only on the newest bar, and stay inside the drawing limits.

Lesson 8 of 18. A plot has one value on every bar. A drawing is a single object that you place where you want it: a label with a text, a line from one point to another, or a box. You place it with a bar number (bar.index, which counts the bars from 0 at the oldest) and a price.

A label where something happens#

Cuprel
indicator "Cross labels"
    edition = 1
    display = price
    max_labels = 200

let fast = tech.ema(close, 10)
let slow = tech.ema(close, 30)
let up = tech.crosses_above(fast, slow)
let down = tech.crosses_below(fast, slow)
if up
    label.new(bar.index, low, f"Up {close:price}", style = label_up, color = color.green)
if down
    label.new(bar.index, high, f"Down {close:price}", style = label_down, color = color.red)
plot(fast, name = "Fast", color = color.green)
plot(slow, name = "Slow", color = color.red)
  • label.new(x, y, text, ...) places a label at bar x and price y. label_up points up at the price from below, label_down points down from above.
  • f"Up {close:price}" is a text with a value in it, written with the symbol's own decimals.
  • The chart keeps the newest 50 labels of a script unless the header says otherwise; max_labels = 200 keeps 200.

Drawing once, on the newest bar#

A drawing made on every bar is made again and again. When you want one set of drawings that describes the chart as it is now, draw on the newest bar only, where bar.is_last is true:

Cuprel
indicator "Recent high and low"
    edition = 1
    display = price

input length: int = 50, name = "Bars", min = 2, max = 500
let top = tech.highest(high, length)
let bottom = tech.lowest(low, length)
if bar.is_last
    line.new(bar.index - length + 1, top, bar.index, top, color = color.green, extend = right)
    line.new(bar.index - length + 1, bottom, bar.index, bottom, color = color.red, extend = right)
    box.new(
        bar.index - length + 1,
        top,
        bar.index,
        bottom,
        border_color = color.gray,
        background = color.fade(color.gray, 92),
    )
plot(close, name = "Close", color = color.gray)

line.new(x1, y1, x2, y2, ...) joins two points; extend = right carries the line on to the right side of the chart. box.new(left, top, right, bottom, ...) draws a rectangle.

Keeping a drawing and changing it#

A drawing can be kept in a state name. Then you can change it later instead of making a new one. This script makes one line on the first bar and moves it on every bar to the latest 20-bar high:

Cuprel
indicator "Moving high line"
    edition = 1
    display = price

state high_line: line = na
let top = tech.highest(high, 20)
if bar.is_first
    high_line = line.new(bar.index, high, bar.index, high, color = color.orange, width = 2)
line.set_xy1(high_line, bar.index - 19, top)
line.set_xy2(high_line, bar.index, top)
plot(close, name = "Close", color = color.gray)

line.set_xy1 and line.set_xy2 move the two ends. The same idea works for labels (label.set_xy, label.set_text) and boxes (box.set_top, box.set_bottom, box.set_right). The other way is to delete the old drawing and make a new one:

Cuprel
indicator "Latest swing high"
    edition = 1
    display = price

state marker: label = na
let swing = tech.pivot_high(high, 5, 5)
if not is_na(swing)
    label.delete(marker)
    marker = label.new(
        bar.index - 5,
        swing,
        f"{swing:price}",
        style = label_down,
        color = color.red,
    )
plot(close, name = "Close", color = color.gray)

Only the newest swing high has a label: each new one deletes the one before. A swing is known only five bars after it happened, which is why the label is placed five bars back.

Boxes around a pattern#

Cuprel
indicator "Inside bar boxes"
    edition = 1
    display = price
    max_boxes = 200

let inside = high < high[1] and low > low[1]
if inside
    box.new(
        bar.index - 1,
        high[1],
        bar.index,
        low[1],
        border_color = color.purple,
        background = color.fade(color.purple, 85),
    )
plot(close, name = "Close", color = color.gray)

An inside bar has its high below and its low above the bar before it; the box covers the bar before and the inside bar.

When there are too many#

A script that makes more drawings than it may keep still runs, but the chart drops the oldest and the engine says so with the warning W-OBJECT-LIMIT. A label on every bar is the classic case:

Cuprel · warning W-OBJECT-LIMIT
indicator "A label on every bar"
    edition = 1
    display = price

label.new(bar.index, high, "bar")
plot(close)

Raise the number in the header (max_labels, max_lines, max_boxes, up to 500), or better, draw less: only where something happens, only on the newest bar, or one drawing that you move. This warning comes from a run, so the editor does not list it in this release; you see the drawings stop at the oldest end of the chart.

Try changing#

  • In "Cross labels", change low to low - (high - low): the up labels sit a little lower, away from the bar.
  • In "Recent high and low", remove if bar.is_last and indent the drawings one step less: the chart is filled with lines, and the oldest are dropped.
  • In "Moving high line", replace tech.highest(high, 20) with tech.lowest(low, 20) and the colour with color.blue: the line follows the 20-bar low.
  • In "Inside bar boxes", set max_boxes = 10: only the ten newest boxes remain.

Checkpoint#

  • You can place a label, a line and a box at a bar number and a price.
  • You can draw only once, on the newest bar, with bar.is_last.
  • You can keep a drawing in a state name and move it, or delete it and draw a new one.
  • You know the limits on drawings and what W-OBJECT-LIMIT means.

Next: Lesson 9: Sessions and a small table