Lifecycle Hooks & Chart Bars
Every script is built around a small set of lifecycle hooks and the bar data that flows through TD.onChartBar. This page covers both in detail.
Lifecycle Hooks
Add a hook by assigning a function to it - every hook is optional
TD.onStart Runs once, after the chart history has been passed through onChartBar. Use it to subscribe to extra symbols, fetch data, or log a start message.TD.onStop Runs when you click Stop. Use it to unsubscribe, remove temporary drawings, or save state - it has up to 5 seconds to finish.TD.onChartBar Runs once for every bar on the chart, then for every live bar. See Working with Chart Bars below.TD.onReset Runs when the chart's symbol or timeframe changes, just before new history is replayed.TD.onError Runs when your code throws an error during start or inside a TD.on listener.TD.onReset = (context) => { ... }All shapes and plots from your script have already been removed at this point, so clear your own arrays, maps and counters to start fresh. context is the new chart context: { symbol, underlying, exchange, symbolType, interval }. New bars have not arrived yet, so if you need to redraw something that doesn't come from onChartBar, schedule it with setTimeout.
let closes = []; TD.onReset = (ctx) => { closes = []; TD.log("Chart changed to " + ctx.symbol); }; TD.onError = (err) => TD.log("Error: " + err.message);
Scrolling the chart back to load older bars does not call onReset. The engine passes newly loaded older bars to onChartBar with isHistory = true, arriving after bars you've already seen - sort by bar.time if order matters.
Working with Chart Bars
TD.onChartBar = (bar, index, isHistory) => { ... }
bar.time Bar time. History bars use epoch milliseconds (a number).bar.open/high/low/close Pricesbar.volume 0 or null for symbols without volume, such as cash indices.index Position in history (0 = oldest). Live bars always use -1.isHistory true while existing chart bars replay, false for live bars.TD.on("bar5min") (see Live Market Data)await data calls inside it (see the Indicators section for the recommended pattern)TD.Chart.plot() and friends, called inside onChartBar, don't need an explicit timebar.time may not be a plain number - convert it before comparing, as belowconst toMs = (t) => t instanceof Date ? t.getTime() : typeof t === "number" ? (t < 1e12 ? t * 1000 : t) : Date.parse(t);
Hold only as many bars as your logic needs, and compare against the previous bar rather than recalculating from scratch.
const bars = []; TD.onChartBar = (bar, index, isHistory) => { bars.push(bar); if (bars.length > 500) bars.shift(); // keep memory bounded const n = bars.length; if (n < 2) return; const prev = bars[n - 2]; if (bar.close > prev.high) { TD.Chart.plotMarker(bar.low, "buy"); } };
Always pair TD.onChartBar with TD.onReset. Almost every "my indicator breaks after I change the symbol" problem is state left over from the previous symbol.