Skip to main content
ATK Pine Script®

plot Family Functions Reference

Reference for plot, plotbar, plotchar, plotarrow, plotshape, barcolor, bgcolor, fill, linefill, and ctx.atk.fill_between in ATK PyneScript V6.

plot Family Overview#

FunctionFirst argument meaningUse it when
plot(source, ...)A dataframe column name created later in the frame builder.You need a row-aligned line or series visual.
plotbar(open, high, low, close, ...)Four dataframe columns that already exist in the prepared frame.You want OHLC bars without dropping to low-level candle routing.
plotchar(series, ...)A boolean or sparse series that decides where characters appear.You want lightweight textual markers like B, S, or dots.
plotarrow(series, ...)A signed numeric strength series.You want up/down arrow direction encoded in the series value itself.
barcolor(series, ...)A per-row color column.You want to tint bars according to signal or regime state.
bgcolor(series, ...)A boolean or sparse activation series.You want a session, state, or regime background band.
fill(ref1, ref2, ...)Two plot or hline references.You want the space between two existing static declaration visuals.
linefill.new(line1, line2, ...)Two line identities, usually plot keys.You want a persistent fill relationship between two named line visuals.
plotshape(series, ...)A boolean or sparse marker series.You need point markers above, below, or at bars.
ctx.atk.fill_between(...)Two ATK bridge line keys.You are already using ctx.atk.plot_line instead of static declarations.

Fastest rule: source usually means dataframe column, key means runtime identity, and title means the user-facing label. These are three distinct things — do not conflate them.

plot() Parameter Encyclopedia#

ParameterHow to choose it
sourceUse the exact output column name that the frame builder will write. If the builder writes frame["ub"], then plot("ub", ...) is the correct source.
keyUse a stable visual identity such as bb_upper. This is the name other visuals can reference later, including fills.
titleUse the human-readable legend label. Keep it short enough for chart legends and settings panels.
colorUse it for default styling only. Do not encode logic here that should live in a series or a separate user setting.
widthUse line width to show visual importance. A basis line might be thicker than its surrounding band boundaries.

plot() Decoded Line by Line#

plot(
    "ub",                 # source: dataframe column name written later by the frame builder
    key="bb_upper",       # key: stable runtime identity other visuals can reference
    title="Upper Band",   # title: user-facing legend or settings label
    color="#00aa00",      # color: default style, not the series logic itself
    width=1,              # width: visual importance and readability
)

Wrong vs Right: source vs key#

# Wrong: using bb_upper as source when the frame writes "ub"
# plot("bb_upper", key="bb_upper", ...)

# Right: source matches the dataframe column; key is the visual identity
plot("ub", key="bb_upper", title="Upper Band", color="#00aa00", width=1)

BBands Pattern: plot and linefill Together#

plot("lb", key="bb_lower", title="Lower", color="#ff0000", width=1)
plot("cb", key="bb_basis", title="Basis", color="#ffa500", width=1)
plot("ub", key="bb_upper", title="Upper", color="#00aa00", width=1)

# linefill.new links visual identities (plot keys), not dataframe column names.
linefill.new("bb_lower", "bb_upper", key="bb_band_fill", color="rgba(63,57,100,0.44)")


def build_indicator_frame(df, params=None):
    frame = df.copy().reset_index(drop=True)
    upper, basis, lower = ta.bbands(frame["close"], length=20, std=2.0, mamode="sma")
    frame["lb"] = lower.reset_index(drop=True)
    frame["cb"] = basis.reset_index(drop=True)
    frame["ub"] = upper.reset_index(drop=True)
    return frame

plotshape(), linefill.new(), and fill_between() Contrast#

plotshape — Wrong vs Right#

# Wrong: trying to invent the trigger inside the declaration mindset.
# plotshape("close > ema", ...)

# Right: prepare the trigger column first.
frame["buy_marker"] = (frame["close"] > frame["ema_fast"]).fillna(False)
plotshape(
    "buy_marker",
    key="buy_marker",
    location="belowbar",
    style="arrow_up",
    color="#00c853",
    text="BUY",
)

linefill.new — Wrong vs Right#

# Wrong: passing dataframe columns instead of visual identities.
# linefill.new("ub", "lb", key="bb_fill", color="rgba(63,57,100,0.44)")

# Right: pass the plot keys created by the declarations.
plot("lb", key="bb_lower", title="Lower", color="#ff0000", width=1)
plot("ub", key="bb_upper", title="Upper", color="#00aa00", width=1)
linefill.new("bb_lower", "bb_upper", key="bb_fill", color="rgba(63,57,100,0.44)")

ctx.atk.fill_between — Wrong vs Right#

# Wrong: mixing static line identities with bridge line identities.
# ctx.atk.fill_between(line1="bb_upper", line2="bb_lower", ...)

# Right: fill between lines created through ctx.atk.plot_line.
ctx.atk.plot_line(key="atk_fast_line", source="fast", color="#00c853")
ctx.atk.plot_line(key="atk_slow_line", source="slow", color="#f23645")
ctx.atk.fill_between(
    key="atk_fast_slow_fill",
    line1="atk_fast_line",
    line2="atk_slow_line",
    color="rgba(41,98,255,0.12)",
    fill_alpha=31,
)

Plot-Family Migration Bundle#

plotbar("open", "high", "low", "close", key="ohlc_surface", color="#2962ff")
plotchar("breakout_signal", key="breakout_chars", char="B", location="abovebar", color="#00c853")
plotarrow("trend_strength", key="trend_arrows", colorup="#00c853", colordown="#f23645")
barcolor("bar_tint", key="bar_tint")
bgcolor("session_bg_signal", key="session_bg", color="rgba(41,98,255,0.10)")

Visual API Decision Tree#

Need to draw something?
|
+-- Does each bar naturally map to one series value or marker?
|   |
|   +-- Yes
|   |   +-- Need a line or series?          -> use plot(...)
|   |   +-- Need a fixed threshold?         -> use hline(...)
|   |   +-- Need a marker?                  -> use plotshape(...), plotchar(...), or plotarrow(...)
|   |   +-- Need fill between static lines? -> use fill(...) or linefill.new(...)
|   |
|   +-- No
|       +-- Explicit chart object (line/label/box)?      -> use ctx.line / ctx.label / ctx.box
|       +-- ATK-native composite (table/zone/profile)?   -> use ctx.atk.*
|
+-- Already using ctx.atk.plot_line for the lines?
    +-- Yes -> use ctx.atk.fill_between(...)
    +-- No  -> keep static declarations and use fill(...) or linefill.new(...)

If the first thing you think is "I just need one more dataframe column", stay in the static declaration family. If the first thing you think is "I need an object" or "I need a grouped payload", move to ctx.* or ctx.atk.*.

See also: