No results for ""
Best Practices
Follow these guidelines to keep scripts reliable, performant, and easy to debug during live market hours. This page also covers Rule 17 in depth, since it governs the overall shape of a script rather than a single line of code.
Prefer sockets over REST — REST calls are strictly one-shot
If live, continuously updating data is needed, subscribe via TD.Feed.addSymbols and a TD.on(...) listener instead of a REST call. When a REST method (TD.Historical.*, TD.Analytics.*, TD.OptionChain.*) is the right tool, call it exactly once, inside onStart. Never wrap a REST call in setInterval, a polling loop, or call it again from inside a TD.on() handler.
TD.onStart = async () => {
setInterval(async () => {
const data = await TD.OptionChain.getOptionChain({
symbol: "NIFTY", expiry: "07-07-2026",
});
// re-fetches every tick of the interval — banned
}, 5000);
};
TD.onStart = async () => {
const data = await TD.OptionChain.getOptionChain({
symbol: "NIFTY", expiry: "07-07-2026",
});
// one-shot, done. For live updates, subscribe instead:
await TD.Feed.addSymbols(["NIFTY26070723500CE"]);
};
Do
Register events at the top level
Call TD.on(...) before any hook runs — never inside onStart or another function.
Always clean up in onStop
Call await TD.Feed.removeSymbols() for every symbol you added. Orphaned subscriptions accumulate across runs and cause memory leaks.
Store symbols in a constant
Declare const SYMBOLS = [...] once at the top. Use the same array in addSymbols and removeSymbols to avoid mismatches.
Define TD.onError
Without an error handler, runtime errors are silently swallowed. Always define TD.onError so you can see what went wrong.
Await every async call, everywhere
Feed, Sheet, Historical, Analytics, and OptionChain calls are all async. Missing await causes race conditions and silently wrong sheet data.
Check response.success before reading data
Every Feed response and most REST responses can fail gracefully. Check the status field before touching symbollist or records.
Use bar1min / bar5min for heavy logic
Move indicator calculations and most sheet writes to a bar event. It fires once per candle — far more manageable than dozens of ticks per second.
Use TD.log for debugging
Prefer TD.log() over console.log(). Output appears in the built-in console panel and is preserved in the session log.
Avoid
Heavy work inside 'tick' or 'bidAsk'
Avoid REST calls, large loops, or many sheet writes inside high-frequency handlers. At peak market hours, events arrive faster than they can be processed.
Looping or polling REST calls
TD.Historical.*, TD.Analytics.*, and TD.OptionChain.* are one-shot. Use sockets for anything that needs to stay live (Rule 17).
Treating data[0] as the symbol name
In every live event, data[0] is the symbolId. Build a lookup map in onStart to match by name.
Using setInterval / setTimeout
Timers registered with setInterval are not cleared on Stop. Use 'heartbeat' for periodic checks instead — it's lifecycle-managed automatically.
Subscribing the same symbol twice
Calling addSymbols with an already-subscribed symbol creates a duplicate stream. De-duplicate your SYMBOLS array before subscribing.
Unbounded sheet row growth
Append-style logging (Templates 3 and 10) fills the sheet quickly on high-frequency events. Prefer a fixed-row overwrite pattern unless an audit log is explicitly wanted.
Method Quick-Lookup
| Namespace | Method | Type | Where to call |
|---|---|---|---|
TD.Feed |
addSymbols / removeSymbols | async | onStart / onStop |
TD.on |
tick / bar1min / bar5min / bidAsk / heartbeat / greeks | listener | top level only |
TD.Sheet |
setCell / setCellRange / getCell / getCellRange | async | anywhere, awaited |
TD.Historical |
getBars / getLastNBars / getTicks / getLastNTicks / getTopNGainers / getTopNLosers / getTopNVolumeGainers | async, one-shot | onStart only |
TD.OptionChain |
getOptionChain / getOptionChainWithGreeks | async, one-shot | onStart only |
TD.Analytics |
getIndexGainers/Losers, getIndustryGainers/Losers, getMostActiveByTurnover/Volume, getOIGainers/Losers | async, one-shot | onStart only |
TD.UI |
showAlert(message, type) | async | anywhere, awaited |
TD.log |
log(...) | sync | anywhere |
Known Gaps
getOptionChain nor getOptionChainWithGreeks documents a field for the underlying index's current spot price. If a script needs spot LTP alongside the chain, subscribe to the underlying separately with TD.Feed.addSymbols and read it from the tick event rather than guessing a row index in the chain response.Quick Checklist Before Clicking Start
TD.on(...)handlers are registered at the top level, not inside any hookTD.onStart— subscribes symbols, checksresponse.success, writes headersTD.onStop— removes the same symbolsTD.onError— defined so errors surface in the console- Every Feed / Sheet / Historical / Analytics / OptionChain call is
awaited and wrapped in try/catch - Numeric fields are parsed with
parseFloat()/parseInt()before use - REST calls (Historical / Analytics / OptionChain) are called once in
onStart— no loop, no timer, no polling - Option chain index symbols are mapped (e.g. "NIFTY 50" → "NIFTY") before calling the API
- Heavy work is inside
'bar1min'/'bar5min', not'tick'or'bidAsk' - Sheet rows are bounded, or an unbounded append is an intentional, explicit choice
Empowering traders and analysts with high-performance real-time market data directly within their spreadsheets.
Resources
Support
© 2026 TrueData Market Data Services LLP. All rights reserved.