Drawing & Indicators
Everything under TD.Chart for putting lines, markers, shapes, boxes and text on the chart - plus how to read the same values the built-in indicators use.
Chart Context
What symbol and timeframe your script is drawing on
const ctx = TD.Chart.getContext(); // { symbol:"NIFTY 50", underlying:"NIFTY", exchange:"NSE", symbolType: ..., interval: 5 } const sym = TD.Chart.getSymbol(); // "NIFTY 50"
underlying is the option-chain name for the chart's instrument - NIFTY 50, NIFTY-I and NIFTY26100622600CE all give NIFTY. Don't store the context in a variable at the start of your script, since the user can change the symbol while it runs - call getContext() each time you need it.
Lines and Histograms
TD.Chart.plot(name, value, options)
TD.Chart.plot("EMA 21", emaValue, { color: "#FF9800" }); // on the price chart TD.Chart.plot("Momentum", mom, { color: "#26A69A", overlay: false }); // separate pane TD.Chart.plot("Delta", d, { overlay: false, style: "histogram", color: "#888" }); // histogram TD.Chart.plot("Level", lvl, { style: "stepline" }); // step line
color Any CSS colour. Defaults to the script's own colour.overlay true for the price chart, false for a separate pane. Defaults to true.style "histogram", "stepline", or omit for a line.time Bar time. Set automatically inside onChartBar, where it defaults to the current bar.Series names: only letters and digits count, so "EMA 21" and "EMA-21" are the same series. Give each series a clearly different name.
Buy / Sell Markers
TD.Chart.plotMarker(price, side, options)
TD.Chart.plotMarker(bar.low, "buy"); // green up-arrow TD.Chart.plotMarker(bar.high, "sell", { color: "#FF00FF" }); // custom colour TD.Chart.plotMarker(bar.low, "buy", { id: "entry_" + bar.time }); // stable id
side is "buy" (green up-arrow) or "sell" (red down-arrow). Options are color, shape ("arrowUp", "arrowDown" and similar), time and id. Without an id, a bar can have only one buy marker and one sell marker - plotting again on the same bar moves the existing marker.
Shapes
TD.Chart.plotShape(type, price, options)
arrowUp, arrowDown, arrowLeft, arrowRight, circle, square, diamond, dot, triangle, ellipse, texthorizontalLine, verticalLinetrendLine, rectangle, polygon, polyline - these need options.points// Horizontal support line TD.Chart.plotShape("horizontalLine", 22450, { id: "support", color: "#16A34A", lineWidth: 2, lineStyle: "dash" }); // Trend line between two bars TD.Chart.plotShape("trendLine", 0, { id: "tl1", points: [{ time: t1, price: 22300 }, { time: t2, price: 22520 }], lineColor: "#3B82F6", lineWidth: 1 }); // Dot above a bar TD.Chart.plotShape("dot", bar.high * 1.001, { color: "orange" });
id Stable identifier. Plotting again with the same id updates the shape in place instead of adding a new one.color Base colour, used for both line and fill.lineColor / lineWidth / lineStyle lineWidth: 0 means no border. Style is "solid", "dash" or "dot".fillColor / fillOpacity Fill colour and opacity (0-1).textColor / fontSize Text styling.time Anchor time. Set automatically inside onChartBar.savable: false Leaves the shape out of the saved chart layout - recommended for generated drawings.selectable: false The user can't select or move the shape.hoverable: false The shape doesn't react to the mouse.locked: true Locks the shape in place.For drawings your script generates in large numbers - profiles, zones, labels - pass { savable: false, selectable: false, hoverable: false }. With all three set, the chart stays responsive even with hundreds of shapes.
Always use an id for horizontalLine and verticalLine. Without one, each script can draw only one of each, and every new call moves it.
Boxes and Zones
TD.Chart.plotBox(id, box, options) - a filled rectangle between two prices
// A. Anchored to time — the box moves with the candles TD.Chart.plotBox("demand_zone", { from: startTime, to: endTime, low: 22300, high: 22350 }, { fillColor: "#16A34A", fillOpacity: 0.15, lineWidth: 0, savable: false }); // B. Anchored to the screen edge — box stays put while you scroll/zoom TD.Chart.plotBox("oi_22500_call", { anchor: "right", offset: 0.004, width: 0.4, low: 22500, high: 22510, heightPx: 10, heightAnchor: "low" }, { fillColor: "#E05252", fillOpacity: 0.35, lineWidth: 0 });
low, high Price range (required).from, to Start and end times (option A).anchor "right" or "left" - the chart edge to pin to (option B).offset Gap from that edge, as a fraction of chart width (0-1).width Box length, as a fraction of chart width (0-1).heightPx Maximum drawn thickness in pixels, so bands don't turn into thick blocks when zoomed in.heightAnchor "low", "high" or "center" - which edge stays fixed under heightPx.Every box needs either from/to or anchor/width. A box with neither has zero width and doesn't appear - the console shows a notice.
Text Labels
TD.Chart.plotText(id, text, price, options)
TD.Chart.plotText("lbl_" + bar.time, "BUY", bar.low, { textColor: "#16A34A", fontSize: 10, offsetYPx: 14, // push down 14px (negative = up) savable: false, selectable: false, hoverable: false }); // A label pinned to the right edge at a price level TD.Chart.plotText("pcr", "PCR 1.12", 22600, { anchor: "right", offset: 0.01, textColor: "#999" });
The label's left edge sits at its anchor point, centred vertically on the price. Use offsetXPx and offsetYPx to move it by pixels. Calling it again with the same id updates the text and position.
Removing Drawings
TD.Chart.removeShapes(ids)
TD.Chart.removeShapes("support"); TD.Chart.removeShapes(["lbl_1", "lbl_2", "zone_A"]);
This removes only shapes your script created with those ids. Your own manual drawings and other scripts' drawings are never touched.
Adding a Built-in Indicator
TD.Chart.addBuiltInIndicator(name, options)
TD.onStart = () => { TD.Chart.addBuiltInIndicator("FastEMA", { periods: 50, color: "#FF9800" }); TD.Chart.addBuiltInIndicator("FastRSI", { periods: 14 }); };
If the chart already has the same indicator with the same parameters, no duplicate is added.
Reading Built-in Indicators
TD.Chart.getIndicatorValues(name, parameters, options) returns the same values the chart draws
const adx = await TD.Chart.getIndicatorValues("FastADX", { periods: 14 }); // { // status:"Success", // indicator:"FastADX", // names: ["FastADX","DI+","DI-"], // times: [ ...epoch ms, oldest first... ], // values: {"FastADX": [...],"DI+": [...],"DI-": [...] } // null where no value // } const last = adx.times.length - 1; TD.log("ADX", adx.values["FastADX"][last], "+DI", adx.values["DI+"][last]);
FastADX FastADX, DI+, DI- · { periods: 14 }FastSuperTrend Up, Down - one is null each bar · { periods: 10, multiplier: 3 }FastATR FastATR · { periods: 14 }FastEMA FastEMA · { periods: 14 }FastRSI, FastSMA and the rest - read result.names to see their line names.count { count: 200 } returns only the newest 200 bars.add { add: false } returns an error instead of adding the indicator when it isn't already on the chart. By default a missing indicator is added to the chart.Fast… names, which match what the chart toolbar adds.This call is asynchronous, so don't call it for every bar inside onChartBar. Collect the bars and read the indicator once, a moment after the history replay finishes.
const bars = []; let timer = null; TD.onChartBar = (bar) => { bars.push(bar); clearTimeout(timer); timer = setTimeout(analyse, 300); // runs once after the replay settles }; async function analyse() { const rsi = await TD.Chart.getIndicatorValues("FastRSI", { periods: 14 }); if (rsi.error) { TD.log("RSI not available: " + rsi.error); return; } const line = rsi.values[rsi.names[0]]; const byTime = new Map(bars.map(b => [b.time, b])); rsi.times.forEach((t, i) => { const b = byTime.get(t); if (b && line[i] !== null && line[i] < 30) { TD.Chart.plotShape("dot", b.low, { id: "os_" + t, time: t, color: "#16A34A", savable: false }); } }); }