# Tradify Charts Script Engine — API Reference > Tradify Charts is a professional financial charting platform with a TypeScript scripting engine for custom indicators and strategies. This document is the complete API reference for writing scripts. This is a CUSTOM TypeScript-based engine. It is NOT PineScript. Do NOT use any PineScript functions. --- ## Execution Model - The script body runs **once per bar**, from oldest bar to newest. - Top-level `let` variables **persist across bars** automatically (the engine auto-hoists them). - `const` re-initializes each bar — use `const` for ta.* results, Series, and computed values. - Use `+series` to convert a Series to a plain number (e.g. `+high`, `+low[1]`, `+time`). - `series[1]` = previous bar's value. Out-of-range lookback returns `NaN`. - ta.* functions pre-compute full arrays on first call, then cache. They are the primary way to compute values that depend on previous bars. - `plot()` registers a Series by name on its FIRST call only — subsequent calls with the same name are ignored. Always pass a Series, not a conditional number. - `fill()` requires two Series objects (not plain numbers). - `box()` draws a rectangle — call it once when a pattern completes, not every bar. - `bgcolor()` colors a single bar's background — not for drawing rectangles. - NaN values in a plotted Series create gaps in the line. - Iteration limit: 50 million operations. --- ## Script Declaration Every script starts with exactly one declaration: - `indicator(name: string, opts?: { overlay?: boolean })` — overlay: true draws on price chart, false in sub-pane - `strategy(name: string, opts?: { overlay?: boolean })` — enables entry/exit signals and backtest stats --- ## Built-in Globals | Name | Type | Description | |------|------|-------------| | open | Series | Open price | | high | Series | High price | | low | Series | Low price | | close | Series | Close price | | volume | Series | Volume | | bar_index | number | Current bar index (0-based) | | time | Series | Current bar timestamp (unix seconds). Supports lookback: `time[1]` = previous bar's time. | | Math | Math | Standard JS Math object | | console.log(...args) | void | Debug output to browser console | | NaN | number | Not-a-Number | | Infinity | number | Infinity | | isNaN(value) | boolean | Check NaN | | isFinite(value) | boolean | Check finite | | parseFloat(string) | number | Parse float | | parseInt(string, radix?) | number | Parse integer | ### Series Type Series is a time-indexed number array aligned with chart candles. - Lookback: `close[0]` = current bar, `close[1]` = previous bar. Out-of-range returns NaN. - Arithmetic coercion: `close * 2` works — Series converts to current bar value via valueOf(). - Series is NOT a number. `.toFixed()`, `.toString()`, `.toPrecision()` do NOT work on Series. Use `+series` or `Number(series)` first. Example: `(+rsi).toFixed(2)`. - All ta.* functions return Series. --- ## Technical Analysis — ta namespace ### Moving Averages All accept (source: Series, period: number) -> Series: - `ta.sma(source, period)` — Simple Moving Average - `ta.ema(source, period)` — Exponential Moving Average - `ta.wma(source, period)` — Weighted Moving Average - `ta.dema(source, period)` — Double EMA - `ta.tema(source, period)` — Triple EMA - `ta.hma(source, period)` — Hull Moving Average - `ta.vwma(source, period)` — Volume Weighted Moving Average ### Oscillators Source-flexible (source: Series, period: number) -> Series: - `ta.rsi(source, period)` — Relative Strength Index (0-100) - `ta.roc(source, period)` — Rate of Change (%) - `ta.momentum(source, period)` — Momentum (price diff) OHLCV-only (no source param): - `ta.cci(period)` -> Series — Commodity Channel Index - `ta.mfi(period)` -> Series — Money Flow Index (0-100) ### Multi-Output - `ta.bollinger(source, period, mult)` -> { middle: Series, upper: Series, lower: Series } - `ta.macd(source, fastPeriod, slowPeriod, signalPeriod)` -> { macd: Series, signal: Series, histogram: Series } - `ta.stochastic(kPeriod, dPeriod, smooth)` -> { k: Series, d: Series } - `ta.adx(diLength, adxSmoothing)` -> { adx: Series, plusDI: Series, minusDI: Series } ### Volume (OHLCV-only) - `ta.obv()` -> Series — On-Balance Volume - `ta.cmf(period)` -> Series — Chaikin Money Flow ### Volatility (OHLCV-only) - `ta.atr(period)` -> Series — Average True Range ### Pivot Detection - `ta.pivotHigh(source, leftBars, rightBars)` -> Series — pivot high price at detection bar, NaN elsewhere - `ta.pivotLow(source, leftBars, rightBars)` -> Series — pivot low price at detection bar, NaN elsewhere ### Utilities - `ta.crossover(a, b)` -> boolean — true when `a` crosses above `b` on current bar - `ta.crossunder(a, b)` -> boolean — true when `a` crosses below `b` on current bar - `ta.highest(source, period)` -> Series — highest value over period - `ta.lowest(source, period)` -> Series — lowest value over period - `ta.change(source, length?)` -> Series — change over length bars (default 1) --- ## Inputs — input namespace Declare user-configurable parameters: - `input.int(key, defaultValue, label, opts?)` -> number — opts: { min?, max?, step? } - `input.float(key, defaultValue, label, opts?)` -> number — opts: { min?, max?, step? } - `input.bool(key, defaultValue, label)` -> boolean - `input.string(key, defaultValue, label)` -> string - `input.select(key, defaultValue, label, options)` -> T — dropdown. `options` is an array of plain values (`['1m','5m','1H']`) OR `{ label, value }` objects. - `input.source(key, defaultValue, label)` -> Series — price source picker (close/open/high/low) - `input.color(key, defaultValue, label)` -> string — color picker, returns hex --- ## Drawing Functions - `plot(series, name, color?, opts?)` — draws a line on the chart. **Must pass a Series, not a number.** opts: { lineWidth?: number } - `fill(series1, series2, color?)` — fills the area between two plotted Series - `hline(price, name?, color?)` — draws a horizontal line at a fixed price level - `bgcolor(color)` — colors the current bar's background (single bar only) - `box(startTime, endTime, priceTop, priceBottom, color?)` — draws a filled rectangle on the chart. Use `+time` to get bar timestamps as numbers. This is the way to highlight price regions/zones. --- ## Strategy API - `strategy.entry(id, direction, opts?)` — open a position. direction: 'long' | 'short'. opts: { sl?, tp?, label? } - `strategy.close(id?)` — close a position - `strategy.position_size` -> number — readonly: 0 = flat, 1 = long, -1 = short Backtest stats computed after execution: totalTrades, winRate (0-1), totalPnL, maxDrawdown, sharpeRatio, profitFactor, avgWin, avgLoss. --- ## Multi-Timeframe — request namespace - `request.timeframe(tf)` -> { open: Series, high: Series, low: Series, close: Series, volume: Series } - Timeframe strings: '1m', '5m', '15m', '30m', '1H', '4H', 'D', 'W' - Data is forward-filled to the chart's timeframe. --- ## Examples ### SMA Indicator (overlay) ```ts indicator('My SMA', { overlay: true }) const len = input.int('length', 20, 'Length', { min: 2, max: 500 }) const src = input.source('source', 'close', 'Source') const sma = ta.sma(src, len) plot(sma, 'SMA', '#2962ff') ``` ### RSI Indicator (sub-pane) ```ts indicator('My RSI', { overlay: false }) const len = input.int('length', 14, 'Length', { min: 2, max: 100 }) const rsi = ta.rsi(close, len) plot(rsi, 'RSI', '#7E57C2') hline(70, 'Overbought', 'rgba(239,83,80,0.5)') hline(30, 'Oversold', 'rgba(38,166,154,0.5)') ``` ### Bollinger Bands (overlay with fill) ```ts indicator('My Bollinger', { overlay: true }) const len = input.int('length', 20, 'Length', { min: 2, max: 200 }) const mult = input.float('mult', 2.0, 'StdDev', { min: 0.1, max: 5 }) const bb = ta.bollinger(close, len, mult) plot(bb.middle, 'Middle', '#2962ff') plot(bb.upper, 'Upper', '#f44336') plot(bb.lower, 'Lower', '#4caf50') fill(bb.upper, bb.lower, 'rgba(33,150,243,0.1)') ``` ### EMA Cross Strategy ```ts strategy('EMA Cross', { overlay: true }) const fast = ta.ema(close, input.int('fast', 9, 'Fast Period')) const slow = ta.ema(close, input.int('slow', 21, 'Slow Period')) plot(fast, 'Fast', '#26A69A') plot(slow, 'Slow', '#EF5350') if (ta.crossover(fast, slow)) strategy.entry('Long', 'long') if (ta.crossunder(fast, slow)) strategy.close('Long') ``` ### RSI Mean Reversion Strategy (with SL/TP) ```ts strategy('RSI Mean Reversion', { overlay: true }) const rsi = ta.rsi(close, 14) const atr = ta.atr(14) if (rsi < 30 && strategy.position_size === 0) { strategy.entry('Long', 'long', { sl: close - atr * 2, tp: close + atr * 3, }) } if (rsi > 70) strategy.close('Long') ``` ### Consecutive Candle Pattern Detection with Boxes This pattern shows how to track consecutive bars and draw rectangles: ```ts indicator('Spike Detector', { overlay: true }) const minLen = input.int('minLen', 3, 'Min Candles', { min: 2, max: 20 }) // --- Bullish: higher lows --- let bullCount = 0 let bullStart = 0 let bullHigh = 0 let bullLow = Infinity if (bar_index > 0 && low > low[1]) { if (bullCount === 0) { bullStart = +time bullHigh = Math.max(+high[1], +high) bullLow = Math.min(+low[1], +low) } bullCount++ bullHigh = Math.max(bullHigh, +high) bullLow = Math.min(bullLow, +low) } else { if (bullCount >= minLen) { box(bullStart, +time, bullHigh, bullLow, 'rgba(38,166,154,0.2)') } bullCount = 0 bullHigh = 0 bullLow = Infinity } // --- Bearish: lower highs --- let bearCount = 0 let bearStart = 0 let bearHigh = 0 let bearLow = Infinity if (bar_index > 0 && high < high[1]) { if (bearCount === 0) { bearStart = +time bearHigh = Math.max(+high[1], +high) bearLow = Math.min(+low[1], +low) } bearCount++ bearHigh = Math.max(bearHigh, +high) bearLow = Math.min(bearLow, +low) } else { if (bearCount >= minLen) { box(bearStart, +time, bearHigh, bearLow, 'rgba(239,83,80,0.2)') } bearCount = 0 bearHigh = 0 bearLow = Infinity } ``` ### Multi-Timeframe Trend Filter Strategy ```ts strategy('MTF Trend Filter', { overlay: true }) const h1 = request.timeframe('1H') const h1Trend = ta.ema(h1.close, 20) const fast = ta.ema(close, 9) const slow = ta.ema(close, 21) plot(fast, 'Fast', '#26A69A') plot(slow, 'Slow', '#EF5350') if (h1.close > h1Trend && ta.crossover(fast, slow)) strategy.entry('Long', 'long') if (h1.close < h1Trend && ta.crossunder(fast, slow)) strategy.entry('Short', 'short') ``` ### Daily SMA Overlay (MTF Indicator) ```ts indicator('Daily SMA', { overlay: true }) const daily = request.timeframe('D') const sma = ta.sma(daily.close, 50) plot(sma, 'Daily SMA 50', '#FF9800', { lineWidth: 2 }) ```