Best Practices and Anti-Patterns
Recommended patterns and common mistakes to avoid when writing ATK PyneScript V6 scripts.
Best Practices#
Do this#
- Compute everything in the appropriate frame builder.
- Use
visual_*columns for row-aligned visuals. - Use
frame.attrsonly for static object config. - Use stable
keyvalues for visuals and inputs. - Prefer
build_mapped_trade_framefor strategies. - Use
import_library(...)inside raw.pyscripts and reserve quoted imports for editor-preprocessed source. - Quote examples from
source/example_user_scriptsso users can paste and run them immediately.
Do not do this#
- Do not compute TA inside
build_visuals. - Do not compute strategy logic inside
build_trade_frame. - Do not store dynamic, slice-sensitive payloads entirely inside
frame.attrs. - Do not teach users outdated examples that hide stage boundaries.
- Do not fix runtime parity problems only inside user examples when the correct fix belongs upstream.
Stage Ownership Summary#
| Stage | What belongs here | What must not happen here |
|---|---|---|
build_indicator_frame | All indicator series computation, source resolution, warmup handling, row-oriented visual_* prep, static attrs config. | Final drawing calls through ctx.*. |
build_signal_frame | Signal logic, filter logic, canonical trade-frame execution fields, strategy visuals prep. | Order schema normalization beyond mapping-only helpers. |
build_trade_frame | Mapping-only normalization, usually build_mapped_trade_frame(...). | ta.*, request.*, rolling windows, dataframe grouping, signal recomputation. |
build_visuals | Render-only mapping into ctx.* or ctx.atk.*. | Mutating the frame, computing TA, storing stale full-frame dynamic payloads in frame.attrs. |
Decision Guide#
| If you need to... | Put it here | Reason |
|---|---|---|
Choose between open, close, hl2, or a user-selected source column | build_indicator_frame or build_signal_frame | Source selection is part of computation, not rendering. |
| Compute EMA, ATR, BBands, MACD, or MTF trend filters | build_indicator_frame or build_signal_frame | All TA and request work belongs in compute stages. |
Emit entry_price, sl, tp | build_signal_frame | Strategies should fully prepare execution fields before trade mapping. |
| Convert canonical trade-frame columns into the runtime's normalized schema | build_trade_frame | This stage exists for normalization, not for new trading logic. |
| Create a dashboard table, zone box, or object bundle from prepared payload | build_visuals | It is pure rendering of already-prepared data. |
| Store a static style bundle such as table colors, widths, headers, or label templates | frame.attrs | Static config is safe here because it does not depend on the current slice length. |
| Store last-row x/y anchors, projected tails, or slice-dependent indices | Do not pre-store them wholesale in frame.attrs | Those values can go stale when the runtime passes only a slice into build_visuals. |
If you use frame.attrs for object-like visuals, store only static config there and derive dynamic x, y, tail, or slice-dependent values from the actual frame passed into build_visuals().