Reference & Support
Logging, alerts and storage; sandbox limits; six complete example scripts; troubleshooting; and a full quick-reference of every TD function.
Logging, Alerts & Storage
Getting information out of your script, and remembering things between runs
TD.log("Price:", bar.close); // normal line console.log({ a: 1, b: [1, 2] }); // objects shown as formatted JSON console.warn("Careful"); // prefixed with ?? console.error("Something broke"); // red error line console.table(rows); // table (first 50 rows, 20 columns)
Alert types are success, error, info and warning. Storage persists in this browser.
await TD.UI.showAlert("NIFTY crossed 25,000", "success"); await TD.Storage.setItem("myScript.lastSignal", JSON.stringify({ side: "buy", time: Date.now() })); const raw = await TD.Storage.getItem("myScript.lastSignal"); const last = raw ? JSON.parse(raw) : null;
JSON.stringify and JSON.parse for objectsmyScript.) so they don't collide with other scriptsLimits & Sandbox Rules
Scripts run in a secure sandbox, and these limits keep the platform stable
setTimeout and setInterval - the script is haltedonStop - after that the script is endedDisabled in scripts: fetch, XMLHttpRequest, WebSocket, EventSource, Worker, document, window, localStorage, sessionStorage, indexedDB, navigator, location, alert, confirm, prompt, eval, Function, importScripts, requestAnimationFrame, queueMicrotask. To get data use the TD.* functions, to show messages use TD.log and TD.UI.showAlert, and to save data use TD.Storage.
Avoid tight loops. while (true) {} or very long synchronous calculations freeze the script and it gets terminated. For repeating work, use TD.on(...) events or a timer of 100 ms or more.
Complete Examples
Six full scripts covering the most common patterns end to end
const FAST = 9, SLOW = 21; let emaFast = null, emaSlow = null, prevDiff = null, count = 0; const ema = (prev, price, len) => prev === null ? price : prev + (2 / (len + 1)) * (price - prev); TD.onReset = () => { emaFast = emaSlow = prevDiff = null; count = 0; }; TD.onChartBar = (bar, index, isHistory) => { emaFast = ema(emaFast, bar.close, FAST); emaSlow = ema(emaSlow, bar.close, SLOW); count++; if (count < SLOW) return; TD.Chart.plot("EMA Fast", emaFast, { color: "#22C55E" }); TD.Chart.plot("EMA Slow", emaSlow, { color: "#EF4444" }); const diff = emaFast - emaSlow; if (prevDiff !== null) { if (prevDiff <= 0 && diff > 0) { TD.Chart.plotMarker(bar.low, "buy"); if (!isHistory) TD.UI.showAlert("EMA bullish crossover on " + TD.Chart.getSymbol(), "success"); } else if (prevDiff >= 0 && diff < 0) { TD.Chart.plotMarker(bar.high, "sell"); if (!isHistory) TD.UI.showAlert("EMA bearish crossover on " + TD.Chart.getSymbol(), "warning"); } } prevDiff = diff; };
This example updates the EMA on every bar it receives. Live bars are 1-minute bars (see Working with Chart Bars), so on higher timeframes the live values will differ from the chart candles. For exact results, combine 1-minute bars into chart candles first.
const QUIET = { savable: false, selectable: false, hoverable: false }; let days = new Map(); // "YYYY-MM-DD" -> { high, low } TD.onReset = () => { days = new Map(); }; TD.onChartBar = (bar, index, isHistory) => { if (!isHistory) return; const key = new Date(bar.time).toISOString().slice(0, 10); const d = days.get(key) || { high: -Infinity, low: Infinity }; d.high = Math.max(d.high, bar.high); d.low = Math.min(d.low, bar.low); days.set(key, d); scheduleDraw(); }; let timer = null; function scheduleDraw() { clearTimeout(timer); timer = setTimeout(draw, 200); } function draw() { const keys = [...days.keys()].sort(); if (keys.length < 2) return; const prev = days.get(keys[keys.length - 2]); TD.Chart.plotShape("horizontalLine", prev.high, Object.assign({ id: "pdh", color: "#16A34A", lineStyle: "dash" }, QUIET)); TD.Chart.plotShape("horizontalLine", prev.low, Object.assign({ id: "pdl", color: "#DC2626", lineStyle: "dash" }, QUIET)); TD.Chart.plotText("pdh_t", "PDH", prev.high, Object.assign({ anchor: "right", offset: 0.08, offsetYPx: -8, textColor: "#16A34A" }, QUIET)); TD.Chart.plotText("pdl_t", "PDL", prev.low, Object.assign({ anchor: "right", offset: 0.08, offsetYPx: 8, textColor: "#DC2626" }, QUIET)); }
const LEVELS = { "RELIANCE": { above: 3050, below: 2950 }, "HDFCBANK": { above: 1750, below: 1680 } }; const fired = new Set(); TD.on("tick", (d) => { const rule = LEVELS[d.symbol]; if (!rule || !d.ltp) return; if (d.ltp >= rule.above && !fired.has(d.symbol + ":up")) { fired.add(d.symbol + ":up"); TD.UI.showAlert(d.symbol + " above " + rule.above + " (" + d.ltp + ")", "success"); } if (d.ltp <= rule.below && !fired.has(d.symbol + ":down")) { fired.add(d.symbol + ":down"); TD.UI.showAlert(d.symbol + " below " + rule.below + " (" + d.ltp + ")", "warning"); } }); TD.onStart = async () => { await TD.Feed.addSymbols(Object.keys(LEVELS)); TD.log("Watching " + Object.keys(LEVELS).join(", ")); };
const QUIET = { savable: false, selectable: false, hoverable: false }; async function update() { const chain = await TD.OptionChain.getOptionChain(); if (chain.error) { TD.log("Chain error: " + chain.error); return; } const c = chain.columns; let callOI = 0, putOI = 0, maxCall = null, maxPut = null; for (const r of chain.records) { const co = Number(r[c.callOI]) || 0, po = Number(r[c.putOI]) || 0; callOI += co; putOI += po; if (!maxCall || co > maxCall.oi) maxCall = { strike: Number(r[c.strike]), oi: co }; if (!maxPut || po > maxPut.oi) maxPut = { strike: Number(r[c.strike]), oi: po }; } const pcr = callOI ? (putOI / callOI).toFixed(2) : "–"; TD.log(chain.resolved.symbol + " " + chain.resolved.expiry + " · PCR " + pcr + " · Max CE OI " + maxCall.strike + " · Max PE OI " + maxPut.strike); TD.Chart.plotShape("horizontalLine", maxCall.strike, Object.assign({ id: "maxce", color: "#DC2626", lineStyle: "dot" }, QUIET)); TD.Chart.plotShape("horizontalLine", maxPut.strike, Object.assign({ id: "maxpe", color: "#16A34A", lineStyle: "dot" }, QUIET)); TD.Chart.plotText("pcr", "PCR " + pcr, maxCall.strike, Object.assign({ anchor: "right", offset: 0.1, offsetYPx: -10 }, QUIET)); } TD.onStart = async () => { await update(); setInterval(update, 60000); // refresh every minute }; TD.onReset = () => setTimeout(update, 1500); // chart symbol changed → reload chain
const bars = []; let timer = null; TD.onReset = () => { bars.length = 0; }; TD.onChartBar = (bar) => { bars.push(bar); clearTimeout(timer); timer = setTimeout(run, 400); }; async function run() { const st = await TD.Chart.getIndicatorValues("FastSuperTrend", { periods: 10, multiplier: 3 }); if (st.error) { TD.log(st.error); return; } const byTime = new Map(bars.map(b => [b.time, b])); const up = st.values["Up"]; for (let i = 1; i < st.times.length; i++) { const nowUp = up[i] !== null, wasUp = up[i - 1] !== null; const b = byTime.get(st.times[i]); if (!b || nowUp === wasUp) continue; TD.Chart.plotMarker(nowUp ? b.low : b.high, nowUp ? "buy" : "sell", { id: "st_" + b.time, time: b.time }); } }
Replace OPTION with a live strike and expiry. Option symbols are written as underlying + expiry (YYMMDD) + strike + CE/PE, so NIFTY26100622600CE is the NIFTY 22600 call expiring 06-10-2026. The option symbol receives greeks and bidAsk (NSE top of book), and the BSE symbol receives bidAskL2 (5-level depth).
const OPTION = "NIFTY26100622600CE"; const NSE_EQ = "RELIANCE"; const BSE_EQ = "RELIANCE_BSE"; const SYMBOLS = [OPTION, NSE_EQ, BSE_EQ]; // Quote events can arrive many times a second, so log each symbol at most once every 5s. const lastLog = {}; const throttled = (key, fn) => { const now = Date.now(); if (now - (lastLog[key] || 0) < 5000) return; lastLog[key] = now; fn(); }; TD.onStart = async () => { try { await TD.Feed.addSymbols(SYMBOLS); console.log("Subscribed:", SYMBOLS.join(", ")); } catch (err) { console.error("Subscribe failed:", err.message); } }; TD.onStop = async () => { await TD.Feed.removeSymbols(SYMBOLS); console.log("Unsubscribed"); }; // Trades (also fires when Greeks or bid/ask change for the symbol) TD.on("tick", (d) => throttled("tick:" + d.symbol, () => console.log(`${d.symbol} LTP ${d.ltp} Vol ${d.volume}`) )); // Completed OHLCV bars TD.on("bar1min", (d) => console.log(`1m ${d.symbol} O ${d.open} H ${d.high} L ${d.low} C ${d.close} V ${d.volume}`) ); TD.on("bar5min", (d) => console.log(`5m ${d.symbol} O ${d.open} H ${d.high} L ${d.low} C ${d.close} V ${d.volume}`) ); // Option Greeks (option symbols only) TD.on("greeks", (d) => throttled("greeks:" + d.symbol, () => console.log(`${d.symbol} IV ${d.iv} Delta ${d.delta} Gamma ${d.gamma} Theta ${d.theta} Vega ${d.vega}`) )); // NSE / MCX top of book TD.on("bidAsk", (d) => throttled("ba:" + d.symbol, () => console.log(`${d.symbol} Bid ${d.bid} x ${d.bidQty} | Ask ${d.ask} x ${d.askQty} (spread ${(d.ask - d.bid).toFixed(2)})`) )); // BSE 5-level depth TD.on("bidAskL2", (d) => throttled("l2:" + d.symbol, () => { console.log(`${d.symbol} depth (total bid ${d.depth.totalBidQty} / ask ${d.depth.totalAskQty})`); console.table(d.depth.bids.map((b, i) => ({ bidOrders: b.orders, bidQty: b.qty, bid: b.price, ask: d.depth.asks[i]?.price, askQty: d.depth.asks[i]?.qty, askOrders: d.depth.asks[i]?.orders }))); })); // Connection alive TD.on("heartbeat", (d) => console.log("Heartbeat", d.timestamp));
Troubleshooting
Common symptoms, their likely cause, and the fix
TD.onChartBar = ... and didn't just declare a function, and that your plotted value is a number, not NaN.TD.on(...) listener, TD.onChartBar, or a timer if it should keep running.clearTimeout the old one before creating a new one.id so plotting again updates them instead of adding new ones.TD.onReset.onChartBar bars are 1-minute bars. Combine them into chart candles first - see Working with Chart Bars.greeks, bidAsk or bidAskL2 events Subscribe to the symbol first with TD.Feed.addSymbols. greeks fires for option symbols only, bidAsk for NSE and MCX symbols, and bidAskL2 for BSE symbols.[API Error: ...] in the console The data request failed. Check symbol spelling, date format (YYYY-MM-DD HH:mm:ss), interval, and your data plan.fetch is disabled Browser networking isn't available in scripts. Use the TD.* data functions."[object Object]" Use JSON.stringify before TD.Storage.setItem and JSON.parse after getItem.console.table(res.records.slice(0, 5))try { ... } catch (e) { console.error(e); } - the full stack trace is shown in the consoleFunction Quick Reference
Every function in the TD object, in one place
Lifecycle TD.onStart = async () => {} TD.onStop = async () => {} TD.onChartBar = (bar, index, isHistory) => {} TD.onReset = (ctx) => {} TD.onError = (err) => {} Chart TD.Chart.getContext() → { symbol, underlying, exchange, symbolType, interval } TD.Chart.getSymbol() → string TD.Chart.plot(name, value, { color, overlay, style, time }) TD.Chart.plotMarker(price,"buy"|"sell", { id, color, shape, time }) TD.Chart.plotShape(type, price, { id, points, color, lineColor, lineWidth, lineStyle, fillColor, fillOpacity, textColor, fontSize, time, savable, selectable, hoverable, locked }) TD.Chart.plotBox(id, { low, high, from, to | anchor, offset, width, heightPx, heightAnchor }, styleOptions) TD.Chart.plotText(id, text, price, { time | anchor, offset, offsetXPx, offsetYPx, textColor, fontSize }) TD.Chart.removeShapes(id | [ids]) TD.Chart.addBuiltInIndicator(name, options) await TD.Chart.getIndicatorValues(name, params, { count, add }) Live data await TD.Feed.addSymbols([symbols]) await TD.Feed.removeSymbols([symbols]) TD.on("tick" | "bar1min" | "bar5min" | "greeks" | "bidAsk" | "bidAskL2" | "heartbeat", callback) Historical await TD.Historical.getBars({ symbol, from, to, interval }) await TD.Historical.getLastNBars({ symbol, nbars, interval }) await TD.Historical.getTicks({ symbol, from, to, bidask }) await TD.Historical.getLastNTicks({ symbol, nticks, bidask }) await TD.Historical.getAllBars({ timestamp, segment, interval }) await TD.Historical.getTopNGainers | getTopNLosers | getTopNVolumeGainers({ segment, topn }) Option chain await TD.OptionChain.getOptionChain({ symbol?, expiry?, exchange? }) await TD.OptionChain.getOptionChainWithGreeks({ symbol?, expiry?, exchange? }) await TD.OptionChain.getExpiryList({ symbol?, exchange? }) Analytics await TD.Analytics.getOIGainers | getOILosers({ series, top }) await TD.Analytics.getIndexGainers | getIndexLosers({ indexName, segment, top }) await TD.Analytics.getIndustryGainers | getIndustryLosers({ industry, segment, top }) await TD.Analytics.getMostActiveByTurnover | getMostActiveByVolume({ indexName, segment, top }) Corporate await TD.Corporate.getAllResults({ symbol, nature, period, type }) await TD.Corporate.getPnL | getBalancesheet | getCashflow({ id, cumulative }) Utilities TD.log(...args) console.log / warn / error / table await TD.UI.showAlert(message, type) await TD.Storage.setItem(key, value) await TD.Storage.getItem(key) await TD.getMarketStatus()
Keep this page open in a second tab while you write your first script - the editor's own autocomplete (type TD.) mirrors this reference exactly.