API for Custom Indicators The script execution environment is based on a secure JavaScript sandbox. Your script has an execution time limit of 1500ms. The length of the returned data array must not exceed 1500. Therefore, the indicator cannot extend deeper than 1500 candles. You can make up to 8 data source requests within a single indicator. define(title, description, type, isPublic, isPublicCode) Registers the indicator in the system. Parameter Type Description title string Indicator name description string Short description type 'onchart' | 'offchart' Indicator placement (on chart or in a separate panel) isPublic true | false Whether the indicator is public isPublicCode true | false Whether the source code is public Example: define("My Indicator", "Shows EMA line", "onchart", true, false); The input and style classes allow you to create user-configurable settings (similar to input() in PineScript). Method Description number(title, defaultValue, min, max, step) Numeric input range(title, defaultValue, min, max, step) Range input select(title, defaultValue, values) Dropdown selection color(title, defaultValue) Color picker boolean(title, defaultValue) Boolean input Example (input) for main parameters: const length = input.number("Period", 14, 1, 100, 1); const type = input.select("Type", 'sma', ['sma', 'ema']); const color = input.color("Line Color", "#FF0000"); Or (style) for visual parameters: const length = style.number("Size", 14, 1, 100, 1); const color = style.select("Background Color", '#FF0000', ["#FF0000", "#AA0000", "#BB0000"]); const color = style.color("Background Color", "#FF0000"); The source class is used to fetch market data from the server side. We do not always guarantee that the returned data exactly matches the candles visible on the chart. For example, during script execution, ohlcv() might return 400 candles, while depths() might return only 300 limit orders (for the heatmap). Use additional checks for the startTime parameter in your code. Currently, the length of the returned data array must not exceed 1500. Therefore, the indicator cannot extend deeper than 1500 candles. Currently, 8 data source requests are allowed within a single indicator. The exchange, ticker, and timeframe parameters are by default tied to the current chart where the indicator is opened. The lookback parameter (history of bars from the current date, but no more than 1500) can be set at your discretion. "ohlcv" Method Description ohlcv(exchange, ticker, timeframe, lookback?) Candle data [ { "open": 115886.17, "high": 115911.96, "low": 115764.27, "close": 115815.16, "volume": 228.41528, "startTime": 1757699100000 }, {...}, ... ] "depths" Method Description depths(exchange, ticker, timeframe, lookback?) Limit orders [ { "data": { "asks": { "109425": 5.467, "109495": 3.564, ... }, "bids": { "33000": 3.746, "35000": 16.49, ... } }, "startTime": 1757699100000 }, {...}, ... ] "clusters" Method Description clusters(exchange, ticker, timeframe, lookback?) Cluster data [ { "data": { "totalBuy": 16053385, "totalSell": 20490989, "0.8654": { "buy": 286845, "sell": 336689 }, "0.8653": { "buy": 271848, "sell": 259167 }, "0.8652": { "buy": 318814, "sell": 373497 }, ... }, "startTime": 1757699100000 }, {...}, ... ] "liquidations" Method Description liquidations(exchange, ticker, timeframe, lookback?) Liquidations data [ { "data": { "totalBuy": 16053385, "totalSell": 20490989, "0.8654": { "buy": 286845, "sell": 336689 }, "0.8653": { "buy": 271848, "sell": 259167 }, "0.8652": { "buy": 318814, "sell": 373497 }, ... }, "startTime": 1757699100000 }, {...}, ... ] "moreData" Method Description moreData(exchange, ticker, timeframe, lookback?) Some additional data [ { "data": { "FR": 0.00003777, // Funding Rate "OI": { // Open Interest Candle "open": 85645.708, "close": 83496.225, "high": 85801.04, "low": 83397.918 }, "LS": { "l": 0.5426, "s": 0.4574 }, // Long/Short Ratio "taLS": { "l": 0.552, "s": 0.448 }, // Top Traders Account Ratio "tpLS": { "l": 0.5996, "s": 0.4004 } // Top Traders Position Ratio }, "startTime": 1757699100000 }, {...}, ... ] Example: const candles = source.ohlcv(); // The exchange, ticker, and timeframe parameters default to the current chart. const closes = candles.map(c => c.close); All arrays are ordered from oldest candle to newest candle. values[0] is the oldest bar. values[values.length - 1] is the latest bar. Global Variables Variable Description Example currentExchange Current exchange on the chart binance, binanceFutures, coinbase, ... currentTicker Current ticker on the chart (format depends on the exchange) BTCUSDT, ADA-USD, BTC/USDC, ... currentTimeframe Current timeframe on the chart 1m, 5m, 15m, ... Example: const candles = source.ohlcv(currentExchange, currentTicker, currentTimeframe); const closes = candles.map(c => c.close); Logging The logger class is used for outputting information and debugging. Method Description log(...args) Adds an entry to the log (with limits on length and quantity) Example: const candles = source.ohlcv(); logger.log("First candle:", candles?.[0]); Visualization The plot class is responsible for drawing graphical elements and data series. Geometric Shapes Method Description box(title, date1, price1, date2, price2, fill, opacity, stroke, strokeOpacity, strokeWidth) Rectangle square(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth) Square diamond(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth) Diamond triangle(title, date1, price1, size, fill, opacity, stroke, strokeOpacity, strokeWidth) Triangle circle(title, date1, price1, radius = 10, fill, opacity, stroke, strokeOpacity, strokeWidth) Circle text(title, value, date1, price1, fontSize, fill, opacity) Text on chart straightLine(title, date1, price1, date2, price2, stroke, strokeOpacity, strokeWidth, strokeDasharray) Straight line Parameter Data Type Description title string Element name (unique identifier on the chart) date1 number (timestamp, ms) Starting timestamp price1 number Starting price (Y-axis coordinate) date2 number (timestamp, ms) Ending timestamp (for lines and rectangles) price2 number Ending price (for lines and rectangles) size number Shape size (in pixels) radius number Circle radius (default 10) fill string (hex, e.g., "#FF0000") Fill color of the shape opacity number (0–1) Fill opacity stroke string (hex, e.g., "#000000") Stroke color strokeOpacity number (0–1) Stroke opacity strokeWidth number Stroke width (in pixels) strokeDasharray array (e.g., [5,5]) Dashed line style fontSize number Text size (in pixels) value string Text value (for text method) Example: plot.text("Text object", "Hello world!", 1757699100000, 115000, 10, "#00C8FF", 0.5); plot.straightLine(`${namePrefix} Open`, level.timeStart, level.open, level.timeEnd, level.open, color); plot.circle(`${namePrefix} High`, level.timeStart, level.high, 3, color); No more than 3000 shapes can be displayed on the chart. Data Series Method Description lineSeries(title, values, stroke, strokeOpacity, strokeWidth, strokeDasharray) Line chart barSeries(title, values, fill, opacity) Bar chart candleSeries(title, openValues, closeValues, highValues, lowValues, fill, opacity) Candles Example: plot.lineSeries("EMA", ta.ema(closes, 20), "#00C8FF"); plot.barSeries("Premium %", premiumPercents, ((d) => d > 0 ? color1 : color2), 1); plot.candleSeries( "Candles", opens, closes, highs, lows, (v, i) => { return opens?.[i] > closes?.[i] ? colorBuy : colorSell } ); No more than 10 data series can be displayed on the chart. Technical Analysis The ta class includes a set of functions similar to PineScript. Helper Functions Method Description highest(values, period) Highest value over a period lowest(values, period) Lowest value over a period variance(values, period) Variance stdev(values, period) Standard deviation crossover(series1, series2) Checks for upward crossover crossunder(series1, series2) Checks for downward crossover cross(series1, series2) Any crossover Moving Averages Method Description sma(values, period) Simple moving average ema(values, period) Exponential moving average wma(values, period) Weighted moving average Indicators Method Description rsi(values, period) Relative Strength Index macd(values, fast, slow, signalPeriod) MACD bollinger(values, period, mult) Bollinger Bands stoch(highs, lows, closes, period, smoothK, smoothD) Stochastic Oscillator atr(highs, lows, closes, period) Average True Range adx(highs, lows, closes, period) Average Directional Index cci(highs, lows, closes, period) Commodity Channel Index vwap(highs, lows, closes, volumes) Volume Weighted Average Price Example: const candles = source.ohlcv(); const closes = candles.map(c => c.close); const rsiValues = ta.rsi(closes, 14); plot.lineSeries("RSI", rsiValues, "#00C8FF"); Alerts Users can create alerts for an indicator from the Alerts tab in the indicator settings on the chart. Alerts are checked on the server side every 30 seconds, notifications are sent to Telegram and/or a user defined Webhook URL. Alert Settings - alertInput The alertInput class declares alert settings. It works exactly like input, but the fields are shown in the Alerts tab and every user configures their own values for their alert. Scripts without alertInput declarations can not be used for alerts. Method Description number(title, defaultValue, min, max, step) Numeric input range(title, defaultValue, min, max, step) Range input select(title, defaultValue, values) Dropdown selection color(title, defaultValue) Color picker boolean(title, defaultValue) Boolean input Example: const alertVolumeThreshold = alertInput.number("Volume threshold", 1000, 0, 10000000, 1); Firing Alerts - alert.fire Method Description fire(title, message, date) Fires an alert notification - All three parameters are REQUIRED. A call with a missing title, message or date is skipped (a warning is written to the log). - Keep the alert title constant, do not generate unique titles. Each title fires only once per run (max 20 titles per run). - date is the candle startTime the signal belongs to: the same title notifies again only when it fires for a NEWER candle. Signals from candles older than the moment the user created their alert are ignored (anti-spam). Example: if (candle.volume > alertVolumeThreshold) { alert.fire("High volume", `vol ${candle.volume}`, candle.startTime); } Shape Alerts - plot.(...).alert(...) All geometric shapes (box, square, diamond, triangle, circle, straightLine, text) return a handle with an .alert() method. The alert fires with the shape's date1 automatically, so the anti-spam logic works without extra code. This is the recommended way for per-candle signals. Two forms: // simple: fires every time the shape is plotted for a new date plot.box(...).alert("High volume", `vol ${candle.volume}`); // callback: return nothing to skip, [title, message] to fire plot.box(...).alert((shape) => { if (candle.volume > alertVolumeThreshold) return ["High volume", `vol ${candle.volume}`]; }); The callback receives the shape data (date1, price1, date2, price2, ...). Series Alerts - plot.(...).alert(...) Data series (lineSeries, barSeries, candleSeries) also return the .alert() handle. The callback receives the LATEST data point and the latest candle date, the alert fires with that date. - lineSeries / barSeries: the callback receives the last value (a number) - candleSeries: the callback receives the last candle {open, close, high, low} plot.lineSeries("RSI", rsiValues, color).alert((rsi, date) => { if (rsi > alertRsiThreshold) return ["RSI high", `RSI ${rsi.toFixed(1)}`]; }); A condition that stays true notifies once per NEW candle. For one-time signals use crossover-style conditions (ta.crossover). Full alerts example: const title = "Volume alerts"; const description = "Shows boxes on high-volume candles"; define(title, description, "onchart", true, true); const color = style.color("Color", "#00C8FF"); const volumeThreshold = input.number("Volume threshold", 1000, 0, 10000000, 1); const alertVolumeThreshold = alertInput.number("Alert volume threshold", 5000, 0, 10000000, 1); const alertCloseThreshold = alertInput.number("Alert close threshold", 100000, 0, 10000000, 1); const candles = source.ohlcv(); const closes = candles.map(c => c.close); // series alert: checks the latest close plot.lineSeries("Closes", closes, color).alert((close, date) => { if (close > alertCloseThreshold) return ["Close above threshold", `close ${close}`]; }); for (let i = 0; i < candles.length; i++) { const candle = candles[i]; if (candle.volume > volumeThreshold) { const right = i < candles.length - 1 ? candles[i + 1].startTime : candle.startTime + 60000; // shape alert: checked against the user's alert threshold plot.box(`Volume ${i}`, candle.startTime, candle.high, right, candle.low, "#00C8FF", 0.3) .alert(() => { if (candle.volume > alertVolumeThreshold) return ["High volume", `vol ${candle.volume}`]; }); } } Examples of indicators const title = "Your first indicator"; const description = "Shows closes lines on the chart"; const indicatorType = "onchart"; define(title, description, type, true, true); const color = input.color("Line Color", "#00C8FF"); const candles = source.ohlcv(); const closes = candles.map(c => c.close); plot.lineSeries("Closes", closes, color); define("Example Indicator", "Demonstrates basic plotting", "onchart", true, true); const color = input.color("Line Color", "#00FF99"); const candles = source.ohlcv(); const closes = candles.map(c => c.close); const ema20 = ta.ema(closes, 20); plot.lineSeries("EMA 20", ema20, color); const title = "Colored Volume Bars"; const description = "Volumes colored based on candle direction."; const indicatorType = "onchart"; const isPublic = true; const isPublicCode = true; define(title, description, indicatorType, isPublic, isPublicCode); // Get OHLC candles const candles = source.ohlcv(); const colorUp = style.color("Color 1", "#2BB462"); // Greenish color for rising prices const colorDown = style.color("Color 2", "#FB4C51"); // Reddish color for falling prices const volumes = candles.map(candle => candle.volume); plot.barSeries("Volume", volumes, (value, index) => { const currentCandle = candles[index]; return currentCandle.close >= currentCandle.open ? colorUp : colorDown; }); define('Coinbase Premium Index', 'Coinbase Premium Index', 'offchart', true, false); const color1 = style.color("Color 1", "#2BB462"); const color2 = style.color("Color 2", "#FB4C51"); // Get OHLC candles const cbCandles = source.ohlcv('coinbase', 'BTC-USD'); const binanceCandles = source.ohlcv('binance', 'BTCUSDT'); // Extract closes const cbCloses = cbCandles.map(c => c.close); const binanceCloses = binanceCandles.map(c => c.close); // --- Coinbase Premium Index calculation --- const premiumPercent = cbCloses.map((cb, i) => { const b = binanceCloses[i]; return ((cb - b) / b) * 100; }); // Plot premium index plot.barSeries("Premium %", premiumPercent, ((d) => d > 0 ? color1 : color2)) // Percentage difference const title = "Candles"; const description = "Simple candles draw"; const indicatorType = "offchart"; const isPublic = true; const isPublicCode = true; define(title, description, indicatorType, isPublic, isPublicCode); const colorBuy = style.color("Buy Color", "#2BB462"); const colorSell = style.color("Sell Color", "#7a3d3f"); const candles = source.ohlcv(); const closes = candles.map(c => c.close); const opens = candles.map(c => c.open); const highs = candles.map(c => c.high); const lows = candles.map(c => c.low); plot.candleSeries( "Candles", opens, closes, highs, lows, (v, i) => { return opens?.[i] > closes?.[i] ? colorBuy : colorSell } ); The script is executed once. Execution order: 1. define() 2. input/style initialization 3. source requests 4. calculations 5. plotting - Never invent new functions. - Use only documented API. - If a function does not exist, explain that it is unavailable. - Always return complete code. - Preserve array lengths. - Use map/filter/reduce when processing arrays. - Do not exceed 8 source requests. - Do not exceed 10 plotted series. - Do not exceed 3000 shapes. - Do not write PineScript. - Do not write TradingView code.