Skip to main content
ATK Pine Script®

ctx.atk.* Bridge Reference

Reference for the ctx.atk.* bridge namespace in ATK PyneScript V6 — tables, rectangles, shape text, polylines, candlesticks, volume profiles, and fill_between.

ctx.atk.* Overview#

The ctx.atk.* bridge provides ATK-native composite renderers that go beyond what static plot declarations can express. All bridge calls belong exclusively inside build_visuals(frame, params, ctx).

HelperUse it for
ctx.atk.tableDashboards and summary cards. Build the payload in the frame builder and render it here.
ctx.atk.rectanglesGrouped support, resistance, or regime zones that belong together as a batch.
ctx.atk.volume_profileRenders a pre-computed volume profile payload. Does not calculate the profile itself.
ctx.atk.shape_textArrow shapes with text labels at specified coordinates.
ctx.atk.polylineMulti-point connected path lines.
ctx.atk.rectangle_rayA projected rectangle that extends to the right edge.
ctx.atk.plot_lineATK bridge line linked to a frame column, usable with fill_between.
ctx.atk.fill_betweenFill between two ATK bridge lines created by ctx.atk.plot_line.
ctx.atk.candlestickFull OHLCV candle rendering through the ATK bridge.
ctx.atk.horizontal_barHorizontal profile bars at specified price levels.
ctx.atk.dual_horizontal_bar_fixedTwo overlaid horizontal profile bar layers at fixed x coordinates.

build_visuals Contract#

def build_visuals(frame, params=None, ctx=None):
    if frame is None or frame.empty or ctx is None:
        return None

    table_payload   = dict(frame.attrs.get("visual_table_payload")   or {})
    zone_payload    = dict(frame.attrs.get("visual_zone_payload")     or {})
    profile_payload = dict(frame.attrs.get("volume_profile_payload")  or {})

    outputs = []
    if table_payload:
        outputs.append(ctx.atk.table(key="summary_table", **table_payload))
    if zone_payload:
        outputs.append(ctx.atk.rectangles(key="zone_batch", **zone_payload))
    if profile_payload:
        outputs.append(ctx.atk.volume_profile(profile_payload, key="volume_profile", orient="right"))
    return outputs

ctx.atk.table#

Build the payload in build_indicator_frame, store it in frame.attrs, then render in build_visuals.

frame.attrs["visual_table_payload"] = {
    "position": "top-left",
    "title": "Signal Summary",
    "columns": 2,
    "rows": 3,
    "cells": [
        {"column": 0, "row": 0, "text": "Metric", "text_color": "#ffffff", "bgcolor": "#0f172a"},
        {"column": 1, "row": 0, "text": "Value",  "text_color": "#ffffff", "bgcolor": "#0f172a"},
        {"column": 0, "row": 1, "text": "Close"},
        {"column": 1, "row": 1, "text": f"{float(last['close']):.2f}", "text_color": "#00c853"},
    ],
}

# In build_visuals:
return ctx.atk.table(key="annotated_dashboard", **payload)

ctx.atk.shape_text, polyline, rectangles, rectangle_ray#

# In build_visuals — all geometry prepared in frame builder:
return [
    ctx.atk.shape_text(
        key="atk_shape_marks",
        points=[(float(last["index"]), float(last["close"]))],
        shapes=[Shape.ArrowUp],
        shape_sizes=[12.0],
        shape_colors=["#2962ff"],
        texts=["LONG"],
        text_sizes=[9.0],
        text_colors=["#ffffff"],
        fonts=["Arial"],
        weights=[500],
        locations=[Location.Above],
        positions=[Position.AboveBar],
    ),
    ctx.atk.polyline(
        key="atk_polyline_path",
        points=[
            (float(first["index"]), float(first["close"])),
            (float(prev["index"]),  float(prev["high"])),
            (float(last["index"]),  float(last["close"])),
        ],
        line_color="#ffd600",
        line_width=1.0,
        show_points=True,
    ),
    ctx.atk.rectangles(
        key="atk_rectangles_zone",
        data={
            "rectangles": [(float(prev["index"]), float(prev["low"] - 0.2),
                            float(last["index"]), float(last["high"] + 0.2))],
            "line_colors": ["#00c853"],
            "line_widths": [1.0],
            "fill_colors": ["#00c853"],
            "fill_alphas": [26],
        },
    ),
    ctx.atk.rectangle_ray(
        key="atk_rectangle_ray_zone",
        x=float(prev["index"]),
        h1=float(prev["low"] - 0.3),
        h2=float(last["high"] + 0.3),
        border_color="#ff6d00",
        fill_color="#ff6d00",
        border_width=1,
    ),
]

ctx.atk.plot_line and fill_between#

def build_visuals(frame, params=None, ctx=None):
    return [
        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,
        ),
    ]

ctx.atk.volume_profile#

# In build_indicator_frame:
try:
    payload = calculate_volume_profile(frame.copy(), look_back, row_size, value_area_pct)
except Exception:
    payload = {}
frame.attrs["volume_profile_payload"] = dict(payload or {})

# In build_visuals:
payload = dict(frame.attrs.get("volume_profile_payload") or {})
return ctx.atk.volume_profile(
    payload,
    key="dual_volume_profile",
    orient="right",
    up_color="#0126A0",
    down_color="#ffa32b",
    value_area_title="VA",
    poc_title="POC",
)

build_visuals is render-only. Never compute TA, call request.security, or mutate the frame inside build_visuals. Prepare everything in build_indicator_frame or build_signal_frame, store static config in frame.attrs, then derive current anchors from the live frame slice passed in.

chart.point Recipes#

chart.point creates coordinate anchors for object-style visuals. Use from_index for bar-position anchors and from_time for absolute timestamp anchors.

from source import box, chart, line

# Index-based anchors — easiest for local bar objects.
trend = line.new(
    chart.point.from_index(10, 100.0),
    chart.point.from_index(20, 105.0),
    key="trend_line",
    color="#2962ff",
)

# Time-based anchor update.
line.set_xy2(trend, chart.point.from_time(1700000600, 106.0))

zone = box.new(
    chart.point.from_index(12, 108.0),
    chart.point.from_index(20, 99.0),
    key="zone_box",
    text="ZONE",
    bgcolor="rgba(41,98,255,0.08)",
)

line, label, box Recipes#

NamespaceBest first methodsWhat to remember
linenew, set_xy1, set_xy2, set_colorUse stable keys if the same conceptual object should persist and update.
labelnew, set_text, set_colorChoose labels when a point needs a message, not just a marker.
boxnew, set_lefttop, set_rightbottom, set_bgcolorBoxes are best for zones and ranges, not simple one-point annotations.
def build_visuals(frame, params=None, ctx=None):
    if frame is None or frame.empty or ctx is None:
        return None

    last   = frame.iloc[-1]
    anchor = frame.iloc[max(len(frame) - 10, 0)]

    ctx.label.new(
        key="last_close_label",
        x=int(last["index"]),
        y=float(last["close"]),
        text="LAST",
        color="#2962ff",
        style="label_down",
    )

    range_box = ctx.box.new(
        key="recent_range_box",
        left=int(anchor["index"]),
        top=float(last["high"]),
        right=int(last["index"]),
        bottom=float(last["low"]),
        bgcolor="rgba(41,98,255,0.08)",
    )
    ctx.box.set_lefttop(range_box, x=int(anchor["index"]), y=float(last["high"] + 0.5))

    return ctx.line.new(
        key="close_slope_line",
        x1=int(anchor["index"]),
        y1=float(anchor["close"]),
        x2=int(last["index"]),
        y2=float(last["close"]),
        color="#ffd600",
    )

See also: