Skip to content
GetProfitable
Search

Plotting close and an SMA

Lesson 3 · about 11 min

plot() is the function you will call more than any other. It takes a series and draws one value per bar. Everything else about it is styling: title, colour, width, line style, and where the plot lives. This lesson builds one script step by step so the options land in context rather than as a list.

Step one: the raw close and a moving average

//@version=6
indicator("Close and SMA", shorttitle="C+SMA", overlay=true)
length = 20
sma = ta.sma(close, length)
plot(close, "Close", color=color.gray, linewidth=1)
plot(sma, "SMA 20", color=color.blue, linewidth=2)

ta.sma(close, length) returns a series: on each bar, the average of the last twenty closes. On the first nineteen bars there are not enough values, so the result is na ("not available") and nothing is drawn. That gap at the left edge of every moving average is normal, not a bug.

The title string ("SMA 20") matters more than it looks. It appears in the data window, in the style settings dialog, and, importantly, it is how alert message placeholders like {{plot("SMA 20")}} refer to the plot later.

Step two: colours

Pine colours come three ways:

  • Named constants: color.blue, color.red, color.gray, color.orange, color.purple, color.teal, color.white, color.black and a few more.
  • RGB: color.rgb(33, 150, 243), with an optional fourth argument for transparency.
  • A base colour with transparency: color.new(color.blue, 70), where the second number is 0 (opaque) to 100 (invisible).

Colour can vary per bar because a colour is just another series. Replace the SMA plot with:

smaColor = close > sma ? color.green : color.red
plot(sma, "SMA 20", color=smaColor, linewidth=2)

Now the average is green while price is above it and red below. The ? : is a ternary: condition, value if true, value if false. It is the most common way to pick a colour, and we return to it in module 2.

Step three: fills and styles

plot() returns an ID. Two IDs can be handed to fill() to shade the space between them:

pClose = plot(close, "Close", color=color.gray)
pSma   = plot(sma, "SMA 20", color=smaColor, linewidth=2)
fill(pClose, pSma, color=color.new(smaColor, 85))

Line style is set with the style argument. The useful ones:

Style Looks like
plot.style_line default connected line
plot.style_linebr line that breaks where the series is na
plot.style_stepline horizontal steps
plot.style_histogram bars from zero
plot.style_columns wider bars from zero
plot.style_circles dots
plot.style_cross small crosses

plot.style_linebr is the one to remember. When you plot a stop level that only exists while you are in a trade, a normal line draws a diagonal from the last stop to the next one; linebr leaves the gap.

Overlay versus pane

overlay=true draws on the price pane using the price scale. overlay=false gives the script its own pane with its own scale. The choice is about units: a moving average of price belongs on price; an RSI, a volume ratio or a count of bars belongs in a pane, because plotting a 0 to 100 oscillator on a chart of a $4,000 index squashes it into a flat line.

A single script can put some plots in the other place using force_overlay=true on the individual plot() call, which is handy for an oscillator script that also wants to mark bars on price. You cannot go the other way and push a plot from an overlay script into a new pane.

Key idea: plot() draws one value per bar; colour, width and style can all be series, so anything you can compute you can display.

Putting it together

//@version=6
indicator("Close and SMA", shorttitle="C+SMA", overlay=true)
length = 20
sma = ta.sma(close, length)
smaColor = close > sma ? color.green : color.red
pClose = plot(close, "Close", color=color.gray, linewidth=1)
pSma   = plot(sma, "SMA 20", color=smaColor, linewidth=2)
fill(pClose, pSma, color=color.new(smaColor, 85))
plot(ta.sma(close, 50), "SMA 50", color=color.new(color.orange, 30), style=plot.style_circles)

Notice the last line calls ta.sma directly inside plot(). That is fine; a function call is an expression and can go anywhere a series is expected. As scripts grow, giving intermediate results a name makes them easier to read and lets you reuse them, so prefer sma50 = ta.sma(close, 50) and then plot(sma50, ...).

Limits worth knowing now

A script can have at most 64 plot-type outputs (plot, plotshape, plotchar, bgcolor, fill all count). You will not hit that soon, but it explains why heavy community indicators use labels and lines instead of plots. Also, plot() must be called in the global scope: you cannot put it inside an if. To plot conditionally, plot condition ? value : na instead.

Try it: Change length to 50, then 200, and look at the left edge of the chart each time. Then change the second plot to style=plot.style_histogram and set overlay=false. Finally, put the plot() inside an if close > sma block and read the error; then fix it with a ternary.

Recap

  • plot(series, title, color, linewidth, style) draws one value per bar; title is used by alerts later.
  • Colours are named constants, color.rgb() or color.new(base, transparency), and can change per bar.
  • fill() shades between two plot IDs; plot.style_linebr breaks lines at na.
  • overlay decides price pane versus own pane; match it to the units of what you plot.
  • Plots must be in the global scope; use cond ? value : na to plot conditionally.

See it drawn

Original diagrams for the ideas on this page. Illustrative, not real market data.

Payoff of a long call at expiryA flat loss equal to the premium below the strike, turning upward at 45 degrees above it.Profit / loss per share08595115125Strike 105Max loss 3 — the premium paidBreakeven 108Profit keeps growingUnderlying price at expiry
Buying a call: payoff at expiry. A 105-strike call bought for 3 loses that whole 3 if the price finishes at or below 105, breaks even at 108, then gains a dollar for every dollar higher. The loss is capped at the premium; the upside is not capped.
A fast and a slow moving average crossingA jagged price line with two smoother average lines through it; the fast average dips below the slow one on the left and cuts back above it in the middle, where a circle marks the crossing.pricefast averageslow averagefast crosses belowfast crosses abovethe slow averageAverages of recent closes; the fast one reacts sooner than the slow one.
Fast and slow moving averages crossing. A moving average is the average of the last few closing prices, redrawn each period. An average over fewer periods turns sooner than one over many, so the two lines cross whenever the recent pace of the market changes.
The spread of outcomes behind an expectancyA histogram of forty trades: a tall block of small losses on the left, a low spread of larger wins on the right, and a line marking the average outcome.NUMBER OF TRADES051024 LOSSES, AVG −$20016 WINS, AVG +$600EXPECTANCY +$120−$400−$200$0+$200+$400+$600+$800PROFIT OR LOSS PER TRADEexpectancy = (40% × $600) − (60% × $200) = +$120 per trade
Expectancy: the average trade. Forty trades sorted by outcome: 24 small losses and 16 larger wins. Weighting each side by how often it happens gives the average result per trade, marked here by the dashed line at +$120.

Finished this module? Take the module quiz.