Formula Analysis

Formula language reference

182 functions in 19 groups, written for array thinking: one line describes the whole history of a symbol. Every example runs as it is β€” press β–Ά to open it in the workstation.

Price & time

The bars themselves, their dates, and facts about the symbol.

Oseries

Open of every bar. Also Open.

Hseries

High of every bar. Also High.

Lseries

Low of every bar. Also Low.

Cseries

Close of every bar. Also Close.

Vseries

Volume of every bar. Also Volume.

Avgseries

Typical price, (H + L + C) / 3.

BarCountnumber

Number of bars loaded for this symbol.

BarIndex()series

0, 1, 2 … for each bar.

DateTime()series

Bar time as Unix seconds (UTC).

DateNum()series

Compact date number: (year βˆ’ 1900)Β·10000 + monthΒ·100 + day. 1250131 is 31 Jan 2025.

Filter = DateNum().ge(1250101);
Year()series

Calendar year of each bar.

Month()series

Month 1–12.

Day()series

Day of month 1–31.

DayOfWeek()series

0 = Sunday … 6 = Saturday.

DayOfYear()series

1–366.

Name()string

Ticker of the symbol being processed.

FullName()string

Company or asset name.

SectorName()string

Sector, where known.

IndustryName()string

Industry, where known.

MarketName()string

Listing market code: US, IDX, TSE, CRYPTO …

Currency()string

Currency the prices are quoted in.

Signals & outputs

What a formula hands back: signals, filters, columns and plots.

Buy = conditionoutput

Long entry signal. Read at the bar's close and filled on the next bar (trade delay 1) at the open by default.

Buy = Cross(C, MA(C, 50));
Sell = conditionoutput

Long exit signal.

Sell = Cross(MA(C, 50), C);
Short = conditionoutput

Short entry signal. Needs direction 'short' or 'both' in the settings, or SetOption("AllowShort", true).

Cover = conditionoutput

Short exit signal.

BuyPrice = seriesoutput

Price to buy at instead of the open/close. Clamped into the bar's high–low range.

BuyPrice = HHV(H, 20).ref(-1); // breakout stop order
SellPrice = seriesoutput

Price to sell at.

ShortPrice = seriesoutput

Price to short at.

CoverPrice = seriesoutput

Price to cover at.

Filter = conditionoutput

Which bars appear in an Exploration. Without Filter only the last bar of every symbol is listed.

Filter = RSI(14).lt(30);
Title = "text"output

Caption shown above the chart.

Plot(series, name, { color, style, pane, width })output

Draw a line on the chart. style: line, histogram, dots, area, step. pane: "price" overlays the candles, any other name opens a separate pane, "auto" decides by scale.

Plot(MA(C, 20), "MA 20", { color: "orange" });
Plot(RSI(14), "RSI", { pane: "RSI" });
PlotShapes(condition, shape, name, { color, position })output

Mark bars on the chart. shape: arrowUp, arrowDown, circle, square. position: aboveBar, belowBar.

PlotShapes(GapUp(), "arrowUp", "Gap", { color: "teal", position: "belowBar" });
AddColumn(series, name, format | { format, color, bg })output

Add a column to the Exploration table. format: 1.2 (two decimals), "pct" (0.12 β†’ 12%), "int", "money", "date". color: a colour name, a series (positive green, negative red) or ColorIf().

AddColumn(ROC(C, 20), "20-day %", { format: 1.1, color: ROC(C, 20) });
AddTextColumn(text | fn(i), name, { color })output

Text column. Pass a string, an array with one entry per bar, or a function of the bar index.

AddTextColumn(SectorName(), "Sector");
SetSort(columnName, descending = true)output

Default sort of the Exploration table.

SetSort("Score");
ColorIf(condition, colorIfTrue, colorIfFalse)array

Per-bar colour list for AddColumn / AddTextColumn.

print(...values)output

Write to the formula log (first 200 lines). Same as _TRACE.

Parameters

Sliders, switches and values the optimiser can vary.

Param(name, default, min, max, step)number

A number the user can change with a slider without editing the code.

var n = Param("Period", 20, 5, 200, 1);
ParamToggle(name, default)number

An on/off switch.

var useVol = ParamToggle("Volume filter", true);
ParamList(name, "A|B|C", default)string

A choice from a list. Returns the chosen text.

var avg = ParamList("Average", "MA|EMA|WMA", "EMA");
Optimize(name, default, min, max, step)number

A parameter the optimiser and walk-forward test vary. In every other run it takes its default (or the value set in the parameter panel).

var fast = Optimize("Fast", 20, 5, 50, 5);

Moving averages

Smoothing lines of every flavour.

MA(series, period)series

Simple moving average.

EMA(series, period)series

Exponential moving average, seeded with the simple average of the first period.

WMA(series, period)series

Linearly weighted moving average.

DEMA(series, period)series

Double exponential moving average.

TEMA(series, period)series

Triple exponential moving average.

HMA(series, period)series

Hull moving average.

Wilders(series, period)series

Wilder's smoothing (used inside RSI and ATR).

AMA(series, factor)series

Adaptive average with a per-bar smoothing factor between 0 and 1.

VWMA(period)series

Volume-weighted moving average of the close.

Oscillators

Momentum readings that swing within a range.

RSI(period = 14)series

Relative Strength Index of the close.

RSIa(series, period = 14)series

RSI of any series.

MACD(fast = 12, slow = 26)series

EMA(fast) βˆ’ EMA(slow) of the close.

Signal(fast = 12, slow = 26, signal = 9)series

Signal line: EMA of MACD.

MACDHist(fast, slow, signal)series

MACD βˆ’ Signal.

StochK(period = 14, smooth = 3)series

Slow stochastic %K.

StochD(period = 14, smooth = 3, d = 3)series

Stochastic %D.

WilliamsR(period = 14)series

Williams %R, 0 to βˆ’100.

CCI(period = 20)series

Commodity Channel Index.

MFI(period = 14)series

Money Flow Index.

ROC(series, period = 1)series

Percent change over the period, in percent (5 means +5%).

Momentum(series, period)series

series βˆ’ series n bars ago.

AroonUp(period = 25)series

Aroon up.

AroonDown(period = 25)series

Aroon down.

Trend & volatility

How strong a trend is and how much prices move.

ATR(period = 14)series

Average True Range.

NATR(period = 14)series

ATR as a percent of the close.

TrueRange()series

True range of every bar.

ADX(period = 14)series

Average Directional Index.

PDI(period = 14)series

+DI.

MDI(period = 14)series

βˆ’DI.

BBandTop(series, period = 20, width = 2)series

Upper Bollinger band.

BBandBot(series, period = 20, width = 2)series

Lower Bollinger band.

KeltnerTop(period = 20, mult = 2)series

EMA + multΒ·ATR.

KeltnerBot(period = 20, mult = 2)series

EMA βˆ’ multΒ·ATR.

SAR(acceleration = 0.02, max = 0.2)series

Parabolic SAR.

Supertrend(period = 10, mult = 3)series

Supertrend line. .dir is +1 in an uptrend, βˆ’1 in a downtrend.

var st = Supertrend(10, 3);
Buy = Cross(st.dir, 0);
GapUp()series

Low above the previous high.

GapDown()series

High below the previous low.

Inside()series

Inside bar.

Outside()series

Outside bar.

Volume

What traded volume says about a move.

OBV()series

On-balance volume.

CMF(period = 20)series

Chaikin Money Flow.

Logic & references

Crosses, look-backs and memory of past events.

Ref(series, -n)series

Value n bars ago. Offsets must be negative or zero β€” a positive offset would read the future and is refused.

var yesterday = Ref(C, -1);
Cross(a, b)series

1 on the bar where a moves from at or below b to above b.

IIf(condition, a, b)series

Bar-by-bar choice.

And(a, b, …)series

1 where every argument is true.

Or(a, b, …)series

1 where any argument is true.

Not(a)series

1 where a is false.

Flip(a, b)series

1 from a bar where a is true until b becomes true.

ExRem(a, b)series

Removes repeated a signals until b appears. Buy = ExRem(Buy, Sell) keeps the first buy of each run.

Hold(a, n)series

1 for n bars after a was true.

BarsSince(condition)series

Bars since the condition was last true.

ValueWhen(condition, series, nth = 1)series

Value of series at the nth most recent bar where condition was true.

HHV(series, period)series

Highest value over the period, including this bar.

LLV(series, period)series

Lowest value over the period.

HHVBars(series, period)series

Bars since the highest value of the period.

LLVBars(series, period)series

Bars since the lowest value of the period.

Highest(series)series

Highest value since the first bar.

Lowest(series)series

Lowest value since the first bar.

HighestSince(condition, series)series

Highest value since condition was last true.

LowestSince(condition, series)series

Lowest value since condition was last true.

Count(condition, period)series

How many of the last n bars were true.

Sum(series, period)series

Rolling sum.

Cum(series)series

Running total.

Statistics

Rolling statistics and regressions.

StDev(series, period)series

Population standard deviation.

ZScore(series, period)series

(series βˆ’ MA) / StDev.

Correlation(a, b, period)series

Rolling Pearson correlation.

LinRegSlope(series, period)series

Slope of the least-squares line per bar.

LinearReg(series, period)series

End value of the least-squares line.

LinRegIntercept(series, period)series

Start value of the least-squares line.

PercentRank(series, period)series

Percent of the previous n values below today's (period up to 500).

Math

Arithmetic that works on whole series at once.

Abs(x)series

Absolute value.

Max(a, b)series

Larger of two values per bar.

Min(a, b)series

Smaller of two values per bar.

Sqrt(x)series

Square root.

Log(x)series

Natural log.

Log10(x)series

Base-10 log.

Exp(x)series

e to the power x.

Pow(x, y)series

x to the power y.

Round(x, digits = 0)series

Round per bar.

Floor(x)series

Round down.

Ceil(x)series

Round up.

Sign(x)series

βˆ’1, 0 or 1.

Nz(x, default = 0)series

Replace empty values (warm-up bars, missing data).

IsNull(x)series

1 where the value is empty.

Add(a, b)series

a + b. Same as a.add(b).

Sub(a, b)series

a βˆ’ b.

Mul(a, b)series

a Γ— b.

Div(a, b)series

a Γ· b, empty where b is zero.

Series(arrayOrNumber)series

Turn a number or a plain array (one value per bar) into a series.

Fundamentals

Annual report figures, switched on at their filing date.

Fundamental(key) or F(key)series

Fiscal-year figure as a step series. Each year's numbers start on the filing date (or 90 days after the year end), so a backtest never sees a report before it was public. Keys: revenue, gross_profit, operating_income, net_income, eps, equity, assets, liabilities, cash, shares, cfo, capex, fcf, dividends, gross_margin, operating_margin, net_margin, roe, roa, debt_to_equity, current_ratio, revenue_growth, eps_growth.

Filter = F("roe").gt(0.15).and(F("revenue_growth").gt(0.1));
PE()series

Price Γ· split-adjusted EPS. Empty when earnings are negative or the report is in another currency without a stored rate.

PB()series

Price Γ· book value per share.

PS()series

Price Γ· revenue per share.

EarningsYield()series

EPS Γ· price.

FCFYield()series

Free cash flow per share Γ· price.

DivYield()series

Dividends paid per share Γ· price.

MarketCap()series

Close Γ— split-adjusted shares.

EPS()series

Split-adjusted earnings per share.

Market & benchmark

How a symbol behaves against its market index.

Benchmark()series

Close of the symbol's market index (S&P 500 for US, IHSG for IDX, Bitcoin for crypto …) aligned to its bars.

BenchmarkName()string

Ticker of that index.

RelStrength()series

Close Γ· benchmark, rebased to 100 at the first bar.

Beta(period = 60)series

Rolling beta of daily returns against the benchmark.

Time frames & other symbols

TimeFrame(period, function () { return series; })series

Run part of the formula on hourly, daily, weekly, monthly, quarterly or yearly bars built from the chart's bars (on 5-minute or hourly charts, "daily" gives the previous day's values), then bring the result back to every daily bar. Each day sees only the last finished period, so there is no look-ahead: a weekly value appears on the first bar of the next week. Plot, AddColumn and Rank must stay outside.

var weeklyUp = TimeFrame("weekly", function () { return C.gt(MA(C, 10)); });
Buy = And(weeklyUp, Cross(C, MA(C, 20)));
Foreign(symbol, field = "C")series

Another symbol's O, H, L, C or V aligned to this symbol's dates (last known value on each bar). Write the ticker as plain text so it can be loaded before the run; up to 6 per formula. Use "~NAME" for one of your composites.

var spy = Foreign("SPY");
var riskOn = spy.gt(MA(spy, 200));
Composite(name, field = "C")series

One of your saved composites. C is the combined value, V is how many symbols went into it that day.

var breadth = Composite("ABOVE200");
Filter = breadth.gt(60);
AddToComposite(value, "~NAME", method = "avg")output

Build your own index across every symbol in an Explore or Scan run. method: avg, sum, median, min, max, or pct (share of symbols where the value is true, 0–100). Saved under your account after the run and readable from any formula with Composite() or Foreign("~NAME").

AddToComposite(C.gt(MA(C, 200)), "~ABOVE200", "pct");
AddToComposite(ROC(C, 20), "~MOM", "median");
Filter = 1;
Rank(value, "name")series

Where this symbol ranks on the value against every other symbol on the same day: 1 = highest. Works in Analysis runs (the server runs the formula twice); on a single chart it is blank.

var r = Rank(ROC(C, 120), "mom");
Buy = r.le(10);
Sell = r.gt(30);
RankPercent(value, "name")series

Same ranking as a percentile: 100 = best on that day, 0 = worst.

RankCount("name")series

How many symbols had a value for that ranking on each day.

Study(name, ratio = 0)series

Value of a line you drew on this symbol's chart, on every bar. A horizontal line is constant; a trendline starts at its first point and extends right; a Fibonacci drawing returns the retracement level for ratio (0 = the second point, 1 = the first, 0.618 = the golden ratio). Blank on symbols without a drawing of that name. Drawings are saved with your account, so scans and backtests see them too.

var support = Study("Line 1");
Buy = Cross(C, Study("Trend 1"));
Sell = C.lt(support);

Alerts

AlertIf(condition, text, kind = "info")output

Raise an alert when the condition is true. kind: buy, sell, info, warning. Explore shows every alert in the range; a Daily scan records alerts on the newest bar, keeps a history and sends an in-app notification. At most 10 per formula.

AlertIf(Cross(C, MA(C, 200)), "Crossed above the 200-day average", "buy");

Candle patterns

CandlePattern(name) or CdlName()series

1 on bars that complete the pattern. 33 patterns: Doji, LongLeggedDoji, DragonflyDoji, GravestoneDoji, SpinningTop, Hammer, HangingMan, InvertedHammer, ShootingStar, BullMarubozu, BearMarubozu, BullBeltHold, BearBeltHold, BullEngulfing, BearEngulfing, BullHarami, BearHarami, Piercing, DarkCloudCover, TweezerBottom, TweezerTop, BullKicker, BearKicker, MorningStar, EveningStar, ThreeWhiteSoldiers, ThreeBlackCrows, ThreeInsideUp, ThreeInsideDown, ThreeOutsideUp, ThreeOutsideDown, RisingThree, FallingThree. Reversal patterns (hammer, stars, tweezers, belt holds) require the matching 5-bar move before them. Every one also exists as Cdl + name, e.g. CdlHammer(). Run the Candle patterns analysis to see which ones actually worked on your list.

Buy = And(CdlBullEngulfing(), C.gt(MA(C, 200)));
Sell = CdlBearEngulfing();
CandlePatterns()array

The list of pattern names with their bars and bias (bullish, bearish, neutral).

Libraries & memory

include("Formula title")output

Run one of your saved formulas first, so its functions and variables can be used here β€” build your own indicator library once and reuse it. Matches the exact title of a formula you own; up to 8 includes, nested at most 3 deep. Library code should define values and functions, not Buy/Plot.

include("My indicators");
Buy = Cross(C, MyTrendLine(C, 20));
StaticVarSet(name, value)output

Remember a value (number, text or series) after this run ends. In an analysis the runtime that ran one symbol keeps it for the next symbols it runs, so it can count or collect across symbols β€” but the order of symbols is not fixed; use Rank() or AddToComposite() for cross-symbol values you rely on.

StaticVarSet("runs", StaticVarGet("runs", 0) + 1);
StaticVarGet(name, fallback)number

Read a remembered value, or the fallback if there is none.

StaticVarRemove(name?)output

Forget one remembered value, or all of them when called without a name.

Custom drawing

PlotText(textOrSeries, barsOrCondition, price?, { color, position })output

Write a label above (or below, position: "below") the bars where the condition is true, or at one bar number (negative counts from the end). Pass a series as the text to print its value at each bar.

PlotText("breakout", Cross(C, HHV(H, 20).ref(-1)));
PlotText(RSI(14), Cross(30, RSI(14)), null, { position: "below", color: "orange" });
PlotOHLC(o, h, l, c, name, { pane, style })output

Draw your own candles (or bars with style: "bars") β€” e.g. a spread, a ratio or Heikin-Ashi computed in the formula. Goes to its own pane unless pane: "price".

var r = C.div(Benchmark());
PlotOHLC(O.div(Benchmark()), H.div(Benchmark()), L.div(Benchmark()), r, "vs index");
PlotGrid(level, { pane, color, label })output

A horizontal reference line at a fixed level in a pane (e.g. 30 and 70 for RSI).

Plot(RSI(14), "RSI", { pane: "RSI" });
PlotGrid(70, { pane: "RSI", color: "red" });
PlotGrid(30, { pane: "RSI", color: "green" });
PlotBand(top, bottom, name, { color, pane })output

Shade the area between two series β€” a channel, a cloud or a band.

PlotBand(BBandTop(C, 20, 2), BBandBot(C, 20, 2), "Bollinger", { color: "#8b5cf6" });
PlotBackground(condition, color)output

Tint the price pane behind every bar where the condition is true, e.g. to show regimes.

PlotBackground(C.lt(MA(C, 200)), "red");
SetBarColor(colorOrColorList)output

Color each candle, usually with ColorIf.

SetBarColor(ColorIf(C.gt(MA(C, 50)), "green", "gray"));

Backtest control

Ranking, sizing, stops and settings inside the formula.

PositionScore = seriesoutput

When more symbols signal than there are free positions, the highest absolute score is bought first.

PositionScore = ROC(C, 120);
PositionSize = number | seriesoutput

Negative = percent of equity (βˆ’20 is 20%). Positive = an amount of money per trade.

PositionSize = -20;
ApplyStop(type, mode, amount)output

type: loss, profit, trailing, nbar. mode: percent, point, bars. The amount can be a series β€” its value on the signal bar is used, so ATR-based stops work. A gap through the stop fills at the open. If one bar touches both the stop and the target, the stop is assumed first.

ApplyStop("loss", "percent", 8);
ApplyStop("trailing", "point", ATR(14).mul(3));
ApplyStop("nbar", "bars", 20);
SetOption(name, value)output

Override a setting from inside the formula: InitialEquity, MaxOpenPositions, CommissionPercent, CommissionMinimum, CommissionPerShare, SlippagePercent, AllowShort, TradeDelay, TradePrice, RoundLot, PositionSizePercent, PointValue, MarginDeposit, TickSize, MaxScaleIns, ScaleOutPercent, BacktestMode, RebalanceBars, WorstRankHeld.

SetOption("MaxOpenPositions", 5);
SetOption("CommissionMinimum", 1);
SetOption("PointValue", 50);
SetOption("MarginDeposit", 12000);
SetPositionSize(amount, mode, stopDistance?)output

How much to buy on each entry. mode percent: % of equity. value: money per trade. shares: a number of shares or contracts. risk: risk amount % of equity on the stop distance (in price points) β€” the size is chosen so hitting the stop loses exactly that much. Amount can be a series.

SetPositionSize(1, "risk", ATR(14).mul(2));
ApplyStop("loss", "point", ATR(14).mul(2));
ScaleIn = conditionoutput

While a trade is open, add another position of the normal size on bars where this is true (pyramiding). Limited by MaxScaleIns (default 3) and free cash. The entry price becomes the average.

ScaleIn = Cross(C, HHV(C, 20).ref(-1));
ScaleOut = conditionoutput

While a trade is open, sell ScaleOutPercent (default 50%) of it on bars where this is true. Each partial sale is listed as its own trade with reason scale-out.

ScaleOut = C.gt(Ref(C, -10).mul(1.1));
SetOption("ScaleOutPercent", 33);
SetBacktestMode("regular" | "rotation")output

rotation ignores Buy and Sell: every RebalanceBars bars it ranks all symbols by PositionScore, holds the top MaxOpenPositions and sells a holding once its rank is worse than WorstRankHeld. Negative scores are shorted when shorts are allowed.

SetBacktestMode("rotation");
SetOption("MaxOpenPositions", 5);
SetOption("RebalanceBars", 21);
PositionScore = ROC(C, 120);
TradeMetric(name, series)output

Record a value on the signal bar of every trade. The backtest report adds a Trade metrics tab comparing winners with losers and splitting trades into groups by this value β€” a quick way to find a filter.

TradeMetric("RSI at entry", RSI(14));
TradeMetric("Distance from 200d %", C.div(MA(C, 200)).sub(1).mul(100));

Series methods

Methods available on every series.

a.gt(b) .ge .lt .le .eq .neseries

Comparisons. JavaScript cannot overload > on series, so write C.gt(MA(C, 20)) instead of C > MA(C, 20).

a.add(b) .sub .mul .div .pow .neg .absseries

Arithmetic on series or numbers.

a.and(b) .or .notseries

Combine conditions.

a.ref(-n)series

Same as Ref(a, -n).

a.crosses(b)series

Same as Cross(a, b).

a.at(i)number

Value at bar i; negative counts from the end (a.at(-1) is the last bar).

a.lastnumber

Value on the last bar.

a.map((value, i) => number)series

Bar-by-bar custom calculation. Use .v for the raw Float64Array when writing loops.

var streak = C.map((v, i) => i > 0 && v > C.v[i - 1] ? 1 : 0);