# Build and verify a script

> A short path from an idea to a checked indicator: data coverage, warm-up, stable signals, runtime checks and common mistakes.

Start with the smallest working signal. Add a dashboard, extra markets and alerts
after the signal has been checked. This is also the recommended workflow for the
AI assistant.

## Decide what the signal promises

State the market and chart interval, the condition for long and short, and **when
the condition becomes knowable**. Use these defaults unless the trader asks for
something else:

- Decisions on `on chart.close`, using confirmed higher-timeframe values.
- Live previews in `on chart.update`, clearly named as developing.
- Entries marked at detection time, never moved to an earlier pivot.
- No invented substitute for missing market data. A missing filter blocks a
  signal; it does not become a neutral or passing filter via `?? 0.0`.

A pivot with three right bars requires three later bars. It cannot be both
confirmed and known on the pivot bar. A structural line may point back to that
pivot, but an entry marker must show when the pivot was detected. For an earlier
signal, change the rule to a causal condition such as a break of an already known
level; explain that this is a different signal. See [repainting](/docs/scripting/guides/repainting).

## Check the data before writing the full script

The chart host builds period subscriptions from loaded chart bars. Requested
timeframes must be whole multiples of the chart interval: a 5s chart can supply
1m bars, but a 1m chart cannot supply 5s bars. Declaring a subscription does not
fetch an independent, unlimited history for it.

Count **source bars**, not chart bars. Fifty hourly candles require at least
50 hours of history (36,000 bars on a 5s chart), plus room for alignment and
warm-up. A seeded EMA may return a number with much less history; a number alone
does not prove that its warm-up is adequate. Loading more history can change its
seed and early values.

Other-market subscriptions on charts currently support `ohlcv`, `vd` and `cvd`.
The quant lab does not supply other-market subscriptions, order-book snapshots
or volume profiles. It rejects those requests rather than treating unavailable
data as a successful study with no trades. Run those strategies on the chart
and inspect the Strategy Tester instead.

## Use the shortest verification loop

1. Read `chart_context`. Select one relevant example and use `lookup_builtins`
   for uncertain signatures; names and arguments are case-sensitive.
2. Write a minimal, formatted script with named inputs and bounded collections.
   Read only the subscriptions the calculation needs.
3. Call `check_script`. It compiles, evaluates setup and checks timeframe/data
   requirements against the active chart without running handlers. Fix errors;
   inspect the reported number of confirmed bars and warm-up needs.
4. Call `apply_script` once the checks pass. Inspect runtime errors, computation
   time, actual subscription coverage, plotted values and marks. Empty output
   is a finding to investigate, not proof that no trading opportunities exist.
5. Use `look_at_chart` to check placement and readability. For a strategy, inspect
   `run_backtest`, its tested period, costs and trade count.
6. For a stability claim, step replay across a signal and across a higher-timeframe
   boundary. Record the detection bar. Advance several bars and compare it with
   the same period after replay/reload using the same inputs and source history.

Compilation, successful execution, visible output and signal stability are four
separate checks. Report which actually passed. A screenshot alone cannot verify
that historical signals never changed.

## Common mistakes and direct fixes

| Symptom | Check or fix |
|---|---|
| Compiles, but nothing appears | Subscription coverage, compatible timeframes, nulls, warm-up and signal conditions |
| EMA or cross behaves differently after a filter change | Evaluate every `ta.*` call on every event of its intended clock, then filter its result; do not hide stateful calls inside `if` or short-circuit expressions |
| Higher-timeframe EMA changes too often | Calculate it in `on higher.close` and store its result in `state`; do not feed repeated higher closes to a chart-clock EMA |
| Entry suddenly appears several bars back | Pivot confirmation delay, negative `offset`, or an explicit past timestamp; separate pivot location from detection time |
| Live arrow appears and vanishes | Forming data or an update handler; offer a separate confirmed signal |
| Indicator trails the price | Distinguish candle-close waiting, pivot confirmation, indicator smoothing and computation time before changing the rule |
| Slow chart or budget error | Remove per-bar scans, prefer `ta.*` or bounded rolling aggregates, draw status cards only under `isLast` |

For runtime limits and browser scheduling, see [performance](/docs/scripting/performance).
