CODESCRIPT

Placing marks and shapes

An indicator line carries a value on every bar. Most of the time, though, what you are after is the event, not the value: the bar where the cross happened, the bar where volume spiked. This page shows you how to stamp those events onto the chart.

Why a separate tool?

plot stitches a series into one unbroken line, bar after bar. Events, on the other hand, are sparse. Try to show a condition that holds on three bars out of a hundred with a line and nothing comes through.

Marking tools do the opposite. Where the condition holds they stamp one thing on that bar; where it does not, they draw nothing at all. There are four of them: plotshape stamps a shape, plotchar stamps a character, plotarrow draws an arrow scaled to a value, and label leaves free text.

NOTE

All four draw on the price panel. None of them opens a lower panel; only plot can do that.

TIP

Before you place a mark, ask yourself one question: does this condition hold on every bar, or only now and then? If it holds on every bar, what you really want is a line.

plotshape: stamping a shape on the signal bar

This is the one you will reach for most. Its first argument is a series that produces true or false. Every bar where it comes out true gets a shape.

CODESCRIPT
// 20 barlik ortalamayi yukari kesen barlari isaretle
ortalama = sma(close, 20)
alSinyali = crossover(close, ortalama)

plot(ortalama, "SMA20", "#787b86")
plotshape(alSinyali, "al", style="triangleup", location="belowbar", color="#26a69a")

A grey SMA20 line, with green triangles under the bars where price crossed above it. Every other bar stays clean.

crossover is true only on the bar where the cross actually happens, not on the bars that follow. That is why the triangles appear one at a time instead of in clusters.

Shape options

style sets the form of the shape. Leave it out and you get circle.

  • arrowup and arrowdown — a strong claim about direction, good for entry and exit signals.
  • triangleup and triangledown — readable even when small, the cleanest pick for crosses.
  • circle, square, diamond — neutral forms that flag a bar without claiming a direction.
  • cross and xcross — for negative events such as a cancel, an invalidation, or a stop-loss.
  • flag — for a standalone event that has to catch the eye.
  • labelup and labeldown — boxed shapes that can hold a short piece of text inside.

Both spellings are valid and give the same result: style="triangleup" or style=shape.triangleup.

Location options

location tells the shape where to sit relative to the bar. Leave it out and you get abovebar.

  • abovebar — just above the bar's high.
  • belowbar — just below the bar's low.
  • top and bottom — a fixed strip at the top or bottom edge of the panel, staying level wherever price goes.
  • absolute — treats the number you feed the series as a price level and puts the shape exactly there.
CODESCRIPT
hizli = sma(close, 10)
yavas = sma(close, 30)

// yukari kesisim: isaret barin ALTINDA ve yukari bakiyor
plotshape(crossover(hizli, yavas), "al", style="triangleup", location="belowbar", color="#26a69a")
// asagi kesisim: isaret barin USTUNDE ve asagi bakiyor
plotshape(crossunder(hizli, yavas), "sat", style="triangledown", location="abovebar", color="#ef5350")

A green triangle below the bar when the fast average crosses above the slow one, and a red triangle above the bar when it crosses back below.

TIP

Match the directions: an up signal belongs belowbar with an upward shape, a down signal abovebar with a downward shape. Pair them the other way round and the chart stops making sense at a glance.

absolute works a little differently. Instead of true or false you pass a number, and the shape lands at that price. Bars where you pass na get nothing, so a single series carries both the position and the condition.

CODESCRIPT
sinyal = crossover(close, sma(close, 20))
// sinyal barinda dip seviyenin yuzde bir altini hedefle, digerlerinde bos birak
seviye = iff(sinyal, low * 0.99, na)

plotshape(seviye, "al", style="triangleup", location="absolute", color="#26a69a")

Green triangles sitting one percent below the low of each cross bar. Bars without a cross are left empty.

Text, size and shift

You can put a short piece of text next to the shape. text is the text itself and textcolor is its color. size controls both the shape and the text beside it.

  • Named sizes: tiny, small, normal, large, huge. Leave it out and you get normal.
  • You can also pass a number; it is read as pixels, between 7 and 40.
  • offset shifts the mark along the bar axis. offset=-1 moves the shape one bar back.
CODESCRIPT
guc = rsi(close, 14)

// asiri alim barina kutu sekil ve iki harflik yazi koy
plotshape(guc > 70, "asiri alim", style="labeldown", location="abovebar",
          color="#ef5350", text="AA", textcolor="#ffffff", size="small")

Small red boxes reading AA in white, above every bar where the momentum reading pushes past 70.

CAUTION

text is meant to stay short: two or three letters, one word at most. Longer text makes the marks overlap and hides the bars behind them. If you need a sentence, use label.

plotchar: placing a character

plotshape's list of shapes is fixed. When you want something that is not on it, plotchar stamps the character you choose. It works like plotshape, except it takes char instead of style.

CODESCRIPT
ortHacim = sma(volume, 20)

// hacim ortalamanin iki katini asarsa panelin ust kenarina H harfi koy
plotchar(volume > ortHacim * 2, "hacim", char="H", location="top", color="#ff9800")

Orange H letters along the top edge of the panel, on the bars where volume ran to more than twice its twenty-bar average.

Note the location="top" here. The letter sits at the top edge of the panel rather than on the price, so it never covers the candles and the reading order stays intact.

NOTE

char holds a single character; leave it out and you get a star glyph. For more than one word, label is the right tool, not plotchar.

plotarrow: showing direction and size together

plotshape and plotchar always draw at the same size; they say nothing about how strong a signal is. plotarrow takes its direction from the sign of your number and its length from the magnitude. One series answers both which way and how much.

CODESCRIPT
// govde buyudukce ok uzar; yon govdenin isaretinden gelir
govde = close - open

plotarrow(govde, "govde", colorup="#26a69a", colordown="#ef5350", minheight=6, maxheight=60)

One arrow per bar: green and up on bullish bars, red and down on bearish ones, with arrow length tracking the size of the body.

Bars whose value is 0 or na get no arrow at all. Use that as a filter: turn the weak signals into na and only the strong ones remain on the chart.

CODESCRIPT
momentum = rsi(close, 14) - 50

// zayif momentumu na yap; na olan barda ok cizilmez
secilmis = iff(abs(momentum) > 15, momentum, na)
plotarrow(secilmis, "momentum", colorup="#26a69a", colordown="#ef5350")

Arrows only on the bars where the momentum reading strays more than 15 from its midpoint; everything in between stays empty, which thins the arrows out considerably.

CAUTION

Never hand plotarrow an absolute value. A series wrapped in abs is always positive, so every arrow points up and the direction is lost entirely.

label: leaving text on a bar

A shape or a character carries one fixed meaning. When you want to show a computed number, a level, or a sentence, you need label. It takes a price level and a text, and puts a boxed note right there.

CODESCRIPT
hizli = ema(close, 12)
yavas = ema(close, 26)

// grafigin son barina tek bir ozet notu birak
if barstate.islast {
  label(close, "Fark: " + str(round(hizli - yavas, 2)), color="#2962ff", style="labeldown", size="small")
}

A single blue label on the last bar of the chart, spelling out the gap between the two exponential averages to two decimals.

CAUTION

If you do not pass label a bar, the label always lands on the last bar of the chart. Even when the condition holds a hundred times, all hundred labels pile onto the same spot and any bar-by-bar reading is gone. When you want text on every signal, plotshape's text argument is the right tool, not label.

That is why label does its best work as a one-off summary: a note, a level, or a result resting on the last bar. The barstate.islast condition gives you exactly that.

mark: the one-line shortcut

mark is a pared-down plotshape. It takes a condition, a form, a location, a color, and a short text. It has none of the finer controls such as offset or textcolor, but one line is all it takes to make a signal visible.

CODESCRIPT
// tek satirda sinyal isareti: ince ayar yok, hiz var
mark(crossover(close, sma(close, 50)), style="triangleup", location="belowbar", color="#26a69a", text="AL")
mark(crossunder(close, sma(close, 50)), style="triangledown", location="abovebar", color="#ef5350", text="SAT")

A green triangle reading AL below the bars where price crosses above the fifty-bar average, and a red one reading SAT above the bars where it crosses below.

A practical habit is to start with mark, confirm the signal really fires where you expect, then move to plotshape. Both accept the same form names, so the switch costs you one line.

Which one, and when

The decision comes down to a single question: what are you trying to show?

  • A continuous value that updates on every bar — plot.
  • A sparse event with one fixed meaning — plotshape.
  • A symbol that is not among the built-in shapes — plotchar.
  • Direction and intensity together — plotarrow.
  • Changing text or a computed number — label.
  • A quick mark with minimal code — mark.
CODESCRIPT
hizli = ema(close, 12)
yavas = ema(close, 26)
al = crossover(hizli, yavas)
sat = crossunder(hizli, yavas)
hacimli = volume > sma(volume, 20) * 1.5

// surekli deger -> cizgi
plot(hizli, "EMA12", "#26a69a")
plot(yavas, "EMA26", "#ef5350")

// seyrek olay -> sekil
plotshape(al, "al", style="triangleup", location="belowbar", color="#26a69a")
plotshape(sat, "sat", style="triangledown", location="abovebar", color="#ef5350")

// ikinci kosul -> harf
plotchar(hacimli, "hacim", char="H", location="top", color="#ff9800")

// tek seferlik ozet -> etiket
if barstate.islast {
  label(close, "Fark: " + str(round(hizli - yavas, 2)), color="#2962ff", style="labeldown", size="small")
}

Two exponential average lines, green and red triangles on the cross bars, H letters along the top of the panel on high-volume bars, and one summary label on the last bar.

All four tools appear in this script, and not one of them is doing another's job. That is how you keep a chart rich without letting it get crowded.

Common mistakes

  • Passing a number where a condition belongs. plotshape counts any non-zero value as true, so an accidental close - open puts a shape on nearly every bar.
  • Misspelling a style or location name. The script raises no error, yet the mark does not come out the way you expected. Copy the names exactly as listed.
  • Attaching a mark to a condition that holds on every bar. Tighten the condition first; if you are past a hundred marks, you are probably marking the wrong thing.
  • Calling label inside a condition without giving it a bar. The labels stack up on the last bar and none of them stays readable.
  • Using plotarrow without a filter. One arrow per bar means price disappears between them.
TIP

Give every mark a title. Unnamed ones end up as shape0, shape1 and so on, and once you have three or four of them you will lose track of which rule is which.

Function reference

plotshape(series, title?, style?, location?, color?, offset?, text?, textcolor?, size?)

Returns nothing. It stamps a shape on the bars where the condition holds.

series
series
A shape is drawn on the bars where this is true. Any value other than zero and na counts as true.
title
string
Optional. The name of the mark. Leave it out and a name such as shape0 is generated.
style
string
Optional. The form of the shape; circle by default. The shape.circle spelling works too.
location
string
Optional. Where the shape sits relative to the bar; abovebar by default. With absolute, the series value is taken as a price level.
color
color
Optional. Color of the shape, given as a hex code.
offset
number
Optional. Shifts the mark along the bar axis; -1 moves it one bar back.
text
string
Optional. Short text placed beside the shape.
textcolor
color
Optional. Color of that text.
size
string or number
Optional. tiny, small, normal, large, huge, or a pixel value between 7 and 40; normal by default.

plotchar(series, title?, char?, location?, color?, offset?, text?, textcolor?, size?)

Returns nothing. It stamps a single character on the bars where the condition holds.

series
series
A character is drawn on the bars where this is true.
title
string
Optional. The name of the mark.
char
string
Optional. The single character to stamp; a star glyph is used if you omit it.
location
string
Optional. Where the character goes; the same options as plotshape.
color
color
Optional. Color of the character.
offset
number
Optional. Shift along the bar axis.
text
string
Optional. Extra short text beside the character.
textcolor
color
Optional. Color of that text.
size
string or number
Optional. Size of the character; normal by default.

plotarrow(series, title?, colorup?, colordown?, offset?, minheight?, maxheight?)

Returns nothing. It draws up or down arrows according to a signed series.

series
series
Above zero draws an up arrow, below zero a down arrow. Bars at 0 or na get nothing.
title
string
Optional. The name of the arrows.
colorup
color
Optional. Color of the up arrows.
colordown
color
Optional. Color of the down arrows.
offset
number
Optional. Shift along the bar axis.
minheight
number
Optional. Pixel length of the shortest arrow; 5 by default.
maxheight
number
Optional. Pixel length of the longest arrow; 100 by default. Lengths are spread between these two limits by the magnitude of the series.

label(price, text, bar?, color?, style?, size?)

Returns nothing. It places a written box at the given price level.

price
number
Vertical position of the label, in price terms.
text
string
The text shown in the box. To show a number, turn it into text with str.
bar
number
Optional. The bar the label sits on. Leave it out and the last bar of the chart is used.
color
color
Optional. Background color of the box.
style
string
Optional. Form of the box; labelup and labeldown are the common ones.
size
string or number
Optional. Text size; normal by default.

mark(condition, style?, location?, color?, text?, size?)

Returns nothing. It stamps a plain mark on the bars where the condition holds.

condition
series
A mark is drawn on the bars where this is true.
style
string
Optional. Form of the mark; circle by default.
location
string
Optional. Where the mark sits; abovebar by default.
color
color
Optional. Color of the mark.
text
string
Optional. Short text beside the mark.
size
string or number
Optional. Size of the mark; normal by default.