What AlgoBarsX is
One language for strategies, indicators, alerts and libraries.
AlgoBarsX is the language AlgoBars runs on. You describe a strategy, an indicator or an alert in a few readable lines, and the same script is used by the chart, the backtester, the alerts engine and live automation. There is one definition of what your rule means, so you never rewrite it for a different tool.
There are four kinds of script. The first word of the file says which one it is.
| Kind | What it is for | What comes back when you run it |
|---|---|---|
strategy | Places and manages trades. | A backtest, a report and a chart. It can be deployed to a demo or live account. |
indicator | Calculates and draws. It can publish values for other scripts to use. | The chart it draws and the values it exports. |
alert | Watches a condition and sends a message. | The messages it would have sent, and the ones it held back because of its own repeat and cooldown settings. |
library | Holds functions and constants you reuse. | It is checked and described. Other scripts import it by name and version. |
Every script is also translated back into plain English by the compiler, so you can check that it says what you meant. That text is generated from the compiled code. If the English is wrong, the code is wrong.
Your first strategy
Write it, read it back, run it.
- Open the Terminal in AlgoBars and start a new strategy. You get a working starter script, not a blank page.
- Replace it with the script below. It buys when a fast average crosses above a slow one, risks 1% of the balance with a 25-pip stop and a target of twice the risk, and closes everything on the opposite cross.
strategy "EMA Cross"market: EURUSDbars: 15minput fast = 20input slow = 50when crosses_above(ema(close, fast), ema(close, slow)):buy risk: 1%, stop: 25 pips, target: 2Rwhen crosses_below(ema(close, fast), ema(close, slow)):close_all- Open the description tab. The compiler reads the script back to you:
EMA Cross is a strategy that trades EURUSD on 15-minute bars.
You can change 2 inputs: fast (default 20) and slow (default 50).
When the EMA of the close over fast bars crosses above the EMA of the close over slow bars, it buys at market, risking 1% of the balance, with a stop 25 pips from the entry and with a target 2R from the entry.
When the EMA of the close over fast bars crosses below the EMA of the close over slow bars, it closes all trades.
- Press Run. A small panel asks what to test on: market, bars, bar type, how far back (1 week to 1 year), lot size and starting balance. The market and bars come filled in from your script, so you are confirming, not retyping.
- You get results, a report, a chart and a replay. The replay plays the finished test back so you can watch each trade open and close and the balance move. Every run is kept under versions with the code that produced it, so you can always go back.
- Change
fastor the stop and run again. Nothing is lost by trying.
The cheat sheet
Most of the language on one screen. It compiles.
One script that touches the header, inputs, constants, state, history, another bar size, confirmations, a rule with timing, a managed order, an event and a plot. Copy it into the Terminal and start deleting what you do not need.
algobarsx 1strategy "Cheat Sheet" # or: indicator, alert, librarymarket: EURUSD # several: markets: EURUSD, GBPUSDbars: 15m # 1m 5m 1h 4h 1d, or renko(5), xray(20)max_open: 1max_daily_loss: 3%input length = 20, min: 5, max: 200 # a setting in the panelconst BUFFER = 2 pips # fixed valuestate wins = 0 # survives between barsfast = ema(close, length) # worked out again on every barprev_close = close[1] # history: one bar backh4 = bars(bars: 4h) # another bar size, closed candles onlyuptrend = h4.close > ema(h4.close, 50)confirmations setup: # named conditionstrend: uptrendmomentum: rsi(close, 14) between 50 and 70require: allwhen setup.passed and crosses_above(close, fast) max 2 per day cooldown 30m:buy risk: 1%, stop: low - BUFFER, target: 2R:breakeven at: 1Rpartial 50% at: 1.5Rtrail by: atr(14) * 2, after: 1.5Rwhen crosses_below(close, fast):close_allon exit(trade):if trade.r > 0:wins += 1log "{trade.tag} closed at {trade.r:0.00}R, {wins} wins so far"plot fast, color: if uptrend then green else redFor the long version, see the Language Tour example, which uses nearly every construct in the language.
How a script is laid out
Header, inputs, calculations, rules, drawing.
A script reads from top to bottom in five parts. Only the header is required.
algobarsx 1# 1. Header: what kind of script this is, and its settingsstrategy "Layout"market: EURUSDbars: 15m# 2. Inputs: what the person running it may changeinput length = 20# 3. Calculations: worked out on every bartrend_up = close > ema(close, length)# 4. Rules: when something is true, do somethingwhen starts(trend_up):buy risk: 1%, stop: 20 pips, target: 2R# 5. Drawing: what the chart showsplot ema(close, length), color: blue- Blocks are indented with spaces. A line ending in a colon opens a block, and the lines under it are indented. Tabs are an error (AS0003).
- Comments start with
#and run to the end of the line. - The optional first line
algobarsx 1names the language version the script was written for. - Line breaks inside
( ),[ ]or{ }are ignored, so long expressions can span lines there. - Names are case-sensitive. A short list of words is reserved, such as
when,on,if,stateandinput. The full list is in the grammar.
At each bar close the runtime works in a fixed order: fills and management inside the bar, then on fill and on exit, then calculations, then confirmations and sequences, then rules in script order, then exports and drawing. Orders your rules create take effect from the next bar. See E21 and E1.
Coming from Pine Script
A real import, the ideas side by side, and the names that translate.
This is a real run of the importer on a small Pine Script script. The result compiles. 8 lines were read: 5 carried over exactly, 2 were adapted and 2 came back as decisions for you.
//@version=5strategy("RSI Dip", overlay=true)len = input.int(14, "RSI Length")r = ta.rsi(close, len)if ta.crossover(r, 30)strategy.entry("L", strategy.long)if ta.crossunder(r, 70)strategy.close("L")plot(ta.ema(close, 200), color=color.orange)strategy "RSI Dip"market: EURUSDbars: 1hinput len = 14r = rsi(close, len)when crosses_above(r, 30):buy size: 1 lot, tag: "L"when crosses_below(r, 70):close_all tag: "L"plot ema(close, 200), color: orangeLine by line
| Status | Your line | What happened |
|---|---|---|
| Exact | strategy("RSI Dip", overlay=true) | strategy() became the script header |
| Exact | len = input.int(14, "RSI Length") | input() became an input |
| Exact | r = ta.rsi(close, len) | a calculation was carried over |
| Your call | strategy.entry("L", strategy.long) | strategy.entry() had no size of its own, so it became one lot: set the size or risk you want |
| Adapted | if ta.crossover(r, 30) | an if that places orders became a rule |
| Exact | strategy.close("L") | strategy.close() became close_all |
| Adapted | if ta.crossunder(r, 70) | an if that places orders became a rule |
| Exact | plot(ta.ema(close, 200), color=color.orange) | plot() became plot |
| Your call | strategy(...) | Pine scripts carry no market or bar size, so EURUSD on 1h was filled in: set the ones you want |
How the ideas translate
| In Pine Script | In AlgoBarsX |
|---|---|
strategy("RSI Dip", overlay=true) | strategy "RSI Dip" with market: and bars: stated. Pine takes both from the chart, so they come back as your decision. |
len = input.int(14, "RSI Length") | input len = 14 |
ta.rsi(close, len) | rsi(close, len) |
if cond, then strategy.entry(...) under it | when cond: with buy under it |
strategy.close("L") | close_all tag: "L" |
plot(x, color=color.orange) | plot x, color: orange |
var count = 0 | state count = 0 |
close[1] | close[1], exactly the same |
risk: 1% and a stop.Names the importer translates for you
| Their name | AlgoBarsX |
|---|---|
ta.sma | sma |
ta.ema | ema |
ta.rma | rma |
ta.wma | wma |
ta.hma | hma |
ta.vwma | vwma |
ta.dema | dema |
ta.tema | tema |
ta.rsi | rsi |
ta.cci | cci |
ta.roc | roc |
ta.mom | momentum |
ta.stdev | stdev |
ta.variance | variance |
ta.highest | highest |
ta.lowest | lowest |
ta.median | median |
ta.linreg | linreg |
ta.correlation | correlation |
ta.atr | atr |
ta.tr | true_range |
ta.mfi | mfi |
ta.obv | obv |
ta.vwap | vwap |
ta.barssince | bars_since |
ta.crossover | crosses_above |
ta.crossunder | crosses_below |
ta.cross | crosses |
ta.cum | cum |
ta.trix | trix |
ta.cmo | cmo |
math.abs | abs |
math.max | max |
math.min | min |
math.round | round |
math.floor | floor |
math.ceil | ceil |
math.sqrt | sqrt |
math.log | log |
math.exp | exp |
math.pow | pow |
math.sign | sign |
bar_index | bar.index |
syminfo.tickerid | market.symbol |
syminfo.ticker | market.symbol |
syminfo.mintick | market.point_size |
color.green | green |
color.red | red |
color.blue | blue |
color.orange | orange |
color.yellow | yellow |
color.purple | purple |
color.teal | teal |
color.gray | gray |
color.grey | gray |
color.white | white |
color.black | black |
color.lime | green |
color.maroon | red |
color.silver | gray |
color.aqua | teal |
strategy.long | long |
strategy.short | short |
Coming from MQL4 and MQL5
A real import, the ideas side by side, and the names that translate.
This is a real run of the importer on a small MQL4 and MQL5 script. The result compiles. 11 lines were read: 6 carried over exactly, 6 were adapted and 2 came back as decisions for you.
#property strictinput int Fast = 20;input int Slow = 50;input double Lots = 0.10;void OnTick(){double f = iMA(NULL, 0, Fast, 0, MODE_EMA, PRICE_CLOSE, 0);double s = iMA(NULL, 0, Slow, 0, MODE_EMA, PRICE_CLOSE, 0);if (f > s && OrdersTotal() == 0)OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, Ask - 250 * Point, Ask + 500 * Point);}strategy "Imported expert advisor"market: EURUSDbars: 1hinput Fast = 20input Slow = 50input Lots = 0.1 lotsf = ema(close, Fast)s = ema(close, Slow)when f > s and trades.open().len == 0:buy size: Lots, stop: close - 250 * market.point_size, target: close + 500 * market.point_sizeLine by line
| Status | Your line | What happened |
|---|---|---|
| Exact | input int Fast = 20 ; | an input carried over |
| Exact | input int Slow = 50 ; | an input carried over |
| Exact | input double Lots = 0.10 ; | an input carried over |
| Adapted | void OnTick ( ) | OnTick became the script body, which runs once per bar; add evaluate: tick to run on every tick |
| Exact | double f = iMA ( NULL , 0 , Fast , 0 , MODE_EMA | a variable carried over |
| Exact | double s = iMA ( NULL , 0 , Slow , 0 , MODE_EMA | a variable carried over |
| Adapted | f > s && OrdersTotal ( ) == 0 | OrdersTotal() became the number of open trades |
| Your call | Symbol ( ) | Symbol() has no equivalent yet |
| Adapted | Ask | Ask became the close: a backtest here has one price per bar, and live trading uses the right side of the spread |
| Adapted | Ask - 250 * Point | Ask became the close: a backtest here has one price per bar, and live trading uses the right side of the spread |
| Adapted | Ask + 500 * Point | Ask became the close: a backtest here has one price per bar, and live trading uses the right side of the spread |
| Exact | OrderSend ( Symbol ( ) , OP_BUY , Lots , Ask , 3 , | the order carried over with its stop and target |
| Adapted | if ( f > s && OrdersTotal ( ) == 0 ) OrderSend ( | a condition that places orders became a rule |
| Your call | the script header | an expert advisor runs on whatever chart it is attached to, so EURUSD on 1h was filled in: set the ones you want |
How the ideas translate
| In MQL4 and MQL5 | In AlgoBarsX |
|---|---|
input int Fast = 20; | input Fast = 20 |
input double Lots = 0.10; | input Lots = 0.1 lots |
iMA(NULL, 0, Fast, 0, MODE_EMA, PRICE_CLOSE, 0) | ema(close, Fast) |
OrdersTotal() == 0 | trades.open().len == 0 |
OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, sl, tp) | buy size: Lots, stop: …, target: … |
Point | market.point_size |
PERIOD_H1, PERIOD_D1 | 1h, 1d |
void OnTick() | the body of the script. Add evaluate: tick to the header if it must run inside the bar. |
Ask - 250 * Point carry over literally. They read better, and survive gaps better, as distances: stop: 250 points.Names the importer translates for you
| Their name | AlgoBarsX |
|---|---|
mode_sma | sma |
mode_ema | ema |
mode_smma | smma |
mode_lwma | wma |
price_close | close |
price_open | open |
price_high | high |
price_low | low |
price_median | hl2 |
price_typical | hlc3 |
price_weighted | ohlc4 |
mathabs | abs |
mathmax | max |
mathmin | min |
mathround | round |
mathfloor | floor |
mathceil | ceil |
mathsqrt | sqrt |
mathlog | log |
mathexp | exp |
mathpow | pow |
mathsign | sign |
_symbol | market.symbol |
symbol | market.symbol |
_point | market.point_size |
point | market.point_size |
bars | bar.index |
period_m1 | 1m |
period_m5 | 5m |
period_m15 | 15m |
period_m30 | 30m |
period_h1 | 1h |
period_h4 | 4h |
period_d1 | 1d |
period_w1 | 1w |
period_mn1 | 1M |
Coming from NinjaScript
A real import, the ideas side by side, and the names that translate.
This is a real run of the importer on a small NinjaScript script. The result compiles. 26 lines were read: 5 carried over exactly, 1 were adapted and 4 came back as decisions for you.
namespace NinjaTrader.NinjaScript.Strategies{public class MacdMomentum : Strategy{protected override void OnStateChange(){if (State == State.SetDefaults){Name = "MACD Momentum";}}protected override void OnBarUpdate(){if (CrossAbove(EMA(Close, 12), EMA(Close, 26), 1)){SetStopLoss(CalculationMode.Ticks, 40);SetProfitTarget(CalculationMode.Ticks, 80);EnterLong();}if (CrossBelow(EMA(Close, 12), EMA(Close, 26), 1)){ExitLong();}}}}strategy "MACD Momentum"market: EURUSDbars: 1hinput risk = 1%when crosses_above(ema(close, 12), ema(close, 26)):buy risk: risk, stop: 40 points, target: 80 pointswhen crosses_below(ema(close, 12), ema(close, 26)):close_all side: longLine by line
| Status | Your line | What happened |
|---|---|---|
| Exact | SetStopLoss ( CalculationMode.Ticks , 40 ) ; | a stop in ticks became points |
| Your call | ;
SetProfitTarget ( CalculationMode.Ticks , 80 ) | this line has no equivalent yet |
| Exact | SetProfitTarget ( CalculationMode.Ticks , 80 ) ; | a stop in ticks became points |
| Your call | ;
EnterLong ( ) ;
} | this line has no equivalent yet |
| Adapted | EnterLong ( ) ;
}
if | NinjaTrader sizes an order by the strategy settings, so this one risks a set share of the balance |
| Your call | ;
}
if ( CrossBelow ( | this line has no equivalent yet |
| Exact | if ( CrossAbove ( EMA ( Close , | a condition became a rule |
| Exact | ExitLong ( ) ;
}
} | an exit carried over |
| Your call | ;
}
}
} | this line has no equivalent yet |
| Exact | if ( CrossBelow ( EMA ( Close , | a condition became a rule |
How the ideas translate
| In NinjaScript | In AlgoBarsX |
|---|---|
OnBarUpdate() | the body of the script |
EMA(Close, 12) | ema(close, 12) |
CrossAbove(a, b, 1) | crosses_above(a, b) |
SetStopLoss(CalculationMode.Ticks, 40) | stop: 40 points, on the order itself |
SetProfitTarget(CalculationMode.Ticks, 80) | target: 80 points |
EnterLong() | buy |
ExitLong() | close_all side: long |
Instrument, TickSize | market.symbol, market.point_size |
SetStopLoss before the entry. The import sizes the trade by risk, and a risk-based order needs a stop to measure from. The C# around the strategy (namespaces, State handling) comes back as notes, not code.Names the importer translates for you
| Their name | AlgoBarsX |
|---|---|
currentbar | bar.index |
instrument | market.symbol |
ticksize | market.point_size |
null | na |
Coming from EasyLanguage
A real import, the ideas side by side, and the names that translate.
This is a real run of the importer on a small EasyLanguage script. The result compiles. 6 lines were read: 3 carried over exactly, 3 were adapted and 1 came back as decisions for you.
Inputs: FastLen(20), SlowLen(50);Variables: FastAvg(0), SlowAvg(0);FastAvg = XAverage(Close, FastLen);SlowAvg = XAverage(Close, SlowLen);If FastAvg crosses over SlowAvg then Buy next bar at market;If FastAvg crosses under SlowAvg then Sell next bar at market;strategy "Imported EasyLanguage strategy"market: EURUSDbars: 1hinput FastLen = 20input SlowLen = 50state FastAvg = 0state SlowAvg = 0FastAvg = ema(close, FastLen)SlowAvg = ema(close, SlowLen)when crosses_above(FastAvg, SlowAvg):buy size: 1 lotwhen crosses_below(FastAvg, SlowAvg):close_all side: longLine by line
| Status | Your line | What happened |
|---|---|---|
| Exact | FastAvg = XAverage ( Close , FastLen ) ; | a calculation carried over |
| Exact | SlowAvg = XAverage ( Close , SlowLen ) ; | a calculation carried over |
| Exact | Buy next bar at market ; | an order at market carried over |
| Adapted | If FastAvg crosses over SlowAvg then Buy next bar at market ; | a condition that places orders became a rule |
| Adapted | Sell next bar at market ; | sell closed the open position, which became close_all on that side |
| Adapted | If FastAvg crosses under SlowAvg then Sell next bar at market ; | a condition that places orders became a rule |
| Your call | the script header | EasyLanguage carries no market or bar size, so EURUSD on 1h was filled in: set the ones you want |
How the ideas translate
| In EasyLanguage | In AlgoBarsX |
|---|---|
Inputs: FastLen(20); | input FastLen = 20 |
Variables: FastAvg(0); | state FastAvg = 0 |
XAverage(Close, FastLen) | ema(close, FastLen) |
Average, WAverage | sma, wma |
If a crosses over b then … | when crosses_above(a, b): |
Buy next bar at market; | buy. Market orders already fill at the next bar's open. |
Sell next bar at market; | close_all side: long |
CurrentBar | bar.index |
state values. Where one is simply recalculated every bar, you can delete the state line and keep the assignment.Names the importer translates for you
| Their name | AlgoBarsX |
|---|---|
average | sma |
xaverage | ema |
waverage | wma |
stddev | stdev |
standarddev | stdev |
truerange | true_range |
avgtruerange | atr |
absvalue | abs |
maxlist | max |
minlist | min |
squareroot | sqrt |
expvalue | exp |
power | pow |
ceiling | ceil |
linearregvalue | linreg |
c | close |
o | open |
h | high |
l | low |
v | volume |
currentbar | bar.index |
barnumber | bar.index |
pi | 3.14159265 |
range | bar.range |
avgprice | ohlc4 |
medianprice | hl2 |
typicalprice | hlc3 |
Coming from thinkScript
A real import, the ideas side by side, and the names that translate.
This is a real run of the importer on a small thinkScript script. The result compiles. 8 lines were read: 7 carried over exactly, 1 were adapted and 0 came back as decisions for you.
input fastLength = 20;input slowLength = 50;def fast = ExpAverage(close, fastLength);def slow = ExpAverage(close, slowLength);plot FastLine = fast;plot SlowLine = slow;AddOrder(OrderType.BUY_TO_OPEN, fast crosses above slow);AddOrder(OrderType.SELL_TO_CLOSE, fast crosses below slow);strategy "Imported study"market: EURUSDbars: 1hinput risk = 1%input fastLength = 20input slowLength = 50fast = ema(close, fastLength)slow = ema(close, slowLength)when crosses_above(fast, slow):buy risk: risk, stop: atr(14) * 2, target: 2Rwhen crosses_below(fast, slow):close_all side: longplot fast as FastLineplot slow as SlowLineLine by line
| Status | Your line | What happened |
|---|---|---|
| Exact | input fastLength = 20 | the input fastLength carried over |
| Exact | input slowLength = 50 | the input slowLength carried over |
| Exact | def fast = ExpAverage(close, fastLength) | fast carried over |
| Exact | def slow = ExpAverage(close, slowLength) | slow carried over |
| Exact | plot FastLine = fast | the plot FastLine carried over |
| Exact | plot SlowLine = slow | the plot SlowLine carried over |
| Adapted | AddOrder(OrderType.BUY_TO_OPEN, fast crosses above slow) | thinkorswim sizes an order by the chart settings, so this one risks a set share of the balance with an ATR stop: set the size you want |
| Exact | AddOrder(OrderType.SELL_TO_CLOSE, fast crosses below slow) | a closing order carried over |
How the ideas translate
| In thinkScript | In AlgoBarsX |
|---|---|
input fastLength = 20; | input fastLength = 20 |
def fast = ExpAverage(close, fastLength); | fast = ema(close, fastLength) |
plot FastLine = fast; | plot fast as FastLine |
fast crosses above slow | crosses_above(fast, slow) |
AddOrder(OrderType.BUY_TO_OPEN, cond) | when cond: with buy under it |
AddOrder(OrderType.SELL_TO_CLOSE, cond) | when cond: with close_all side: long under it |
yes, no | true, false |
BarNumber() | bar.index |
risk input with a stop of two ATRs and a 2R target. Check those three numbers before anything else.Names the importer translates for you
| Their name | AlgoBarsX |
|---|---|
yes | true |
no | false |
double | number |
barnumber | bar.index |
bar_number | bar.index |
getsymbol | market.symbol |
Coming from Python
A real import, the ideas side by side, and the names that translate.
This is a real run of the importer on a small Python script. The result compiles. 5 lines were read: 4 carried over exactly, 1 were adapted and 0 came back as decisions for you.
import pandas_ta as tadf["fast"] = ta.ema(df["close"], length=20)df["slow"] = ta.ema(df["close"], length=50)df["rsi"] = ta.rsi(df["close"], length=14)df["long"] = (df["fast"] > df["slow"]) & (df["rsi"] < 70)indicator "Imported Python script"pane: pricefast = ema(close, 20)slow = ema(close, 50)rsi_value = rsi(close, 14)long = fast > slow and rsi_value < 70Line by line
| Status | Your line | What happened |
|---|---|---|
| Exact | df["fast"] = ta.ema(df["close"], length=20) | a calculation carried over |
| Exact | df["slow"] = ta.ema(df["close"], length=50) | a calculation carried over |
| Adapted | df["rsi"] = ta.rsi(df["close"], length=14) | rsi is the name of a built-in here, so it became rsi_value |
| Exact | df["rsi"] = ta.rsi(df["close"], length=14) | a calculation carried over |
| Exact | df["long"] = (df["fast"] > df["slow"]) & (df["rsi"] < 70) | a calculation carried over |
How the ideas translate
| In Python | In AlgoBarsX |
|---|---|
df["fast"] = ta.ema(df["close"], length=20) | fast = ema(close, 20) |
ta.rsi(df["close"], length=14) | rsi(close, 14) |
(a > b) & (c < 70) | a > b and c < 70 |
a column named rsi | renamed to rsi_value, so it does not hide the function |
df["close"].shift(1) | close[1] |
df["high"].rolling(20).max() | highest(high, 20). Rolling calculations come back as a decision, so you pick the function. |
when rules to turn it into a strategy.Names the importer translates for you
| Their name | AlgoBarsX |
|---|---|
mom | momentum |
natr | atr |
willr | williams_r |
Values, units and types
Percent, pips, R and money are real types, not bare numbers.
Most mistakes in trading code are unit mistakes. AlgoBarsX makes the unit part of the value, and the compiler checks it.
stop_distance = 20 pipstick_buffer = 5 pointsrisk_now = 1%cash_risk = $200wait = 30mopens_at = 08:00level = high + 2 pipstwo_percent = 2% of account.balance| You write | It means |
|---|---|
1%, 0.5% | A percent. No space before the sign. Use 2% of account.balance to say what it is a percent of. Where the base is unclear the compiler asks: “1% of what?” (AS0305) |
20 pips, 5 points | A distance, converted with each market's own pip and point size. |
2R, 1.5R | A multiple of the trade's initial stop distance. Valid only where a trade has a stop. |
$200 | Money in the account currency. |
1 lot, 0.3 lots | Position size. |
10 bars | A count of bars. |
30s 15m 4h 1d 1w 1M | A length of time. The same words are bar sizes. |
08:00, 2026-01-15 | A time of day and a date. |
#22c55e, green.fade(80) | A colour. Named colours can fade and blend. |
1_000_000 | A number. Underscores are allowed between digits. |
na | No value. Test with is_na, replace with nz. |
A stop written as a bare number is caught before anything runs: “stop 25 has no unit. Did you mean 25 pips?” (AS0302). A price level and a distance are different things, and the compiler knows which one an option expects.
Text with live values
Text in double quotes can include any expression in braces, with an optional format after a colon. Write {{ for a literal brace.
on bar close:log "RSI {rsi(close, 14):0.0} on {market.symbol}"Units follow the value, not the text
The engine works a unit out from the code. It does not look for the word “pips” in what you typed.
- A price with a distance taken off it is still a price.
stop: lowest(low, 10) - 3 pipsis a level on the chart. - One price taken from another is a distance.
- A distance stays a distance when you scale it, as in
atr(14) * 2. - A unit held in an input travels with it. After
input risk = 1%, the orderrisk: riskrisks one percent, exactly as if you had written1%.
All eight units are listed under Units.
Series and history
Every value has a past. Read it without looking ahead.
close is not one number. It is a series with a value on every bar. Square brackets read earlier bars: close[1] is the previous bar's close. A value at a bar never depends on a later bar, and the test suite proves it by rewriting the future and checking the past did not move.
prev_close = close[1]range_high = highest(high, 20)[1]rising = close > close[1] and close[1] > close[2]recent = was(rsi(close, 14) < 30, within: 5 bars)steady = held(close > ema(close, 50), for: 3 bars)since_cross = bars_since(crosses_above(close, ema(close, 50)))safe = nz(close[500], close)wasis true if a condition was true at some point in recent bars.heldis true if it has been true for consecutive bars.crosses_above,crosses_belowandcrossesdetect crossings.startsandendsare true on the bar a condition becomes true or stops being true.bars_sincecounts bars since a condition was last true.- Reading further back than the data goes gives
na.nzsupplies a fallback. - Reading forward is an error, not a bug waiting to happen: “close[-1] would read a future bar. History counts back from the current bar: close[1] is the previous bar.” (AS0203)
- Indicator calls update on every bar, even when their line sits in a branch that did not run, so a reading is never stale.
Built-in price series: open high low close volume time hl2 hlc3 ohlc4, plus facts about the bar such as bar.index, bar.confirmed and bar.range. See Bar variables.
Operators and expressions
and, or, not, between, in, if-then-else.
in_band = rsi(close, 14) between 40 and 60morning = hour in 8..11bias = if close > ema(close, 200) then 1 else -1power = 2 ** 3calm = not (atr(14) > atr(14)[10])first_hour = time_of_day between 08:00 and 09:00| Operator | Meaning |
|---|---|
+ - * / % ** | Arithmetic. ** is power and groups from the right, so 2 ** 3 ** 2 is 512. |
== != < <= > >= | Comparison. |
and or not | Logic, in words. |
x between a and b | True when x is inside the range. |
x in 8..11 | True when x is in a range or a list. |
if c then a else b | Chooses a value inside an expression. |
2% of account.balance | Turns a percent into an amount. |
+= -= *= /= | Update a state value in place. |
Time variables available everywhere: hour, minute, day_of_week and time_of_day.
Inputs and constants
What the person running the script may change.
An input becomes a setting in the script's panel. Its type comes from its default value, so input risk = 1% is a percent and input higher_tf = 4h is a bar size. A const is fixed.
input fast = 20, label: "Fast length", min: 2, max: 500, group: "Trend"input source = close, label: "Source"input higher_tf = 4h, label: "Higher timeframe"input session = "london", options: ["london", "new_york", "asia"]input risk = 1%, min: 0.1%, max: 5%, step: 0.1%input show_zones = true, group: "Display"input zone_color = blue.fade(70), group: "Display", visible_if: show_zonesconst RISK_CAP = 2%| Option | What it does |
|---|---|
label | The name shown in the panel. |
min, max, step | Limits and step size. |
options | A fixed list of choices. |
group | Groups inputs under a heading. |
visible_if | Shows this input only when another one is on. |
An input nobody reads is flagged as a hint (AS0601).
Variables and state
Values recalculated each bar, and values that survive between bars.
A plain assignment such as trend_up = close > ema(close, 50) is worked out again on every bar. A state value is set once and keeps whatever you last put in it, which is how you count things or remember a price.
state triggers = 0state last_entry: price = nawhen close > open:triggers += 1last_entry = closeon day change:triggers = 0You can annotate a type when the default does not say enough: state last_entry: price = na.
Control flow
if, for, match, break and continue.
if regime == Regime.trending:risk_now = 1%elif regime == Regime.volatile:risk_now = 0.5%else:risk_now = 0.25%for level in levels:if close > level.price:level.touched += 1for i in 0..50:if i > 10:breakcontinuematch regime:Regime.trending: log "trending"Regime.ranging: log "ranging"if/elif/elsechoose between blocks.for x in listandfor i in 0..50loop.breakandcontinuework as you expect.matchpicks a branch by value, which reads well with anenum.
Functions, types, enums and lists
Simple on top, a full language underneath.
type Level:price: pricetouched: int = 0formed_at: timeenum Regime: trending, ranging, volatilefn swing_strength(len: int) -> number:up = highest(high, len) - closedown = close - lowest(low, len)return (down - up) / atr(14)action fn enter_long(size_risk: percent = 1%) -> bool:buy risk: size_risk, stop: 20 pips, target: 2Rreturn truefndefines a pure function. It calculates and returns a value.action fnmay place orders, so it can only be called where orders are allowed. A plainfnthat tries to trade is an error (AS0403).typedefines a record with named, typed fields and optional defaults.enumdefines a fixed set of names.- Parameters can have types and defaults:
fn f(source: series<number> = close, length: int = 20) -> number.
Lists and lambdas
levels.push(Level(price: high, formed_at: time))levels = levels.filter(l => l.touched == 0).keep_last(50)ranked = levels.sort_by((a, b) => a.price - b.price)Lists support push, pop, remove, filter, map, sort_by, keep_last, first, last, len and contains, plus the aggregates sum, mean, min and max. A lambda is written x => expression.
The header and its settings
Markets, bar size, limits and trading hours.
strategy "Language Tour"markets: EURUSD, GBPUSDbars: 15mevaluate: bar_closemax_open: 3max_open_per_side: 2direction: bothopposite: reversepyramiding: 3min_distance: 20 pipsmax_daily_loss: 3%max_drawdown: 10%trade_only: within sessions london, new_yorkEvery setting, with its type and default:
| Setting | Type | Default | What it does |
|---|---|---|---|
market | symbol | chart symbol | Symbol the script runs on. |
markets | list<symbol> | Several symbols; alerts evaluate each independently. | |
bars | bartype | chart bars | Bar type: timeframe, range(n), xray(n), renko(n) or heikin_ashi(tf). |
evaluate | string | bar_close | bar_close (default, never repaints) or tick. |
pane | string | price | price, new or a named pane. |
max_open | int | 1 | Open trades allowed at once. |
max_open_per_side | int | Open trades allowed per side. | |
direction | string | both | both, long or short. |
opposite | string | ignore | On an opposite signal: close, reverse, ignore or hedge (where the venue supports it). |
warmup | int | Bars required before rules run (computed by the compiler when omitted). | |
repeat | string | once per bar | once, once per bar, once per bar close or every time. |
cooldown | duration | bars | Minimum time or bars between notifications. | |
expires | date | duration | When the alert stops. | |
check | duration | every 1m | How often account-only alerts are checked. |
show_on_chart | bool | true | Draw fire points, watched levels and live status on the chart. |
max_daily_loss | percent | money | Pause the deployment after this loss in a day. | |
max_drawdown | percent | money | Pause the deployment at this drawdown. | |
max_total_risk | percent | Risk allowed across open trades. | |
pyramiding | int | 1 | Entries allowed in the same direction. |
min_distance | distance | Smallest distance between pyramided entries. | |
trade_only | sessions | Sessions in which the strategy may open trades. |
Limits are checked when an entry fills, not when it is placed (E19). With trade_only, entries are only created inside the listed sessions, while exits and management carry on outside them (E22). The named sessions are sydney, tokyo, asia, frankfurt, london and new_york.
Rules: when something is true, do something
The when rule and its timing modifiers.
A rule is when <condition> [modifiers]: followed by what to do. Name a rule with as to read its counters later, such as long_entry.triggers.
when long_setup.passed as long_entry every 4th:buy risk: risk, stop: atr(14) * 1.5, target: 3Rwhen starts(long_setup.passed) skip first 3 max 2 per day cooldown 30m:triggers += 1if triggers % 4 == 0:buy risk: 0.5%, stop: atr(14) * 1.5, target: 3RModifiers say when a rule may fire, on the rule itself, so there are no counters to build by hand:
| Modifier | What it does |
|---|---|
every <ordinal> | every bar | Act on every Nth trigger, or count every bar the condition holds. |
skip first <n> | Ignore the first N triggers, then act on every trigger. |
max <n> per <period> | Cap actions per day, session, hour or week. |
cooldown <duration | n bars> | Ignore triggers for a while after acting. |
once per bar | In tick mode, act at most once per bar. |
within sessions <name>, ... | Only trigger inside the named sessions. |
from <time> to <time> [zone] | Only trigger inside a daily time window. |
Confirmations
Name each condition a setup needs.
A confirmations block lists named conditions and how many must agree: require: all or require: at least N. It gives you .passed to use in a rule.
confirmations long_setup:trend: trend_upmomentum: rsi(close, 14) > 55volume: volume > sma(volume, 20) * 1.5structure: break_of_structure(direction: up)higher_tf_trend: h4.close > ema(h4.close, 50)ribbon_up: ribbon.uprequire: at least 5The backtest report breaks trades down by which confirmations agreed, so you can see which ones earn their place.
Sequences: setups that happen in steps
Sweep, reclaim, retest, inside a bar limit.
A sequence waits for its steps in order, within a number of bars. Later steps can refer to the bar where an earlier step happened, such as sweep.high. reset_if abandons the attempt. The sequence gives you .completed.
sequence liquidity_grab within 30 bars:step sweep: low < lowest(low, 20)[1]step reclaim: close > sweep.highstep retest: low <= reclaim.close and close > reclaim.closereset_if: close < sweep.lowExamples: Sweep and Reclaim, Renko Pullback, State Machine Momentum.
Orders
Market, limit, stop, close, modify, cancel.
when was(trend_up, within: 5 bars) and held(close > open, for: 3 bars) every bar:buy limit: lowest(low, 5), size: 1 lot, expires: 10 barsbuy stop: high + 2 pips, risk: 0.5%, stop_loss: low - 2 pips, target: 2R, ghost: truesell market: GBPUSD, size: 0.3 lotswhen history.today.pnl < -(2% of account.balance) or account.margin_level < 150%: close_allfor trade in trades.open(tag: "breakout"):if trade.r >= 3 and rsi(close, 14) > 75:close trade, size: 50%for trade in trades.open(side: long):modify trade, stop: trade.entry_pricecancel orders.pending(tag: "grid")buyandsellwith nolimit:or entrystop:are market orders. They fill at the next bar's open (E1).buy limit: priceplaces a pending limit order.buy stop: priceplaces a pending stop order, and its protective stop is thenstop_loss:.expires:cancels a pending order after a number of bars or a length of time (E17).ghost: truekeeps the stop and target off the broker. AlgoBars enforces them instead.tag:labels an order and its trade, so you can close, cancel or report on a group.close,close_all,modifyandcancelact on open trades and pending orders.
What happens when a signal arrives opposite an open trade is the header setting opposite: ignore (the default), close, reverse or hedge (E18).
Sizing and risk
State what you are willing to lose. Size follows.
Give an order a risk: as a percent of the balance or as money, and a stop. The size is worked out for you:
size = risk amount ÷ (stop distance × contract size × quote-to-account rate)
It is rounded down to the lot step and held between the market's minimum and maximum lot. If the minimum lot would carry more than you asked for, the trade still opens at the minimum lot and is marked “above requested risk” with the risk it actually carries (E11).
- Distances in pips, points or R are measured from the actual fill price, so a gap moves the stop and target with the entry (E9).
- A risk-based order with no stop is an error: “Risk-based size needs a stop to measure risk from.” (AS0304)
size_fortells you the lot size an entry would use, without placing it.- You can also size directly with
size: 1 lot.
Trade management
Breakeven, partial closes, trailing stops and exits.
Management is written under the order it belongs to, as an indented block after the order line.
when liquidity_grab.completed from 08:00 to 11:00 Europe/London:buy risk: 1%, stop: liquidity_grab.sweep.low - 3 points, target: 2R + 5 pips, tag: "breakout":breakeven at: 1R, offset: 2 pipspartial 30% at: 1.5Rpartial 30% at: 2.5Rtrail by: atr(14), after: 2Rexit after: 48 barsexit when: crosses_below(close, ema(close, 20))| Line | What it does | Rule |
|---|---|---|
breakeven | Moves the stop to the entry price, plus an optional offset, once a level is reached. | E13 |
partial | Closes part of the original size at a level. Each partial fires once. | E15 |
trail | Follows the best price reached by a distance, after a level. It only ever tightens. | E14 |
exit | Closes after a number of bars, a length of time, or when a condition is true. | E16 |
You can also manage trades from anywhere with a loop over trades.open(...), using close and modify. Example: Active Trade Manager.
Rules about a trade that is still open
An open trade knows how long it has been open and where it stands. trades.last() is the trade being held when there is one, bars_open counts its bars, and r is its result so far measured against its own risk.
when close > open:buy risk: 1%, stop: 50 pips, target: 4Rwhen trades.last().bars_open > 3:close_allLoss guards and limits
What stops a strategy, and for how long.
max_daily_loss: at each bar close, the day's closed profit plus open profit is compared with the limit. When it is reached, all trades close and pending orders are cancelled at the next bar's open, and new entries are rejected until the next UTC day.max_drawdownworks the same way against the highest equity reached, and pauses the deployment until you resume it.max_open,max_open_per_side,pyramiding,max_total_riskandmin_distancereject an entry, with a reason, at the moment it would fill.
The rule is E20. You can also write your own guard as an ordinary rule:
when history.today.pnl < -(2% of account.balance) or account.margin_level < 150%: close_allEvents
Run code when something happens.
on start:log "starting on {market.symbol}"on bar close:log "bar {bar.index}"on fill(order):log "filled {order.size}"on exit(trade):log "{trade.tag} closed at {trade.r:0.00}R after {long_entry.triggers} triggers"on session open "new_york":log "New York is open"on day change:triggers = 0| Event | When it runs |
|---|---|
on start: | Runs once before the first evaluated bar. |
on bar close: | Runs on every confirmed bar. |
on tick: | Runs on every price update (evaluate: tick). |
on fill(order): | Runs when an order fills. |
on exit(trade): | Runs when a trade closes, with trade.pnl, trade.r and trade.reason. |
on session open "<name>": | Runs when a session opens. |
on day change: | Runs at each calendar day boundary. |
on render(canvas): | Custom vector drawing for the visible range. |
on tick needs a script that evaluates on ticks (AS0404). See Bar close or every tick.
Bar close or every tick
One header line changes when the script runs.
By default a script runs after each bar closes. With evaluate: tick it runs at every point of the price path, and market orders fill at the price that triggered them.
strategy "Tick Scalper"market: XAUUSDbars: 1mevaluate: tickmax_open: 1input max_stretch = 2.0when crosses_above(close, vwap()) once per bar cooldown 2 bars:buy risk: 0.5%, stop: 30 points, target: 1.5R, ghost: trueon tick:if not bar.confirmed and close < vwap() - atr(14) * max_stretch:close_all side: long- Inside a forming bar, prices are live and indicators are not.
close,highandlowmove with the tick, while indicator values stay as they were at the last bar close. Nothing repaints. bar.confirmedis false during a tick and true at the close.- In a backtest, the 1-minute bars inside each bar supply the price path. When a bar has no finer data stored, its own path is used and the run is labelled “approximate intrabar”, listing the bars affected.
Other markets, bar sizes and bar types
Higher timeframes, other symbols, Renko, Heikin Ashi and X-Ray bars.
h4 = bars(bars: higher_tf)gold = bars(XAUUSD, bars: 15m)gold_atr = atr(14, on: gold)cov = data.coverage(EURUSD, bars: range(10))spread_z = zscore(log(close_of(EURUSD) / close_of(GBPUSD)), 100)first_hour = time_of_day between 08:00 and 09:00recent_high = intrabar(1m).high.max()barsreads another bar size, another symbol, or both. A higher bar's value appears only once its candle has closed, so a 5-minute rule cannot peek at a 4-hour bar early.intrabarreads the finer bars inside the current one.close_ofis a shortcut for another symbol's close.- Many functions take
on:to calculate on another set of bars, such asatr(14, on: gold). data.coveragetells a script how much data it actually has, and whether it is exact.
coverage = data.coverage(EURUSD, bars: range(10))enough_history = coverage.exact and coverage.bars >= 5000Several markets in one strategy
strategy "EURUSD and GBPUSD Mean Reversion"markets: EURUSD, GBPUSDbars: 1hmax_open: 2input lookback = 200input entry_z = 2.0input exit_z = 0.5eur = bars(EURUSD)gbp = bars(GBPUSD)hedge = beta(returns(eur.close), returns(gbp.close), lookback)Full example: EURUSD and GBPUSD Mean Reversion. Orders take market: to say which symbol they are for.
Other bar types
The header's bars: accepts more than a time: renko, heikin_ashi, range and xray.
strategy "Renko Pullback"market: US500bars: renko(5)indicator "X-Ray Flow Bands"bars: xray(20)Reading the backtest report
Every number, what it means and how it is worked out.
Everything in a report is derived from the trades the engine actually filled and the equity at each bar close, so any number can be traced back to a trade. Costs are never added or assumed: profit is the price difference (E12).
How it went
| Number | What it means |
|---|---|
| Trades | How many trades completed. Trades still open when the test ends are closed at the last bar's close and marked “open at end” (E24). |
| Net profit | Gross profit minus gross loss. |
| Win rate | Winning trades as a share of all trades. |
| Profit factor | Gross profit ÷ gross loss. What it made for every unit it lost. |
| Expectancy | The average result per trade, in money and in R. In R it is the mean of every trade's R outcome. |
| Payoff | Average win ÷ average loss. |
What it cost
| Number | What it means |
|---|---|
| Max drawdown | The deepest fall from a high in equity, in money and percent, with how many bars it stayed below that high. |
| Recovery factor | Net profit ÷ max drawdown. |
| Sharpe | Mean return ÷ the deviation of returns, annualised. |
| Sortino | The same, counting only downside deviation. |
| SQN | √(number of trades) × mean R ÷ deviation of R. How steady the R outcomes were. |
| Time in market | The share of bars with a trade open. |
| CAGR, Calmar, Ulcer index | Also calculated by the engine: compound annual growth, growth ÷ max drawdown percent, and the root mean square of drawdown percentages. |
How the trades behaved
| Number | What it means |
|---|---|
| Bars per trade | Average length of a trade. |
| Average run-up | The best each trade saw while it was open (MFE). |
| Average drawdown in trade | The worst each trade saw while it was open (MAE). |
| Streaks, largest win and loss | The longest run of wins and of losses, and the single biggest of each. |
| R distribution | How many trades ended in each band of R. |
Breakdowns
By the rule that opened the trade, by how the trade ended, by the confirmations that agreed, by side, by tag and by month. Name your rules with as and tag your orders, and these tables become much more useful.
Labels worth noticing
- Above requested risk: the minimum lot carried more risk than the order asked for (E11).
- Approximate intrabar: a tick-evaluated run had no finer data for some bars, which are listed (E2).
- Unfilled: a market order created on the last bar of the test (E1).
Demo and live deployments
What changes when a script runs for real, and what does not.
Deploying a strategy runs the same script on a demo or live account. The runner evaluates each closed bar, hands out instructions, and waits to be told what actually happened.
- The broker decides fills. A backtest may decide a fill itself. Live, a trade exists only once the broker has confirmed it, and an instruction is never sent twice.
- Restarts are safe. Open trades and their management, rule counters, sequences part way through and pending orders live in a snapshot. After a restart the runner replays recent bars to warm the indicators back up and continues where it left off, so a restart cannot quietly abandon a stop.
- Demo follows the written rules. Demo deployments fill under the same execution rules as a backtest: a market order fills at the next bar's open, and when a stop and target are both reached in one bar the stop fills first unless finer data says otherwise.
- Live uses the side that can trade. A buy is triggered and filled on the ask and a sell on the bid. A long trade's stop and target trigger on the bid, a short trade's on the ask (E3, E4, E5). Backtests have no spread, so this is one reason live results differ.
- Guards still apply.
max_daily_lossstops new entries until the next UTC day, andmax_drawdownpauses the deployment until you resume it (E20). - The deployment chooses where it runs. A script says what it was written for, and that comes filled in, but the market, the bar size, the trade size and the balance to trade belong to the deployment. A strategy written for EURUSD on the hour can be deployed on gold on five-minute bars by changing two fields.
- Demo or live is said plainly. Demo fills against the AlgoBars engine and costs nothing. Live sends real orders to a real broker.
- Live sizes the way the test did. The live runner places a stop where the backtest placed it and sizes a trade the same way, so what was tested is what trades.
- Edits are explained first. Changing a script that is already running shows what the change would do once it is live, before you make it.
Chart replay uses the same runner and the same rules, so what appears in replay is what the script would have done.
Letting AlgoBarsX generate a strategy
Composed from the language, compiled and backtested before you see it.
Generated strategies are not written by a model. Entries, filters, exits and management are assembled from pieces of the language that are known to compile, and every candidate is then compiled and backtested. A strategy is only handed back once it has actually traded, so you never receive something that looks plausible and does nothing. The same seed and the same bars give the same strategy every time.
| Strength | What you get | AlgoBuilder tier |
|---|---|---|
| 1 | A plain two-rule strategy. | Simple, Basic |
| 2 | More filters. | Standard |
| 3 | The default middle ground. | Balanced |
| 4 | More structure and management. | Advanced |
| 5 | Confirmations, sequences, higher bar sizes and full trade management. | Complex, Extreme |
What comes back is ordinary AlgoBarsX. Read its plain-English description, open it in the Terminal and change it.
Why did my order not fill?
Seven reasons, each with the rule behind it.
- It was created on the last bar. A market order fills at the next bar's open. With no next bar it is reported as unfilled (E1).
- Price never reached it. A buy limit fills when the low is at or below its price, a buy stop when the high is at or above it. Sells mirror that (E4).
- It expired.
expires: 10 barscancels at the close of the tenth bar after it was placed (E17). - A limit rejected it.
max_open,max_open_per_side,pyramiding,max_total_riskandmin_distanceare checked when the entry would fill. A rejected entry is recorded with its reason (E19). - It was outside trading hours. With
trade_only, entries are only created inside the listed sessions (E22). - A loss guard had tripped. After
max_daily_loss, new entries are rejected until the next UTC day (E20). - There was already a trade the other way. The default for
oppositeisignore(E18).
Why does live differ from my backtest?
Costs, the bid and ask, gaps and your broker.
- Backtests have no costs. No spread, commission, fees, swaps or slippage. Profit is the price difference (E12).
- Live trades on the bid and the ask. A buy fills on the ask, a sell on the bid, and stops and targets trigger on the side that can actually trade (E3, E5). A backtest has one price.
- Gaps move fills. When a bar opens beyond a stop, the fill is the open, not the stop price (E8).
- The broker decides live fills. A live trade exists only once your broker confirms it. Execution speed and liquidity are outside the script.
- The data may be thinner.
data.coveragetells a script how much history it has and whether it is exact.
Past performance does not guarantee future results.
The bar reached my target. Why was I stopped out?
When one bar reaches both, the stop fills first.
Why is my stop not where I expected?
Distances follow the fill. Levels do not.
- A stop written as a distance (
25 pips,atr(14) * 2,2R) is measured from the actual fill price. If the entry fills 8 pips higher after a gap, the stop is 8 pips higher too (E9). - A stop written as a level (
low[1],1.0850) stays exactly where you put it. If a gap makes it invalid, for example a long stop at or above the fill, the order is cancelled and recorded as “stop beyond entry” (E10). breakevenandtrailmove stops during the trade, and a trailing stop only ever tightens (E13, E14).
Why is my position smaller or bigger than I asked?
Lot steps, minimum lots and “above requested risk”.
Why did my backtest stop early?
The test account ran out of money.
An account with nothing left cannot trade on. When equity is gone, the backtest stops at that bar, closes what was open and marks the run as liquidated, with the bar and the time. Everything after that point did not happen, so the engine does not pretend it did. The replay stops at the same place.
If this surprises you, check the trade size and the stop first. A large fixed lot size on a small starting balance is the usual cause.
What size does buy trade when I give no size?
The usual amount, set where the script runs.
A script that says buy and nothing more is a reasonable script. It means “the usual amount”, and the usual amount belongs to wherever the script is run, not to the script. In the Terminal that is the Lot size in the backtest panel and the Trade size when you deploy. When nothing is set anywhere, it is the smallest lot the market deals in.
A script that names a size: or a risk: is never overruled.
Why was my order for another market refused?
An order is only filled on the market it names.
An order that names a market the run is not trading is refused, with a reason. Filling it on the current market would be worse than not filling it: a pairs trade would quietly become two orders on one instrument. List every market the strategy trades under markets: in the header. The live runner refuses it too, and says so.
Why is my indicator empty at the start?
Warm-up.
An average of 200 bars has nothing to say for the first 199. Each function's reference entry gives its warm-up, and rules do not run until the script's warm-up has passed. The compiler works the warm-up out for you. Set warmup: in the header to override it.
A higher bar size also shows nothing new until its candle closes, by design. A 5-minute rule never sees a 4-hour bar early.
Why did my alert only fire once?
repeat, cooldown and expires.
An alert's own settings decide when it may speak again. The default repeat is once per bar. cooldown sets the shortest gap between messages, and expires switches the alert off. Testing an alert lists the messages it sent and the ones it held back, with the reason.
What does “approximate intrabar” mean?
A tick-evaluated run that lacked finer data for some bars.
A script with evaluate: tick needs the price path inside each bar. When a bar has no 1-minute data stored, its own open, high, low and close are used instead, the run is labelled “approximate intrabar”, and the affected bars are listed (E2). Treat those stretches with more caution.
The compiler rejected my script. Now what?
Read the message. It says what to change.
Every message names the problem, the line and the column, and says what to do. Many carry a one-click fix. Search this page for the code, such as AS0304, or browse Compiler Messages. The most common ones:
- AS0201 a name the compiler does not know, usually a typo. It suggests the nearest name.
- AS0302 a number with no unit where a distance is expected.
- AS0303 and AS0304 an R target or a risk-based size with no stop to measure from.
- AS0003 a tab used for indentation. Use spaces.
- AS0401 something in the wrong kind of script, such as an order in an indicator.
Writing an indicator
Calculate, draw, and publish values.
indicator "Trend Ribbon"pane: priceinput fast = 20input slow = 50fast_line = ema(close, fast)slow_line = ema(close, slow)export up = fast_line > slow_lineexport strength = (fast_line - slow_line) / atr(14)plot fast_line as fast, color: if up then green else redplot slow_line as slow, color: graypane: pricedraws over the price chart.pane: newgives the indicator its own pane.plotdraws a series.asgives the plot a name. Colours can depend on a condition.exportpublishes a value. Anything exported can be read by a strategy, an alert or another indicator that uses this one.
Using an indicator or a library from another script
use indicator "Trend Ribbon" v3 as ribbon (fast: 10, slow: 30)use library "Quant Toolkit" v2 as qtYou import by name and version, give it a short name with as, and set its inputs in brackets. After that, ribbon.up reads like any other value. A script names the version it uses, so an update to the indicator does not silently change a strategy that depends on it.
Drawing on the chart
Plots, fills, boxes, Fibonacci, profiles, tables and dashboards.
plot ema(close, fast) as fast_line, color: if trend_up then green else red, width: 2plot (high + low) / 2, color: gray, style: stepfill ribbon.fast, ribbon.slow, color: green.fade(80)hline 70, style: dashed, color: #22c55emark arrow_up, at: below, when: crosses_above(close, ema(close, fast)), color: greenlabel "Entry", at: (bar.index, high)line from: (bar.index - 20, lowest(low, 20)), to: (bar.index, lowest(low, 20)), extend: rightbox id: "range", from: (bar.index - 10, highest(high, 10)), to: (bar.index, lowest(low, 10)), color: blue.fade(85)bar_color if close > open then green else redbackground red.fade(90), when: regime == Regime.volatileprofile rows: 24, range: session, side: rightfib from: (bar.index - 50, lowest(low, 50)), to: (bar.index, highest(high, 50))dashboard position: top_right, rows: [["Regime", "{regime}"], ["Triggers", "{triggers}"]]There are 26 drawing commands. Each is documented under Drawing commands.
- Give a drawing an
id:to update the same object on later bars instead of adding a new one. - Positions are written as
(bar, price)pairs, such as(bar.index - 20, lowest(low, 20)). - Colours: ten named colours, hex values,
.fade(percent), andgradient,linear_gradientandradial_gradient.
The canvas
When no command fits, on render(canvas) gives you a vector canvas for the visible range: paths, lines, fills and text.
on render(canvas):for z in levels:shape = canvas.path()shape.move_to(z.formed_at, z.price)shape.line_to(canvas.last_bar, z.price)shape.stroke(zone_color, width: 1)canvas.text("{z.touched}x", at: (canvas.last_bar, z.price), align: right)Examples: Volatility Regime Canvas, Volume Heatmap, Multi-Timeframe Trend Table, Opening Range Dashboard.
Writing an alert
The condition, the message, and when it may repeat.
alert "RSI bullish divergence"check: every 15mrepeat: once per barshow_on_chart: trueinput rsi_length = 14input lookback = 30momentum_now = rsi(close, rsi_length)lower_low = low < lowest(low, lookback)[1]higher_rsi = momentum_now > lowest(momentum_now, lookback)[1]when lower_low and higher_rsi and was(momentum_now < 30, within: 5 bars):notify "Bullish RSI divergence on {market.symbol}: RSI {momentum_now:0.0}"| Setting | What it does |
|---|---|
check | How often the condition is checked, such as every 1m. |
repeat | once, once per bar (the default), once per bar close or every time. |
cooldown | The shortest gap between two messages. |
expires | When the alert stops watching. |
show_on_chart | Marks the chart where it fired. |
notify sends the message. In version 1 it is delivered in the app and in AI chat. Testing an alert shows you the messages it would have sent and the ones its own settings held back.
Alerts that watch the account, not the chart
alert "Margin and exposure"check: every 5mrepeat: onceexpires: 30dwhen account.free_margin < 20% of account.equity or positions.len >= 8:notify "Free margin {account.free_margin:$0} with {positions.len} open positions"More: Daily risk guard, Intrabar spike, Tokyo open gap, Distribution shift.
Writing and using a library
Functions and constants you reuse.
library "Quant Toolkit"const TRADING_DAYS = 252fn kelly_fraction(win_rate: number, payoff: number) -> number:return win_rate - (1 - win_rate) / payofffn annualized_volatility(source: series<number> = close, length: int = 20) -> number:return stdev(returns(source), length) * sqrt(TRADING_DAYS)fn position_heat(risks: list<percent>) -> percent:total = 0%for r in risks:total += rreturn totalA library holds functions, constants and types. It cannot place orders or draw (AS0405). Import it with use library "Quant Toolkit" v2 as qt and call qt.kelly_fraction(0.55, 1.8).
Examples: Quant Toolkit, Risk Parity, Ranking Tools, and a strategy that uses one: Kelly Sized Breakout.
Moving averages
Functions: sma, ema, wma, rma, smma, hma…
sma ema wma rma smma hma vwma dema tema kama alma t3 zlema
Simple moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
average = sma(close, 20)Exponential moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
fast = ema(close, 20)Linearly weighted moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
weighted = wma(close, 20)Wilder's moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
smoothed = rma(close, 14)Smoothed moving average (identical to rma).
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
smoothed = smma(close, 14)Hull moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
hull = hma(close, 20)Volume-weighted moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
by_volume = vwma(close, 20)Double exponential moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
double = dema(close, 20)Triple exponential moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
triple = tema(close, 20)Kaufman's adaptive moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 10 | 1 to 5000 | Efficiency ratio length. |
fast | int | 2 | 1 to 500 | Fastest smoothing length. |
slow | int | 30 | 1 to 5000 | Slowest smoothing length. |
adaptive = kama(close, 10, fast: 2, slow: 30)Arnaud Legoux moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 9 | 1 to 5000 | Number of bars in the calculation. |
offset | number | 0.85 | 0 to 1 | Gaussian offset from 0 to 1. |
sigma | number | 6 | 0.1 to 100 | Gaussian width. |
smooth = alma(close, 9, offset: 0.85, sigma: 6)Tillson T3 moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 5 | 1 to 5000 | Number of bars in the calculation. |
factor | number | 0.7 | 0 to 1 | Volume factor. |
t = t3(close, 5, factor: 0.7)Zero-lag exponential moving average.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
zero_lag = zlema(close, 20)Momentum
Functions: rsi, stoch, stoch_rsi, macd, cci, williams_r…
rsi stoch stoch_rsi macd cci williams_r roc momentum tsi ultimate_osc awesome_osc ppo cmo trix mfi
Relative strength index, from 0 to 100.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
r = rsi(close, 14)Stochastic oscillator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
k_length | int | 14 | 1 to 5000 | %K lookback. |
k_smoothing | int | 3 | 1 to 500 | %K smoothing. |
d_smoothing | int | 3 | 1 to 500 | %D smoothing. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
k | series<number> | %K. |
d | series<number> | %D. |
s = stoch(14, 3, 3)Stochastic RSI.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
rsi_length | int | 14 | 1 to 5000 | RSI length. |
stoch_length | int | 14 | 1 to 5000 | Stochastic length. |
k | int | 3 | 1 to 500 | %K smoothing. |
d | int | 3 | 1 to 500 | %D smoothing. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
k | series<number> | %K. |
d | series<number> | %D. |
srsi = stoch_rsi(close, 14, 14, 3, 3)Moving average convergence divergence.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
fast | int | 12 | 1 to 5000 | Fast EMA length. |
slow | int | 26 | 1 to 5000 | Slow EMA length. |
signal | int | 9 | 1 to 5000 | Signal EMA length. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
macd | series<number> | MACD line. |
signal | series<number> | Signal line. |
histogram | series<number> | MACD minus signal. |
m = macd(close, 12, 26, 9)rising = macd(close).histogram > 0Commodity channel index.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | hlc3 | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
c = cci(hlc3, 20)Williams %R.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
wr = williams_r(14)Rate of change in percent.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 9 | 1 to 5000 | Number of bars in the calculation. |
change = roc(close, 9)Difference from the value length bars ago.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 10 | 1 to 5000 | Number of bars in the calculation. |
mom = momentum(close, 10)True strength index.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
long | int | 25 | 1 to 5000 | Long smoothing. |
short | int | 13 | 1 to 5000 | Short smoothing. |
signal | int | 13 | 1 to 5000 | Signal length. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
tsi | series<number> | TSI line. |
signal | series<number> | Signal line. |
t = tsi(close, 25, 13, 13)Ultimate oscillator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
fast | int | 7 | 1 to 5000 | Fast length. |
middle | int | 14 | 1 to 5000 | Middle length. |
slow | int | 28 | 1 to 5000 | Slow length. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
uo = ultimate_osc(7, 14, 28)Awesome oscillator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
fast | int | 5 | 1 to 5000 | Fast length. |
slow | int | 34 | 1 to 5000 | Slow length. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
ao = awesome_osc(5, 34)Percentage price oscillator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
fast | int | 12 | 1 to 5000 | Fast EMA length. |
slow | int | 26 | 1 to 5000 | Slow EMA length. |
signal | int | 9 | 1 to 5000 | Signal EMA length. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
macd | series<number> | PPO line. |
signal | series<number> | Signal line. |
histogram | series<number> | PPO minus signal. |
pp = ppo(close, 12, 26, 9)Chande momentum oscillator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 9 | 1 to 5000 | Number of bars in the calculation. |
chande = cmo(close, 9)Triple-smoothed EMA rate of change.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 18 | 1 to 5000 | Number of bars in the calculation. |
tx = trix(close, 18)Money flow index.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
flow_index = mfi(14)Trend
Functions: supertrend, psar, adx, dmi, ichimoku, aroon…
supertrend psar adx dmi ichimoku aroon vortex linreg
Supertrend line and direction.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 10 | 1 to 5000 | ATR length. |
multiplier | number | 3.0 | 0.1 to 50 | ATR multiplier. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
line | series<price> | Supertrend line. |
direction | series<int> | 1 when up, -1 when down. |
st = supertrend(10, multiplier: 3.0)Parabolic SAR.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
start | number | 0.02 | 0.001 to 1 | Starting acceleration. |
increment | number | 0.02 | 0.001 to 1 | Acceleration step. |
maximum | number | 0.2 | 0.001 to 1 | Maximum acceleration. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
sar = psar(start: 0.02, increment: 0.02, maximum: 0.2)Average directional index.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
strength = adx(14)Directional movement index.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
plus | series<number> | +DI. |
minus | series<number> | -DI. |
adx | series<number> | ADX. |
movement = dmi(14)Ichimoku cloud.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
conversion | int | 9 | 1 to 500 | Conversion line length. |
base | int | 26 | 1 to 500 | Base line length. |
span_b | int | 52 | 1 to 1000 | Leading span B length. |
displacement | int | 26 | 1 to 500 | Cloud displacement. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
conversion | series<price> | Conversion line. |
base | series<price> | Base line. |
span_a | series<price> | Leading span A. |
span_b | series<price> | Leading span B. |
lagging | series<price> | Lagging span. |
cloud = ichimoku(conversion: 9, base: 26, span_b: 52, displacement: 26)Aroon up and down.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 25 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
up | series<number> | Aroon up. |
down | series<number> | Aroon down. |
ar = aroon(25)Vortex indicator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
plus | series<number> | VI+. |
minus | series<number> | VI-. |
vx = vortex(14)Linear regression value.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
offset | int | 0 | 0 to 500 | Bars back from the current bar. |
fit = linreg(close, 20)Volatility
Functions: atr, true_range, bollinger, keltner, donchian, envelope…
atr true_range bollinger keltner donchian envelope hist_volatility choppiness
Average true range.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
stop_distance = atr(14) * 1.5True range of the current bar.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
tr = true_range()Bollinger Bands.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
multiplier | number | 2.0 | 0.1 to 10 | Standard deviations. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
upper | series<price> | Upper band. |
middle | series<price> | Middle band. |
lower | series<price> | Lower band. |
width | series<number> | Band width relative to the middle. |
percent_b | series<number> | Position inside the bands. |
bb = bollinger(close, 20, 2.0)squeeze = bollinger(close).width < 0.02Keltner channels.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
multiplier | number | 2.0 | 0.1 to 10 | ATR multiplier. |
atr_length | int | 10 | 1 to 5000 | ATR length. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
upper | series<price> | Upper channel. |
middle | series<price> | Middle line. |
lower | series<price> | Lower channel. |
kc = keltner(close, 20, 2.0, 10)Donchian channels.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
upper | series<price> | Highest high. |
middle | series<price> | Midpoint. |
lower | series<price> | Lowest low. |
dc = donchian(20)Moving average envelope.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
percent | percent | 2% | Distance from the average. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
upper | series<price> | Upper line. |
middle | series<price> | Average. |
lower | series<price> | Lower line. |
env = envelope(close, 20, percent: 2%)Historical volatility.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
annualize | int | 252 | 1 to 100000 | Periods per year. |
hv = hist_volatility(close, 20)Choppiness index.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 14 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
chop = choppiness(14)Volume
Functions: vwap, obv, cmf, ad_line, pvt, volume_osc
vwap obv cmf ad_line pvt volume_osc
Volume-weighted average price, restarting at each session or period.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
anchor | string | "session" | Where the average restarts. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
fair = vwap(anchor: "session")On-balance volume.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
balance_volume = obv()Chaikin money flow.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
money_flow = cmf(20)Accumulation/distribution line.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
ad = ad_line()Price volume trend.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
trend_volume = pvt()Volume oscillator.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
fast | int | 5 | 1 to 5000 | Fast length. |
slow | int | 10 | 1 to 5000 | Slow length. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
vo = volume_osc(5, 10)Smart money concepts
Functions: order_blocks, fair_value_gaps, break_of_structure, change_of_character, liquidity_sweeps, premium_discount…
order_blocks fair_value_gaps break_of_structure change_of_character liquidity_sweeps premium_discount optimal_trade_entry
Order blocks as zones with price bounds, formation time and status.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 50 | 1 to 5000 | Bars to scan. |
new_only | bool | false | Only blocks that formed on this bar. | |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
blocks = order_blocks(50, new_only: true)Fair value gaps as zones.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 50 | 1 to 5000 | Bars to scan. |
min_size | price | 0 | Smallest gap to include. | |
new_only | bool | false | Only gaps that formed on this bar. | |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
gaps = fair_value_gaps(50, min_size: 0)True on the bar structure breaks.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
direction | string | both | up, down or both. | |
swing_length | int | 5 | 1 to 100 | Swing size in bars. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
bos = break_of_structure(direction: up)True on the bar character changes.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
direction | string | both | up, down or both. | |
swing_length | int | 5 | 1 to 100 | Swing size in bars. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
choch = change_of_character(direction: down)True on the bar a prior high or low is swept and rejected.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
swept = liquidity_sweeps(20)Optimal trade entry zone of the latest swing.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
swing_length | int | 5 | 1 to 100 | Swing size in bars. |
low | percent | 62% | Shallow retracement. | |
high | percent | 79% | Deep retracement. | |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
ote = optimal_trade_entry(swing_length: 5)Market structure
Functions: highest, lowest, pivots, swing_high, swing_low, fractals…
highest lowest pivots swing_high swing_low fractals zigzag support_resistance
Highest value over the last bars.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | high | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
top = highest(high, 20)Lowest value over the last bars.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | low | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
bottom = lowest(low, 20)Confirmed pivot highs and lows; each fires on its confirmation bar, never the pivot bar.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
left | int | 5 | 1 to 500 | Bars to the left. |
right | int | 5 | 1 to 500 | Bars to the right. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
high | series<price> | Latest confirmed pivot high. |
low | series<price> | Latest confirmed pivot low. |
pv = pivots(left: 5, right: 5)Latest confirmed swing high.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
left | int | 5 | 1 to 500 | Bars to the left. |
right | int | 5 | 1 to 500 | Bars to the right. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
last_swing_high = swing_high(left: 5, right: 5)Latest confirmed swing low.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
left | int | 5 | 1 to 500 | Bars to the left. |
right | int | 5 | 1 to 500 | Bars to the right. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
last_swing_low = swing_low(left: 5, right: 5)Williams fractals.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
periods | int | 2 | 1 to 50 | Bars on each side. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
up | series<bool> | Up fractal confirmed. |
down | series<bool> | Down fractal confirmed. |
fr = fractals(2)Zigzag swing points.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
deviation | percent | 5% | Smallest reversal. | |
depth | int | 10 | 1 to 500 | Fewest bars between points. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
swings = zigzag(deviation: 5%, depth: 10)Support and resistance levels.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
length | int | 200 | 1 to 5000 | Bars to scan. |
touches | int | 2 | 1 to 100 | Fewest touches for a level. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
key_levels = support_resistance(200, touches: 2)Candle patterns
Functions: engulfing, hammer, shooting_star, doji, pin_bar, inside_bar…
engulfing hammer shooting_star doji pin_bar inside_bar outside_bar morning_star evening_star three_soldiers three_crows
Bullish or bearish engulfing candle.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
direction | string | both | up, down or both. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = engulfing()Hammer candle.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = hammer()Shooting star candle.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = shooting_star()Doji candle.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
max_body | percent | 10% | Largest body as a share of the range. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = doji()Pin bar.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
min_wick | percent | 66% | Smallest wick as a share of the range. |
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = pin_bar()Bar inside the previous bar.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = inside_bar()Bar engulfing the previous bar.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = outside_bar()Morning star, three bars.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = morning_star()Evening star, three bars.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = evening_star()Three white soldiers.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = three_soldiers()Three black crows.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
on | BarSet | bars() | Bars to calculate on; defaults to the script's own bars. |
signal = three_crows()Statistics
Functions: stdev, mean, median, variance, percentile, percent_rank…
stdev mean median variance percentile percent_rank zscore returns correlation covariance beta skew kurtosis rank autocorrelation hurst
Standard deviation.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
dispersion = stdev(close, 20)Mean value.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
avg = mean(close, 20)Median value.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
mid = median(close, 20)Variance.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
var_now = variance(close, 20)Value at a percentile of recent values.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 100 | 1 to 5000 | Number of bars in the calculation. |
percent | percent | 50% | Percentile to return. |
p90 = percentile(close, 100, percent: 90%)How many of the recent values are below this one, as a percentage from 0 to 100.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 100 | 1 to 5000 | Number of bars in the calculation. |
standing = percent_rank(close, 100)Standard score of the current value.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
z = zscore(close, 20)Fractional change between bars.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
periods | int | 1 | 1 to 5000 | Bars between the two values. |
r1 = returns(close)Pearson correlation.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
a | series<number> | required | First series. | |
b | series<number> | required | Second series. | |
length | int | 50 | 1 to 5000 | Number of bars in the calculation. |
corr = correlation(close_of(EURUSD), close_of(GBPUSD), 50)Covariance.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
a | series<number> | required | First series. | |
b | series<number> | required | Second series. | |
length | int | 50 | 1 to 5000 | Number of bars in the calculation. |
cov_now = covariance(returns(close_of(EURUSD)), returns(close_of(GBPUSD)), 50)Beta of an asset against a benchmark.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
asset | series<number> | required | Asset returns. | |
benchmark | series<number> | required | Benchmark returns. | |
length | int | 100 | 1 to 5000 | Number of bars in the calculation. |
b = beta(returns(close_of(EURUSD)), returns(close_of(GBPUSD)), 100)Skewness.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 50 | 1 to 5000 | Number of bars in the calculation. |
asymmetry = skew(returns(close), 50)Excess kurtosis.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 50 | 1 to 5000 | Number of bars in the calculation. |
tails = kurtosis(returns(close), 50)Rank of the current value among recent values.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 50 | 1 to 5000 | Number of bars in the calculation. |
position_rank = rank(close, 50)Autocorrelation at a lag.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 50 | 1 to 5000 | Number of bars in the calculation. |
lag | int | 1 | 1 to 500 | Bars of lag. |
ac = autocorrelation(returns(close), 50, lag: 1)Hurst exponent, on returns rather than prices. Prices trend by construction, so hurst(close, n) reads high for a random walk, a trend and a mean-reverting market alike and cannot tell them apart; hurst(returns(close), n) can.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 100 | 1 to 5000 | Number of bars in the calculation. |
persistence = hurst(returns(close), 100)Regression
Functions: linreg_slope, linreg_intercept, r_squared, ols
linreg_slope linreg_intercept r_squared ols
Slope of the least-squares line.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
slope = linreg_slope(close, 20)Intercept of the least-squares line.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
intercept = linreg_intercept(close, 20)Coefficient of determination of the least-squares line.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
source | series<number> | close | Series to calculate from. | |
length | int | 20 | 1 to 5000 | Number of bars in the calculation. |
fit_quality = r_squared(close, 20)Multi-factor least-squares regression.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
target | series<number> | required | Dependent series. | |
factors | list<series<number>> | required | Explanatory series. | |
length | int | 100 | 1 to 5000 | Number of bars in the calculation. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
coefficients | list<number> | One per factor. |
intercept | number | Intercept. |
r_squared | number | Fit quality. |
residual | series<number> | Latest residual. |
model = ols(returns(close), [returns(close_of(EURUSD)), returns(close_of(XAUUSD))], 100)Matrices
Functions: matrix, transpose, multiply, inverse, covariance_matrix
matrix transpose multiply inverse covariance_matrix
A numeric matrix filled with a value.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
rows | int | required | Rows. |
columns | int | required | Columns. |
fill | number | 0 | Initial value. |
grid = matrix(3, 3)Transpose a matrix.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
m | matrix | required | Matrix. |
flipped = transpose(grid)Matrix product.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | matrix | required | Left matrix. |
b | matrix | required | Right matrix. |
product = multiply(grid, transpose(grid))Matrix inverse; na when the matrix is singular.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
m | matrix | required | Square matrix. |
inv = inverse(grid)Covariance matrix of several series.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
series | list<series<number>> | required | Series to compare. | |
length | int | 100 | 1 to 5000 | Number of bars in the calculation. |
cov_m = covariance_matrix([returns(close_of(EURUSD)), returns(close_of(GBPUSD))], 100)Conditions
Functions: crosses_above, crosses_below, crosses, starts, ends, bars_since…
crosses_above crosses_below crosses starts ends bars_since was held
True on the bar where a crosses above b.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | series<number> | required | Series that crosses. |
b | series<number> | required | Series or level crossed. |
long_signal = crosses_above(close, ema(close, 20))True on the bar where a crosses below b.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | series<number> | required | Series that crosses. |
b | series<number> | required | Series or level crossed. |
short_signal = crosses_below(close, ema(close, 20))True on the bar where a crosses b in either direction.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | series<number> | required | First series. |
b | series<number> | required | Second series. |
any_cross = crosses(ema(close, 20), ema(close, 50))True on the bar a condition becomes true.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
condition | series<bool> | required | Condition to watch. |
breakout = starts(close > highest(high, 20)[1])True on the bar a condition stops being true.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
condition | series<bool> | required | Condition to watch. |
trend_over = ends(ema(close, 20) > ema(close, 50))Bars since a condition was last true.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
condition | series<bool> | required | Condition to watch. |
age = bars_since(crosses_above(close, ema(close, 20)))True if a condition was true at some point in recent bars.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
condition | series<bool> | required | Condition to watch. |
within | bars | 5 bars | How far back to look. |
recent = was(crosses_above(close, ema(close, 20)), within: 5 bars)True if a condition has been true for consecutive bars.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
condition | series<bool> | required | Condition to watch. |
for | bars | 3 bars | Bars in a row. |
steady = held(close > open, for: 3 bars)Data and bar types
Functions: bars, close_of, intrabar, data.coverage, range, xray…
bars close_of intrabar data.coverage range xray renko heikin_ashi
Bars of another bar type or symbol. Higher-timeframe values change only when that bar confirms, so look-ahead is impossible.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
symbol | symbol | market.symbol | Market to read. |
bars | bartype | bar.type | Bar type to read. |
h4 = bars(bars: 4h)gold = bars(XAUUSD, bars: 15m)Close series of another symbol, aligned by bar close time.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
symbol | symbol | required | Market to read. |
bars | bartype | bar.type | Bar type to read. |
ratio = close_of(EURUSD) / close_of(GBPUSD)Lower-timeframe bars inside the current bar, as a list.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
bars | bartype | 1m | Lower bar type. |
minute_bars = intrabar(1m)The stored date range for a symbol and bar type, and whether results on it are exact.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
symbol | symbol | market.symbol | Market to check. |
bars | bartype | bar.type | Bar type to check. |
Outputs, read with a dot
| Name | Type | What it is |
|---|---|---|
from | time | First available bar. |
to | time | Last available bar. |
bars | int | Number of bars. |
source | string | "stored" or "built". |
exact | bool | false when rebuilt from coarser data. |
cov = data.coverage(EURUSD, bars: range(10))Range bars of a fixed size; stored tick-built bars where they exist, otherwise built from candles.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
size | number | distance | 10 | Bar size; a bare number follows the chart convention for the symbol. |
bars_r = bars(bars: range(10 pips))Range bars built with the chart's x-ray algorithm.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
size | number | distance | 10 | Bar size. |
bars_x = bars(bars: xray(10))Renko bricks built with the chart's renko algorithm.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
size | number | distance | 10 | Brick size. |
bars_k = bars(bars: renko(5 points))Heikin-Ashi bars computed from time bars.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
timeframe | duration | 1h | Underlying timeframe. |
smooth_bars = bars(bars: heikin_ashi(1h))Sizing
Functions: size_for
The lot size an entry would use for a risk and stop, rounded down to the lot step.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
symbol | symbol | market.symbol | Market to size. |
risk | percent | money | required | Risk to take. |
stop | distance | required | Stop distance. |
eur_size = size_for(EURUSD, risk: 0.5%, stop: 30 pips)Math
Functions: abs, min, max, round, floor, ceil…
abs min max round floor ceil sqrt log exp pow clamp sign lerp
Absolute value.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
body_size = abs(close - open)Smaller of two values.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | number | required | First value. |
b | number | required | Second value. |
body_low = min(open, close)Larger of two values.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | number | required | First value. |
b | number | required | Second value. |
body_high = max(open, close)Round to a number of decimals.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
value | number | required | Value. | |
decimals | int | 0 | 0 to 12 | Decimal places. |
shown = round(close, 2)Round down.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
whole = floor(close)Round up.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
whole_up = ceil(close)Square root, deterministic across devices.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
root = sqrt(252)Natural logarithm, deterministic across devices.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
log_price = log(close)Exponential, deterministic across devices.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
growth = exp(0.05)Power, deterministic across devices.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
base | number | required | Base. |
exponent | number | required | Exponent. |
squared = pow(close, 2)Limit a value to a range.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
low | number | required | Lowest allowed. |
high | number | required | Highest allowed. |
bounded = clamp(rsi(close, 14), 20, 80)-1, 0 or 1.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value. |
side_sign = sign(close - open)Linear interpolation between two values.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
a | number | required | Start. |
b | number | required | End. |
t | number | required | Fraction from 0 to 1. |
halfway = lerp(low, high, 0.5)Missing values
Functions: nz, is_na
Replace a missing value.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value that may be na. |
fallback | number | 0 | Replacement. |
safe = nz(close[1], close)True when a value is missing.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value to test. |
warming_up = is_na(ema(close, 200))Colours
Functions: rgb, gradient, linear_gradient, radial_gradient
rgb gradient linear_gradient radial_gradient
Color from red, green, blue and optional transparency.
Parameters
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
red | int | required | 0 to 255. | |
green | int | required | 0 to 255. | |
blue | int | required | 0 to 255. | |
transparency | number | 0 | 0 to 100 | Percent transparent, 0 to 100. |
brand = rgb(34, 197, 94)Color between two colors according to a value.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
value | number | required | Value to map. |
low | color | required | Color at the low end. |
high | color | required | Color at the high end. |
from | number | 0 | Value mapped to low. |
to | number | 100 | Value mapped to high. |
heat = gradient(rsi(close, 14), low: red, high: green)Linear gradient fill for shapes and channels.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
start | color | required | Start color. |
end | color | required | End color. |
shade = linear_gradient(green.fade(60), green.fade(95))Radial gradient fill for shapes.
Parameters
| Name | Type | Default | What it is |
|---|---|---|---|
inner | color | required | Center color. |
outer | color | required | Edge color. |
glow = radial_gradient(blue, blue.fade(100))Order commands
Commands: buy, sell, close, close_all, modify, cancel
buy sell close close_all modify cancel
buy [options]#Open a long position or place a buy order.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
size | lots | percent | money | 0.01 lots | Position size in lots, or % / $ of balance as notional. |
risk | percent | money | none | Risk per trade; size is computed from the stop distance and rounded down to the lot step. |
stop | price | distance | none | Protective stop. For a pending stop order, the entry price (its protective stop is then stop_loss). |
target | price | distance | none | Take-profit price or distance, such as 2R. |
limit | price | none | Entry price of a pending limit order. |
stop_loss | price | distance | none | Protective stop of a pending stop order. |
ghost | bool | false | Keep stop and target off the broker; AlgoBars enforces them. |
expires | bars | duration | none | When a pending order expires. |
market | symbol | market.symbol | Which symbol, in strategies with several markets. |
tag | string | "" | Label carried by the order and its trade. |
buy risk: 1%, stop: 20 pips, target: 2Rsell [options]#Open a short position or place a sell order.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
size | lots | percent | money | 0.01 lots | Position size in lots, or % / $ of balance as notional. |
risk | percent | money | none | Risk per trade; size is computed from the stop distance and rounded down to the lot step. |
stop | price | distance | none | Protective stop. For a pending stop order, the entry price (its protective stop is then stop_loss). |
target | price | distance | none | Take-profit price or distance, such as 2R. |
limit | price | none | Entry price of a pending limit order. |
stop_loss | price | distance | none | Protective stop of a pending stop order. |
ghost | bool | false | Keep stop and target off the broker; AlgoBars enforces them. |
expires | bars | duration | none | When a pending order expires. |
market | symbol | market.symbol | Which symbol, in strategies with several markets. |
tag | string | "" | Label carried by the order and its trade. |
sell risk: $200, stop: highest(high, 10) + 2 pips, target: 2Rclose <trade> [options]#Close a trade, fully or partly.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
trade | Trade | required | Trade to close. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
size | percent | lots | 100% | How much to close. |
close trades.last(), size: 50%close_all [options]#Close all of the strategy's open trades, optionally filtered.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
side | string | both | long, short or both. |
tag | string | "" | Only trades with this tag. |
market | symbol | market.symbol | Only this symbol. |
close_all side: longmodify <trade> [options]#Change the stop or target of a trade.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
trade | Trade | required | Trade to change. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
stop | price | distance | unchanged | New stop. |
target | price | distance | unchanged | New target. |
modify trades.last(), stop: trades.last().entry_pricecancel <orders>#Cancel pending orders.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
orders | list<Order> | required | Orders to cancel. |
cancel orders.pending(tag: "grid")Trade management commands
Commands: partial, breakeven, trail, exit
partial <amount> [options]#Close part of the trade at a level.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
amount | percent | required | Share of the trade to close. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
at | price | distance | required | Level, such as 1.5R. |
partial 50% at: 2Rbreakeven [options]#Move the stop to the entry price at a level.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
at | price | distance | required | Level, such as 1R. |
offset | distance | 0 pips | Distance beyond entry. |
breakeven at: 1R, offset: 2 pipstrail [options]#Trail the stop behind price.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
by | distance | required | Trailing distance. |
after | price | distance | 0R | Start trailing once this level is reached. |
trail by: atr(14), after: 2Rexit [options]#Close the trade after a time or when a condition becomes true.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
after | bars | duration | none | Close after this long. |
when | bool | false | Close when true. |
exit after: 48 barsDrawing commands
Commands: plot, fill, hline, mark, label, line, box, table…
plot fill hline mark label line box table bar_color background pane polygon polyline curve channel profile heatmap cells candles fib pitchfork arrow icon image tooltip dashboard
plot <series> [options]#Draw a series.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
series | series<number> | required | Values to draw. |
Options
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
style | string | line | line, step, area, histogram, columns, circles or cross. | |
color | color | blue | Color. | |
width | int | 1 | 1 to 10 | Line width in pixels. |
pane | string | price | price, new or a named pane. |
plot ema(close, 20) as fast, color: green, width: 2fill <a> <b> [options]#Fill between two plots or levels.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
a | series<number> | required | First edge. |
b | series<number> | required | Second edge. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
color | color | blue | Color. |
fill ema(close, 20), ema(close, 50), color: green.fade(80)hline <value> [options]#Horizontal level.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
value | price | required | Level. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
style | string | dashed | solid, dashed or dotted. |
color | color | gray | Color. |
hline 70, style: dashed, color: redmark <shape> [options]#Marker on bars where a condition is true.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
shape | string | required | Built-in shape, emoji or SVG path. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
at | string | price | below | above, below or a price. |
when | bool | true | Where to draw. |
color | color | blue | Color. |
size | string | small | tiny, small, normal or large. |
mark arrow_up, at: below, when: crosses_above(close, ema(close, 20))label <text> [options]#Text at a bar and price.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
text | string | required | Text. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
at | tuple<bar | time, price> | required | Bar and price. |
style | string | normal | Label style. |
color | color | blue | Color. |
label "Entry", at: (bar.index, high)line [options]#Trend line or ray.
Options
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
from | tuple<bar | time, price> | required | Start point. | |
to | tuple<bar | time, price> | required | End point. | |
extend | string | none | none, left, right or both. | |
color | color | blue | Color. | |
width | int | 1 | 1 to 10 | Line width in pixels. |
line from: (bar.index - 20, low), to: (bar.index, low), extend: rightbox [options]#Rectangle or zone.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
id | string | automatic | Id for updating the same box later. |
from | tuple<bar | time, price> | required | One corner. |
to | tuple<bar | time, price> | required | Opposite corner. |
color | color | blue | Color. |
border | color | none | Border color. |
box from: (bar.index - 10, high), to: (bar.index, low), color: blue.fade(85)table [options]#On-chart table.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
position | string | top_right | Corner or edge of the chart. |
rows | list<list<string>> | required | Cell text by row. |
table position: top_right, rows: [["RSI", "{rsi(close, 14):0}"]]bar_color <color>#Recolor price bars.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
color | color | required | Bar color. |
bar_color if close > open then green else redbackground <color> [options]#Shade the background.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
color | color | required | Background color. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
when | bool | true | Where to shade. |
background red.fade(90), when: rsi(close, 14) > 70pane <name> [options]#Declare a named pane shared by several plots.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
name | string | required | Pane name. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
height | percent | 25% | Share of the chart height. |
pane "Oscillators", height: 30%polygon [options]#Closed shape.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
points | list<tuple<bar | time, price>> | required | Corner points. |
fill | color | none | Fill color. |
border | color | blue | Border color. |
polygon points: [(bar.index - 5, high), (bar.index, low), (bar.index - 5, low)], fill: blue.fade(80)polyline [options]#Open multi-segment line.
Options
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
points | list<tuple<bar | time, price>> | required | Points in order. | |
color | color | blue | Color. | |
width | int | 1 | 1 to 10 | Line width in pixels. |
polyline points: [(bar.index - 10, low), (bar.index - 5, high), (bar.index, low)]curve [options]#Smoothed curve through points.
Options
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
points | list<tuple<bar | time, price>> | required | Points in order. | |
smooth | number | 0.5 | 0 to 1 | Smoothing from 0 to 1. |
color | color | blue | Color. |
curve points: [(bar.index - 10, low), (bar.index - 5, high), (bar.index, low)], smooth: 0.5channel [options]#Channel between two series with a fill.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
upper | series<price> | required | Upper edge. |
lower | series<price> | required | Lower edge. |
fill | fill | blue.fade(85) | Fill color or gradient. |
channel upper: bollinger(close).upper, lower: bollinger(close).lower, fill: blue.fade(85)profile [options]#Horizontal histogram on the price axis (volume profile, market profile).
Options
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
rows | int | 24 | 2 to 1000 | Price rows. |
range | string | visible | visible, session or fixed. | |
side | string | right | left or right. | |
value_area | percent | 70% | Value area share. |
profile rows: 24, range: session, side: rightheatmap [options]#Price-by-time heatmap.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
cells | list<Cell> | required | Cells with bar, price and value. |
palette | string | "thermal" | Color palette. |
heatmap cells: liquidity_cells, palette: "thermal"cells [options]#Cell grid inside each bar (footprint style).
Options
| Name | Type | Default | What it is |
|---|---|---|---|
rows | list<list<string>> | required | Cell text by row. |
columns | int | 2 | Columns per bar. |
cells rows: footprint_rows, columns: 2candles [options]#Custom candles in any pane.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
open | series<number> | required | Open. |
high | series<number> | required | High. |
low | series<number> | required | Low. |
close | series<number> | required | Close. |
color | color | blue | Color. |
pane | string | new | price, new or a named pane. |
candles open: open - close_of(GBPUSD), high: high - close_of(GBPUSD), low: low - close_of(GBPUSD), close: close - close_of(GBPUSD)fib [options]#Fibonacci retracement or extension.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
from | tuple<bar | time, price> | required | Swing start. |
to | tuple<bar | time, price> | required | Swing end. |
levels | list<number> | [0.236, 0.382, 0.5, 0.618, 0.786] | Levels to draw. |
fib from: (bar.index - 50, lowest(low, 50)), to: (bar.index, highest(high, 50))pitchfork [options]#Andrews pitchfork.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
points | list<tuple<bar | time, price>> | required | Three anchor points. |
pitchfork points: [(bar.index - 30, low), (bar.index - 20, high), (bar.index - 10, low)]arrow [options]#Arrow between two points.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
from | tuple<bar | time, price> | required | Tail. |
to | tuple<bar | time, price> | required | Head. |
color | color | blue | Color. |
arrow from: (bar.index - 5, high), to: (bar.index, close)icon <name> [options]#Icon glyph.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
name | string | required | Icon name. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
at | tuple<bar | time, price> | required | Bar and price. |
color | color | blue | Color. |
icon "flag", at: (bar.index, high)image <asset> [options]#Small image uploaded with the script.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
asset | string | required | Asset name. |
Options
| Name | Type | Default | Range | What it is |
|---|---|---|---|---|
at | tuple<bar | time, price> | required | Bar and price. | |
width | int | 24 | 4 to 512 | Width in pixels. |
image "logo", at: (bar.index, high), width: 24tooltip <text> [options]#Hover tooltip.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
text | string | required | Tooltip text. |
Options
| Name | Type | Default | What it is |
|---|---|---|---|
at | tuple<bar | time, price> | required | Bar and price. |
tooltip "Swing high", at: (bar.index, high)dashboard [options]#Panel pinned to the screen that stays put while the chart scrolls.
Options
| Name | Type | Default | What it is |
|---|---|---|---|
position | string | top_right | Corner or edge of the screen. |
rows | list<list<string>> | required | Cell text by row. |
dashboard position: top_right, rows: [["Trend", "up"]]Alert commands
Commands: notify
notify <message>#Send a notification (in-app and AI chat in v1).
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
message | string | required | Message text with {value} interpolation. |
notify "RSI is {rsi(close, 14):0.0}"Logging
Commands: log
log <message>#Write a line to the Terminal console.
Arguments
| Name | Type | Default | What it is |
|---|---|---|---|
message | string | required | Message text with {value} interpolation. |
log "close {close}"Header settings
Every line you can write under the script header.
market markets bars evaluate pane max_open max_open_per_side direction opposite warmup repeat cooldown expires check show_on_chart max_daily_loss max_drawdown max_total_risk pyramiding min_distance trade_only
markets: <list<symbol>>#Several symbols; alerts evaluate each independently.
markets: EURUSD, GBPUSDbars: <bartype>#Bar type: timeframe, range(n), xray(n), renko(n) or heikin_ashi(tf).
bars: 15mevaluate: <string>#bar_close (default, never repaints) or tick.
evaluate: bar_closeopposite: <string>#On an opposite signal: close, reverse, ignore or hedge (where the venue supports it).
opposite: reversewarmup: <int>#Bars required before rules run (computed by the compiler when omitted).
warmup: 300repeat: <string>#once, once per bar, once per bar close or every time.
repeat: once per barshow_on_chart: <bool>#Draw fire points, watched levels and live status on the chart.
show_on_chart: truemax_daily_loss: <percent | money>#Pause the deployment after this loss in a day.
max_daily_loss: 3%max_drawdown: <percent | money>#Pause the deployment at this drawdown.
max_drawdown: 10%min_distance: <distance>#Smallest distance between pyramided entries.
min_distance: 20 pipstrade_only: <sessions>#Sessions in which the strategy may open trades.
trade_only: within sessions london, new_yorkRule modifiers
When a rule may fire.
every skip max cooldown once within from
every <ordinal> | every bar#Act on every Nth trigger, or count every bar the condition holds.
when crosses_above(close, ema(close, 20)) every 4th:buy risk: 1%, stop: 20 pips, target: 2Rskip first <n>#Ignore the first N triggers, then act on every trigger.
when crosses_above(close, ema(close, 20)) skip first 3:buy risk: 1%, stop: 20 pips, target: 2Rmax <n> per <period>#Cap actions per day, session, hour or week.
when crosses_above(close, ema(close, 20)) max 2 per day:buy risk: 1%, stop: 20 pips, target: 2Rcooldown <duration | n bars>#Ignore triggers for a while after acting.
when rsi(close, 14) < 30 cooldown 30m:notify "Oversold"once per bar#In tick mode, act at most once per bar.
when close > highest(high, 20)[1] once per bar:notify "Breakout"within sessions <name>, ...#Only trigger inside the named sessions.
when crosses_above(close, vwap()) within sessions london, new_york:buy risk: 1%, stop: 20 pips, target: 2Rfrom <time> to <time> [zone]#Only trigger inside a daily time window.
when crosses_above(close, vwap()) from 08:00 to 11:00 Europe/London:buy risk: 1%, stop: 20 pips, target: 2REvents
Blocks that run when something happens.
start bar close tick fill exit session open day change render
on start:#Runs once before the first evaluated bar.
on start:log "Deployed"on bar close:#Runs on every confirmed bar.
on bar close:log "Bar {bar.index}"on exit(trade):#Runs when a trade closes, with trade.pnl, trade.r and trade.reason.
on exit(trade):log "Closed at {trade.r:0.00}R"on session open "<name>":#Runs when a session opens.
on session open "london":log "London open"on day change:#Runs at each calendar day boundary.
on day change:log "New day"on render(canvas):#Custom vector drawing for the visible range.
on render(canvas):canvas.text("Hello", at: (canvas.last_bar, close))Units
pips, points, percent, R, money, lots, bars, duration.
pips points percent R money lots bars duration
Distance in pips, converted with each symbol's pip size.
stop_distance = 20 pipsDistance in points, converted with each symbol's point size.
buffer = 150 pointsA share of a base named by context, or by `of`.
risk_share = 1%Multiple of the trade's initial stop distance; valid only where a trade has a stop.
buy risk: 1%, stop: 20 pips, target: 2RPosition size, using the symbol's contract size and lot step.
size_now = 0.5 lotsCount of bars of the script's bar type.
window = 10 barsWall-clock time using UTC bar timestamps.
wait = 4hBar variables
Built-in values: open, high, low, close, volume, time…
open high low close volume time hl2 hlc3 ohlc4 bar.index bar.confirmed bar.is_first bar.is_last bar.range bar.body bar.direction bar.type
Close of the bar (final on confirmed bars).
last_price = closeVolume, or tick count on range, x-ray and renko bars where recorded.
activity = volumeBar time (completion time on range, x-ray and renko bars).
stamp = time(open + high + low + close) / 4.
average_price = ohlc4Index of the bar from the start of history.
n = bar.indexTrue once the bar has closed.
closed = bar.confirmedTrue on the first evaluated bar.
starting = bar.is_firstTrue on the last historical bar.
caught_up = bar.is_lastAbsolute distance between open and close.
body_now = bar.bodyThe script's bar type.
current_bars = bar.typeTime variables
Built-in values: hour, minute, day_of_week, time_of_day
hour minute day_of_week time_of_day
Hour of the bar time, 0 to 23, in UTC or the configured time zone.
morning = hour in 8..111 Monday to 7 Sunday.
friday = day_of_week == 5Clock time of the bar, for windows such as time_of_day between 08:00 and 09:00.
first_hour = time_of_day between 08:00 and 09:00Market variables
Built-in values: market.symbol, market.name, market.category, market.pip_size, market.point_size, market.contract_size…
market.symbol market.name market.category market.pip_size market.point_size market.contract_size market.lot_min market.lot_max market.lot_step market.leverage_tiers market.currency_base market.currency_quote market.session market.is_open market.next_open market.next_close
Symbol the script runs on.
sym = market.symbolforex, crypto, index, commodity or stock.
asset_class = market.categoryPrice size of one pip.
pip = market.pip_sizePrice size of one point.
point = market.point_sizeUnits per lot.
units = market.contract_sizeSmallest order size.
smallest = market.lot_minLargest order size.
largest = market.lot_maxSize increment.
increment = market.lot_stepLeverage by position size.
tiers = market.leverage_tiersBase currency.
base_ccy = market.currency_baseQuote currency.
quote_ccy = market.currency_quoteTrading session times and time zone.
hours = market.sessionTrue while the market is open.
trading_now = market.is_openNext open time.
reopens = market.next_openNext close time.
closes = market.next_closeAccount variables
Built-in values: account.balance, account.equity, account.margin, account.free_margin, account.margin_level, account.currency…
account.balance account.equity account.margin account.free_margin account.margin_level account.currency account.leverage account.mode account.venue positions orders trades history
Balance plus open profit and loss.
eq = account.equityMargin available.
available = account.free_marginEquity as a percentage of margin.
level = account.margin_levelhouse, metatrader, ibkr or a broker name.
where = account.venueOpen positions on the account, filterable by symbol, side and source.
open_count = positions.lenPending orders on the account.
waiting = orders.pending(tag: "grid")The strategy's own trades: open(), closed(), last().
mine = trades.open()Closed trades with aggregates such as pnl, win_rate and profit_factor.
today_pnl = history.today.pnlNamed colours
Built-in values: green, red, blue, gray, white, black…
green red blue gray white black orange yellow purple teal
The green color; use .fade(percent) for transparency.
tint = green.fade(50)The red color; use .fade(percent) for transparency.
tint = red.fade(50)The blue color; use .fade(percent) for transparency.
tint = blue.fade(50)The gray color; use .fade(percent) for transparency.
tint = gray.fade(50)The white color; use .fade(percent) for transparency.
tint = white.fade(50)The black color; use .fade(percent) for transparency.
tint = black.fade(50)The orange color; use .fade(percent) for transparency.
tint = orange.fade(50)The yellow color; use .fade(percent) for transparency.
tint = yellow.fade(50)The purple color; use .fade(percent) for transparency.
tint = purple.fade(50)The teal color; use .fade(percent) for transparency.
tint = teal.fade(50)Types
Records the language hands you: Trade, Order, Bar, Zone, Canvas and more.
Bar BarState BarSet Zone Level Swing Cell Trade Order Trades Orders HistoryPeriod History Session LeverageTier Canvas Path color
Bar#One bar.
Fields
| Name | Type | What it is |
|---|---|---|
open | price | Open. |
high | price | High. |
low | price | Low. |
close | price | Close. |
volume | number | Volume, or tick count on range, x-ray and renko bars. |
time | time | Bar time. |
index | int | Bar index. |
first_high = intrabar(1m).first().highBarState#Status of a bar set.
Fields
| Name | Type | What it is |
|---|---|---|
fresh | bool | false when this symbol had no bar in the latest interval. |
index | int | Bar index. |
confirmed | bool | true once the bar has closed. |
gold_fresh = bars(XAUUSD).bar.freshBarSet#Bars of a symbol and bar type, aligned to the script.
Fields
| Name | Type | What it is |
|---|---|---|
open | price | Open. |
high | price | High. |
low | price | Low. |
close | price | Close. |
volume | number | Volume, or tick count on range, x-ray and renko bars. |
time | time | Bar time. |
bar | BarState | Bar status. |
h4_close = bars(bars: 4h).closeZone#A price zone, such as an order block or fair value gap.
Fields
| Name | Type | What it is |
|---|---|---|
id | string | Stable id. |
top | price | Upper bound. |
bottom | price | Lower bound. |
mid | price | Midpoint. |
bullish | bool | true for bullish zones. |
mitigated | bool | true once price has traded back into it. |
formed_bar | int | Bar index where it formed. |
formed_time | time | When it formed. |
touches | int | Times price has returned to it. |
status | string | active, mitigated or broken. |
fresh_blocks = order_blocks().filter(z => not z.mitigated)Level#A support or resistance level.
Fields
| Name | Type | What it is |
|---|---|---|
price | price | Level price. |
touches | int | Touches. |
formed_bar | int | Bar index where it formed. |
strength | number | Relative strength from 0 to 1. |
strong_levels = support_resistance().filter(l => l.touches >= 3)Swing#A swing point.
Fields
| Name | Type | What it is |
|---|---|---|
price | price | Swing price. |
bar | int | Bar index. |
time | time | Time. |
direction | int | 1 for a swing high, -1 for a swing low. |
last_turn = zigzag().last().priceCell#One heatmap or footprint cell.
Fields
| Name | Type | What it is |
|---|---|---|
bar | int | Bar index. |
price | price | Price. |
value | number | Value shown. |
hot_cells = liquidity_cells.filter(c => c.value > 0)Trade#An open or closed trade.
Fields
| Name | Type | What it is |
|---|---|---|
id | string | Trade id. |
symbol | symbol | Symbol. |
side | string | long or short. |
size | lots | Size. |
entry_price | price | Entry price. |
entry_time | time | Entry time. |
exit_price | price | Exit price, once closed. |
stop | price | Current stop. |
target | price | Current target. |
r | number | Current R multiple. |
pnl | money | Profit or loss. |
pnl_pct | percent | Profit or loss as a share of balance. |
mae | money | Maximum adverse excursion. |
mfe | money | Maximum favorable excursion. |
bars_open | int | Bars since entry. |
tag | string | Tag. |
reason | string | Why it closed: target, stop, rule, manual or management. |
best_r = trades.closed().map(t => t.r).max()Order#A pending or filled order.
Fields
| Name | Type | What it is |
|---|---|---|
id | string | Order id. |
type | string | market, limit, stop or stop_limit. |
side | string | buy or sell. |
symbol | symbol | Symbol. |
price | price | Order price. |
size | lots | Size. |
expires | time | Expiry time. |
tag | string | Tag. |
grid_count = orders.pending(tag: "grid").lenTrades#The strategy's own trades.
Methods
| Method | Returns | What it does |
|---|---|---|
open(tag, side, market) | list<Trade> | Open trades. |
closed(tag, side, market) | list<Trade> | Closed trades. |
last(tag, side, market) | Trade | The most recent trade. |
open_longs = trades.open(side: long).lenOrders#Pending orders on the account.
Methods
| Method | Returns | What it does |
|---|---|---|
pending(tag, side, market) | list<Order> | Pending orders. |
waiting_count = orders.pending().lenHistoryPeriod#Aggregates of closed trades for a period.
Fields
| Name | Type | What it is |
|---|---|---|
count | int | Closed trades. |
pnl | money | Net profit or loss. |
win_rate | percent | Share of winning trades. |
profit_factor | number | Gross profit divided by gross loss. |
expectancy | money | Average result per trade. |
max_drawdown | money | Largest peak-to-trough decline. |
avg_r | number | Average R multiple. |
week_pnl = history.this_week.pnlHistory#Closed trades on the account.
Fields
| Name | Type | What it is |
|---|---|---|
today | HistoryPeriod | Today. |
this_week | HistoryPeriod | This week. |
this_month | HistoryPeriod | This month. |
count | int | Closed trades. |
pnl | money | Net profit or loss. |
win_rate | percent | Share of winning trades. |
profit_factor | number | Gross profit divided by gross loss. |
expectancy | money | Average result per trade. |
max_drawdown | money | Largest peak-to-trough decline. |
avg_r | number | Average R multiple. |
Methods
| Method | Returns | What it does |
|---|---|---|
last(period) | HistoryPeriod | A trailing period. |
month_win_rate = history.last(30d).win_rateSession#A trading session.
Fields
| Name | Type | What it is |
|---|---|---|
name | string | Session name. |
open | time | Open time. |
close | time | Close time. |
timezone | string | Time zone. |
opens_at = market.session.openLeverageTier#Leverage for a band of position sizes.
Fields
| Name | Type | What it is |
|---|---|---|
max_size | lots | Largest size in the band. |
leverage | number | Leverage. |
tier_count = market.leverage_tiers.lenCanvas#The vector canvas passed to on render.
Fields
| Name | Type | What it is |
|---|---|---|
last_bar | int | Index of the last bar. |
visible_from | int | First visible bar. |
visible_to | int | Last visible bar. |
price_min | price | Lowest visible price. |
price_max | price | Highest visible price. |
width | number | Width in pixels. |
height | number | Height in pixels. |
Methods
| Method | Returns | What it does |
|---|---|---|
path() | Path | Start a new shape. |
text(text, at, align, color, size) | void | Draw text. |
rect(from, to, fill, border) | void | Draw a rectangle. |
line(from, to, color, width) | void | Draw a line. |
on render(canvas):canvas.text("Hi", at: (canvas.last_bar, close))Path#A vector shape being drawn.
Methods
| Method | Returns | What it does |
|---|---|---|
move_to(bar, price) | Path | Move without drawing. |
line_to(bar, price) | Path | Draw a straight segment. |
curve_to(bar, price, control_bar, control_price) | Path | Draw a curved segment. |
close() | Path | Close the shape. |
fill(style) | Path | Fill the shape. |
stroke(color, width) | Path | Outline the shape. |
on render(canvas):shape = canvas.path()color#Functions available on every color.
Methods
| Method | Returns | What it does |
|---|---|---|
fade(amount) | color | The same color with transparency. |
soft_green = green.fade(80)These rules say exactly how orders fill and how trades are managed. Every rule has test cases, and the backtester, the demo engine and live trade management must all pass them. Rule IDs are stable: a change in behaviour gets a new rule ID. Nothing in these rules adds costs.
Timing
E1 Bar-close evaluation, E2 Tick evaluation
Scripts with evaluate: bar_close (the default) run after each bar closes. Orders created at that close take effect from the next bar: market orders fill at the next bar's open, and pending orders can first fill during the next bar. A market order created at the last bar of a test never fills and is reported as unfilled.
Scripts with evaluate: tick run at every point of the price path (E6), and market orders fill at the price of the point that triggered them, without waiting for the bar to close. Live, that is the next tick after the condition becomes true; in a backtest it is the next stored price. When a bar has no finer stored data, its own path is used and the run is labelled "approximate intrabar", listing the bars affected.
Inside a forming bar, prices are live and indicators are not: close, high, low and everything derived from them move with the tick, while indicator values stay as they were at the last bar close, because a bar that has not finished cannot have changed them yet. bar.confirmed is false during a tick and true at the close. Pending orders placed by a tick still wait for the next bar, as in E1.
Fills
E3 Limit orders, E4 Stop orders, E5 Stops and targets, E6 Price path inside a bar, E7 Stop before target, E8 Same-bar exits
A buy limit at price L fills at the bar's open when the open is at or below L; otherwise it fills at L when the price path reaches L. A sell limit mirrors this above the market. Touching the price is enough; queue position is not modelled.
Backtests have no spread, so one price per bar triggers and fills every order. Live and demo trading use the side that can actually trade: a buy is triggered and filled on the ask, a sell on the bid. A script never chooses a side; the deployment does.
A buy stop at price S fills at the bar's open when the open is at or above S; otherwise it fills at S when the price path reaches S. A sell stop mirrors this below the market. Live, a buy stop triggers on the ask and a sell stop on the bid, as in E3.
A trade's stop and target are closing orders: a long trade's stop is a sell stop and its target a sell limit, and a short trade's are the mirror. They fill under E3 and E4, including at the open when a bar gaps past them. Live, a long trade's stop and target are triggered on the bid and a short trade's on the ask, because that is the side that closes them.
When events inside a bar need an order, the 1-minute bars inside it are used, because 1m is the stored base resolution; sub-minute history is not kept beyond the most recent bars, and tick data arrives later. A 1-minute bar, or any bar with no finer data stored, follows the path open → nearer extreme → farther extreme → close, where the nearer extreme is whichever of the high and low is closer to the open. When both are equally close, a bar that closes at or above its open goes to the low first, and a bar that closes below its open goes to the high first. Every price between two path points is passed through in order. Reports name the resolution each run used.
When a trade's stop and target are both reached within one bar after the trade exists, and finer data cannot tell which came first, the stop fills. This applies even when the path of E6 would reach the target first.
A trade's stop, target and management are active from the moment it fills, so a trade can close in the bar where it opened, following E6 and E7.
Prices, distances and size
E9 Distances and R, E10 Price rounding, E11 Risk-based size, E12 No costs
Distances given in pips, points or price are measured from the actual fill price, so a gap moves the stop and target with the entry. Price levels are used as given. R is the distance between the actual fill price and the initial stop, fixed when the trade opens; for a pending stop order the initial stop is its stop_loss.
Every order, stop and target price is rounded to the symbol's tick size when it is set, with halves rounded away from zero, using decimal arithmetic.
Size = risk amount ÷ (stop distance × contract size × quote-to-account rate), rounded down to the lot step, then held between the symbol's minimum and maximum lot. Percent risk uses the balance, and the stop distance is measured from the actual fill price, both at the moment of the fill. A size that rounds below the minimum lot trades at the minimum lot, and the trade is marked "above requested risk" with the risk it actually carries, so reports and the Terminal can show it.
Fills carry no spread, commission, fees, swaps or slippage. Profit is (exit price − entry price) × size × contract size × quote-to-account rate at the exit, negated for short trades. Gap fills at the open (E3–E5) are prices that traded, not slippage.
Trade management
E13 Breakeven, E14 Trailing stops, E15 Partial closes, E16 Time and condition exits, E17 Pending order expiry
When the price path reaches the at level, the stop moves to the entry price plus offset for a long trade (minus for a short trade) if that tightens it. The new stop applies to the rest of the path.
Once the price path reaches the after level, the stop follows the most favourable price reached since then, less by for a long trade (plus for a short trade), and only ever tightens. It updates at every point of the path. Distances that come from indicators use their value at the last closed bar.
partial P at: X closes P of the trade's original size when the price path reaches X (at X, or at the open when the bar gaps past it), rounded down to the lot step. A partial that rounds to zero is skipped. If the size left would fall below the minimum lot, the whole trade closes. Each partial fires once.
exit after: N bars closes a trade at the open of the bar after the Nth bar close following its entry bar. exit after: with a duration closes it at the open after the first bar close at or beyond that time since the fill. exit when: closes it at the open after the bar close where the condition is true.
expires: N bars keeps an order active for the N bars after the bar that created it; expires: with a duration keeps it active until the first bar close at or beyond that time. An order that has not filled by then is cancelled at that close. Pending orders still open when a test ends are cancelled.
Strategy controls
E18 Opposite signals, E19 Limits checked at the fill, E20 Loss guards, E21 Order of work at a bar close, E22 Trading hours
With opposite: ignore (the default), entries opposite an open trade are rejected. With close, the opposite entry closes open trades at its fill price and opens nothing. With reverse, it closes open trades and opens the new trade at the same price. With hedge, it opens alongside open trades where the venue supports hedging, and behaves as ignore elsewhere.
When an entry fills, it is rejected with a reason if it would exceed max_open, max_open_per_side, one plus pyramiding trades in the same direction, or max_total_risk, or if it would be closer than min_distance to the entry price of an open trade in the same direction. Pending orders are checked when they fill, not when they are placed.
At each bar close, the day's closed profit plus the open profit at that close is compared with max_daily_loss. When the limit is reached, all trades close and pending orders are cancelled at the next bar's open, and new entries are rejected until the next UTC day. max_drawdown works the same way against the highest equity reached, and pauses the deployment until it is resumed.
At each bar close the runtime (1) completes the fills and management inside the bar in path order, (2) runs on fill and on exit for them in the order they happened, (3) runs calculations, (4) updates confirmations and sequences, (5) runs rules and on bar close in script order, (6) records exports and drawing, so outputs always see this bar's final values, and (7) queues the orders the rules created for the next bar. Queued closes run before queued entries. Indicator calls update on every bar, even when their line sits in a branch that did not run.
With trade_only, entries are only created at the close of bars that open inside the listed sessions; other entries are skipped with a reason. Exits, management and pending orders already placed continue outside the sessions.
Results
E23 Reproducibility, E24 End of test
The compiler checks a script before a single bar is touched. Every message says what is wrong and what to do, and many carry a one-click fix. There are 50 codes, and a code is never reused for a different meaning. Where a message is shown below, it is the compiler's real output for a small mistake.
Text and layout
AS0001, AS0002, AS0003, AS0004, AS0005, AS0006
Syntax
AS0010, AS0011, AS0012, AS0013, AS0014, AS0015, AS0016…
Missing ":" after the rule condition.
A script starts with its type and a name, such as strategy "My Strategy" or indicator "My Indicator".
Put what the rule does on indented lines below.
Names and types
AS0201, AS0202, AS0203, AS0204, AS0205, AS0206, AS0207…
crosses_abve isn't a function. Did you mean crosses_above?
close[-1] would read a future bar. History counts back from the current bar: close[1] is the previous bar.
+ needs numbers, but this combines a price and text.
length must be at least 1.
crosses_above needs b (series or level crossed).
top isn't a field of Bands.
max_opn isn't a strategy setting. Did you mean max_open?
on candle isn't an event. Events are start, bar close, tick, fill(order), exit(trade), session open, day change and render(canvas).
Units and risk
AS0301, AS0302, AS0303, AS0304, AS0305, AS0306
stop 25 has no unit. Did you mean 25 pips?
2R needs a stop to measure from. Add a stop to this trade, such as stop: 20 pips.
Risk-based size needs a stop to measure risk from. Add a stop, such as stop: 20 pips.
1% of what? A percentage needs a base here, such as 1% of 20 pips.
Where things may go
AS0401, AS0402, AS0403, AS0404, AS0405
buy places or manages trades, which only strategies do. Use this indicator in a strategy instead.
breakeven only works inside the management block of a buy or sell. Indent it under the entry.
Only action functions can place or change orders; write action fn f.
on tick needs evaluate: tick in the header.
A library holds functions, types, enums and constants. Move this into a strategy, indicator or alert.
Data
AS0501, AS0502, AS0503
mars isn't a session. Sessions are sydney, tokyo, asia, london, frankfurt and new_york.
Hints
AS0601
Input unused is never used.
61 complete scripts covering every part of the language. Each one compiles, and each is shown with the compiler's own plain-English description. Copy one into the Terminal and change it. They are teaching examples, not recommendations, and none of them is a forecast of results.
EMA Cross
EMA Cross is a strategy that trades EURUSD on 15-minute bars.
core language
strategy "EMA Cross"market: EURUSDbars: 15minput fast = 20input slow = 50when crosses_above(ema(close, fast), ema(close, slow)):buy risk: 1%, stop: 25 pips, target: 2Rwhen crosses_below(ema(close, fast), ema(close, slow)):close_allEMA Cross is a strategy that trades EURUSD on 15-minute bars.
You can change 2 inputs: fast (default 20) and slow (default 50).
When the EMA of the close over fast bars crosses above the EMA of the close over slow bars, it buys at market, risking 1% of the balance, with a stop 25 pips from the entry and with a target 2R from the entry.
When the EMA of the close over fast bars crosses below the EMA of the close over slow bars, it closes all trades.
Six-Confirmation Momentum
Six-Confirmation Momentum is a strategy that trades XAUUSD on 5-minute bars. It holds at most 1 open trade and stops for the day after losing 3%.
confirmationstrade managementother bar sizesuses a library or indicatorsmart moneysessions and time
strategy "Six-Confirmation Momentum"market: XAUUSDbars: 5mmax_open: 1max_daily_loss: 3%use indicator "Trend Ribbon" v3 as ribbon (fast: 10, slow: 30)input risk = 0.5%confirmations long_setup:trend: ema(close, 50) > ema(close, 200)higher_tf: bars(bars: 1h).close > ema(bars(bars: 1h).close, 50)momentum: rsi(close, 14) between 55 and 70volume: volume > sma(volume, 20) * 1.5structure: break_of_structure(direction: up)ribbon: ribbon.up and ribbon.strength > 0.5require: allwhen long_setup.passed as long_entry every 4th within sessions london, new_york:buy risk: risk, stop: lowest(low, 10) - 5 points, target: 3R, tag: "momentum":breakeven at: 1Rpartial 50% at: 2Rtrail by: atr(14) * 1.5, after: 2Rexit after: 60 barson exit(trade):log "{trade.tag} closed at {trade.r:0.00}R after {long_entry.triggers} triggers"Six-Confirmation Momentum is a strategy that trades XAUUSD on 5-minute bars. It holds at most 1 open trade and stops for the day after losing 3%.
It uses the indicator "Trend Ribbon" (version 3) as ribbon, with fast set to 10 and slow set to 30.
You can change one input: risk (default 0.5%).
long_setup passes when all of these 6 conditions are true: trend (the 50-bar EMA of the close is above the 200-bar EMA of the close); higher_tf (the close of 1-hour bars is above the 50-bar EMA of the close of 1-hour bars); momentum (the 14-bar RSI is between 55 and 70); volume (volume is above 1.5 × the 20-bar SMA of volume); structure (a bullish break of structure); ribbon (ribbon.up and ribbon.strength is above 0.5).
When long_setup passes (on every 4th time and during the London and New York sessions), it buys at market, risking risk of the balance, with a stop at the lowest low of the last 10 bars minus 5 points, with a target 3R from the entry and tagged "momentum"; once open, it moves the stop to breakeven at 1R, closes 50% at 2R, trails the stop by 1.5 × the 14-bar ATR once the trade reaches 2R and exits after 60 bars.
When a trade closes, it logs "{trade.tag} closed at {trade.r:0.00}R after {long_entry.triggers} triggers".
This description may be incomplete because the script has errors.
Ribbon Multi-Timeframe
Ribbon Multi-Timeframe is a strategy that trades GBPUSD on 15-minute bars.
uses a library or indicator
strategy "Ribbon Multi-Timeframe"market: GBPUSDbars: 15muse indicator "Trend Ribbon" v3 as ribbon (fast: 10, slow: 30)ribbon_4h = ribbon.on(bars: 4h)when ribbon_4h.up and crosses_above(close, ribbon.fast) and ribbon.strength > 0.5:buy risk: 1%, stop: atr(14) * 1.5, target: 2Rwhen ends(ribbon.up):close_all side: longRibbon Multi-Timeframe is a strategy that trades GBPUSD on 15-minute bars.
It uses the indicator "Trend Ribbon" (version 3) as ribbon, with fast set to 10 and slow set to 30.
It calculates ribbon_4h as ribbon.on(bars: 4h).
When ribbon_4h.up and the close crosses above ribbon.fast and ribbon.strength is above 0.5, it buys at market, risking 1% of the balance, with a stop 1.5 × the 14-bar ATR from the entry and with a target 2R from the entry.
When ribbon.up stops being true, it closes all long trades.
This description may be incomplete because the script has errors.
Sweep and Reclaim
Sweep and Reclaim is a strategy that trades XAUUSD on 20-tick range bars.
sequencerenko, heikin ashi, x-ray
strategy "Sweep and Reclaim"market: XAUUSDbars: range(20)sequence grab within 25 bars:step sweep: low < lowest(low, 30)[1]step reclaim: close > sweep.highstep retest: low <= reclaim.close and close > reclaim.closereset_if: close < sweep.lowwhen grab.completed:buy risk: 1%, stop: grab.sweep.low - 3 points, target: 2.5RSweep and Reclaim is a strategy that trades XAUUSD on 20-tick range bars.
grab completes when these steps happen in order within 25 bars: sweep, when the low is below the previous bar's lowest low of the last 30 bars; then reclaim, when the close is above sweep.high; then retest, when the low is at or below reclaim.close and the close is above reclaim.close. It starts over if the close is below sweep.low.
When grab completes, it buys at market, risking 1% of the balance, with a stop at the low of the sweep step minus 3 points and with a target 2.5R from the entry.
EURUSD and GBPUSD Mean Reversion
EURUSD and GBPUSD Mean Reversion is a strategy that trades EURUSD and GBPUSD on 1-hour bars. It holds at most 2 open trades.
several marketsstatistics and matrices
strategy "EURUSD and GBPUSD Mean Reversion"markets: EURUSD, GBPUSDbars: 1hmax_open: 2input lookback = 200input entry_z = 2.0input exit_z = 0.5eur = bars(EURUSD)gbp = bars(GBPUSD)hedge = beta(returns(eur.close), returns(gbp.close), lookback)z = zscore(log(eur.close / gbp.close), lookback)when z > entry_z and trades.open(tag: "pair").len == 0:eur_size = size_for(EURUSD, risk: 0.5%, stop: atr(14, on: eur) * 3)sell market: EURUSD, size: eur_size, tag: "pair"buy market: GBPUSD, size: eur_size * hedge, tag: "pair"when abs(z) < exit_z:close_all tag: "pair"EURUSD and GBPUSD Mean Reversion is a strategy that trades EURUSD and GBPUSD on 1-hour bars. It holds at most 2 open trades.
You can change 3 inputs: lookback (default 200), entry_z (default 2.0) and exit_z (default 0.5).
It calculates eur as EURUSD bars, gbp as GBPUSD bars, hedge as the beta of the returns of eur.close against the returns of gbp.close over lookback bars and z as the z-score of the logarithm of eur.close divided by gbp.close over lookback bars.
When z is above entry_z and the number of open trades tagged "pair" is 0, it sets eur_size to the size of EURUSD that risks 0.5% with a stop 3 × the 14-bar ATR of eur away; it also sells EURUSD at market, with a size of eur_size and tagged "pair"; it also buys GBPUSD at market, with a size of eur_size × hedge and tagged "pair".
When the absolute value of z is below exit_z, it closes all trades tagged "pair".
Language Tour
Language Tour is a strategy that trades EURUSD and GBPUSD on 15-minute bars. It holds at most 3 open trades (2 per side), reverses on an opposite sign
confirmationssequencetrade managementother bar sizesseveral marketsinside the barrenko, heikin ashi, x-rayuses a library or indicatorcanvastables and dashboardsaccount and historystatefunctions and typessmart moneystatistics and matricessessions and timepending orders
algobarsx 1# Exercises every construct in spec sections 3-16.strategy "Language Tour"markets: EURUSD, GBPUSDbars: 15mevaluate: bar_closemax_open: 3max_open_per_side: 2direction: bothopposite: reversepyramiding: 3min_distance: 20 pipsmax_daily_loss: 3%max_drawdown: 10%trade_only: within sessions london, new_yorkuse indicator "Trend Ribbon" v3 as ribbon (fast: 10, slow: 30)use library "Quant Toolkit" v2 as qtinput fast = 20, label: "Fast length", min: 2, max: 500, group: "Trend"input source = close, label: "Source"input higher_tf = 4h, label: "Higher timeframe"input session = "london", options: ["london", "new_york", "asia"]input risk = 1%, min: 0.1%, max: 5%, step: 0.1%input show_zones = true, group: "Display"input zone_color = blue.fade(70), group: "Display", visible_if: show_zonesconst RISK_CAP = 2%type Level:price: pricetouched: int = 0formed_at: timeenum Regime: trending, ranging, volatilefn swing_strength(len: int) -> number:up = highest(high, len) - closedown = close - lowest(low, len)return (down - up) / atr(14)action fn enter_long(size_risk: percent = 1%) -> bool:buy risk: size_risk, stop: 20 pips, target: 2Rreturn truestate triggers = 0state last_entry: price = nastate regime = Regime.rangingstate levels: list<Level> = []trend_up = ema(close, 50) > ema(close, 200)distance = (source - ema(source, fast)) / atr(14)threshold: number = 1.5h4 = bars(bars: higher_tf)gold = bars(XAUUSD, bars: 15m)gold_atr = atr(14, on: gold)cov = data.coverage(EURUSD, bars: range(10))spread_z = zscore(log(close_of(EURUSD) / close_of(GBPUSD)), 100)first_hour = time_of_day between 08:00 and 09:00recent_high = intrabar(1m).high.max()kelly = qt.kelly_fraction(0.55, 1.8)power = 2 ** 3 ** 2in_window = hour in 8..11if regime == Regime.trending:risk_now = 1%elif regime == Regime.volatile:risk_now = 0.5%else:risk_now = 0.25%for level in levels:if close > level.price:level.touched += 1for i in 0..50:if i > 10:breakcontinuematch regime:Regime.trending: log "trending"Regime.ranging: log "ranging"levels.push(Level(price: high, formed_at: time))levels = levels.filter(l => l.touched == 0).keep_last(50)ranked = levels.sort_by((a, b) => a.price - b.price)confirmations long_setup:trend: trend_upmomentum: rsi(close, 14) > 55volume: volume > sma(volume, 20) * 1.5structure: break_of_structure(direction: up)higher_tf_trend: h4.close > ema(h4.close, 50)ribbon_up: ribbon.uprequire: at least 5sequence liquidity_grab within 30 bars:step sweep: low < lowest(low, 20)[1]step reclaim: close > sweep.highstep retest: low <= reclaim.close and close > reclaim.closereset_if: close < sweep.lowwhen long_setup.passed as long_entry every 4th:buy risk: risk, stop: atr(14) * 1.5, target: 3Rwhen starts(long_setup.passed) skip first 3 max 2 per day cooldown 30m:triggers += 1if triggers % 4 == 0:buy risk: 0.5%, stop: atr(14) * 1.5, target: 3Rwhen liquidity_grab.completed from 08:00 to 11:00 Europe/London:buy risk: 1%, stop: liquidity_grab.sweep.low - 3 points, target: 2R + 5 pips, tag: "breakout":breakeven at: 1R, offset: 2 pipspartial 30% at: 1.5Rpartial 30% at: 2.5Rtrail by: atr(14), after: 2Rexit after: 48 barsexit when: crosses_below(close, ema(close, 20))when crosses_below(close, ema(close, 50)) cooldown 5 bars:sell risk: $200, stop: highest(high, 10) + 2 pips, target: lowest(low, 50)when was(trend_up, within: 5 bars) and held(close > open, for: 3 bars) every bar:buy limit: lowest(low, 5), size: 1 lot, expires: 10 barsbuy stop: high + 2 pips, risk: 0.5%, stop_loss: low - 2 pips, target: 2R, ghost: truesell market: GBPUSD, size: 0.3 lotswhen history.today.pnl < -(2% of account.balance) or account.margin_level < 150%: close_allfor trade in trades.open(tag: "breakout"):if trade.r >= 3 and rsi(close, 14) > 75:close trade, size: 50%for trade in trades.open(side: long):modify trade, stop: trade.entry_pricecancel orders.pending(tag: "grid")on start:log "starting on {market.symbol}"on bar close:log "bar {bar.index}"on fill(order):log "filled {order.size}"on exit(trade):log "{trade.tag} closed at {trade.r:0.00}R after {long_entry.triggers} triggers"on session open "new_york":log "New York is open"on day change:triggers = 0on render(canvas):for z in levels:shape = canvas.path()shape.move_to(z.formed_at, z.price)shape.line_to(canvas.last_bar, z.price)shape.stroke(zone_color, width: 1)canvas.text("{z.touched}x", at: (canvas.last_bar, z.price), align: right)plot ema(close, fast) as fast_line, color: if trend_up then green else red, width: 2plot (high + low) / 2, color: gray, style: stepfill ribbon.fast, ribbon.slow, color: green.fade(80)hline 70, style: dashed, color: #22c55emark arrow_up, at: below, when: crosses_above(close, ema(close, fast)), color: greenlabel "Entry", at: (bar.index, high)line from: (bar.index - 20, lowest(low, 20)), to: (bar.index, lowest(low, 20)), extend: rightbox id: "range", from: (bar.index - 10, highest(high, 10)), to: (bar.index, lowest(low, 10)), color: blue.fade(85)bar_color if close > open then green else redbackground red.fade(90), when: regime == Regime.volatileprofile rows: 24, range: session, side: rightfib from: (bar.index - 50, lowest(low, 50)), to: (bar.index, highest(high, 50))dashboard position: top_right, rows: [["Regime", "{regime}"], ["Triggers", "{triggers}"]]Language Tour is a strategy that trades EURUSD and GBPUSD on 15-minute bars. It holds at most 3 open trades (2 per side), reverses on an opposite signal, adds up to 3 entries in the same direction, keeps at least 20 pips between entries, stops for the day after losing 3%, stops trading after a 10% drawdown and trades only during the London and New York sessions.
It uses the indicator "Trend Ribbon" (version 3) as ribbon, with fast set to 10 and slow set to 30.
It uses the library "Quant Toolkit" (version 2) as qt.
You can change 7 inputs: fast (default 20, shown as "Fast length"), source (default close, shown as "Source"), higher_tf (default 4h, shown as "Higher timeframe"), session (default "london"), risk (default 1%), show_zones (default true) and zone_color (default blue.fade(70)).
It defines the constant RISK_CAP as 2%.
It defines swing_strength(len), which returns a number and the action enter_long(size_risk), which returns true or false.
It remembers triggers, last_entry, regime and levels from one bar to the next.
It calculates trend_up as whether the 50-bar EMA of the close is above the 200-bar EMA of the close, distance as (source minus the EMA of source over fast bars) divided by the 14-bar ATR, threshold as 1.5, h4 as higher_tf bars, gold as XAUUSD 15-minute bars, gold_atr as the 14-bar ATR of gold, cov as the data.coverage (symbol EURUSD, bars the range (size 10)), spread_z as the 100-bar z-score of the logarithm of the close of EURUSD divided by the close of GBPUSD, first_hour as whether the time of day is between 08:00 and 09:00, recent_high as intrabar(1m).high.max(), kelly as qt.kelly_fraction(0.55, 1.8), power as 2 to the power of 3 to the power of 2 and 3 more values.
long_setup passes when at least 5 of these 6 conditions are true: trend (trend_up); momentum (the 14-bar RSI is above 55); volume (volume is above 1.5 × the 20-bar SMA of volume); structure (a bullish break of structure); higher_tf_trend (h4.close is above the 50-bar EMA of h4.close); ribbon_up (ribbon.up).
liquidity_grab completes when these steps happen in order within 30 bars: sweep, when the low is below the previous bar's lowest low of the last 20 bars; then reclaim, when the close is above sweep.high; then retest, when the low is at or below reclaim.close and the close is above reclaim.close. It starts over if the close is below sweep.low.
When long_setup passes (on every 4th time), it buys at market, risking risk of the balance, with a stop 1.5 × the 14-bar ATR from the entry and with a target 3R from the entry.
When long_setup passes becomes true (ignoring the first 3 times, at most 2 times per day and waiting at least 30 minutes between actions), it adds 1 to triggers; it also checks whether triggers modulo 4 is 0 and, if so, buys at market, risking 0.5% of the balance, with a stop 1.5 × the 14-bar ATR from the entry and with a target 3R from the entry.
When liquidity_grab completes (between 08:00 and 11:00 Europe/London time), it buys at market, risking 1% of the balance, with a stop at the low of the sweep step minus 3 points, with a target 2R plus 5 pips from the entry and tagged "breakout"; once open, it moves the stop to breakeven at 1R plus 2 pips, closes 30% at 1.5R, closes 30% at 2.5R, trails the stop by the 14-bar ATR once the trade reaches 2R, exits after 48 bars and exits when the close crosses below the 20-bar EMA of the close.
When the close crosses below the 50-bar EMA of the close (waiting at least 5 bars between actions), it sells at market, risking $200, with a stop at the highest high of the last 10 bars plus 2 pips and with a target at the lowest low of the last 50 bars.
When trend_up at some point within the last 5 bars and the close is above the open for 3 bars in a row (on every bar while it holds), it buys with a limit order at the lowest low of the last 5 bars, with a size of 1 lot and expiring after 10 bars; it also buys with a stop order at the high plus 2 pips, risking 0.5% of the balance, with a stop-loss at the low minus 2 pips, with a target 2R from the entry and kept hidden from the broker until it triggers; it also sells GBPUSD at market, with a size of 0.3 lots.
When today's closed profit or loss is below minus 2% of the account balance or the margin level is below 150%, it closes all trades.
When the script starts, it logs "starting on {market.symbol}".
At every bar close, it logs "bar {bar.index}".
When an order fills, it logs "filled {order.size}".
When a trade closes, it logs "{trade.tag} closed at {trade.r:0.00}R after {long_entry.triggers} triggers".
When the New York session opens, it logs "New York is open".
When a new day begins, it sets triggers to 0.
Whenever the chart is drawn, it goes through each z in levels and sets shape to canvas.path(), moves to a point, draws a line segment, outlines the shape and writes "{z.touched}x" on the chart.
On each bar, it checks whether regime is trending and, if so, sets risk_now to 1%; otherwise, if regime is volatile, sets risk_now to 0.5%; otherwise sets risk_now to 0.25%.
On each bar, it goes through each level in levels and checks whether the close is above level.price and, if so, adds 1 to level.touched.
On each bar, it goes through each i in 0 to 50 and checks whether i is above 10 and, if so, stops the loop; it also moves on to the next item.
On each bar, it checks regime: for trending it logs "trending"; for ranging it logs "ranging".
On each bar, it goes through each trade in open trades tagged "breakout" and checks whether trade.r is at or above 3 and the 14-bar RSI is above 75 and, if so, closes trade (50% of it).
On each bar, it goes through each trade in open trades on the long side and moves the stop to trade.entry_price for trade.
On each bar, it cancels pending orders tagged "grid".
On the chart, it plots the EMA of the close over fast bars as fast_line, plots (the high plus the low) divided by 2, shades between ribbon.fast and ribbon.slow, draws a horizontal line at 70, marks arrow up below the bar when the close crosses above the EMA of the close over fast bars, labels "Entry", draws a line, draws a box, colors the bars, shades the background when regime is volatile, draws a volume profile, draws Fibonacci levels and shows a dashboard.
This description may be incomplete because the script has errors.
Tick Scalper
Tick Scalper is a strategy that trades XAUUSD on 1-minute bars, checking its rules on every price change. It holds at most 1 open trade.
inside the bar
strategy "Tick Scalper"market: XAUUSDbars: 1mevaluate: tickmax_open: 1input max_stretch = 2.0when crosses_above(close, vwap()) once per bar cooldown 2 bars:buy risk: 0.5%, stop: 30 points, target: 1.5R, ghost: trueon tick:if not bar.confirmed and close < vwap() - atr(14) * max_stretch:close_all side: longTick Scalper is a strategy that trades XAUUSD on 1-minute bars, checking its rules on every price change. It holds at most 1 open trade.
You can change one input: max_stretch (default 2.0).
When the close crosses above VWAP (at most once per bar and waiting at least 2 bars between actions), it buys at market, risking 0.5% of the balance, with a stop 30 points from the entry, with a target 1.5R from the entry and kept hidden from the broker until it triggers.
On every price change, it checks whether not the bar has closed and the close is below VWAP minus the 14-bar ATR × max_stretch and, if so, closes all long trades.
Bollinger Mean Reversion
Bollinger Mean Reversion is a strategy that trades EURUSD on 1-hour bars. It holds at most 1 open trade and closes its trade on an opposite signal.
pending orders
strategy "Bollinger Mean Reversion"market: EURUSDbars: 1hmax_open: 1opposite: closeinput length = 20input width = 2.0bands = bollinger(close, length, multiplier: width)when crosses_below(close, bands.lower) and rsi(close, 14) < 30:buy limit: bands.lower, risk: 1%, stop: bands.lower - atr(14), target: bands.middle, expires: 5 barswhen crosses_above(close, bands.upper) and rsi(close, 14) > 70:sell limit: bands.upper, risk: 1%, stop: bands.upper + atr(14), target: bands.middle, expires: 5 barsplot bands.upper, color: grayplot bands.middle, color: blueplot bands.lower, color: grayBollinger Mean Reversion is a strategy that trades EURUSD on 1-hour bars. It holds at most 1 open trade and closes its trade on an opposite signal.
You can change 2 inputs: length (default 20) and width (default 2.0).
It calculates bands as the Bollinger Bands (source the close, length length, multiplier width).
When the close crosses below bands.lower and the 14-bar RSI is below 30, it buys with a limit order at bands.lower, risking 1% of the balance, with a stop at bands.lower minus the 14-bar ATR, with a target at bands.middle and expiring after 5 bars.
When the close crosses above bands.upper and the 14-bar RSI is above 70, it sells with a limit order at bands.upper, risking 1% of the balance, with a stop at bands.upper plus the 14-bar ATR, with a target at bands.middle and expiring after 5 bars.
On the chart, it plots bands.upper, bands.middle and bands.lower.
London Open Breakout
London Open Breakout is a strategy that trades GBPUSD on 5-minute bars. It holds at most 1 open trade and trades only during the London session.
sessions and timepending orders
strategy "London Open Breakout"market: GBPUSDbars: 5mmax_open: 1trade_only: within sessions londoninput range_bars = 12input buffer = 2 pipsrange_high = highest(high, range_bars)range_low = lowest(low, range_bars)when time_of_day == 08:00 max 1 per day:buy stop: range_high + buffer, risk: 0.5%, stop_loss: range_low - buffer, target: 2R, expires: 24 bars, tag: "breakout"sell stop: range_low - buffer, risk: 0.5%, stop_loss: range_high + buffer, target: 2R, expires: 24 bars, tag: "breakout"when time_of_day >= 16:00:close_all tag: "breakout"cancel orders.pending(tag: "breakout")plot range_high, color: green, style: stepplot range_low, color: red, style: stepLondon Open Breakout is a strategy that trades GBPUSD on 5-minute bars. It holds at most 1 open trade and trades only during the London session.
You can change 2 inputs: range_bars (default 12) and buffer (default 2 pips).
It calculates range_high as the highest high of the last range_bars bars and range_low as the lowest low of the last range_bars bars.
When the time of day is 08:00 (at most 1 time per day), it buys with a stop order at range_high plus buffer, risking 0.5% of the balance, with a stop-loss at range_low minus buffer, with a target 2R from the entry, expiring after 24 bars and tagged "breakout"; it also sells with a stop order at range_low minus buffer, risking 0.5% of the balance, with a stop-loss at range_high plus buffer, with a target 2R from the entry, expiring after 24 bars and tagged "breakout".
When the time of day is at or above 16:00, it closes all trades tagged "breakout" and cancels pending orders tagged "breakout".
On the chart, it plots range_high and range_low.
Supertrend Trend Rider
Supertrend Trend Rider is a strategy that trades XAUUSD on 1-hour bars. It takes long trades only, adds up to 2 entries in the same direction and keep
trade management
strategy "Supertrend Trend Rider"market: XAUUSDbars: 1hdirection: longpyramiding: 2min_distance: 300 pointsinput atr_length = 10input factor = 3.0trend = supertrend(atr_length, factor)when trend.direction == 1 and crosses_above(close, ema(close, 21)):buy risk: 0.75%, stop: trend.line, target: 4R, tag: "rider":breakeven at: 1R, offset: 50 pointspartial 25% at: 2Rtrail by: atr(14) * 2, after: 2Rexit when: trend.direction == -1plot trend.line, color: if trend.direction == 1 then green else red, width: 2Supertrend Trend Rider is a strategy that trades XAUUSD on 1-hour bars. It takes long trades only, adds up to 2 entries in the same direction and keeps at least 300 points between entries.
You can change 2 inputs: atr_length (default 10) and factor (default 3.0).
It calculates trend as the Supertrend (length atr_length, multiplier factor).
When trend.direction is 1 and the close crosses above the 21-bar EMA of the close, it buys at market, risking 0.75% of the balance, with a stop at trend.line, with a target 4R from the entry and tagged "rider"; once open, it moves the stop to breakeven at 1R plus 50 points, closes 25% at 2R, trails the stop by 2 × the 14-bar ATR once the trade reaches 2R and exits when trend.direction is -1.
On the chart, it plots trend.line.
Grid Accumulator
Grid Accumulator is a strategy that trades EURUSD on 15-minute bars. It holds at most 5 open trades, takes long trades only and adds up to 5 entries i
statepending orders
strategy "Grid Accumulator"market: EURUSDbars: 15mdirection: longpyramiding: 5max_open: 5input levels = 5input spacing = 15 pipsinput grid_size = 0.1 lotsstate anchor: price = nawhen trades.open(tag: "grid").len == 0 and orders.pending(tag: "grid").len == 0 and close > ema(close, 200):anchor = closefor step in 1..levels:buy limit: close - spacing * step, size: grid_size, stop: close - spacing * (levels + 2), target: close + spacing, tag: "grid"when close < anchor - spacing * (levels + 2):close_all tag: "grid"cancel orders.pending(tag: "grid")Grid Accumulator is a strategy that trades EURUSD on 15-minute bars. It holds at most 5 open trades, takes long trades only and adds up to 5 entries in the same direction.
You can change 3 inputs: levels (default 5), spacing (default 15 pips) and grid_size (default 0.1 lots).
It remembers anchor from one bar to the next.
When the number of open trades tagged "grid" is 0 and the number of pending orders tagged "grid" is 0 and the close is above the 200-bar EMA of the close, it sets anchor to the close; it also goes through each step in 1 to levels and buys with a limit order at the close minus spacing × step, with a size of grid_size, with a stop at the close minus spacing × (levels plus 2), with a target at the close plus spacing and tagged "grid".
When the close is below anchor minus spacing × (levels plus 2), it closes all trades tagged "grid" and cancels pending orders tagged "grid".
Quiet Hours Trend
Quiet Hours Trend is a strategy that trades USDJPY on 15-minute bars. It holds at most 2 open trades (1 per side) and never risks more than 2% across
sessions and time
strategy "Quiet Hours Trend"market: USDJPYbars: 15mmax_open: 2max_open_per_side: 1max_total_risk: 2%input risk = 0.5%fast_line = ema(close, 9)slow_line = ema(close, 34)when crosses_above(fast_line, slow_line) from 01:00 to 11:00 UTC skip first 1 max 3 per session cooldown 30m:buy risk: risk, stop: 20 pips, target: 1.8Rwhen crosses_below(fast_line, slow_line) from 01:00 to 11:00 UTC skip first 1 max 3 per session cooldown 30m:sell risk: risk, stop: 20 pips, target: 1.8Rwhen hour >= 20:close_allQuiet Hours Trend is a strategy that trades USDJPY on 15-minute bars. It holds at most 2 open trades (1 per side) and never risks more than 2% across open trades.
You can change one input: risk (default 0.5%).
It calculates fast_line as the 9-bar EMA of the close and slow_line as the 34-bar EMA of the close.
When fast_line crosses above slow_line (between 01:00 and 11:00 UTC, ignoring the first time, at most 3 times per session and waiting at least 30 minutes between actions), it buys at market, risking risk of the balance, with a stop 20 pips from the entry and with a target 1.8R from the entry.
When fast_line crosses below slow_line (between 01:00 and 11:00 UTC, ignoring the first time, at most 3 times per session and waiting at least 30 minutes between actions), it sells at market, risking risk of the balance, with a stop 20 pips from the entry and with a target 1.8R from the entry.
When the hour is at or above 20, it closes all trades.
Heikin-Ashi Trend
Heikin-Ashi Trend is a strategy that trades BTCUSD on 1-hour Heikin-Ashi bars. It takes long trades only.
renko, heikin ashi, x-ray
strategy "Heikin-Ashi Trend"market: BTCUSDbars: heikin_ashi(1h)direction: longinput slow = 50when close > open and close[1] > open[1] and close > ema(close, slow):buy risk: 1%, stop: lowest(low, 5), target: 3Rwhen close < open and close[1] < open[1]:close_allHeikin-Ashi Trend is a strategy that trades BTCUSD on 1-hour Heikin-Ashi bars. It takes long trades only.
You can change one input: slow (default 50).
When the close is above the open and the previous bar's close is above the previous bar's open and the close is above the EMA of the close over slow bars, it buys at market, risking 1% of the balance, with a stop at the lowest low of the last 5 bars and with a target 3R from the entry.
When the close is below the open and the previous bar's close is below the previous bar's open, it closes all trades.
Renko Pullback
Renko Pullback is a strategy that trades US500 on 5-point renko bars. It holds at most 1 open trade.
sequencerenko, heikin ashi, x-ray
strategy "Renko Pullback"market: US500bars: renko(5)max_open: 1sequence pullback within 12 bars:step impulse: close > open and close[1] > open[1] and close[2] > open[2]step dip: close < openstep resume: close > open and close > dip.highreset_if: close < impulse.lowwhen pullback.completed:buy risk: 1%, stop: pullback.dip.low - 2 points, target: 2RRenko Pullback is a strategy that trades US500 on 5-point renko bars. It holds at most 1 open trade.
pullback completes when these steps happen in order within 12 bars: impulse, when the close is above the open and the previous bar's close is above the previous bar's open and the close 2 bars ago is above the open 2 bars ago; then dip, when the close is below the open; then resume, when the close is above the open and the close is above dip.high. It starts over if the close is below impulse.low.
When pullback completes, it buys at market, risking 1% of the balance, with a stop at the low of the dip step minus 2 points and with a target 2R from the entry.
Momentum Rotation
Momentum Rotation is a strategy that trades EURUSD, GBPUSD, USDJPY and AUDUSD on 4-hour bars. It holds at most 1 open trade.
several markets
strategy "Momentum Rotation"markets: EURUSD, GBPUSD, USDJPY, AUDUSDbars: 4hmax_open: 1input lookback = 30input threshold = 2%when bar.confirmed and trades.open(tag: "rotation").len == 0:for s in [EURUSD, GBPUSD, USDJPY, AUDUSD]:if roc(close_of(s), lookback) > threshold:buy market: s, risk: 0.5%, stop: 30 pips, target: 2R, tag: "rotation"breakwhen trades.open(tag: "rotation").len > 0 and roc(close, lookback) < 0:close_all tag: "rotation"Momentum Rotation is a strategy that trades EURUSD, GBPUSD, USDJPY and AUDUSD on 4-hour bars. It holds at most 1 open trade.
You can change 2 inputs: lookback (default 30) and threshold (default 2%).
When the bar has closed and the number of open trades tagged "rotation" is 0, it goes through each s in EURUSD, GBPUSD, USDJPY and AUDUSD and checks whether the rate of change of the close of s over lookback bars is above threshold and, if so, buys s at market, risking 0.5% of the balance, with a stop 30 pips from the entry, with a target 2R from the entry and tagged "rotation"; it also stops the loop.
When the number of open trades tagged "rotation" is above 0 and the rate of change over lookback bars is below 0, it closes all trades tagged "rotation".
Kelly Sized Breakout
Kelly Sized Breakout is a strategy that trades XAUUSD on 30-minute bars. It holds at most 1 open trade.
uses a library or indicatoraccount and history
strategy "Kelly Sized Breakout"market: XAUUSDbars: 30mmax_open: 1use library "Quant Toolkit" v2 as qtinput max_risk = 2%stats = history.last(90d)edge = qt.kelly_fraction(stats.win_rate / 100%, 2.0)risk_now = clamp(edge * 50%, 0.25%, max_risk)breakout = crosses_above(close, highest(high, 20)[1])when breakout and stats.count >= 20:buy risk: risk_now, stop: atr(14) * 1.5, target: 2Rwhen breakout and stats.count < 20:buy risk: 0.25%, stop: atr(14) * 1.5, target: 2RKelly Sized Breakout is a strategy that trades XAUUSD on 30-minute bars. It holds at most 1 open trade.
It uses the library "Quant Toolkit" (version 2) as qt.
You can change one input: max_risk (default 2%).
It calculates stats as closed-trade results over the last 90 days, edge as qt.kelly_fraction(stats.win_rate / 100%, 2.0), risk_now as edge × 50% kept between 0.25% and max_risk and breakout as whether the close crosses above the previous bar's highest high of the last 20 bars.
When breakout and stats.count is at or above 20, it buys at market, risking risk_now of the balance, with a stop 1.5 × the 14-bar ATR from the entry and with a target 2R from the entry.
When breakout and stats.count is below 20, it buys at market, risking 0.25% of the balance, with a stop 1.5 × the 14-bar ATR from the entry and with a target 2R from the entry.
This description may be incomplete because the script has errors.
Candle Reversal
Candle Reversal is a strategy that trades EURUSD on 1-hour bars. It holds at most 1 open trade.
confirmations
strategy "Candle Reversal"market: EURUSDbars: 1hmax_open: 1confirmations reversal_long:pattern: hammer() or engulfing(direction: up) or pin_bar()oversold: rsi(close, 14) < 35support: low <= lowest(low, 50)[1] + atr(14) * 0.5require: at least 2when reversal_long.passed:buy risk: 1%, stop: low - atr(14) * 0.5, target: 2.5RCandle Reversal is a strategy that trades EURUSD on 1-hour bars. It holds at most 1 open trade.
reversal_long passes when at least 2 of these 3 conditions are true: pattern (a hammer pattern or a bullish engulfing pattern or a pin bar pattern); oversold (the 14-bar RSI is below 35); support (the low is at or below the previous bar's lowest low of the last 50 bars plus 0.5 × the 14-bar ATR).
When reversal_long passes, it buys at market, risking 1% of the balance, with a stop at the low minus 0.5 × the 14-bar ATR and with a target 2.5R from the entry.
Squeeze Breakout
Squeeze Breakout is a strategy that trades NAS100 on 15-minute bars. It holds at most 1 open trade and waits 200 bars before trading.
trade management
strategy "Squeeze Breakout"market: NAS100bars: 15mmax_open: 1warmup: 200bb = bollinger(close, 20, multiplier: 2.0)kc = keltner(close, 20, multiplier: 1.5)squeeze_on = bb.upper < kc.upper and bb.lower > kc.lowerreleased = ends(squeeze_on)momentum_up = linreg_slope(close, 20) > 0when released and momentum_up and close > kc.upper:buy risk: 1%, stop: kc.middle, target: 2R:trail by: atr(14) * 2, after: 1Rwhen released and not momentum_up and close < kc.lower:sell risk: 1%, stop: kc.middle, target: 2R:trail by: atr(14) * 2, after: 1Rbackground orange.fade(90), when: squeeze_onSqueeze Breakout is a strategy that trades NAS100 on 15-minute bars. It holds at most 1 open trade and waits 200 bars before trading.
It calculates bb as the Bollinger Bands (source the close, length 20, multiplier 2.0), kc as the Keltner Channels (source the close, length 20, multiplier 1.5), squeeze_on as whether bb.upper is below kc.upper and bb.lower is above kc.lower, released as whether squeeze_on stops being true and momentum_up as whether the linreg slope (source the close, length 20) is above 0.
When released and momentum_up and the close is above kc.upper, it buys at market, risking 1% of the balance, with a stop at kc.middle and with a target 2R from the entry; once open, it trails the stop by 2 × the 14-bar ATR once the trade reaches 1R.
When released and not momentum_up and the close is below kc.lower, it sells at market, risking 1% of the balance, with a stop at kc.middle and with a target 2R from the entry; once open, it trails the stop by 2 × the 14-bar ATR once the trade reaches 1R.
On the chart, it shades the background when squeeze_on.
DMI Trend Filter
DMI Trend Filter is a strategy that trades EURJPY on 1-hour bars. It holds at most 1 open trade and reverses on an opposite signal.
core language
strategy "DMI Trend Filter"market: EURJPYbars: 1hmax_open: 1opposite: reverseinput adx_floor = 25d = dmi(14)trending = d.adx > adx_floorwhen trending and crosses_above(d.plus, d.minus):buy risk: 1%, stop: psar(), target: 3Rwhen trending and crosses_above(d.minus, d.plus):sell risk: 1%, stop: psar(), target: 3Rplot psar(), style: circles, color: yellowDMI Trend Filter is a strategy that trades EURJPY on 1-hour bars. It holds at most 1 open trade and reverses on an opposite signal.
You can change one input: adx_floor (default 25).
It calculates d as the directional movement (length 14) and trending as whether d.adx is above adx_floor.
When trending and d.plus crosses above d.minus, it buys at market, risking 1% of the balance, with a stop at the parabolic SAR and with a target 3R from the entry.
When trending and d.minus crosses above d.plus, it sells at market, risking 1% of the balance, with a stop at the parabolic SAR and with a target 3R from the entry.
On the chart, it plots the parabolic SAR.
Turtle Channels
Turtle Channels is a strategy that trades XAUUSD on daily bars. It adds up to 4 entries in the same direction, keeps at least 500 points between entri
core language
strategy "Turtle Channels"market: XAUUSDbars: 1dpyramiding: 4min_distance: 500 pointsmax_drawdown: 20%input entry_length = 20input exit_length = 10input unit_risk = 0.5%entry_channel = donchian(entry_length)exit_channel = donchian(exit_length)when crosses_above(close, entry_channel.upper[1]):buy risk: unit_risk, stop: atr(20) * 2, tag: "turtle"when crosses_below(close, exit_channel.lower[1]):close_all side: long, tag: "turtle"plot entry_channel.upper, color: greenplot exit_channel.lower, color: redTurtle Channels is a strategy that trades XAUUSD on daily bars. It adds up to 4 entries in the same direction, keeps at least 500 points between entries and stops trading after a 20% drawdown.
You can change 3 inputs: entry_length (default 20), exit_length (default 10) and unit_risk (default 0.5%).
It calculates entry_channel as the Donchian Channels (length entry_length) and exit_channel as the Donchian Channels (length exit_length).
When the close crosses above the previous bar's entry_channel.upper, it buys at market, risking unit_risk of the balance, with a stop 2 × the 20-bar ATR from the entry and tagged "turtle".
When the close crosses below the previous bar's exit_channel.lower, it closes all long trades tagged "turtle".
On the chart, it plots entry_channel.upper and exit_channel.lower.
Smart Money Pullback
Smart Money Pullback is a strategy that trades GBPJPY on 15-minute bars. It holds at most 1 open trade and trades only during the London and New York
confirmationstrade managementsmart moneysessions and time
strategy "Smart Money Pullback"market: GBPJPYbars: 15mmax_open: 1trade_only: within sessions london, new_yorkpd = premium_discount(80)entry_zone = optimal_trade_entry(swing_length: 5)confirmations smc_long:choch: was(change_of_character(direction: up), within: 20 bars)swept: was(liquidity_sweeps(30), within: 10 bars)in_discount: close < pd.equilibriumat_entry_zone: low <= entry_zone.top and close >= entry_zone.bottomrequire: allwhen smc_long.passed cooldown 10 bars:buy risk: 0.75%, stop: entry_zone.bottom - atr(14) * 0.25, target: pd.premium.bottom:breakeven at: 1.5Rbox id: "premium", from: (bar.index - 80, pd.premium.top), to: (bar.index, pd.premium.bottom), color: red.fade(90)box id: "discount", from: (bar.index - 80, pd.discount.top), to: (bar.index, pd.discount.bottom), color: green.fade(90)Smart Money Pullback is a strategy that trades GBPJPY on 15-minute bars. It holds at most 1 open trade and trades only during the London and New York sessions.
It calculates pd as the premium and discount zones (length 80) and entry_zone as the optimal trade entry zone (swing length 5).
smc_long passes when all of these 4 conditions are true: choch (the change of character (direction up) at some point within the last 20 bars); swept (the liquidity sweeps (length 30) at some point within the last 10 bars); in_discount (the close is below pd.equilibrium); at_entry_zone (the low is at or below entry_zone.top and the close is at or above entry_zone.bottom).
When smc_long passes (waiting at least 10 bars between actions), it buys at market, risking 0.75% of the balance, with a stop at entry_zone.bottom minus 0.25 × the 14-bar ATR and with a target at pd.premium.bottom; once open, it moves the stop to breakeven at 1.5R.
On the chart, it draws a box and draws a box.
Regression Spread
Regression Spread is a strategy that trades AUDUSD and NZDUSD on 1-hour bars. It holds at most 2 open trades.
several marketsstatistics and matrices
strategy "Regression Spread"markets: AUDUSD, NZDUSDbars: 1hmax_open: 2input lookback = 250input entry_z = 2.2aud = bars(AUDUSD)nzd = bars(NZDUSD)model = ols(aud.close, [nzd.close], lookback)z = zscore(model.residual, lookback)fit_ok = model.r_squared > 0.6flat = trades.open(tag: "spread").len == 0when fit_ok and z > entry_z and flat:sell market: AUDUSD, risk: 0.5%, stop: atr(14, on: aud) * 3, tag: "spread"buy market: NZDUSD, risk: 0.5%, stop: atr(14, on: nzd) * 3, tag: "spread"when fit_ok and z < -entry_z and flat:buy market: AUDUSD, risk: 0.5%, stop: atr(14, on: aud) * 3, tag: "spread"sell market: NZDUSD, risk: 0.5%, stop: atr(14, on: nzd) * 3, tag: "spread"when abs(z) < 0.25 or not fit_ok:close_all tag: "spread"Regression Spread is a strategy that trades AUDUSD and NZDUSD on 1-hour bars. It holds at most 2 open trades.
You can change 2 inputs: lookback (default 250) and entry_z (default 2.2).
It calculates aud as AUDUSD bars, nzd as NZDUSD bars, model as the ols (target aud.close, factors nzd.close, length lookback), z as the z-score of model.residual over lookback bars, fit_ok as whether model.r_squared is above 0.6 and flat as whether the number of open trades tagged "spread" is 0.
When fit_ok and z is above entry_z and flat, it sells AUDUSD at market, risking 0.5% of the balance, with a stop 3 × the 14-bar ATR of aud from the entry and tagged "spread"; it also buys NZDUSD at market, risking 0.5% of the balance, with a stop 3 × the 14-bar ATR of nzd from the entry and tagged "spread".
When fit_ok and z is below -entry_z and flat, it buys AUDUSD at market, risking 0.5% of the balance, with a stop 3 × the 14-bar ATR of aud from the entry and tagged "spread"; it also sells NZDUSD at market, risking 0.5% of the balance, with a stop 3 × the 14-bar ATR of nzd from the entry and tagged "spread".
When the absolute value of z is below 0.25 or not fit_ok, it closes all trades tagged "spread".
Coverage-Aware Scalper
Coverage-Aware Scalper is a strategy that trades EURUSD on 10-tick range bars. It holds at most 1 open trade.
renko, heikin ashi, x-ray
strategy "Coverage-Aware Scalper"market: EURUSDbars: range(10)max_open: 1coverage = data.coverage(EURUSD, bars: range(10))enough_history = coverage.exact and coverage.bars >= 5000when enough_history and crosses_above(close, ema(close, 34)):buy risk: 0.5%, stop: 8 pips, target: 1.5Rwhen enough_history and crosses_below(close, ema(close, 34)):sell risk: 0.5%, stop: 8 pips, target: 1.5Ron start:log "Data from {coverage.from} to {coverage.to}, {coverage.bars} bars from {coverage.source}"Coverage-Aware Scalper is a strategy that trades EURUSD on 10-tick range bars. It holds at most 1 open trade.
It calculates coverage as the data.coverage (symbol EURUSD, bars the range (size 10)) and enough_history as whether coverage.exact and coverage.bars is at or above 5000.
When enough_history and the close crosses above the 34-bar EMA of the close, it buys at market, risking 0.5% of the balance, with a stop 8 pips from the entry and with a target 1.5R from the entry.
When enough_history and the close crosses below the 34-bar EMA of the close, it sells at market, risking 0.5% of the balance, with a stop 8 pips from the entry and with a target 1.5R from the entry.
When the script starts, it logs "Data from {coverage.from} to {coverage.to}, {coverage.bars} bars from {coverage.source}".
Performance Throttle
Performance Throttle is a strategy that trades EURUSD on 30-minute bars. It holds at most 1 open trade and stops for the day after losing $500.
account and history
strategy "Performance Throttle"market: EURUSDbars: 30mmax_open: 1max_daily_loss: $500input base_risk = 1%week = history.this_weekrecent_r = trades.closed(tag: "trend").keep_last(10).map(t => t.r).sum()risk_scale = if week.profit_factor < 1 or recent_r < -3 then 0.5 else 1.0when crosses_above(ema(close, 20), ema(close, 50)):buy risk: base_risk * risk_scale, stop: 25 pips, target: 2R, tag: "trend"on exit(trade):if trade.reason == "stop" and trade.r <= -1:log "Stopped out; recent R total is {recent_r:0.0}"Performance Throttle is a strategy that trades EURUSD on 30-minute bars. It holds at most 1 open trade and stops for the day after losing $500.
You can change one input: base_risk (default 1%).
It calculates week as history.this_week, recent_r as trades.closed(tag: "trend").keep_last(10).map(t => t.r).sum() and risk_scale as 0.5 when week.profit_factor is below 1 or recent_r is below -3, otherwise 1.0.
When the 20-bar EMA of the close crosses above the 50-bar EMA of the close, it buys at market, risking base_risk × risk_scale of the balance, with a stop 25 pips from the entry, with a target 2R from the entry and tagged "trend".
When a trade closes, it checks whether trade.reason is "stop" and trade.r is at or below -1 and, if so, logs "Stopped out; recent R total is {recent_r:0.0}".
Hedged Breakout
Hedged Breakout is a strategy that trades EURUSD on 1-hour bars. It holds at most 2 open trades (1 per side) and hedges on an opposite signal.
core language
strategy "Hedged Breakout"market: EURUSDbars: 1hopposite: hedgemax_open: 2max_open_per_side: 1when crosses_above(close, highest(high, 24)[1]):buy risk: 0.5%, stop: 40 pips, target: 80 pips, tag: "up"when crosses_below(close, lowest(low, 24)[1]):sell risk: 0.5%, stop: 40 pips, target: 80 pips, tag: "down"for trade in trades.open():if trade.bars_open >= 48:close tradeHedged Breakout is a strategy that trades EURUSD on 1-hour bars. It holds at most 2 open trades (1 per side) and hedges on an opposite signal.
When the close crosses above the previous bar's highest high of the last 24 bars, it buys at market, risking 0.5% of the balance, with a stop 40 pips from the entry, with a target 80 pips from the entry and tagged "up".
When the close crosses below the previous bar's lowest low of the last 24 bars, it sells at market, risking 0.5% of the balance, with a stop 40 pips from the entry, with a target 80 pips from the entry and tagged "down".
On each bar, it goes through each trade in open trades and checks whether trade.bars_open is at or above 48 and, if so, closes trade.
Active Trade Manager
Active Trade Manager is a strategy that trades XAUUSD on 5-minute bars. It holds at most 1 open trade.
core language
strategy "Active Trade Manager"market: XAUUSDbars: 5mmax_open: 1when crosses_above(close, vwap()) and rsi(close, 7) > 55:buy risk: 1%, stop: 2 * atr(14), target: 3R, tag: "managed"for trade in trades.open(tag: "managed"):if trade.r >= 1 and trade.stop < trade.entry_price:modify trade, stop: trade.entry_priceelif trade.r >= 2:close trade, size: 50%modify trade, target: trade.entry_price + atr(14) * 6Active Trade Manager is a strategy that trades XAUUSD on 5-minute bars. It holds at most 1 open trade.
When the close crosses above VWAP and the 7-bar RSI is above 55, it buys at market, risking 1% of the balance, with a stop 2 × the 14-bar ATR from the entry, with a target 3R from the entry and tagged "managed".
On each bar, it goes through each trade in open trades tagged "managed" and checks whether trade.r is at or above 1 and trade.stop is below trade.entry_price and, if so, moves the stop to trade.entry_price for trade; otherwise, if trade.r is at or above 2, closes trade (50% of it) and moves the target to trade.entry_price plus 6 × the 14-bar ATR for trade.
Pyramid Momentum
Pyramid Momentum is a strategy that trades NAS100 on 1-hour bars. It takes long trades only, adds up to 3 entries in the same direction, keeps at leas
state
strategy "Pyramid Momentum"market: NAS100bars: 1hdirection: longpyramiding: 3min_distance: 100 pointsmax_total_risk: 3%state adds = 0when crosses_above(close, ema(close, 50)) and trades.open().len == 0:buy risk: 1%, stop: 3 * atr(14), tag: "core"adds = 0when trades.open().len > 0 and adds < 2 and close > trades.last().entry_price + 2 * atr(14):buy risk: 0.5%, stop: 3 * atr(14), tag: "add"adds += 1when crosses_below(close, ema(close, 50)):close_allPyramid Momentum is a strategy that trades NAS100 on 1-hour bars. It takes long trades only, adds up to 3 entries in the same direction, keeps at least 100 points between entries and never risks more than 3% across open trades.
It remembers adds from one bar to the next.
When the close crosses above the 50-bar EMA of the close and the number of open trades is 0, it buys at market, risking 1% of the balance, with a stop 3 × the 14-bar ATR from the entry and tagged "core"; it also sets adds to 0.
When the number of open trades is above 0 and adds is below 2 and the close is above trades.last().entry_price plus 2 × the 14-bar ATR, it buys at market, risking 0.5% of the balance, with a stop 3 × the 14-bar ATR from the entry and tagged "add"; it also adds 1 to adds.
When the close crosses below the 50-bar EMA of the close, it closes all trades.
Hurst Regime Switch
Hurst Regime Switch is a strategy that trades EURUSD on 4-hour bars. It holds at most 1 open trade.
statistics and matrices
strategy "Hurst Regime Switch"market: EURUSDbars: 4hmax_open: 1input window = 200h = hurst(close, window)trending = h > 0.55mean_reverting = h < 0.45z = zscore(close, 50)when trending and crosses_above(close, kama(close, 20)):buy risk: 1%, stop: 2 * atr(14), target: 3R, tag: "trend"when mean_reverting and z < -2:buy risk: 0.5%, stop: 1.5 * atr(14), target: mean(close, 50), tag: "revert"when mean_reverting and z > 2:sell risk: 0.5%, stop: 1.5 * atr(14), target: mean(close, 50), tag: "revert"Hurst Regime Switch is a strategy that trades EURUSD on 4-hour bars. It holds at most 1 open trade.
You can change one input: window (default 200).
It calculates h as the hurst (source the close, length window), trending as whether h is above 0.55, mean_reverting as whether h is below 0.45 and z as the 50-bar z-score of the close.
When trending and the close crosses above the 20-bar KAMA of the close, it buys at market, risking 1% of the balance, with a stop 2 × the 14-bar ATR from the entry, with a target 3R from the entry and tagged "trend".
When mean_reverting and z is below -2, it buys at market, risking 0.5% of the balance, with a stop 1.5 × the 14-bar ATR from the entry, with a target at the 50-bar average of the close and tagged "revert".
When mean_reverting and z is above 2, it sells at market, risking 0.5% of the balance, with a stop 1.5 × the 14-bar ATR from the entry, with a target at the 50-bar average of the close and tagged "revert".
Aroon Vortex Confirmation
Aroon Vortex Confirmation is a strategy that trades GBPUSD on 30-minute bars. It holds at most 1 open trade.
confirmations
strategy "Aroon Vortex Confirmation"market: GBPUSDbars: 30mmax_open: 1confirmations bull:aroon_up: aroon(25).up > 70vortex_up: vortex(14).plus > vortex(14).minuscmf_positive: cmf(20) > 0choppiness_low: choppiness(14) < 50require: at least 3when bull.passed and not bull.passed[1]:buy risk: 1%, stop: swing_low(5, 5), target: 2RAroon Vortex Confirmation is a strategy that trades GBPUSD on 30-minute bars. It holds at most 1 open trade.
bull passes when at least 3 of these 4 conditions are true: aroon_up (the up of the Aroon (length 25) is above 70); vortex_up (the plus of the Vortex (length 14) is above the minus of the Vortex (length 14)); cmf_positive (the Chaikin money flow (length 20) is above 0); choppiness_low (the choppiness index (length 14) is below 50).
When bull passes and not the previous bar's bull passes, it buys at market, risking 1% of the balance, with a stop at the last swing low (left 5, right 5) and with a target 2R from the entry.
ALMA Envelope Reversion
ALMA Envelope Reversion is a strategy that trades USDCAD on 15-minute bars. It holds at most 1 open trade.
pending orders
strategy "ALMA Envelope Reversion"market: USDCADbars: 15mmax_open: 1input envelope_width = 0.4%env = envelope(alma(close, 21), 21, percent: envelope_width)when crosses_below(close, env.lower) and cci(hlc3, 20) < -150:buy limit: env.lower, risk: 0.5%, stop: env.lower - 1.5 * atr(14), target: env.middle, expires: 4 barswhen crosses_above(close, env.upper) and cci(hlc3, 20) > 150:sell limit: env.upper, risk: 0.5%, stop: env.upper + 1.5 * atr(14), target: env.middle, expires: 4 barschannel upper: env.upper, lower: env.lowerALMA Envelope Reversion is a strategy that trades USDCAD on 15-minute bars. It holds at most 1 open trade.
You can change one input: envelope_width (default 0.4%).
It calculates env as the moving average envelope (source the 21-bar ALMA of the close, length 21, percent envelope_width).
When the close crosses below env.lower and the 20-bar CCI of the typical price is below -150, it buys with a limit order at env.lower, risking 0.5% of the balance, with a stop at env.lower minus 1.5 × the 14-bar ATR, with a target at env.middle and expiring after 4 bars.
When the close crosses above env.upper and the 20-bar CCI of the typical price is above 150, it sells with a limit order at env.upper, risking 0.5% of the balance, with a stop at env.upper plus 1.5 × the 14-bar ATR, with a target at env.middle and expiring after 4 bars.
On the chart, it draws a channel.
Awesome Oscillator Saucer
Awesome Oscillator Saucer is a strategy that trades EURGBP on 1-hour bars. It holds at most 1 open trade.
sequence
strategy "Awesome Oscillator Saucer"market: EURGBPbars: 1hmax_open: 1ao = awesome_osc()sequence saucer within 5 bars:step first_red: ao > 0 and ao < ao[1]step second_red: ao > 0 and ao < ao[1]step green_bar: ao > 0 and ao > ao[1]reset_if: ao < 0when saucer.completed:buy risk: 0.75%, stop: saucer.first_red.low, target: 2RAwesome Oscillator Saucer is a strategy that trades EURGBP on 1-hour bars. It holds at most 1 open trade.
It calculates ao as the awesome oscillator.
saucer completes when these steps happen in order within 5 bars: first_red, when ao is above 0 and ao is below the previous bar's ao; then second_red, when ao is above 0 and ao is below the previous bar's ao; then green_bar, when ao is above 0 and ao is above the previous bar's ao. It starts over if ao is below 0.
When saucer completes, it buys at market, risking 0.75% of the balance, with a stop at the low of the first_red step and with a target 2R from the entry.
State Machine Momentum
State Machine Momentum is a strategy that trades EURUSD on 15-minute bars. It holds at most 1 open trade and stops for the day after losing 2%.
tables and dashboardsstatefunctions and types
strategy "State Machine Momentum"market: EURUSDbars: 15mmax_open: 1max_daily_loss: 2%enum Phase: waiting, armed, in_trade, coolingstate phase = Phase.waitingstate armed_at = 0state entries = 0state exit_prices: map<string, price> = []action fn enter(reason: string) -> bool:buy risk: 0.5%, stop: 1.5 * atr(14), target: 2R, tag: reasonreturn truematch phase:Phase.waiting:if rsi(close, 14) < 35:phase = Phase.armedarmed_at = bar.indexPhase.armed:if crosses_above(rsi(close, 14), 40):phase = Phase.in_tradeelif bar.index - armed_at > 20:phase = Phase.waitingPhase.in_trade:if trades.open().len == 0 and bar.index - armed_at > 1:phase = Phase.coolingarmed_at = bar.indexPhase.cooling:if bar.index - armed_at > 10:phase = Phase.waitingwhen starts(phase == Phase.in_trade):enter("state machine")entries += 1on exit(trade):exit_prices.set(trade.tag, trade.exit_price)dashboard position: top_left, rows: [["Phase", "{phase}"], ["Entries", "{entries}"]]State Machine Momentum is a strategy that trades EURUSD on 15-minute bars. It holds at most 1 open trade and stops for the day after losing 2%.
It defines the action enter(reason), which returns true or false.
It remembers phase, armed_at, entries and exit_prices from one bar to the next.
When phase is in trade becomes true, it runs enter and adds 1 to entries.
When a trade closes, it works out exit_prices.set(trade.tag, trade.exit_price).
On each bar, it checks phase: for waiting it checks whether the 14-bar RSI is below 35 and, if so, sets phase to armed and sets armed_at to the bar number; for armed it checks whether the 14-bar RSI crosses above 40 and, if so, sets phase to in trade; otherwise, if the bar number minus armed_at is above 20, sets phase to waiting; for in trade it checks whether the number of open trades is 0 and the bar number minus armed_at is above 1 and, if so, sets phase to cooling and sets armed_at to the bar number; for cooling it checks whether the bar number minus armed_at is above 10 and, if so, sets phase to waiting.
On the chart, it shows a dashboard.
Trend Ribbon
Trend Ribbon is an indicator drawn over the price chart.
core language
indicator "Trend Ribbon"pane: priceinput fast = 20input slow = 50fast_line = ema(close, fast)slow_line = ema(close, slow)export up = fast_line > slow_lineexport strength = (fast_line - slow_line) / atr(14)plot fast_line as fast, color: if up then green else redplot slow_line as slow, color: grayTrend Ribbon is an indicator drawn over the price chart.
You can change 2 inputs: fast (default 20) and slow (default 50).
It calculates fast_line as the EMA of the close over fast bars, slow_line as the EMA of the close over slow bars, up as whether fast_line is above slow_line and strength as (fast_line minus slow_line) divided by the 14-bar ATR. It shares up and strength with scripts that use it.
On the chart, it plots fast_line as fast and plots slow_line as slow.
Live Order Blocks
Live Order Blocks is an indicator drawn over the price chart.
statesmart money
indicator "Live Order Blocks"pane: priceinput keep = 10state zones: list<Zone> = []for ob in order_blocks(new_only: true):zones.push(ob)zones = zones.filter(z => not z.mitigated).keep_last(keep)for z in zones:box id: z.id, from: (z.formed_bar, z.top), to: (bar.index, z.bottom), color: if z.bullish then green.fade(80) else red.fade(80)Live Order Blocks is an indicator drawn over the price chart.
You can change one input: keep (default 10).
It remembers zones from one bar to the next.
It calculates zones as zones.filter(z => not z.mitigated).keep_last(keep).
On each bar, it goes through each ob in the order blocks and adds ob to zones.
On each bar, it goes through each z in zones and draws a box.
MACD Histogram
MACD Histogram is an indicator drawn in its own pane.
core language
indicator "MACD Histogram"pane: newinput fast = 12input slow = 26input signal_length = 9m = macd(close, fast, slow, signal_length)plot m.histogram, style: histogram, color: if m.histogram >= 0 then green.fade(30) else red.fade(30)plot m.macd, color: blueplot m.signal, color: orangehline 0, style: dotted, color: graymark circle, at: below, when: crosses_above(m.macd, m.signal), color: greenMACD Histogram is an indicator drawn in its own pane.
You can change 3 inputs: fast (default 12), slow (default 26) and signal_length (default 9).
It calculates m as the MACD (source the close, fast fast, slow slow, signal signal_length).
On the chart, it plots m.histogram, m.macd and m.signal; it also draws a horizontal line at 0; it also marks circle below the bar when m.macd crosses above m.signal.
Session VWAP and Profile
Session VWAP and Profile is an indicator drawn over the price chart.
tables and dashboards
indicator "Session VWAP and Profile"pane: priceinput rows = 24input value_area = 70%session_vwap = vwap(anchor: "session")upper_band = session_vwap + stdev(close, 20)lower_band = session_vwap - stdev(close, 20)plot session_vwap as vwap_line, color: purple, width: 2plot upper_band, color: purple.fade(60), style: stepplot lower_band, color: purple.fade(60), style: stepfill upper_band, lower_band, color: purple.fade(92)profile rows: rows, range: session, side: right, value_area: value_areaSession VWAP and Profile is an indicator drawn over the price chart.
You can change 2 inputs: rows (default 24) and value_area (default 70%).
It calculates session_vwap as VWAP, upper_band as session_vwap plus the 20-bar standard deviation of the close and lower_band as session_vwap minus the 20-bar standard deviation of the close.
On the chart, it plots session_vwap as vwap_line, plots upper_band and lower_band, shades between upper_band and lower_band and draws a volume profile.
Fair Value Gaps
Fair Value Gaps is an indicator drawn over the price chart.
statesmart money
indicator "Fair Value Gaps"pane: priceinput keep = 15input min_gap = 0.5state gaps: list<Zone> = []for gap in fair_value_gaps(min_size: min_gap, new_only: true):gaps.push(gap)gaps = gaps.filter(g => not g.mitigated).keep_last(keep)for g in gaps:box id: g.id, from: (g.formed_bar, g.top), to: (bar.index, g.bottom), color: if g.bullish then teal.fade(75) else orange.fade(75)label if g.bullish then "FVG +" else "FVG -", at: (g.formed_bar, g.mid), color: grayFair Value Gaps is an indicator drawn over the price chart.
You can change 2 inputs: keep (default 15) and min_gap (default 0.5).
It remembers gaps from one bar to the next.
It calculates gaps as gaps.filter(g => not g.mitigated).keep_last(keep).
On each bar, it goes through each gap in the fair value gaps and adds gap to gaps.
On each bar, it goes through each g in gaps and draws a box and labels if g.bullish then "FVG +" else "FVG -".
Ichimoku Cloud
Ichimoku Cloud is an indicator drawn over the price chart.
core language
indicator "Ichimoku Cloud"pane: pricecloud = ichimoku()plot cloud.conversion, color: blueplot cloud.base, color: redplot cloud.span_a, color: green.fade(40)plot cloud.span_b, color: red.fade(40)fill cloud.span_a, cloud.span_b, color: if cloud.span_a > cloud.span_b then green.fade(85) else red.fade(85)plot cloud.lagging, color: grayIchimoku Cloud is an indicator drawn over the price chart.
It calculates cloud as the Ichimoku cloud.
On the chart, it plots cloud.conversion, cloud.base, cloud.span_a and cloud.span_b; it also shades between cloud.span_a and cloud.span_b; it also plots cloud.lagging.
Opening Range Dashboard
Opening Range Dashboard is an indicator drawn over the price chart.
tables and dashboardsstate
indicator "Opening Range Dashboard"pane: priceinput range_start = 09:30input range_end = 10:00state or_high: price = nastate or_low: price = nain_range = time_of_day between range_start and range_endif time_of_day == range_start:or_high = highor_low = lowelif in_range:or_high = max(or_high, high)or_low = min(or_low, low)background blue.fade(92), when: in_rangehline or_high, style: dashed, color: greenhline or_low, style: dashed, color: reddashboard position: top_right, rows: [["Range high", "{or_high:0.00}"], ["Range low", "{or_low:0.00}"], ["Width", "{or_high - or_low:0.00}"]]Opening Range Dashboard is an indicator drawn over the price chart.
You can change 2 inputs: range_start (default 09:30) and range_end (default 10:00).
It remembers or_high and or_low from one bar to the next.
It calculates in_range as whether the time of day is between range_start and range_end.
On each bar, it checks whether the time of day is range_start and, if so, sets or_high to the high and sets or_low to the low; otherwise, if in_range, sets or_high to the max (a or_high, b the high); it also sets or_low to the min (a or_low, b the low).
On the chart, it shades the background when in_range, draws a horizontal line at or_high, draws a horizontal line at or_low and shows a dashboard.
Volatility Regime Canvas
Volatility Regime Canvas is an indicator drawn in its own pane.
canvasstatefunctions and types
indicator "Volatility Regime Canvas"pane: newenum Regime: calm, normal, stormyinput fast_vol = 20input slow_vol = 100state regime = Regime.normalratio = stdev(returns(close), fast_vol) / stdev(returns(close), slow_vol)if ratio < 0.8:regime = Regime.calmelif ratio > 1.3:regime = Regime.stormyelse:regime = Regime.normalplot ratio as vol_ratio, color: whitehline 1, style: dotted, color: grayon render(canvas):tint = if regime == Regime.stormy then red.fade(80) else if regime == Regime.calm then green.fade(80) else gray.fade(90)canvas.rect(from: (canvas.visible_from, canvas.price_max), to: (canvas.last_bar, canvas.price_min), fill: tint)match regime:Regime.calm: canvas.text("calm", at: (canvas.last_bar, canvas.price_max), align: right, color: green)Regime.normal: canvas.text("normal", at: (canvas.last_bar, canvas.price_max), align: right)Regime.stormy: canvas.text("stormy", at: (canvas.last_bar, canvas.price_max), align: right, color: red)Volatility Regime Canvas is an indicator drawn in its own pane.
You can change 2 inputs: fast_vol (default 20) and slow_vol (default 100).
It remembers regime from one bar to the next.
It calculates ratio as the standard deviation of the returns of the close over fast_vol bars divided by the standard deviation of the returns of the close over slow_vol bars.
Whenever the chart is drawn, it sets tint to red at 80% transparency when regime is stormy, otherwise green at 80% transparency when regime is calm, otherwise gray at 90% transparency; it also draws a rectangle; it also checks regime: for calm it writes "calm" on the chart; for normal it writes "normal" on the chart; for stormy it writes "stormy" on the chart.
On each bar, it checks whether ratio is below 0.8 and, if so, sets regime to calm; otherwise, if ratio is above 1.3, sets regime to stormy; otherwise sets regime to normal.
On the chart, it plots ratio as vol_ratio and draws a horizontal line at 1.
Support and Resistance
Support and Resistance is an indicator drawn over the price chart.
core language
indicator "Support and Resistance"pane: priceinput lookback = 300input min_touches = 3found = support_resistance(lookback, touches: min_touches).filter(l => l.touches >= min_touches)strongest = found.sort_by(l => -l.strength)for level in strongest.keep_last(6):line from: (bar.index - 50, level.price), to: (bar.index, level.price), extend: right, color: if level.price > close then red.fade(40) else green.fade(40), width: 2label "{level.touches} touches", at: (bar.index, level.price), color: graySupport and Resistance is an indicator drawn over the price chart.
You can change 2 inputs: lookback (default 300) and min_touches (default 3).
It calculates found as support_resistance(lookback, touches: min_touches).filter(l => l.touches >= min_touches) and strongest as found.sort_by(l => -l.strength).
On each bar, it goes through each level in strongest.keep_last(6) and draws a line and labels "{level.touches} touches".
ZigZag Fibonacci
ZigZag Fibonacci is an indicator drawn over the price chart.
core language
indicator "ZigZag Fibonacci"pane: priceinput deviation = 3%swings = zigzag(deviation: deviation, depth: 12)if swings.len >= 2:last_swing = swings.last()previous_swing = swings.get(swings.len - 2)fib from: (previous_swing.bar, previous_swing.price), to: (last_swing.bar, last_swing.price), levels: [0.382, 0.5, 0.618, 0.786]polyline points: swings.map(s => (s.bar, s.price)), color: gray, width: 1ZigZag Fibonacci is an indicator drawn over the price chart.
You can change one input: deviation (default 3%).
It calculates swings as the zigzag swings (deviation deviation, depth 12).
On each bar, it checks whether the number of swings is at or above 2 and, if so, sets last_swing to swings.last(), sets previous_swing to swings.get(swings.len - 2), draws Fibonacci levels and draws a polyline.
Portfolio Risk Matrix
Portfolio Risk Matrix is an indicator drawn in its own pane.
several marketsstatistics and matrices
indicator "Portfolio Risk Matrix"pane: newmarkets: EURUSD, GBPUSD, USDJPYinput lookback = 100eur = returns(close_of(EURUSD))gbp = returns(close_of(GBPUSD))jpy = returns(close_of(USDJPY))cov = covariance_matrix([eur, gbp, jpy], lookback)weights = matrix(3, 1, fill: 0.3333)portfolio_variance = multiply(multiply(transpose(weights), cov), weights)eur_gbp_corr = correlation(eur, gbp, lookback)eur_jpy_corr = correlation(eur, jpy, lookback)plot eur_gbp_corr as eur_gbp, color: blueplot eur_jpy_corr as eur_jpy, color: orangehline 0, style: dotted, color: graycells rows: [["Pair", "Correlation"], ["EUR/GBP", "{eur_gbp_corr:0.00}"], ["EUR/JPY", "{eur_jpy_corr:0.00}"]], columns: 2Portfolio Risk Matrix is an indicator drawn in its own pane.
You can change one input: lookback (default 100).
It calculates eur as the returns of the close of EURUSD, gbp as the returns of the close of GBPUSD, jpy as the returns of the close of USDJPY, cov as the covariance matrix (series eur, gbp and jpy, length lookback), weights as the matrix (rows 3, columns 1, fill 0.3333), portfolio_variance as the multiply (a the multiply (a the transpose (m weights), b cov), b weights), eur_gbp_corr as the correlation (a eur, b gbp, length lookback) and eur_jpy_corr as the correlation (a eur, b jpy, length lookback).
On the chart, it plots eur_gbp_corr as eur_gbp, plots eur_jpy_corr as eur_jpy, draws a horizontal line at 0 and draws a cells.
Volume Heatmap
Volume Heatmap is an indicator drawn over the price chart.
tables and dashboardsstate
indicator "Volume Heatmap"pane: priceinput columns = 30input rows = 12state heat: list<Cell> = []top = highest(high, columns)bottom = lowest(low, columns)row_height = (top - bottom) / rowsif bar.confirmed:for i in 0..rows:level = bottom + row_height * iheat.push(Cell(bar: bar.index, price: level, value: volume * (1 - abs(close - level) / (top - bottom))))heat = heat.keep_last(columns * rows)heatmap cells: heat, palette: "thermal"Volume Heatmap is an indicator drawn over the price chart.
You can change 2 inputs: columns (default 30) and rows (default 12).
It remembers heat from one bar to the next.
It calculates top as the highest high of the last columns bars, bottom as the lowest low of the last columns bars and row_height as (top minus bottom) divided by rows.
On each bar, it checks whether the bar has closed and, if so, goes through each i in 0 to rows and sets level to bottom plus row_height × i; it also adds Cell(bar: bar.index, price: level, value: volume * (1 - abs(close - level) / (top - bottom))) to heat; it also sets heat to heat.keep_last(columns * rows).
On the chart, it draws a heatmap.
Gradient RSI Candles
Gradient RSI Candles is an indicator drawn in its own pane.
core language
indicator "Gradient RSI Candles"pane: newinput length = 14strength = rsi(close, length)tint = gradient(strength, low: red, high: green, from: 30, to: 70)pane "RSI", height: 30%candles open: open, high: high, low: low, close: close, color: tint, pane: "RSI"plot strength, color: tint, width: 2, pane: "RSI"hline 70, style: dashed, color: grayhline 30, style: dashed, color: grayGradient RSI Candles is an indicator drawn in its own pane.
You can change one input: length (default 14).
It calculates strength as the RSI over length bars and tint as the gradient (value strength, low red, high green, from 30, to 70).
On the chart, it draws a pane, draws a candles, plots strength, draws a horizontal line at 70 and draws a horizontal line at 30.
X-Ray Flow Bands
X-Ray Flow Bands is an indicator drawn over the price chart.
renko, heikin ashi, x-ray
indicator "X-Ray Flow Bands"bars: xray(20)input band_length = 30center = hma(close, band_length)width = stdev(close, band_length) * 2up_band = center + widthdown_band = center - widthchannel upper: up_band, lower: down_band, fill: linear_gradient(start: green.fade(80), end: red.fade(80))plot center, color: white, width: 2mark triangle_up, at: below, when: crosses_above(close, down_band), color: green, size: tinymark triangle_down, at: above, when: crosses_below(close, up_band), color: red, size: tinyX-Ray Flow Bands is an indicator drawn over the price chart.
You can change one input: band_length (default 30).
It calculates center as the HMA of the close over band_length bars, width as 2 × the standard deviation of the close over band_length bars, up_band as center plus width and down_band as center minus width.
On the chart, it draws a channel, plots center, marks triangle up below the bar when the close crosses above down_band and marks triangle down above the bar when the close crosses below up_band.
Multi-Timeframe Trend Table
Multi-Timeframe Trend Table is an indicator drawn over the price chart.
other bar sizestables and dashboards
indicator "Multi-Timeframe Trend Table"pane: priceh1 = bars(bars: 1h)h4 = bars(bars: 4h)d1 = bars(bars: 1d)h1_up = h1.close > ema(h1.close, 50)h4_up = h4.close > ema(h4.close, 50)d1_up = d1.close > ema(d1.close, 50)score = (if h1_up then 1 else 0) + (if h4_up then 1 else 0) + (if d1_up then 1 else 0)table position: bottom_right, rows: [["1h", if h1_up then "up" else "down"], ["4h", if h4_up then "up" else "down"], ["1d", if d1_up then "up" else "down"], ["Score", "{score}/3"]]bar_color if score == 3 then green else if score == 0 then red else grayMulti-Timeframe Trend Table is an indicator drawn over the price chart.
It calculates h1 as 1-hour bars, h4 as 4-hour bars, d1 as daily bars, h1_up as whether h1.close is above the 50-bar EMA of h1.close, h4_up as whether h4.close is above the 50-bar EMA of h4.close, d1_up as whether d1.close is above the 50-bar EMA of d1.close and score as 1 when h1_up, otherwise 0 plus 1 when h4_up, otherwise 0 plus 1 when d1_up, otherwise 0.
On the chart, it shows a table and colors the bars.
Fractal Pitchfork
Fractal Pitchfork is an indicator drawn over the price chart.
state
indicator "Fractal Pitchfork"pane: pricestate points: list<tuple<int, price>> = []f = fractals(2)if f.up:points.push((bar.index - 2, high[2]))if f.down:points.push((bar.index - 2, low[2]))points = points.keep_last(3)if points.len == 3:pitchfork points: pointspolygon points: points, fill: blue.fade(90), border: bluearrow from: points.get(0), to: points.get(2), color: grayFractal Pitchfork is an indicator drawn over the price chart.
It remembers points from one bar to the next.
It calculates f as the fractals (periods 2) and points as points.keep_last(3).
On each bar, it checks whether f.up and, if so, adds (bar.index - 2, high[2]) to points.
On each bar, it checks whether f.down and, if so, adds (bar.index - 2, low[2]) to points.
On each bar, it checks whether the number of points is 3 and, if so, draws a pitchfork, draws a polygon and draws an arrow.
Move Annotations
Move Annotations is an indicator drawn over the price chart.
core language
indicator "Move Annotations"pane: pricebig_move = abs(close - open) > 2 * atr(14)gap_up = open > high[1]gap_down = open < low[1]if big_move:icon "bolt", at: (bar.index, high), color: yellowtooltip "Range {bar.range:0.00} vs ATR {atr(14):0.00}", at: (bar.index, high)if gap_up:label "Gap up", at: (bar.index, low), color: greenif gap_down:label "Gap down", at: (bar.index, high), color: redimage "logo.svg", at: (bar.index, close), width: 16Move Annotations is an indicator drawn over the price chart.
It calculates big_move as whether the absolute value of the close minus the open is above 2 × the 14-bar ATR, gap_up as whether the open is above the previous bar's high and gap_down as whether the open is below the previous bar's low.
On each bar, it checks whether big_move and, if so, draws an icon and draws a tooltip.
On each bar, it checks whether gap_up and, if so, labels "Gap up".
On each bar, it checks whether gap_down and, if so, labels "Gap down".
On the chart, it draws an image.
Momentum Composite
Momentum Composite is an indicator drawn in its own pane.
core language
indicator "Momentum Composite"pane: newinput trix_length = 18trix_line = trix(close, trix_length)volume_flow = volume_osc(5, 20)mfi_line = mfi(14)export composite = (trix_line * 100 + volume_flow / 10 + (mfi_line - 50) / 50) / 3export bullish = composite > 0 and composite > composite[1]plot composite as composite_line, style: columns, color: if bullish then teal else grayhline 0, color: grayMomentum Composite is an indicator drawn in its own pane.
You can change one input: trix_length (default 18).
It calculates trix_line as the TRIX (source the close, length trix_length), volume_flow as the volume oscillator (fast 5, slow 20), mfi_line as the money flow index (length 14), composite as (100 × trix_line plus volume_flow divided by 10 plus (mfi_line minus 50) divided by 50) divided by 3 and bullish as whether composite is above 0 and composite is above the previous bar's composite. It shares composite and bullish with scripts that use it.
On the chart, it plots composite as composite_line and draws a horizontal line at 0.
Daily risk guard
Daily risk guard is an alert that checks every 1 minute, can fire every time its condition is met and waits 4 hours between alerts.
account and history
alert "Daily risk guard"check: every 1mrepeat: every timecooldown: 4hwhen history.today.pnl < -(2% of account.balance) or account.margin_level < 150%:notify "Daily P&L {history.today.pnl:$0.00}, margin level {account.margin_level:0}%"Daily risk guard is an alert that checks every 1 minute, can fire every time its condition is met and waits 4 hours between alerts.
When today's closed profit or loss is below minus 2% of the account balance or the margin level is below 150%, it sends the notification "Daily P&L {history.today.pnl:$0.00}, margin level {account.margin_level:0}%".
RSI bullish divergence
RSI bullish divergence is an alert that checks every 15 minutes, fires at most once per bar and shows where it fires on the chart.
core language
alert "RSI bullish divergence"check: every 15mrepeat: once per barshow_on_chart: trueinput rsi_length = 14input lookback = 30momentum_now = rsi(close, rsi_length)lower_low = low < lowest(low, lookback)[1]higher_rsi = momentum_now > lowest(momentum_now, lookback)[1]when lower_low and higher_rsi and was(momentum_now < 30, within: 5 bars):notify "Bullish RSI divergence on {market.symbol}: RSI {momentum_now:0.0}"RSI bullish divergence is an alert that checks every 15 minutes, fires at most once per bar and shows where it fires on the chart.
You can change 2 inputs: rsi_length (default 14) and lookback (default 30).
It calculates momentum_now as the RSI over rsi_length bars, lower_low as whether the low is below the previous bar's lowest low of the last lookback bars and higher_rsi as whether momentum_now is above the previous bar's lowest momentum_now of the last lookback bars.
When lower_low and higher_rsi and momentum_now is below 30 at some point within the last 5 bars, it sends the notification "Bullish RSI divergence on {market.symbol}: RSI {momentum_now:0.0}".
Margin and exposure
Margin and exposure is an alert that checks every 5 minutes and fires only once.
account and history
alert "Margin and exposure"check: every 5mrepeat: onceexpires: 30dwhen account.free_margin < 20% of account.equity or positions.len >= 8:notify "Free margin {account.free_margin:$0} with {positions.len} open positions"Margin and exposure is an alert that checks every 5 minutes and fires only once.
When free margin is below 20% of equity or the number of open positions is at or above 8, it sends the notification "Free margin {account.free_margin:$0} with {positions.len} open positions".
Stochastic cross in extremes
Stochastic cross in extremes is an alert that checks every 1 hour, can fire every time its condition is met, waits 3 bars between alerts and shows whe
core language
alert "Stochastic cross in extremes"check: every 1hrepeat: every timecooldown: 3 barsshow_on_chart: truek_line = stoch().kd_line = stoch().dwhen crosses_above(k_line, d_line) and k_line < 20:notify "Stochastic bullish cross at {k_line:0} on {market.symbol}"when crosses_below(k_line, d_line) and k_line > 80:notify "Stochastic bearish cross at {k_line:0} on {market.symbol}"Stochastic cross in extremes is an alert that checks every 1 hour, can fire every time its condition is met, waits 3 bars between alerts and shows where it fires on the chart.
It calculates k_line as the k of the stochastic and d_line as the d of the stochastic.
When k_line crosses above d_line and k_line is below 20, it sends the notification "Stochastic bullish cross at {k_line:0} on {market.symbol}".
When k_line crosses below d_line and k_line is above 80, it sends the notification "Stochastic bearish cross at {k_line:0} on {market.symbol}".
Intrabar spike
Intrabar spike is an alert that checks every 1 hour and fires at most once per bar.
inside the bar
alert "Intrabar spike"check: every 1hrepeat: once per bar closeinner = intrabar(bars: 1m)spike = inner.high.max() - inner.low.min()when inner.len > 0 and spike > atr(14) * 1.5:notify "Minute-level spike of {spike:0.00} inside the hour on {market.symbol}"Intrabar spike is an alert that checks every 1 hour and fires at most once per bar.
It calculates inner as the bars inside this bar (bars 1 minute) and spike as inner.high.max() minus inner.low.min().
When the number of inner is above 0 and spike is above 1.5 × the 14-bar ATR, it sends the notification "Minute-level spike of {spike:0.00} inside the hour on {market.symbol}".
Tokyo open gap
Tokyo open gap is an alert that checks every 1 minute and can fire every time its condition is met.
state
alert "Tokyo open gap"check: every 1mrepeat: every timestate tokyo_open_price: price = naon session open "tokyo":tokyo_open_price = opennotify "Tokyo session opened at {open:0.000} on {market.symbol}"when tokyo_open_price > 0 and abs(close - tokyo_open_price) > atr(14) * 3:notify "Price moved more than 3 ATR from the Tokyo open"Tokyo open gap is an alert that checks every 1 minute and can fire every time its condition is met.
It remembers tokyo_open_price from one bar to the next.
When the Tokyo session opens, it sets tokyo_open_price to the open and sends the notification "Tokyo session opened at {open:0.000} on {market.symbol}".
When tokyo_open_price is above 0 and the absolute value of the close minus tokyo_open_price is above 3 × the 14-bar ATR, it sends the notification "Price moved more than 3 ATR from the Tokyo open".
Distribution shift
Distribution shift is an alert that checks every 1 hour and fires at most once per bar.
statistics and matrices
alert "Distribution shift"check: every 1hrepeat: once per barwarmup: 300r = returns(close)skewness = skew(r, 100)tails = kurtosis(r, 100)memory = autocorrelation(r, 100, lag: 1)when abs(skewness) > 1 and tails > 5:notify "Fat-tailed, skewed returns: skew {skewness:0.00}, kurtosis {tails:0.0}, lag-1 autocorrelation {memory:0.00}"Distribution shift is an alert that checks every 1 hour and fires at most once per bar.
It calculates r as the returns of the close, skewness as the skew (source r, length 100), tails as the kurtosis (source r, length 100) and memory as the autocorrelation (source r, length 100, lag 1).
When the absolute value of skewness is above 1 and tails is above 5, it sends the notification "Fat-tailed, skewed returns: skew {skewness:0.00}, kurtosis {tails:0.0}, lag-1 autocorrelation {memory:0.00}".
Ribbon and TSI agreement
Ribbon and TSI agreement is an alert that checks every 15 minutes and fires at most once per bar.
uses a library or indicator
alert "Ribbon and TSI agreement"check: every 15mrepeat: once per baruse indicator "Trend Ribbon" v3 as ribbon (fast: 8, slow: 21)t = tsi(close, long: 25, short: 13, signal: 7)p = ppo(close)when ribbon.up and crosses_above(t.tsi, t.signal) and p.histogram > 0:notify "Trend Ribbon up with TSI and PPO momentum on {market.symbol}"Ribbon and TSI agreement is an alert that checks every 15 minutes and fires at most once per bar.
It uses the indicator "Trend Ribbon" (version 3) as ribbon, with fast set to 8 and slow set to 21.
It calculates t as the true strength index (source the close, long 25, short 13, signal 7) and p as the percentage price oscillator (source the close).
When ribbon.up and t.tsi crosses above t.signal and p.histogram is above 0, it sends the notification "Trend Ribbon up with TSI and PPO momentum on {market.symbol}".
This description may be incomplete because the script has errors.
Quant Toolkit
Quant Toolkit is a library of reusable functions and constants for other scripts.
functions and types
library "Quant Toolkit"const TRADING_DAYS = 252fn kelly_fraction(win_rate: number, payoff: number) -> number:return win_rate - (1 - win_rate) / payofffn annualized_volatility(source: series<number> = close, length: int = 20) -> number:return stdev(returns(source), length) * sqrt(TRADING_DAYS)fn position_heat(risks: list<percent>) -> percent:total = 0%for r in risks:total += rreturn totalQuant Toolkit is a library of reusable functions and constants for other scripts.
It defines the constant TRADING_DAYS as 252.
It defines kelly_fraction(win_rate, payoff), which returns a number, annualized_volatility(source, length), which returns a number and position_heat(risks), which returns a percentage.
Risk Parity
Risk Parity is a library of reusable functions and constants for other scripts.
functions and types
library "Risk Parity"const MAX_WEIGHT = 40%type Asset:symbol: symbolvolatility: numberweight: percent = 0%fn inverse_vol_weights(assets: list<Asset>) -> list<percent>:total = 0.0for a in assets:total += 1 / a.volatilityweights = assets.map(a => (1 / a.volatility) / total * 100%)return weights.map(w => min(w, MAX_WEIGHT))fn lots_for_weight(balance: money, weight: percent, contract_value: money) -> lots:return (weight of balance) / contract_value * 1 lotRisk Parity is a library of reusable functions and constants for other scripts.
It defines the constant MAX_WEIGHT as 40%.
It defines inverse_vol_weights(assets), which returns list<percent> and lots_for_weight(balance, weight, contract_value), which returns a size in lots.
Ranking Tools
Ranking Tools is a library of reusable functions and constants for other scripts.
functions and typesstatistics and matrices
library "Ranking Tools"type Scored:symbol: symbolscore: numberfn top_n(items: list<Scored>, n: int) -> list<Scored>:return items.sort_by(item => item.score).keep_last(n)fn normalize(value: number, low: number, high: number) -> number:if high == low:return 0return clamp((value - low) / (high - low), 0, 1)fn percentile_band(source: series<number> = close, length: int = 100) -> number:return percentile(source, length, percent: 90%) - percentile(source, length, percent: 10%)Ranking Tools is a library of reusable functions and constants for other scripts.
It defines top_n(items, n), which returns list<Scored>, normalize(value, low, high), which returns a number and percentile_band(source, length), which returns a number.
Importing from other languages
Pine Script, MQL4 and MQL5, NinjaScript, EasyLanguage, thinkScript and Python.
Paste a script from another platform and AlgoBarsX works out the language, parses the source, maps each construct, and compiles the result before calling it done. An import that does not compile says so.
| Reads | Notes |
|---|---|
| Pine Script | Strategies and indicators. |
| MQL4 and MQL5 | Expert advisors. OnTick becomes the script body, and OrderSend becomes an order with its stop and target attached. |
| NinjaScript | OnBarUpdate carries over. The C# around it comes back as notes. |
| EasyLanguage | TradeStation strategies. |
| thinkScript | Studies and conditions. |
| Python | backtrader, freqtrade, and plain pandas with pandas-ta or TA-Lib. |
Every line comes back with one of three statuses: exact (carried over as is), adapted (changed, with a note saying how), or your call (a decision handed back to you, with the original kept as a comment).
//@version=5strategy("EMA Cross", overlay=true)fast = input.int(20, "Fast")slow = input.int(50, "Slow")if ta.crossover(ta.ema(close, fast), ta.ema(close, slow))strategy.entry("Long", strategy.long)if ta.crossunder(ta.ema(close, fast), ta.ema(close, slow))strategy.close("Long")strategy "EMA Cross"market: EURUSDbars: 1hinput fast = 20input slow = 50when crosses_above(ema(close, fast), ema(close, slow)):buy size: 1 lot, tag: "Long"when crosses_below(ema(close, fast), ema(close, slow)):close_all tag: "Long"| Status | Your line | What happened |
|---|---|---|
| Exact | strategy("EMA Cross", overlay=true) | strategy() became the script header |
| Exact | fast = input.int(20, "Fast") | input() became an input |
| Exact | slow = input.int(50, "Slow") | input() became an input |
| Your call | strategy.entry("Long", strategy.long) | strategy.entry() had no size of its own, so it became one lot: set the size or risk you want |
| Adapted | if ta.crossover(ta.ema(close, fast), ta.ema(close, slow)) | an if that places orders became a rule |
| Exact | strategy.close("Long") | strategy.close() became close_all |
| Adapted | if ta.crossunder(ta.ema(close, fast), ta.ema(close, slow)) | an if that places orders became a rule |
| Your call | strategy(...) | Pine scripts carry no market or bar size, so EURUSD on 1h was filled in: set the ones you want |
Files: open and sealed
Share a script with or without its code.
Save any script as an .abx file to keep it, send it to someone, or import it into another account.
| Property | Open file | Sealed file |
|---|---|---|
| Carries the code | Yes | No. The code travels sealed and only AlgoBars can open it. |
| Can be run and deployed | Yes | Yes |
| Can be read or edited by whoever imports it | Yes | No |
| States its name, what it trades, its inputs and its plain-English description | Yes | Yes |
A sealed script is closed about how it works, never about what it does. Sealing happens on the platform, not in your browser.
What the Terminal does with a script
Compile, describe, run, report, versions, deploy.
| The Terminal | What it does |
|---|---|
| Compiles as you work | Names, types, units, price levels against distances, R, where things may go, and imports are all checked before anything runs. |
| Description | The script in plain English, generated from the compiled code. |
| Convert to AlgoBarsX | Type what you want in plain English. When the editor holds a description and not code, this button appears and writes the script. |
| Run | A strategy is backtested, reported and charted. An indicator is run and charted. An alert is checked against its own settings. Before a backtest you confirm the market, bars, bar type, how far back to test (1 week to 1 year), the lot size and the starting balance. |
| Replay | Plays a finished backtest back. Trades appear as they open and settle as they close, and the balance moves with them. If the account ran out, the replay stops where the test stopped. |
| Report | How it went, what it cost, how the trades behaved, then breakdowns by rule, exit, confirmation, side, tag and month. |
| Versions | Every run is kept with the code that produced it. Restore any earlier build. |
| Reference and autocomplete | Every built-in is documented in the editor and offered as you type, from the same registry this page is built from. |
| Import, save and seal | Bring in a script from another language or an .abx file, and save your own as an open or sealed file. |
| Deploy | Run a strategy on a demo or live account, or switch an alert on. You choose the market, the bar size, the trade size and the balance to trade, so one script can run on more than one market. Editing a live script explains what the change would do before it is made. |
| Strategies list | Strategies written in AlgoBarsX appear with your other strategies, where each one can be deployed, opened or deleted. |
| Import from a file | The import menu takes files as well as pasted code: .pine, .mq4, .mq5, .cs, .els, .py and plain text. |
The engine promises five things: no look-ahead, the same answer every time, indicators that always update, higher bar sizes that only appear once their candle has closed, and no hidden costs in the numbers. The language's own test suite has 678 tests, and all of them must pass on every build.
The formal grammar
EBNF, reserved words and lexical rules.
The formal grammar, for anyone building tooling such as a syntax highlighter or a linter. Notation is ISO/IEC 14977 style EBNF. The parser implements this grammar exactly, and every example on this page parses with zero diagnostics.
(* ═══════════════════════════════════════════════════════════════════════════════
AlgoBarsX v1 — formal grammar (Phase 0)
───────────────────────────────────────────────────────────────────────────────
Notation: ISO/IEC 14977 style EBNF. "x" = terminal, [ ] = optional, { } = repeat,
( | ) = choice. UPPER-CASE names are tokens produced by the lexer (src/lexer/lexer.ts).
The parser (src/parser/parser.ts) implements this grammar exactly; the acceptance scripts in
acceptance/*.algo must parse with zero diagnostics.
═══════════════════════════════════════════════════════════════════════════════ *)
(* ─── 1. Lexical structure ─────────────────────────────────────────────────────── *)
(* Logical lines. A NEWLINE ends a statement. Line breaks inside ( ), [ ] or { } are
ignored, so expressions may span lines there. Blank lines and comment-only lines
produce no tokens. Indentation (spaces; tabs are an error) produces INDENT when a
line is indented deeper than the enclosing block and one DEDENT per closed block. *)
NEWLINE = ? end of a logical line ? ;
INDENT = ? start of a deeper indentation level ? ;
DEDENT = ? return to an enclosing indentation level ? ;
EOF = ? end of input ? ;
comment = "#" , { ? any character except line break ? } ;
(* "#" followed by exactly 6 or 8 hex digits and a non-name character is a COLOR,
except at the start of a line, where "#" always begins a comment. *)
IDENT = letter , { letter | digit | "_" } ; (* case-sensitive *)
letter = "A" | … | "Z" | "a" | … | "z" | "_" ;
digits = digit , { [ "_" ] , digit } ; (* 1_000_000 *)
NUMBER = digits , [ "." , digits ] , [ ( "e" | "E" ) , [ "+" | "-" ] , digits ] ;
PERCENT = NUMBER , "%" ; (* no space: 1% 0.5% *)
RMULT = NUMBER , "R" ; (* no space: 2R 1.5R *)
MONEY = "$" , NUMBER ; (* $500 *)
DURATION = digits , ( "s" | "m" | "h" | "d" | "w" | "M" ) ; (* 30s 15m 4h 1d 1w 1M — also timeframes *)
ORDINAL = digits , ( "st" | "nd" | "rd" | "th" ) ; (* 4th — only inside rule modifiers *)
TIME = digit , [ digit ] , ":" , digit , digit ; (* 08:00 *)
DATE = digit , digit , digit , digit , "-" , digit , digit , "-" , digit , digit ; (* 2026-01-15 *)
COLOR = "#" , hex6 , [ hex2 ] ; (* #22c55e #22c55e80 *)
STRING = '"' , { character | escape | "{{" | "}}" | interpolation } , '"' ;
escape = "\" , ( '"' | "\" | "n" | "t" ) ;
interpolation = "{" , expression , [ ":" , format ] , "}" ; (* "RSI {rsi(close, 14):0.0}" *)
UNIT_WORD = "pips" | "pip" | "points" | "point" | "lots" | "lot" | "bars" | "bar" ;
(* a NUMBER followed by a UNIT_WORD is one unit value: 20 pips, 10 bars *)
(* Reserved words — cannot be used as names:
strategy indicator alert library use as input const state export type enum fn action
return if elif else for in match when on and or not then between of true false na
break continue confirmations sequence
Reserved words MAY be used after "." (ribbon.on), as named-argument names (on: gold)
and as option names (step: 0.1%). Every other word, such as every, cooldown, session,
bar, max, from or step, is contextual and remains usable as a name. *)
(* ─── 2. Program and header ────────────────────────────────────────────────────── *)
program = { NEWLINE } , [ version_line ] , header , { statement } , EOF ;
version_line = "algobarsx" , NUMBER , NEWLINE ;
header = script_kind , STRING , NEWLINE , [ INDENT , setting , { setting } , DEDENT ] ;
script_kind = "strategy" | "indicator" | "alert" | "library" ;
setting = name , ":" , setting_item , { "," , setting_item } , NEWLINE ;
setting_item = expression , { expression } ; (* juxtaposed words: once per bar close, every 1m *)
name = IDENT | reserved_word ;
(* ─── 3. Statements ────────────────────────────────────────────────────────────── *)
statement = use_stmt | input_stmt | const_stmt | state_stmt | export_stmt | enum_stmt
| type_decl | fn_decl | if_stmt | for_stmt | match_stmt | when_stmt | on_stmt
| confirmations | sequence | command | assignment | call_stmt
| return_stmt | "break" , NEWLINE | "continue" , NEWLINE ;
block = NEWLINE , INDENT , statement , { statement } , DEDENT
| statement ; (* one statement on the same line *)
use_stmt = "use" , ( "indicator" | "library" ) , STRING , [ VERSION ] , "as" , IDENT ,
[ "(" , [ arguments ] , ")" ] , NEWLINE ;
VERSION = ? an IDENT of the form v<digits>, such as v3 ? ;
input_stmt = "input" , IDENT , "=" , expression , { "," , option } , NEWLINE ;
const_stmt = "const" , IDENT , [ ":" , type ] , "=" , expression , NEWLINE ;
state_stmt = "state" , IDENT , [ ":" , type ] , "=" , expression , NEWLINE ;
export_stmt = "export" , IDENT , "=" , expression , NEWLINE ;
enum_stmt = "enum" , IDENT , ":" , IDENT , { "," , IDENT } , NEWLINE ;
type_decl = "type" , IDENT , ":" , NEWLINE , INDENT , field , { field } , DEDENT ;
field = IDENT , ":" , type , [ "=" , expression ] , NEWLINE ;
type = name , [ "<" , type , { "," , type } , ">" ] ; (* number, list<Level>, map<string, number> *)
fn_decl = [ "action" ] , "fn" , IDENT , "(" , [ param , { "," , param } ] , ")" ,
[ "->" , type ] , ":" , block ;
param = IDENT , ":" , type , [ "=" , expression ] ;
assignment = IDENT , ":" , type , "=" , expression , NEWLINE (* typed declaration *)
| target , assign_op , expression , NEWLINE ;
target = postfix ; (* must be a name, a field or an item *)
assign_op = "=" | "+=" | "-=" | "*=" | "/=" ;
call_stmt = postfix , NEWLINE ; (* must end in a call: levels.push(x) *)
return_stmt = "return" , [ expression ] , NEWLINE ;
if_stmt = "if" , expression , ":" , block ,
{ "elif" , expression , ":" , block } , [ "else" , ":" , block ] ;
for_stmt = "for" , IDENT , "in" , expression , ":" , block ; (* for i in 0..50 / for z in zones *)
match_stmt = "match" , expression , ":" , NEWLINE , INDENT , match_arm , { match_arm } , DEDENT ;
match_arm = expression , ":" , block ;
(* ─── 4. Rules, events, confirmations and sequences ────────────────────────────── *)
when_stmt = "when" , expression , [ "as" , IDENT ] , { modifier } , ":" , block ;
modifier = "every" , ( ORDINAL | "bar" ) (* every 4th · every bar *)
| "skip" , "first" , NUMBER (* skip first 3 *)
| "max" , NUMBER , "per" , IDENT (* max 2 per day *)
| "cooldown" , ( DURATION | NUMBER , UNIT_WORD ) (* cooldown 30m · cooldown 5 bars *)
| "once" , "per" , "bar" (* once per bar *)
| "within" , ( "session" | "sessions" ) , IDENT , { "," , IDENT }
| "from" , TIME , "to" , TIME , [ zone ] ; (* from 08:00 to 11:00 Europe/London *)
zone = IDENT , { "/" , IDENT } ;
on_stmt = "on" , IDENT , { IDENT } , [ "(" , IDENT , ")" ] , [ STRING ] , ":" , block ;
(* on start · on bar close · on exit(trade) · on session open "new_york" · on render(canvas) *)
confirmations = "confirmations" , IDENT , ":" , NEWLINE , INDENT ,
confirm_line , { confirm_line } , DEDENT ;
confirm_line = IDENT , ":" , expression , NEWLINE
| "require" , ":" , ( "all" | "any" | "at" , "least" , NUMBER ) , NEWLINE ;
sequence = "sequence" , IDENT , [ "within" , ( NUMBER , UNIT_WORD | DURATION ) ] , ":" , NEWLINE ,
INDENT , sequence_line , { sequence_line } , DEDENT ;
sequence_line = "step" , IDENT , ":" , expression , NEWLINE
| "reset_if" , ":" , expression , NEWLINE ;
(* ─── 5. Commands (orders, management, notifications, drawing) ─────────────────── *)
command = COMMAND_WORD , [ command_items ] , ( NEWLINE | ":" , NEWLINE , INDENT , statement , { statement } , DEDENT ) ;
command_items = command_item , { [ "," ] , command_item } ; (* the comma may be omitted before an option *)
command_item = option | expression , [ "as" , IDENT ] ; (* positional values come before options *)
option = name , ":" , expression ;
COMMAND_WORD = "buy" | "sell" | "close" | "close_all" | "modify" | "cancel" | "notify" | "log"
| "partial" | "breakeven" | "trail" | "exit"
| "plot" | "fill" | "hline" | "mark" | "label" | "line" | "box" | "table" | "bar_color"
| "background" | "pane" | "polygon" | "polyline" | "curve" | "channel" | "profile"
| "heatmap" | "cells" | "candles" | "fib" | "pitchfork" | "arrow" | "icon" | "image"
| "tooltip" | "dashboard" ;
(* A command word starts a command only when the next token can start a value or ends the
line; "close > open" is an expression, "close trade" is a command. "plot (a + b) / 2"
is a command because of the space before "(". *)
(* ─── 6. Expressions (lowest to highest precedence) ─────────────────────────────── *)
expression = lambda | conditional | or_expr ;
lambda = ( IDENT | "(" , [ IDENT , { "," , IDENT } ] , ")" ) , "=>" , expression ;
conditional = "if" , expression , "then" , expression , "else" , expression ;
or_expr = and_expr , { "or" , and_expr } ;
and_expr = not_expr , { "and" , not_expr } ;
not_expr = "not" , not_expr | comparison ;
comparison = range_expr , [ ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) , range_expr
| "in" , range_expr
| "between" , range_expr , "and" , range_expr ] ; (* non-associative *)
range_expr = additive , [ ".." , additive ] ;
additive = of_expr , { ( "+" | "-" ) , of_expr } ;
of_expr = multiplicative , [ "of" , multiplicative ] ; (* 2% of account.balance *)
multiplicative= unary , { ( "*" | "/" | "%" ) , unary } ;
unary = ( "-" | "+" ) , unary | power ;
power = postfix , [ "**" , unary ] ; (* right-associative *)
postfix = primary , { "(" , [ arguments ] , ")" | "[" , expression , "]" | "." , name } ;
arguments = argument , { "," , argument } ;
argument = [ name , ":" ] , expression ;
primary = NUMBER , [ UNIT_WORD ] | PERCENT | RMULT | MONEY | DURATION | TIME | DATE | COLOR | STRING
| "true" | "false" | "na" | IDENT
| "(" , expression , ")"
| "(" , expression , "," , expression , { "," , expression } , ")" (* tuple: (bar, price) *)
| "[" , [ expression , { "," , expression } ] , "]" ; (* list *)
Try a function name such as
rsi, a setting such as max_open, a rule such as E11, or a code such as AS0304.