Visual Anti-Patterns
Wrong vs right for visual authoring — choosing the wrong visual family, mixing static and bridge fill identities, and stale attrs coordinates.
Wrong vs Right for Common Authoring Mistakes#
Choosing the wrong visual family#
# Wrong: create object machinery for a simple row-aligned line.
def build_visuals(frame, params=None, ctx=None):
return ctx.line.new(key="ema_line", x1=0, y1=100, x2=10, y2=101)
# Right: if the frame already has one value per row, stay with plot(...).
plot("ema_fast", key="ema_fast", title="EMA Fast", color="#00c853", width=2)Mixing static and bridge fill identities#
# Wrong: trying to fill between static keys with the bridge helper.
# ctx.atk.fill_between(line1="bb_upper", line2="bb_lower", ...)
# Right: bridge fills between bridge line keys.
ctx.atk.plot_line(key="fast_line", source="fast", color="#00c853")
ctx.atk.plot_line(key="slow_line", source="slow", color="#f23645")
ctx.atk.fill_between(key="trend_fill", line1="fast_line", line2="slow_line", fill_alpha=24)Computing inside build_visuals#
# Wrong: TA computation inside the render stage.
def build_visuals(frame, params=None, ctx=None):
ema = ta.ema(frame["close"], 20) # DO NOT do this here
last_ema = float(ema.iloc[-1])
return ctx.label.new(key="ema_label", x=int(frame.iloc[-1]["index"]), y=last_ema, text="EMA")
# Right: compute in build_indicator_frame, read in build_visuals.
def build_indicator_frame(df, params=None):
frame = df.copy().reset_index(drop=True)
frame["ema_fast"] = ta.ema(frame["close"], 20)
return frame
def build_visuals(frame, params=None, ctx=None):
last = frame.iloc[-1]
return ctx.label.new(
key="ema_label",
x=int(last["index"]),
y=float(last["ema_fast"]),
text="EMA",
)attrs Are for Static Config, Not Frozen Coordinates#
The safe rule is simple: colors, headers, labels, and other stable config can live in frame.attrs.
Coordinates, tail-derived anchors, and current slice geometry should be derived from the frame passed
into build_visuals. Otherwise historic or sliced rendering can reuse stale payloads.
Never store bar-index coordinates or price levels in frame.attrs. Those values change with each update.
Only store stable display config — colors, font sizes, header strings, style tokens.