Documentation

Block Reference

Complete reference for all 100 blocks the engine runs: the 98 the editor offers, plus two retired ones kept so a saved graph still loads. Every block has a strongly-typed socket system: Number, Bar, Boolean, DateTime. Invalid connections are rejected before you run.

Blocks marked tunable can be exposed to PSO optimization. All indicators are computed on closed bars only, so there is no lookahead bias.

Naming a block. Right-click any block on the canvas for Name this block, Duplicate, Delete and a link straight to that block’s entry below. Without a name a block is identified by the automatic id it was given when you added it (n3, n7, …), and that id is what Optimize and Sensitivity show on their axes, so two blocks of the same type read as n3.period and n7.period. Naming them turns those labels into Fast EMA.period and Slow EMA.period. A name is a label only: it never changes a result, no two blocks in one strategy may share one, and it may not contain a dot, because the labels are name.parameter. Clearing a name brings the automatic id back.

Source14 Aggregator12 Indicator41 Gate6 Compare5 Logic4 Math13 Sink4 Note1

Source

Raw data inputs: bar prices, tick prices, position state, time constants. Source blocks have no inputs.

Tick Price
Source.Tick.Price
Latest tick bid/ask/mid prices from the tick data. Only offered when the graph timeframe is set to TICKS. If the data carries no bid/ask, all three outputs equal the last price. For bar-based graphs use Source.Bar.Current instead.
bidNumber use for SHORT entries
midNumber use for indicators
askNumber use for LONG entries
Current Bar
Source.Bar.Current
The bar that just closed. The primary data source for bar-based graphs. Not available for tick data (use Tick Price → Aggregator instead).
barBar closed OHLCV bar
Current Time
Source.Time.Now
Retired: no longer offered in the palette. Unix-millisecond timestamp of the current bar’s start (its period open), or of the tick on tick data. The sentence that used to stand here described two things the editor has never allowed: this output is a DateTime, Gate.Session takes a Bar, and no block in the catalog declares a DateTime input at all; the connection validator refuses DateTime → Number. For a time-of-day window use Gate.Session, which is the one clock in the graph that follows CET/CEST, and whose four hour and minute parameters can be handed to the optimizer. The block is kept here and in the catalog so a graph saved with one still loads and still says what it is.
timeDateTime Unix ms
Constant (Number)
Source.Constant.Number
A fixed number that does not change bar-to-bar. Mark as tunable to expose to PSO. The optimizer will search the range you specify.
valueNumber
valuenumber default 0tunable
Constant (Time)
Source.Constant.DateTime
Retired: no longer offered in the palette. A fixed time-of-day in CET (e.g. 09:00), emitted as minutes since midnight. It has no consumer: no block in the catalog declares a DateTime input, so the comparison against Source.Time.Now this block was designed for could never be wired, and the two are on different scales in any case, one an absolute instant and one a time of day. For a time-of-day window use Gate.Session. The block is kept here and in the catalog so a graph saved with one still loads and still says what it is.
valueDateTime
hourinteger 0–23, default 9
minuteinteger 0–59, default 0
Constant (Boolean)
Source.Constant.Boolean
A fixed true or false value. Useful for temporarily disabling a gate branch without rewiring.
valueBoolean
valueboolean default true
Bar → Field
Source.Bar.Field
Extracts a single numeric field from a Bar object. Use to feed a specific price field (e.g. close) into a Math or Compare block. timestamp gives the bar’s epoch time in milliseconds, always the START of the bar’s period, so an M15 bar reading 09:00 covers 09:00 up to 09:15 whatever time its first tick arrived.
barBar
valueNumber
fieldenum open | high | low | close | volume | bid | ask | timestamp, default close
Bars Since Entry
Source.BarsSinceEntry
Number of graph evaluations since the current position was filled: base bars on a bar graph (not aggregated bars), ticks on a TICKS graph. Returns −1 when flat, 0 on the bar the entry fills (the bar after the signal), 1 on the next, etc. Use with Compare.GTE for time-based exits.
valueNumber −1 when flat
High Since Entry
Source.HighSinceEntry
Tracks the maximum bar.high seen since the position opened. Returns 0 when flat. Classic use: HighSinceEntry − (ATR × mult) → Sink.UpdateStop for a trailing stop on longs that follows the high since entry. A chandelier stop over the last N bars uses the lower of this and the high of those N bars.
valueNumber 0 when flat
Low Since Entry
Source.LowSinceEntry
Tracks the minimum bar.low seen since the position opened. Returns 0 when flat. Mirror of High Since Entry for short positions: LowSinceEntry + (ATR × mult) → trailing stop.
valueNumber 0 when flat
Position Entry Price
Source.PositionEntryPrice
The fill price of the currently open position (the oldest one, when several are open). Returns 0 when flat. Use to compute distance-from-entry for profit-target or breakeven logic.
valueNumber 0 when flat
Position Stop-Loss
Source.PositionStopLoss
The current stop-loss price level of the open position (the oldest one, when several are open). Returns 0 when flat. Use as the lower bound in trailing logic: Math.Max(PositionStopLoss, newTrail) prevents the stop moving away.
valueNumber 0 when flat
Position Initial Stop Distance
Source.PositionInitialStopDistance
The distance between the entry price and the stop the position was opened with, in price points. This is R, the risk the position was sized by. Unlike Source.PositionStopLoss, it does not move when the stop trails, so it stays usable as a reference after the stop has been moved. Returns 0 when flat, and also 0 when no initial stop was recorded, so treat 0 as unknown rather than as no risk.
valueNumber 0 when flat or unknown
Position PnL (points)
Source.PositionPnLPoints
Unrealised PnL in price points: close − entry for longs, entry − close for shorts. Returns 0 when flat. Use with Compare.GT to gate trailing stop activation (e.g. only trail after +20 pts profit).
valueNumber 0 when flat

Aggregator

Timeframe resampling. All aggregators emit the last fully closed bar: no bar that is still forming, no lookahead. A bar is held stable until the next one closes. The first bar after the data starts can cover only part of its period, and the bar-fed aggregators do not fill gaps: a period without base bars produces no bar. The TickToBar aggregators set a bar’s volume to the number of ticks in it.

Levels hold, events fire once. While a higher-timeframe bar is still forming, the aggregator re-emits the last completed one on every base bar, and downstream blocks hold their state. For a level that is what you want: an Indicator.EMA behind an Aggregator.M5 keeps reading the same value across all five M1 bars. An event is different: a candlestick or chart pattern, or Indicator.SwingHigh’s confirmed, is a statement about one completed bar, so it is reported on the single base bar that carries that bar and reads false on the rest of the period. One pattern occurrence is one trigger, whatever timeframe it sits behind. ready is a level, not an event, and stays true in between.

Quiet periods are filled, long gaps are not filled completely. When no tick arrives for one or more whole periods (a lunch lull, an overnight break, a weekend), a TickToBar aggregator emits a synthetic bar for each skipped period, flat at the last traded price with volume 0, so downstream rolling windows keep advancing instead of jumping. Synthetic bars are capped at 500 per gap: a weekend on Aggregator.TickToBar.M1 is about 2880 skipped minutes, so the series steps forward in time once the cap is reached. Zero volume is the reliable way to tell a synthetic bar from a real one that happened to trade flat. Filler is also truncated if trading resumes faster than the aggregator can publish it (one bar leaves per incoming tick), because a real bar always takes precedence over leftover filler. Real bars are never dropped, and the ticks that arrive during filling are counted in the bar they belong to.

Where a day starts, and why there is more than one answer. The product uses two day boundaries, deliberately, and they are one hour apart:

So a D1 bar inside a graph and a D1 bar on the chart are not the same bar, and neither of them starts the day at local midnight through the summer. Gate.Session is the exception: it is the only clock in the graph that follows CET/CEST, so within one graph a session window and an Aggregator.D1 bar disagree about when a day begins for half the year. Both rules are kept rather than unified, because changing either one silently moves every saved D1 strategy’s results; this paragraph is here so the difference is visible instead of discovered.

Several timeframes in one graph. A graph can use any number of aggregators side by side, each fed from the base bar or from another aggregator (a chain), and each keeps its own state: two aggregators of the same period on the same input publish the same bars. A higher-timeframe bar becomes visible on the base bar whose end reaches the bar’s end. An M15 bar from 10:00 to 10:15 is first seen by the decision made on the M1 bar 10:14, and an entry that decision triggers fills on the next base bar. A chain Aggregator.M15 → Aggregator.M60 publishes the same hourly bars as a direct Aggregator.M60. When a gap in the data closes two periods on one base bar, both are published in order, so an indicator behind the aggregator counts each of them once. Indicators behind an aggregator warm up in its bars: an Indicator.EMA with period 3 behind Aggregator.D1 needs three completed days. Source.BarsSinceEntry counts base bars, not aggregated ones.

The period must fit the bars it receives. An aggregator’s period must be a whole multiple of the bars it receives, from the dataset or from the aggregator in front of it. The engine builds a higher-timeframe bar from whole input bars, so an Aggregator.M15 on M10 bars would build bars of 20 and 10 minutes, and an Aggregator.M5 on M15 bars cannot split them and would pass every M15 bar through. A run with such a graph is refused with a message that names the block. An aggregator equal to its input passes the bars through unchanged and is allowed.

Diagnostic Mode builds its bars differently again. A diagnostic aggregates your data to the timeframe you pick by anchoring buckets on the dataset’s session open and discarding any bucket that did not receive a full set of base bars, and it refuses H4 and D1 outright because the daily maintenance break leaves those permanently short. That is a different analytical lens on purpose (it isolates exit mechanics), and it means diagnostic numbers are not meant to be compared like-for-like with a backtest of the same strategy on the same data.

Tick→M1 Aggregator
Aggregator.TickToBar.M1
Aggregates raw tick prices into 1-minute OHLC bars. Wire Source.Tick.Price → mid into price, then feed bar output to indicators. Only available in tick-data mode.
priceNumber
barBar completed M1 bar
Tick→M5 Aggregator
Aggregator.TickToBar.M5
Aggregates raw tick prices into 5-minute OHLC bars. Same semantics as Tick→M1 but at 5-minute granularity.
priceNumber
barBar completed M5 bar
M1 Aggregator
Aggregator.M1
Pass-through for M1 bars. The output is identical to the input. Included for graph symmetry when mixing M1 and higher-TF aggregators in the same graph.
barBar
barBar
M5 Aggregator
Aggregator.M5
Resamples finer bars (e.g. M1) into M5 bars. Output only changes when a new M5 bar closes, so it stays stable throughout the M5 period.
barBar
barBar last closed M5 bar
M15 Aggregator
Aggregator.M15
Resamples finer bars into M15 bars. Use to build multi-timeframe setups: e.g. M5 graph that filters entries via M15 EMA slope.
barBar
barBar last closed M15 bar
M30 Aggregator
Aggregator.M30
Resamples finer bars into M30 bars. No lookahead: output is frozen at the most recent completed M30 bar.
barBar
barBar last closed M30 bar
M60 (H1) Aggregator
Aggregator.M60
Resamples finer bars into 60-minute (H1) bars. Stable throughout the hour; emits a new bar on each H1 close.
barBar
barBar last closed H1 bar
Daily (D1) Aggregator
Aggregator.D1
Resamples intraday bars into daily bars (day boundary at UTC+1 midnight). Emits the last completed daily bar, stable all day. Use with Indicator.EMA for a clean daily-trend filter.
barBar
barBar last closed daily bar
Ticks → M15
Aggregator.TickToBar.M15
Builds M15 bars straight from a TICKS dataset, the same way Aggregator.TickToBar.M1 builds M1 bars: a period closes when a tick arrives whose timestamp falls into the next 15-minute slot, so everything downstream reads a CLOSED bar. Only usable when the graph timeframe is TICKS.
priceNumber
barBar the last CLOSED M15 bar
Ticks → M30
Aggregator.TickToBar.M30
Builds M30 bars straight from a TICKS dataset, the same way Aggregator.TickToBar.M1 builds M1 bars: a period closes when a tick arrives whose timestamp falls into the next 30-minute slot, so everything downstream reads a CLOSED bar. Only usable when the graph timeframe is TICKS.
priceNumber
barBar the last CLOSED M30 bar
Ticks → H1
Aggregator.TickToBar.H1
Builds H1 bars straight from a TICKS dataset, the same way Aggregator.TickToBar.M1 builds M1 bars: a period closes when a tick arrives whose timestamp falls into the next 60-minute slot, so everything downstream reads a CLOSED bar. Only usable when the graph timeframe is TICKS.
priceNumber
barBar the last CLOSED H1 bar
Ticks → D1
Aggregator.TickToBar.D1
Builds D1 bars straight from a TICKS dataset, the same way Aggregator.TickToBar.M1 builds M1 bars: a period closes when a tick arrives whose timestamp falls into the next day (days run 23:00–23:00 UTC, see the note above), so everything downstream reads a CLOSED bar. Only usable when the graph timeframe is TICKS.
priceNumber
barBar the last CLOSED D1 bar

Indicator

Technical indicators computed on closed-bar data. Until enough history exists, ready is false and the value is a placeholder: 0 for most blocks, 50 for RSI and Stochastic, the average of the partly filled window for SMA, and the current bar’s own field for Highest/Lowest. Gate on ready, not on the value. Formulas shown use standard notation: N = period, t = current bar.

EMA
Indicator.EMA
Exponential Moving Average. Weights recent closes more heavily than older ones. The smoothing factor α determines the decay rate.
α = 2 / (N + 1) EMA[t] = close[t] × α + EMA[t-1] × (1 - α) Seed: EMA[N-1] = SMA of first N closes
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
SMA
Indicator.SMA
Simple Moving Average. Arithmetic mean of the last N closes. Every close in the window carries the same weight, so it reacts to a new close more slowly than an EMA of the same period. It is also the middle line of Bollinger Bands.
SMA[t] = (close[t] + close[t-1] + … + close[t-N+1]) / N Before N closes exist: the average of the closes seen so far (ready = false)
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
Median(N)
Indicator.Median
Rolling median of the chosen field over the last N bars, the outlier-resistant counterpart to SMA. Over [98, 100, 101, 102, 150] the median is 101 while the average is 110.2: one print moves the mean nine points and the middle of the window none. Odd N takes the middle value of the sorted window; even N takes the mean of the two middle values, the same convention numpy and pandas use.
Median[t] = middle value of sort(field[t-N+1..t]); even N: mean of the two middle values
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
fieldenum open | high | low | close, default close
MAD(N)
Indicator.MAD
Median Absolute Deviation: the median of the absolute distances from the window's own median, over the same N bars. The robust analogue of standard deviation, and the denominator of an outlier-resistant z-score (close − Median) / MAD. Over [98, 100, 101, 102, 150] the deviations are [3, 1, 0, 1, 49] and the MAD is 1, where a standard deviation is about 20. A completely flat window gives MAD = 0, which is a true value and not an undefined one. It computes its own median, so no Indicator.Median node is required in the graph.
MAD[t] = Median(|field[i] − Median[t]|) for i in [t-N+1..t]
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
fieldenum open | high | low | close, default close
WMA(N)
Indicator.WMA
Linearly-weighted moving average of the chosen field over the last N bars: the oldest value in the window carries weight 1 and the newest weight N. It is the same average as SMA with roughly half the lag, because recent bars dominate. An SMA(20) and a WMA(20) on the same series turn at different bars, which is the whole point of choosing one. Matches TA-Lib's WMA exactly. Nothing is published before the window is full: a weighted average over a partly filled window is a different average, not a warmed-up one, and its weights would be wrong as well as its span.
WMA[t] = Σ i · field[t-N+i] / (N · (N+1) / 2), i = 1..N
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
fieldenum open | high | low | close, default close
StdDev(N)
Indicator.StdDev
Population standard deviation of the chosen field over the last N bars, divided by N, not by N-1, which is what Bollinger Bands already computes internally and what TA-Lib's STDDEV returns. A sample deviation here would make StdDev(20) disagree with the middle-to-upper distance of BollingerBands(20, 1) in the same graph. It is a price DISTANCE and not a level: “2 × StdDev” is the idiom it is for, and it is the denominator of a z-score (close − SMA) / StdDev. A completely flat window gives 0: a true value, not an undefined one; Math.Div refuses a divisor within 1e-12 of zero, so a z-score built on it cannot produce Inf or NaN.
StdDev[t] = √( Σ (field[i] − mean)² / N ) for i in [t-N+1..t]
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
fieldenum open | high | low | close, default close
ATR
Indicator.ATR
Average True Range. Direction-free volatility measure. The True Range captures the largest of the current candle range, the gap-up, and the gap-down relative to the prior close. ATR is the Wilder-smoothed average.
TR[t] = max(high[t] − low[t], |high[t] − close[t-1]|, |low[t] − close[t-1]|) ATR[t] = (ATR[t-1] × (N-1) + TR[t]) / N (Wilder smoothing) Seed: ATR[N] = mean(TR[1..N]) (bar 0 has no previous close, so the first TR is on bar 1 and ATR is ready after N+1 bars)
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–200, default 14tunable
RSI
Indicator.RSI
Relative Strength Index (0–100). Measures momentum by comparing average gains to average losses using Wilder smoothing. Conventional reference levels are 30 and 70; the block outputs the number and marks no level.
ΔC[t] = close[t] − close[t-1] Gain[t] = max(ΔC, 0), Loss[t] = max(−ΔC, 0) AvgGain / AvgLoss: Wilder EMA(N) of Gain / Loss RS = AvgGain / AvgLoss RSI = 100 − 100 / (1 + RS) AvgLoss = 0 (including a completely flat window) → RSI = 100 Reads 50 until N+1 closes have been seen
bar →Bar
value ←Number 0–100
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–100, default 14tunable
ADX
Indicator.ADX
Average Directional Index (0–100). Measures trend strength irrespective of direction. It is the smoothed DX, and DX grows as +DI and −DI move apart relative to their sum; levels such as 20 and 25 are common reference points, and the block outputs the number only. The directional components +DI and −DI, from which ADX is derived, are exposed as separate outputs.
+DM = high[t] − high[t-1] if > 0 else 0 (and > −DM) −DM = low[t-1] − low[t] if > 0 else 0 (and > +DM) Smooth +DM, −DM, TR with Wilder EMA(N) +DI = 100 × Smooth(+DM) / ATR(N) −DI = 100 × Smooth(−DM) / ATR(N) DX = 100 × |+DI − −DI| / (+DI + −DI) ADX = Wilder EMA(DX, N)
bar →Bar
value ←Number 0–100
plus_di ←Number +DI. Available after period+1 bars, earlier than value, which needs about 2×period bars
minus_di ←Number −DI. Same availability as plus_di
ready ←Boolean true once ADX itself is initialised. It needs period DX values on top of the warmup, so later than +DI/−DI
periodinteger 2–100, default 14tunable
CCI
Indicator.CCI
Commodity Channel Index (Lambert). Measures how far the typical price sits from its own moving average, scaled by the mean absolute deviation. Oscillates around 0; readings beyond ±100 are conventionally read as stretched. Note the scaling term is the mean absolute deviation, not the standard deviation.
TP = (high + low + close) / 3 SMA = SMA(TP, N) MD = mean(|TP[i] − SMA|) over the N-bar window CCI = (TP − SMA) / (0.015 × MD)
bar →Bar
value ←Number 0 during warmup, and 0 when the window is perfectly flat (the index is undefined there)
ready ←Boolean false until period bars have been seen
periodinteger 2–200, default 20tunable
A zero-line cross is Math.CrossesAbove / Math.CrossesBelow against a constant 0. The cross only counts once this block has warmed up, so what it reads while warming up is never taken for a crossing.
Williams %R
Indicator.Williams
Where the close sits inside the N-bar high/low range, as a negative percentage: −100 at the bottom of the range, 0 at the top. The inverse of Stochastic %K, related exactly by %R = %K − 100. Conventionally < −80 reads as stretched-low and > −20 as stretched-high.
HH = Highest(high, N), LL = Lowest(low, N) %R = −100 × (HH − close) / (HH − LL)
bar →Bar
value ←Number −100–0. Reports the neutral midpoint −50 when the N-bar range has zero width, because 0 would falsely read as “closed at the top”
ready ←Boolean false until period bars have been seen
periodinteger 2–200, default 14tunable
Parabolic SAR
Indicator.ParabolicSAR
Wilder’s stop-and-reverse trailing level. This outputs a price, not an oscillator reading: compare it against the close, not against a constant. It trails behind the trend, accelerating as the trend extends, and when price penetrates it the level flips to the other side of the market. Distinct from the PARABOLIC_SAR exit strategy, which is anchored on a single trade’s entry and only exits; this one runs continuously and reverses.
SAR(t) = SAR(t−1) + AF × (EP − SAR(t−1)) EP = the extreme reached by the current leg (highest high while long) AF starts at acceleration, steps by it on each new extreme, and is capped at maximum The SAR never reaches into the previous two bars’ range. On penetration it jumps to the prior EP (not to the current price), AF resets, and the side flips.
bar →Bar
value ←Number a price level
is_long ←Boolean the side currently being trailed; false while not ready
ready ←Boolean false until two bars have been seen; one bar establishes no direction
accelerationnumber 0.001–0.5, default 0.02tunable
maximumnumber 0.01–1.0, default 0.2tunable if set below acceleration it is raised to acceleration, so AF never steps
Momentum
Indicator.Momentum
How far price has moved over the last N bars, in price units. Positive means the close is above where it was N bars ago. Reach for Indicator.ROC instead when you want the same move as a percentage. A fixed momentum threshold means something different at different price levels.
Momentum = close − close[N bars ago]
bar →Bar
value ←Number price units, signed
ready ←Boolean false until period+1 closes have been seen, because a lookback of N bars needs N+1 closes
periodinteger 1–200, default 10tunable
A zero-line cross is Math.CrossesAbove / Math.CrossesBelow against a constant 0. The cross only counts once this block has warmed up, so what it reads while warming up is never taken for a crossing.
Rate of Change
Indicator.ROC
The same move as Indicator.Momentum, expressed as a percentage of where price was. The denominator is the old close, not the current one, so the reading answers “how far has it come from there” and stays comparable across price levels.
ROC = 100 × (close − close[N]) / close[N]
bar →Bar
value ←Number percent, signed. 0 when the reference close is 0
ready ←Boolean false until period+1 closes have been seen
periodinteger 1–200, default 10tunable
On a series whose cycle length equals period, ROC is exactly 0 on every ready bar, because the lookback lands on the same phase every time.
A zero-line cross is Math.CrossesAbove / Math.CrossesBelow against a constant 0. The cross only counts once this block has warmed up, so what it reads while warming up is never taken for a crossing.
VWAP
Indicator.VWAP
A session average of the close, reset each trading day at 23:00 UTC (midnight at a fixed UTC+1, all year). The weight is the bar’s volume field, and the datasets this platform works with carry no traded volume: on bar files that field holds the bar’s range, on bars built from ticks by an Aggregator.TickToBar block it holds the number of ticks. So this is a range- or activity-weighted average, not a volume-weighted one. On the first bar of a session it equals that bar’s close.
w[i] = volume field of bar i (1 when it is 0) VWAP[t] = Σ(close[i] × w[i], i=session_start..t) / Σ(w[i], i=session_start..t) Resets: daily at 23:00 UTC (fixed UTC+1)
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
Stochastic
Indicator.Stochastic
Stochastic oscillator (0–100). %K measures where the close sits within the recent high-low range; %D smooths %K. Conventional reference levels are 20 and 80; a %K/%D crossover is %K moving from one side of %D to the other.
%K[t] = 100 × (close[t] − Lowest(low, k_period)) / (Highest(high, k_period) − Lowest(low, k_period)) %D[t] = SMA(%K, d_period)
bar →Bar
k ←Number fast stochastic 0–100
d ←Number signal line 0–100
ready ←Boolean false until k_period + d_period − 1 bars have been seen; read k and d only once this is true (both read 50 before then; k is also 50 when the range has zero width)
k_periodinteger 2–100, default 14tunable
d_periodinteger 1–50, default 3tunable
Bollinger Bands
Indicator.BollingerBands
Volatility envelope: SMA ± σ standard deviations. A close at the lower band lies σ standard deviations below the middle line. Narrow bands mean a small standard deviation of the recent closes.
Middle = SMA(close, N) σ_std = population std dev of close over N bars Upper = Middle + sigma × σ_std Lower = Middle − sigma × σ_std
bar →Bar
upper / middle / lower ←Number
ready ←Boolean false until period bars have been seen; read the bands only once this is true (all three read 0 before)
periodinteger 2–500, default 20tunable
sigmanumber 0.1–5.0, default 2.0tunable
Highest(N)
Indicator.Highest
Rolling maximum of a specified bar field over the last N CLOSED bars, excluding the bar being evaluated (the value is read before the current bar enters the window, so that close > Highest(N) can actually be true). Use for Donchian-style breakout levels or resistance detection.
Highest[t] = max(field[t-1], field[t-2], …, field[t-N]) Until N prior bars exist: ready = false and value echoes the current bar’s own field
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
fieldenum open|high|low|close, default high
Lowest(N)
Indicator.Lowest
Rolling minimum of a specified bar field over the last N CLOSED bars, excluding the bar being evaluated. Mirror of Highest(N): the lower line of a Donchian channel; close < Lowest(N) is true when the close is below that minimum.
Lowest[t] = min(field[t-1], field[t-2], …, field[t-N]) Until N prior bars exist: ready = false and value echoes the current bar’s own field
bar →Bar
value ←Number
ready ←Boolean false while the indicator is still warming up; read value only once this is true
periodinteger 2–500, default 20tunable
fieldenum open|high|low|close, default low
Consecutive Up/Down Bars
Indicator.ConsecutiveBars
Counts the current streak of consecutive higher-close or lower-close bars. The opposite counter resets to 0 on a direction change, and an unchanged close ends both streaks. For example, after four lower closes in a row down_count is 4.
if close[t] > close[t-1]: up_count += 1; down_count = 0 elif close[t] < close[t-1]: down_count += 1; up_count = 0 else (close unchanged): up_count = 0; down_count = 0
bar →Bar
up_count ←Number resets to 0 on a down bar
down_count ←Number resets to 0 on an up bar
Swing High
Indicator.SwingHigh
Identifies confirmed pivot highs using an N-bar fractal: bar[t].high must be strictly higher than every other high of the surrounding 2N+1 bars, so two equal highs are not a pivot. Because the right-side N bars must close before confirmation, the signal fires N bars after the actual pivot, with no lookahead bias. The price output holds the most recent confirmed pivot high level.
Pivot at bar[p] if: high[p] > high[i] for every other i in [p-N, p+N] Confirmed at bar[p+N] (right side complete) price = high[p] (held until next pivot; 0 until the first one)
bar →Bar
price ←Number most recent swing-high level
confirmed ←Boolean true only on confirmation bar
lookbackinteger 1–50, default 5tunable
Swing Low
Indicator.SwingLow
Mirror of Swing High for pivot lows. Fires N bars after the actual low point once the right-side N bars have confirmed the minimum. The price output holds the most recent confirmed pivot low; confirmed is true on the bar that confirms it.
Pivot at bar[p] if: low[p] < low[i] for every other i in [p-N, p+N] Confirmed at bar[p+N] price = low[p] (held until next pivot; 0 until the first one)
bar →Bar
price ←Number most recent swing-low level
confirmed ←Boolean true only on confirmation bar
lookbackinteger 1–50, default 5tunable
MACD
Indicator.MACD
Moving Average Convergence Divergence. Classic momentum indicator: the MACD line measures the gap between a fast and slow EMA. The signal line is an EMA of the MACD. The histogram makes divergences visible bar-to-bar.
MACD[t] = EMA(close, fast) − EMA(close, slow) signal[t] = EMA(MACD, signal_period) histogram[t] = MACD[t] − signal[t] Positive histogram → upward momentum building All three read 0 until slow_period + signal_period − 1 bars (no ready port)
bar →Bar
macd ←Number MACD line
signal ←Number signal line
histogram ←Number MACD − signal
fast_periodinteger 2–200, default 12tunable
slow_periodinteger 5–500, default 26tunable
signal_periodinteger 2–100, default 9tunable
Use Math.CrossesAbove(macd, signal) for signal-line crosses, and Math.CrossesAbove(histogram, 0) / Math.CrossesBelow(histogram, 0) for a histogram sign change. Neither fires before MACD has warmed up, even though MACD has no ready port of its own.
Efficiency Ratio (ER)
Indicator.EfficiencyRatio
Kaufman Efficiency Ratio. Divides the net close-to-close displacement over N bars by the total path length (the sum of the N absolute bar-to-bar changes), limited to 0..1. 1.0 = price moved N bars in one direction without retracing; near 0 = the net move was small against the path travelled.
direction = |close[t] − close[t-N]| noise = Σ |close[i] − close[i-1]| for i = t-N+1 .. t ER[t] = direction / noise (range 0.0–1.0) Returns 0 until N+1 bars available
bar →Bar
value ←Number 0.0–1.0
periodinteger 2–500, default 20tunable
Keltner Channel
Indicator.KeltnerChannel
ATR-based envelope around an EMA: middle = EMA of the close, upper / lower = middle ± mult × ATR. The width follows the Average True Range, where Bollinger Bands follow the standard deviation of the close; whether the Bollinger Bands lie inside the channel is a condition you can wire with Compare blocks.
middle = EMA(close, ema_period) upper = middle + mult × ATR(atr_period) lower = middle − mult × ATR(atr_period)
bar →Bar
upper / middle / lower ←Number
ema_periodinteger 2–500, default 20tunable
atr_periodinteger 2–200, default 14tunable
multnumber 0.1–10.0, default 2.0tunable
Z-Score
Indicator.ZScore
Rolling statistical z-score of close relative to its recent mean and standard deviation. Measures how many standard deviations price is from its recent average. Z = −2 is a close two standard deviations below its recent mean; Z = +2 is two above it.
mean[t] = SMA(close, N) std[t] = population std dev of close over N bars Z[t] = (close[t] − mean[t]) / std[t] Returns 0 until N bars available. std=0 → Z=0.
bar →Bar
value ←Number typical range ±1.5 to ±3.0
periodinteger 2–500, default 20tunable
A zero-line cross is Math.CrossesAbove / Math.CrossesBelow against a constant 0. Z-Score reads 0 while it warms up and has no ready port, but the cross still waits until it is warmed up.
Pivot Points
Indicator.PivotPoints
Classic floor-trader pivot points computed from the prior day's high, low, and close. Days are cut at 23:00 UTC all year (a fixed UTC+1, no daylight saving). All 7 levels update once per day and are stable intraday. Returns 0 for all outputs until the first day roll; the first levels are built from whatever part of that first day the run saw. Every day that has bars counts as a day, so a short Sunday session in the data becomes Monday’s “previous day”.
P = (prev_H + prev_L + prev_C) / 3 R1 = 2P − prev_L R2 = P + (prev_H − prev_L) R3 = prev_H + 2 × (P − prev_L) S1 = 2P − prev_H S2 = P − (prev_H − prev_L) S3 = prev_L − 2 × (prev_H − P)
bar →Bar
p ←Number pivot point
r1, r2, r3 ←Number resistance levels 1–3
s1, s2, s3 ←Number support levels 1–3
Support & Resistance
Indicator.SupportResistance
Composite structural level detector. Combines pivot fractals, session extremes, prior-day high/low, overnight range, weekly extremes, opening range, and round numbers into a unified support/resistance model. A new pivot level within merge_zone_pts of an existing pivot level is merged into it; session, previous-day, overnight, weekly, opening-range and round-number levels are not merged. Proximity gates use ATR multiples to define the "touch zone". All clock times are a fixed UTC+1 (no daylight saving), and the overnight window always opens at 22:00. If atr_value is not wired, 1.0 price unit is used as the ATR. A pivot level is discarded once a close breaks it by 0.3 × ATR.
Inputs: bar + atr_value Level sources: • N-bar pivot fractal highs/lows (pivot_lookback) • Session high/low (the current day, reset at 23:00 UTC; session_open_hour/_minute only place the opening range) • Previous calendar day H/L • Overnight range H/L (22:00–overnight_end_hour CET) • Weekly H/L (Monday-aligned week) • Opening range H/L (first or_duration_minutes) • Round numbers (every round_number_step pts) Merge: a pivot level within merge_zone_pts of a pivot level → one level at_support = dist_support ≤ touch_zone_atr_mult × ATR
barBar
atr_valueNumber from Indicator.ATR
support / resistanceNumber nearest level price
dist_support / dist_resistanceNumber price units (close − support, resistance − close); −1 when no level exists
at_support / at_resistanceBoolean proximity gate
session_high, session_lowNumber
prev_day_high, prev_day_lowNumber
overnight_high, overnight_lowNumber
weekly_high, weekly_lowNumber
or_high, or_lowNumber opening range
or_completeBoolean OR window closed
pivot_lookbackinteger 3–50, default 10tunable
touch_zone_atr_multnumber 0.1–2.0, default 0.5tunable
merge_zone_ptsnumber 1–50, default 5tunable
round_number_stepnumber 10–1000, default 100tunable
session_open_hourinteger 0–23, default 8tunable
session_open_minuteinteger 0–59, default 0tunable
or_duration_minutesinteger 5–120, default 30tunable
overnight_end_hourinteger 0–12, default 9tunable
Second Attempt
Indicator.SecondAttempt
Fires when price makes its second approach to a level (a bar within 0.5 ATR of it) after having pulled back by at least pullback_min_atr ATRs. If either phase lasts longer than max_bars_between bars, counting starts again. Wire a SupportResistance.support price into level.
State machine per level: IDLE → TOUCHING (the bar's low reaches the level + 0.5 × ATR; the high reaches level − 0.5 × ATR for resistance) TOUCHING → PULLED_BACK (a close more than pullback_min_atr × ATR away) PULLED_BACK → TOUCHING (second approach) → is_second_attempt = true (fires once) attempt_count: 1 from the first touch until the second, 2 on the bar that fires, 0 otherwise max_bars_between: resets state if too much time passes A level that moves by more than 0.5 × ATR restarts the count
bar →Bar
level →Number e.g. SupportResistance.support
atr →Number
is_second_attempt ←Boolean
attempt_count ←Number
is_supportboolean default true
pullback_min_atrnumber 0.1–3.0, default 0.5tunable
max_bars_betweeninteger 3–100, default 20tunable
Break + Retest
Indicator.LevelBreakRetest
Detects a clean level break then waits for a pullback retest. A break is confirmed when close is beyond the level by break_atr_mult × ATR. Once broken, is_breakout stays true until a retest is detected, max_retest_bars expires, or a close back beyond the level by break_atr_mult × ATR cancels the break. A break that fired or expired cannot re-arm in the same direction until price has come back inside that threshold. A level of 0 is treated as a real level, so gate the input when no level exists. Retesting the broken level from the new side fires is_retest_long or is_retest_short once.
Break confirmed: close > level + break_atr_mult × ATR (upward) → is_breakout = true, broken_level = level Retest (long): low ≤ broken_level + touch_zone_atr_mult × ATR and close ≥ broken_level → is_retest_long = true (fires once) Invalidated (long): close < broken_level − break_atr_mult × ATR State resets if max_retest_bars exceeded
bar →Bar
level →Number
atr →Number
is_breakout ←Boolean true while waiting for retest
is_retest_long ←Boolean fires once on long retest
is_retest_short ←Boolean fires once on short retest
broken_level ←Number the broken level up to its retest bar, else 0
break_atr_multnumber 0.1–2.0, default 0.3tunable
touch_zone_atr_multnumber 0.1–2.0, default 0.5tunable
max_retest_barsinteger 3–60, default 15tunable
Doji
Indicator.Doji
Single-bar indecision candle: a body that is negligible next to the bar’s own range. The threshold is a fraction of the range, not a price, so the same setting means the same thing on EURUSD and on the DAX. A bar with zero range (open = high = low = close, which turns up in synthetic and thin data) is deliberately not reported: that is missing data, not indecision.
body = |close − open| range = high − low doji = range > 0 and body ≤ body_ratio × range
bar →Bar
value ←Boolean true on a bar whose body is at most body_ratio of its range
ready ←Boolean false until the first bar has been seen
body_rationumber 0.001–0.5, default 0.1tunable
Hammer / Shooting Star
Indicator.Hammer
These outputs name a shape, not a direction. A hammer and a hanging man are the same candle; so are an inverted hammer and a shooting star. What separates them is the trend that came before, which this block cannot see. Combine it with Gate.TrendGate or an EMA comparison to supply that context. All three thresholds are fractions of the bar’s range, and since range = body + upper + lower, the defaults 0.6 / 0.3 / 0.1 form one partition of the range rather than three unrelated numbers. They also imply the textbook “lower shadow at least twice the body” without ever dividing by a body, which can be zero.
upper = high − max(open, close) lower = min(open, close) − low hammer = lower ≥ shadow_ratio × range and body ≤ body_ratio × range and upper ≤ opposite_ratio × range shooting_star = the same with upper and lower exchanged
bar →Bar
hammer ←Boolean long lower shadow, small body near the top (hammer / hanging man shape)
shooting_star ←Boolean long upper shadow, small body near the bottom (inverted hammer / shooting star shape)
ready ←Boolean false until the first bar has been seen
shadow_rationumber 0.3–0.95, default 0.6tunable
body_rationumber 0.01–0.6, default 0.3tunable
opposite_rationumber 0.0–0.4, default 0.1tunable
Engulfing
Indicator.Engulfing
Two-bar reversal: the current body swallows the previous one and the two bars have opposite colours. Note the bound on the open is inclusive. In continuous FX and CFD data open[t] equals close[t−1] on essentially every bar, so the textbook’s strict “opens beyond the previous close” would make bullish engulfing almost undetectable; the strict bound is kept where the pattern’s actual claim lives, on the close. The previous bar is guaranteed a non-zero body by the colour test, so the classic “everything engulfs a doji” false positive cannot arise.
bullish = prev bearish and current bullish and open ≤ prev_close and close > prev_open bearish = prev bullish and current bearish and open ≥ prev_close and close < prev_open
bar →Bar
bullish ←Boolean down bar followed by an up bar closing above the down bar’s open
bearish ←Boolean up bar followed by a down bar closing below the up bar’s open
ready ←Boolean false until two bars have been seen
Harami
Indicator.Harami
Two-bar inside pattern: the previous body swallows the current one, colours opposite. These are the engulfing inequalities with the close bound flipped, which makes harami and engulfing mutually exclusive: one needs a close beyond the previous open, the other short of it, so no bar can report both.
bullish = prev bearish and current bullish and open ≥ prev_close and close < prev_open bearish = prev bullish and current bearish and open ≤ prev_close and close > prev_open
bar →Bar
bullish ←Boolean down bar followed by a small up bar contained inside its body
bearish ←Boolean up bar followed by a small down bar contained inside its body
ready ←Boolean false until two bars have been seen
Piercing Line / Dark Cloud
Indicator.PiercingDarkCloud
Two-bar partial reversal: the current bar reverses past a penetration level inside the previous body but stops short of the previous open. A close beyond the previous open is an engulfing, so the two blocks never claim the same bar. The textbook demands an opening gap beyond the prior bar’s extreme; that is relaxed to “opens at or beyond the previous close” for the same continuous-data reason as Engulfing.
level = prev_close + penetration × (prev_open − prev_close) bullish = prev bearish and current bullish and open ≤ prev_close and close > level and close < prev_open bearish = the mirror image
bar →Bar
bullish ←Boolean piercing line
bearish ←Boolean dark cloud cover
ready ←Boolean false until two bars have been seen
penetrationnumber 0.1–0.9, default 0.5tunable
Morning / Evening Star
Indicator.Star
Three-bar reversal: a directional bar, a small “star” body, then a bar reclaiming past the penetration level of the first body. The star’s body limit is relative to the first bar’s body, so the block behaves the same on a quiet day and a violent one. The textbook gap around the star cannot occur in continuous data; it is relaxed to “the star reaches down to at least the first bar’s close” (up to, for an evening star), which keeps the part of the claim that carries meaning and drops the part that only encodes a session boundary.
level = first_close + penetration × (first_open − first_close) bullish = first bar bearish and star_body ≤ star_body_ratio × first_body and min(star_open, star_close) ≤ first_close and third bar bullish and third_close > level bearish = the mirror image
bar →Bar
bullish ←Boolean morning star
bearish ←Boolean evening star
ready ←Boolean false until three bars have been seen
star_body_rationumber 0.05–1.0, default 0.5tunable
penetrationnumber 0.1–0.9, default 0.5tunable
Three Soldiers / Crows
Indicator.ThreeSoldiers
Three consecutive same-colour bars with progressively extending closes, each opening inside the previous bar’s body. That last clause is what separates the pattern from “three up bars in a row”: a sequence that gaps away from each previous body is a different thing entirely. The bound is inclusive because in continuous data the open sits exactly on the previous close, which is the far edge of the body.
bullish = three bullish bars and close₁ > close₀ and close₂ > close₁ and open₁ between open₀ and close₀ (inclusive) and open₂ between open₁ and close₁ (inclusive) bearish = the mirror image
bar →Bar
bullish ←Boolean three white soldiers
bearish ←Boolean three black crows
ready ←Boolean false until three bars have been seen
Double Top / Bottom
Indicator.DoubleTopBottom
Two level extremes either side of one counter-pivot, completed when the close breaks that counter-pivot (the neckline). Built on the same confirmed-pivot detector as Indicator.SwingHigh / SwingLow.

Every tolerance is a fraction of the pattern’s own height (the distance from the peaks down to the trough between them), never of price and never of ATR. A pattern twice as tall is allowed twice the absolute discrepancy between its peaks, because it looks the same. That makes one default work across instruments, timeframes and pattern sizes alike. If you want a minimum size filter as well, compose one from Indicator.ATR: that is a volatility question, not a shape question, and folding it in here would give the block a second hidden time scale.

It fires on the breakout close, not when the shape completes. The second pivot is only confirmed lookback bars after the fact, so firing there would be both late and untradeable. A close-based break is also immune to the single-wick false break that a high/low-based one is not. The pattern is discarded unfired if price closes beyond the two extremes first, if the confirmation window expires, or if a new pivot changes the skeleton.
A, M, B = three consecutive alternating pivots height = |extreme(A, B) − M| level : |A − B| ≤ price_tolerance × height timing : longer leg ≤ time_ratio_max × shorter leg window : the break must land within confirm_within_ratio × (B.bar − A.bar) bars of B (the product is rounded down, and a break on the last bar of the window still counts; B is confirmed only lookback bars later, so a product below lookback can never fire) double top → bearish on the first close below M double bottom → bullish on the first close above M
bar →Bar
bullish ←Boolean double bottom: true on the bar closing above the neckline
bearish ←Boolean double top: true on the bar closing below the neckline
ready ←Boolean false until three confirmed pivots exist
lookbackinteger 1–50, default 5tunable
price_tolerancenumber 0.01–0.5, default 0.1tunable
time_ratio_maxnumber 1.0–10.0, default 3.0tunable
confirm_within_rationumber 0.1–5.0, default 1.0tunable
Head & Shoulders
Indicator.HeadAndShoulders
Five-pivot reversal: two roughly level shoulders around a dominant head, completed when the close breaks the neckline. The neckline is fitted through the two troughs and extrapolated forward, not assumed horizontal. A sloping neckline is a named variant of the pattern, and neckline_tolerance only bounds how steep the fit may get before the shape stops being a head and shoulders at all. It is looser than price_tolerance for exactly that reason: a sloping neckline is normal, asymmetric shoulders are not.

Note that price_tolerance works in both directions: the shoulders must be within it of each other, and the head must stand clear of them by more than it. One threshold defines what “indistinguishable” means; the shoulders must be it and the head must not.
H₁, T₁, HEAD, T₂, H₂ = five alternating pivots height = HEAD − (T₁ + T₂) / 2 (inverse: the mirror; must be > 0) head : HEAD − higher shoulder > price_tolerance × height (inverse: lower shoulder − HEAD) shoulders : |H₁ − H₂| ≤ price_tolerance × height neckline : |T₁ − T₂| ≤ neckline_tolerance × height N(t) = T₁ + (T₂ − T₁) × (t − t₁) / (t₂ − t₁) fires on the first close through N(t)
bar →Bar
bullish ←Boolean inverse head and shoulders: true on the bar closing above the neckline
bearish ←Boolean head and shoulders top: true on the bar closing below the neckline
ready ←Boolean false until five confirmed pivots exist
lookbackinteger 1–50, default 5tunable
price_tolerancenumber 0.01–0.5, default 0.1tunable
neckline_tolerancenumber 0.01–1.0, default 0.25tunable
confirm_within_rationumber 0.1–5.0, default 1.0tunable
Triangle
Indicator.Triangle
Four-pivot continuation shapes on converging boundaries: ascending (flat highs, rising lows), descending (flat lows, falling highs) and symmetrical (both converging). A shape with both boundaries flat is a rectangle and matches none of the three.

The break levels are horizontal: the upper one is the higher of the two swing highs, the lower one the lower of the two swing lows, both fixed when the shape is recognised, not the sloping trendlines. A symmetrical triangle therefore breaks only on a close beyond its outermost pivot.

This is the one pattern block with more than two signal outputs. The three shape ports are true on each completed bar while the shape is armed (behind an aggregator: only on the evaluation that completes a bar), so a graph can gate on “a triangle is forming” separately from the break. An ascending triangle that breaks downward is a failure worth reacting to, and the direction ports alone cannot express that. The tolerance is a fraction of the triangle’s own span.
span = highest high − lowest low of the four pivots band = price_tolerance × span ascending = |H₂ − H₁| ≤ band and L₂ − L₁ > band descending = |L₂ − L₁| ≤ band and H₁ − H₂ > band symmetrical = H₁ − H₂ > band and L₂ − L₁ > band upper = max(H₁, H₂), lower = min(L₁, L₂) (horizontal) bullish → close above upper bearish → close below lower window: confirm_within_ratio × (last pivot bar − first pivot bar) bars after the last pivot
bar →Bar
bullish ←Boolean close breaks above an ascending or symmetrical triangle
bearish ←Boolean close breaks below a descending or symmetrical triangle
ascending ←Boolean true while an ascending triangle is recognised and unbroken
descending ←Boolean true while a descending triangle is recognised and unbroken
symmetrical ←Boolean true while a symmetrical triangle is recognised and unbroken
ready ←Boolean false until four confirmed pivots exist
lookbackinteger 1–50, default 5tunable
price_tolerancenumber 0.01–0.5, default 0.1tunable
confirm_within_rationumber 0.1–5.0, default 1.0tunable
Flag
Indicator.Flag
Four-pivot continuation: a strong impulse leg (the pole) followed by a shallow counter-trend consolidation, completed when the close breaks back through the pole’s tip. pole_ratio is the pole’s height as a multiple of the consolidation’s own height. That ratio is the visual definition of a flag, and it is self-relative like every other threshold here. max_retracement caps how much of the pole the pullback may give back; deeper than that is a reversal candidate, not a continuation, and the two should not share a signal.

The default lookback is 3 rather than 5 because a flag’s consolidation is short by definition, and a 5-bar fractal often cannot resolve two pivots inside one.
P₀, P₁, P₂, P₃ = four alternating pivots (bull flag: low, high, low, high) pole = P₁ − P₀ flag_range = P₃ − P₂ strength : pole ≥ pole_ratio × flag_range shallow : P₂ > P₁ − max_retracement × pole contained : P₃ ≤ P₁ holds : P₂ > P₀ fires bullish on the first close above P₁ discarded if a close falls below P₂ first window: confirm_within_ratio × (P₃.bar − P₁.bar) bars after P₃
bar →Bar
bullish ←Boolean bull flag: true on the bar closing above the pole high
bearish ←Boolean bear flag: true on the bar closing below the pole low
ready ←Boolean false until four confirmed pivots exist
lookbackinteger 1–50, default 3tunable
pole_rationumber 1.0–10.0, default 2.0tunable
max_retracementnumber 0.05–0.95, default 0.5tunable
confirm_within_rationumber 0.1–5.0, default 1.0tunable

Gate

Filters that block or allow entry based on time, trend, volatility, or regime. Wire Gate outputs into Logic.And with your entry conditions. While warming up, Trend Gate and Volatility Gate allow entries, HMM Regime and Regime Cluster block them (allow = false), and Regime Filter reports NEUTRAL.

Session Filter
Gate.Session
True iff the incoming bar’s timestamp (the start of its period) falls within the configured CET window. Feed it the base bar: fed from an aggregator it tests the start of the last completed higher-timeframe bar. Wired into an entry's trigger, it restricts entries to the configured time window. The window is half-open (start ≤ t < end), and a start after the end means an overnight session. This is the only daily clock in the graph that follows CET/CEST: Aggregator.D1, Indicator.VWAP and Indicator.PivotPoints key their day on a fixed UTC+1 all year (see the note under the Aggregators). All four hour and minute parameters can be marked tunable and searched by PSO, which is how a session start is optimized rather than guessed.
bar →Bar
allowed ←Boolean
start_hourinteger 0–23, default 7tunable
start_minuteinteger 0–59, default 0tunable
end_hourinteger 0–23, default 20tunable
end_minuteinteger 0–59, default 0tunable
use_cetboolean default true false evaluates the window in UTC
Trend Gate
Gate.TrendGate
Blocks LONG entries while the ATR-normalised EMA slope is below the threshold (block_long_below_slope). The slope output is available for further gating or plotting.
slope[t] = (EMA[t] − EMA[t − slope_lookback]) / ATR[t] (ATR EMA-smoothed, α = 2 / (atr_period + 1)) allow_long = slope[t] ≥ block_long_below_slope
bar →Bar
allow_long ←Boolean
slope ←Number + = uptrend, − = downtrend
ema_periodinteger 5–500, default 50tunable
atr_periodinteger 2–200, default 14tunable
slope_lookbackinteger 2–200, default 12tunable
block_long_below_slopenumber −5.0–5.0, default −0.5tunable
Volatility Gate
Gate.VolatilityGate
Blocks all entries when current ATR is in an extreme percentile of recent history. Prevents trading during abnormal volatility spikes (e.g. flash crashes, macro data releases). The percentile output shows the rolling rank 0–1.
atr_history = rolling window of ATR(atr_period) values percentile[t] = rank of ATR[t] in atr_history / len(atr_history) (0=lowest, 1=highest) allow_entry = percentile[t] ≤ block_above_percentile Filters only after min(200, percentile_window) ATR samples; until then allow_entry = true and percentile = 0 (ATR EMA-smoothed, α = 2 / (atr_period + 1))
bar →Bar
allow_entry ←Boolean
percentile ←Number 0–1 ATR rank
atr_periodinteger 2–200, default 14tunable
percentile_windowinteger 100–20000, default 2000tunable
block_above_percentilenumber 0.0–1.0, default 0.80tunable
Regime Filter
Gate.RegimeFilter
Classifies the current market into four mutually-exclusive regimes using a multi-signal model. Each regime is exposed as a Boolean output so you can gate different strategy logic to the regime where it performs best.
Inputs used: slope = slow-EMA change in % per bar over ema_slope_lookback separation = |EMA(fast) − EMA(slow)| / EMA(slow), in % ATR percentile (rolling) range_compression = (high − low of the last bo_lookback_bars) / (ATR × √bo_lookback_bars) Regimes: TRENDING = |slope| ≥ min_slope AND separation ≥ min_sep AND ATR / 100-bar mean ATR ≥ min_atr_ratio_for_trending MEAN_REVERTING = |slope| ≤ max_slope AND atr_pct ≤ max_pct BREAKOUT = range_compression ≤ bo_min_range_compression AND ATR / 100-bar mean ATR ≥ min_atr_ratio_for_breakout NEUTRAL = none of the above Several match → TRENDING, then BREAKOUT, then MEAN_REVERTING After each change the regime is held for min_bars_in_regime bars, then switches on the first bar that classifies differently NEUTRAL until max(ema_slow_period, atr_period) + 10 bars
bar →Bar
is_trending ←Boolean
is_mean_reverting ←Boolean
is_breakout ←Boolean
is_neutral ←Boolean
ema_fast_periodinteger 2–200, default 20tunable
ema_slow_periodinteger 5–500, default 50tunable
atr_periodinteger 2–200, default 14tunable
ema_slope_lookbackinteger 2–200, default 10tunable
min_bars_in_regimeinteger 1–200, default 10tunable
trending_min_ema_slopenumber 0–5, default 0.0081tunable
trending_min_ema_separationnumber 0–5, default 0.0254tunable
mr_max_ema_slopenumber 0–5, default 0.0017tunable
mr_max_atr_percentilenumber 0–1, default 0.4tunable
bo_min_range_compressionnumber 0–5, default 0.5tunable
bo_lookback_barsinteger 2–200, default 20tunable
min_atr_ratio_for_trendingnumber 0–3, default 0.79tunable
min_atr_ratio_for_breakoutnumber 0–3, default 0.64tunable
HMM Regime
Gate.HMMRegime
A Gaussian hidden Markov model with n_states states (2–5, default 3) over each bar's log-return and rolling volatility. Inside a walk-forward run the model is fitted on every block's in-sample window and applied to that block's bars with the forward filter, one bar at a time: every probability uses only the bars up to the current one, never later ones. The next block refits from scratch. States are numbered by ascending fitted volatility mean, so p_state_0 is the lowest-volatility state after every refit. The probability outputs at or beyond the node's own state count read 0, and a handed-in model must have that same state count. Usable in walk-forward runs, and in a backtest with an in-sample split (the model is fitted on the in-sample part) or one that is handed the walk-forward block's own model (Block Detail). A backtest with neither refuses the graph, and so do Optimize and the parameter sweep. While ready is false, allow is false, most_likely_state is −1 and every probability reads 1/n_states (the uniform prior, not a posterior), so a Compare on a probability, or an ordering Compare on most_likely_state, can still pass on those bars: gate on ready.
observation[t] = ( ln(close[t] / close[t-1]), stdev of the last vol_lookback log-returns ) fit (per walk-forward block, in-sample window only): Baum-Welch, n_restarts seeded starts, keep the highest log-likelihood inference (every bar, fixed model): p[t] = normalise( (p[t-1] · A) ⊙ N(observation[t] | state) ) allow = p_state_<gate_state>[t] ≥ min_probability
bar →Bar
allow ←Boolean false while not ready
ready ←Boolean
p_state_0 ←Number lowest volatility state; 1/n_states while not ready
p_state_1 ←Number 1/n_states while not ready
p_state_2 ←Number 0 when n_states = 2; 1/n_states while not ready
p_state_3 ←Number 0 when n_states ≤ 3; 1/n_states while not ready
p_state_4 ←Number 0 when n_states ≤ 4; 1/n_states while not ready
most_likely_state ←Number 0–(n_states−1); −1 while not ready
n_statesinteger 2–5, default 3
vol_lookbackinteger 2–500, default 10
use_volumeboolean default false; adds the bar’s volume relative to its rolling mean as an observation
use_rangeboolean default false; adds the bar’s range, (high − low) / close, as an observation
n_restartsinteger 1–50, default 5
max_iterationsinteger 10–2000, default 200
seedinteger default 42
gate_stateinteger 0–(n_states−1), default 0
min_probabilitynumber 0.0–1.0, default 0.50
Regime Cluster
Gate.RegimeCluster
Finds market regimes empirically instead of by fixed thresholds. Each bar is described by three features: realised volatility (ATR over close), trend strength (ADX over adx_period) and the lag-1 autocorrelation of log returns, which separates mean-reverting from momentum character. No volume: the bar datasets do not reliably carry one. Inside a walk-forward run the block is fitted with k-means on every block's in-sample window; the number of clusters is chosen automatically by silhouette score over the fixed range 2–6, counting only partitions whose smallest cluster holds at least 5% of the window, because a handful of transition bars after a regime switch is not a regime of its own. If no cluster count meets that rule, the most balanced partition is kept and the walk-forward overview flags it. Each feature is weighted by how clearly its values fall into groups, beyond what random noise would show, so a feature that is only noise does not steer the clusters; the weights are shown per block in the walk-forward overview. Each bar is then assigned to the nearest cluster using only that bar's own features, which makes it causal by construction. The next block refits from scratch. Clusters are numbered by ascending volatility centroid, so cluster 0 is the calmest cluster after every refit. Only cluster 0 keeps that meaning across blocks: the count is chosen again for every block, so cluster 2 in one block need not be the same regime as cluster 2 in the next. The is_cluster_k outputs at or beyond the fitted count read false. A cluster is described only by its measured feature means (mean volatility, mean trend strength, mean autocorrelation of its bars); nothing ranks one cluster against another. Usable in walk-forward runs, and in a backtest with an in-sample split (fitted on the in-sample part) or one that is handed the walk-forward block's own model (Block Detail). A backtest with neither refuses the graph, and so do Optimize and the parameter sweep, and so does a gate_cluster the fit did not find: the run is refused rather than silently never allowing. While ready is false, allow and every is_cluster_k are false, cluster is −1 and the feature outputs read 0: gate on ready.
feature[t] = ( ATR(atr_period)[t] / close[t], ADX(adx_period)[t], lag-1 autocorrelation of the last autocorr_lookback log returns ) fit (per walk-forward block, in-sample window only): standardise each feature by the window's mean and std. dev. weight each feature by its grouping beyond Gaussian noise of the same size for k in 2..6: weighted k-means (n_restarts k-means++ starts, lowest inertia kept) eligible k: every cluster holds at least 5% of the window keep the eligible k with the highest silhouette score (smaller k on a tie) number clusters by ascending volatility centroid inference (every bar, fixed model): cluster[t] = nearest centroid of the standardised feature[t], same weights allow = cluster[t] == gate_cluster
bar →Bar
allow ←Boolean cluster == gate_cluster; false while not ready
ready ←Boolean features warm and a fitted model has assigned a bar
cluster ←Number 0–(n_clusters−1), 0 = lowest volatility; −1 while not ready
n_clusters ←Number the count the fit chose, 2–6; 0 without a model
is_cluster_0 ←Boolean the lowest-volatility cluster; false while not ready
is_cluster_1 ←Boolean false while not ready
is_cluster_2 ←Boolean false when the fit found fewer than 3 clusters
is_cluster_3 ←Boolean false when the fit found fewer than 4 clusters
is_cluster_4 ←Boolean false when the fit found fewer than 5 clusters
is_cluster_5 ←Boolean false when the fit found fewer than 6 clusters
volatility ←Number ATR / close; 0 while warming up
trend_strength ←Number ADX, 0–100; 0 while warming up
autocorrelation ←Number −1..1, negative = mean-reverting, positive = momentum; 0 while warming up
atr_periodinteger 2–200, default 14
adx_periodinteger 2–200, default 14
autocorr_lookbackinteger 5–500, default 20
gate_clusterinteger 0–5, default 0; must be below the fitted cluster count
n_restartsinteger 1–50, default 5
max_iterationsinteger 10–1000, default 100
seedinteger default 42

Compare

Numeric comparison operators. Two Number inputs → one Boolean output. No parameters except Compare.EQ which has an epsilon tolerance.

BlockTypeLogicNotes
a > bCompare.GTtrue if a > bstrict, no tolerance
a ≥ bCompare.GTEtrue if a ≥ b
a < bCompare.LTtrue if a < bstrict
a ≤ bCompare.LTEtrue if a ≤ b
a ≈ bCompare.EQ|a − b| ≤ εparam epsilon default 1e-9

All compare blocks have two Number inputs named a and b, and one Boolean output named value.

Logic

Boolean logic gates for combining conditions. AND/OR/NOT have no parameters. IF-THEN-ELSE bridges Boolean conditions into numeric values.

BlockTypeInputsOutputLogic
ANDLogic.Anda, b (Boolean)Booleana AND b
ORLogic.Ora, b (Boolean)Booleana OR b
NOTLogic.Nota (Boolean)BooleanNOT a
IF-THEN-ELSELogic.IfThenElsecondition (Bool), value_if_true, value_if_false (Number)Number, output port resultcondition ? a : b

Use Logic.And to combine gate conditions (session AND regime AND entry condition). Use Logic.IfThenElse to select between two stop distances based on a condition, or implement a breakeven guard.

Math

Arithmetic operations on Number streams. Most have two Number inputs and one Number output. Crossover blocks output Boolean. Rolling blocks operate over a history window.

BlockTypeFormula / Notes
a + bMath.Addvalue = a + b
a − bMath.Subvalue = a − b. Use: HighSinceEntry − ATR×mult for trailing
a × bMath.Mulvalue = a × b. Use: ATR × constant for dynamic SL sizing
a ÷ bMath.Divvalue = a / b. Returns 0 if |b| ≤ 1e-12 (prevents NaN)
|a|Math.Absvalue = |a|. Single input.
min(a,b)Math.Minvalue = min(a, b)
max(a,b)Math.Maxvalue = max(a, b). Use: max(PositionStopLoss, newTrail) to only tighten
a crosses above bMath.CrossesAbovetrue only on the bar where a transitions from ≤ b to > b (prev_a ≤ prev_b AND a > b)
a crosses below bMath.CrossesBelowtrue only on the bar where a transitions from ≥ b to < b. Both blocks fire only between two samples on which every warming-up block feeding them (indicators, Math.Lag, gates, also through other Math blocks) was ready, so a warm-up reading is never taken for a crossing. A constant has no warm-up, so a cross against 0 is an ordinary cross. A 0 read directly from an output where 0 means “no level” (a swing price before the first swing, a support or resistance that does not exist, broken_level with no live break) counts as missing.
Rolling SMAMath.RollingSMASMA of upstream Number stream over param period (2–1000, default 20)tunable. E.g. SMA(ATR, 50). Outputs value (Number) and ready (Boolean, false until the window is full); before that, value is the average of the samples seen so far.
Lag(N)Math.LagReturns value from N bars ago (param period 1–500, default 1). Returns 0 until N+1 values seen. Input/output: Number Outputs value (Number) and ready (Boolean, false until N+1 values have streamed).
LagBar(N)Math.LagBarReturns the upstream Bar from N completed higher-timeframe bars ago. Advances only when bar.timestamp changes. It counts completed bars from any upstream Aggregator (D1, M60, M30, M15, M5, M1) regardless of the graph's base timeframe. An aggregator already publishes the last completed bar, so Aggregator.D1 → Bar.Field(high) is yesterday's high; Aggregator.D1 → LagBar(1) → Bar.Field(high) is the day before yesterday, and LagBar(N) is N completed days before the last completed one. Returns no bar until N+1 distinct bars seen.
Custom formulaMath.FormulaYour own calculation over inputs you declare on the block: each input has a name and a type (Number or Boolean, at most 16), the one output likewise, and the formula is written over the input names. Operators: + - * / % ^ (^ is the power, right-associative, and binds tighter than a sign: -2^2 is −4), comparisons < <= > >= == != (not chained; == on numbers is exact), and or not (also && || !) and a conditional c ? a : b or if(c, a, b). Functions: abs sqrt floor ceil round sign exp log min max pow clamp (round is half away from zero, log is natural, clamp(x, lo, hi) = min(max(x, lo), hi)). Nothing else exists: no loops, no text, no names other than the inputs. The formula is checked when the graph is saved and again when a run starts; one that does not compile refuses the graph, and every declared input must be connected. Stateless: the same inputs always give the same value. A result that is not a finite number (division by zero, the square root of a negative number, log of zero) publishes no value on that bar rather than 0; b == 0 ? 0 : a / b is the guard, and ? :, if, and, or evaluate only the side they need. Example: inputs high, low, close; formula (high - low) / close * 100, the bar’s range in percent.

The Math.CrossesAbove / Math.CrossesBelow blocks output Boolean. Math.LagBar takes a Bar input and outputs a Bar. All other Math blocks take and output Number. RollingSMA and Lag take a single input named value; all others take a (and b where applicable).

Sink

Output blocks that interact with the position manager. Sinks have no outputs; they cause side effects (opening positions, closing them, moving stops).

Entry LONG
Sink.Entry.Long
Opens a LONG position when trigger is true and the strategy’s position limits allow one more long. Under the default of one position at a time that means no position is open, no fill is pending and no other entry fired on this bar. If LONG and SHORT trigger together, only the first in graph order is taken. In Graph Settings a strategy can allow up to 8 open positions per direction, and longs and shorts open at the same time (hedging); each entry then opens its own position with its own stop and target, and a pending fill counts against the limit like an open position. Every entry is a market order that fills at the next bar’s open, plus spread and slippage. SL and TP distances are captured at fire-time and measured from the actual fill price. stop_loss_distance is required: without a value above 0 the entry never fires.
triggerBoolean entry fires on true while the position limits allow it (default: when flat)
stop_loss_distanceNumber required, > 0: points below fill price
take_profit_distanceNumber optional: points above fill price; a negative value blocks the entry
reasonstring default "graph_long"; the label this entry records on its trade, shown next to the exit reason in the trade list (plain text, at most 64 characters, control characters removed); it never changes the fill
sizenumber default 1.0: position size in units; must be greater than 0
Entry SHORT
Sink.Entry.Short
Opens a SHORT position under the same rules as Entry LONG: at the next bar’s open, only while the position limits allow one more short (by default: only when no position is open), and only with a stop_loss_distance above 0. SL and TP are distances from fill price; they are added and subtracted respectively in the SHORT direction automatically.
triggerBoolean entry fires on true while the position limits allow it (default: when flat)
stop_loss_distanceNumber required, > 0: points above fill price
take_profit_distanceNumber optional: points below fill price; a negative value blocks the entry
reasonstring default "graph_short"; the label this entry records on its trade, shown next to the exit reason in the trade list (plain text, at most 64 characters, control characters removed); it never changes the fill
sizenumber default 1.0: position size in units; must be greater than 0
Exit
Sink.Exit
Closes open position(s) when trigger fires true. target is required and says which: all, oldest, newest, or by_id. There is no default: a graph that does not state a target will not load, because "close the position" is ambiguous the moment two are open. side_filter narrows the candidates first, so target=oldest with side_filter=SHORT closes the oldest short. An exit fills at the close of the bar it fires on, minus spread and slippage, unlike an entry, which fills at the next bar’s open.
triggerBoolean
position_idNumber optional; required only when target=by_id; names the position to close
targetenum all | oldest | newest | by_id; no default, must be set
side_filterenum ANY | LONG | SHORT, default ANY
reasonstring default "graph_exit"; a label recorded on the trade; a graph exit is always priced as a market exit, whatever it is called
fractionnumber default 1: the share of each chosen position to close. 1 closes it outright; 0.5 closes half and leaves the rest open with its stop-loss and take-profit unchanged. The share is of the position's current size, so two exits of 0.5 leave a quarter. Each share closed appears as its own trade, with its own P&L.tunable
min_remaining_sizenumber default 0. When a partial close would leave less than this size, the whole position closes instead. Use it for the smallest position you can actually hold (for example, one minimum lot). 0 never closes the rest early.tunable
Update Stop-Loss
Sink.UpdateStop
Moves the stop of open position(s) to the absolute price value when trigger fires. Only tightens: if the new stop would widen the risk, the update is ignored. This prevents trailing stop logic from accidentally moving the stop away from the position. side_filter narrows the positions first; target then picks which of them get the new stop: all of them by default, or only the oldest, the newest, or the one named by position_id, as in Exit.
For LONG: new_stop applied only if value > current_stop and value < close For SHORT: new_stop applied only if value < current_stop and value > close Applies to the open positions side_filter lets through that target picks Typical pattern: HighSinceEntry → Sub(ATR×mult) → Max(PositionStopLoss, …) → UpdateStop
triggerBoolean fires only while in position
valueNumber absolute stop price level
position_idNumber optional; required only when target=by_id; names the position whose stop moves
targetenum all | oldest | newest | by_id, default all
side_filterenum ANY | LONG | SHORT, default ANY

Note

An annotation on the canvas. It has no sockets and one parameter, its text, and the engine does not evaluate it. A strategy computes exactly the same trades with or without one.

Note
Note.Text
Free text on the canvas, for explaining a strategy to whoever opens it next, yourself included. No inputs, no outputs, and its only parameter is the text itself: the engine skips the node entirely, so the text can say anything without changing a result. Use it where a block name (right-click → Name this block) is too short for what needs saying.
texttext default empty; the block is not evaluated