Node Reference

Full specification of every Strategy Studio node — ports, parameters, and behavior.

Every built-in node, grouped by Library category. Notation: port:type for ports (* = required input); param(kind=default) for parameters.

coin params accept either a literal symbol such as BTC or the special "$CURRENT_MARKET" token, exposed in the Inspector as Current market. Every coin param defaults to Current market: the strategy trades the market you start it on.

Port types

There are 9 port types. They split into two axes of meaning — see Core Concepts for the full model.

TypeMeaning
number
A scalar level/state, readable on any tick.
`number\
candle`
Accepts a number or a candle; indicators use this for their value input (a candle is reduced via the source param).
boolean
A sustained true/false level.
candle
One OHLCV bar { t, o, h, l, c, v }.
series<candle>
A rolling history of candles (data.candles → history).
orderIntent
An order to be submitted, produced by an exec.* node.
signal<boolean>
A momentary boolean event — true for exactly the tick it fires.
signal<number>
A momentary numeric event — carries a value only on the tick it fires.
any
Untyped escape hatch; connects in either direction.

Compatibility rule: identical types connect; any connects to anything; a signal<T> may feed the matching level (signal<boolean>→boolean, signal<number>→number/number|candle); but a level may not feed a signal<> input — convert it first with logic.edge. See signal vs level.

Per-node timeframe override

data.candles, clock.bar, ind.crossover, and every indicator carry an optional timeframe param:

  • Empty (default) = inherit the graph's global timeframe (the data.timeframe node).
  • A non-empty value must be a coarser timeframe and an integer multiple of the base (e.g. a 4h override on a 1h graph). The node resamples the base candle stream into higher-timeframe bars locally and only updates on each HTF bar close — it holds its last value between closes (no repaint). A finer or non-aligned override is a validation error.

This is how you read, e.g., a 4h EMA inside a 1h strategy. See Multi-timeframe.

Data

Market data sources: candles, prices, book, position, balance, funding, open interest, liquidations, run statistics.

data.timeframe — Timeframe

Sets the graph's global/base timeframe used by backtest, paper, and live runs. Exactly one allowed per graph.

  • Out: timeframe:any, minutes:number, ms:number
  • Params: timeframe(timeframe="1h")

data.candles — Candles

OHLCV stream for a coin at the strategy timeframe (or the optional higher-timeframe override).

  • Out: candle:candle, close:number, high:number, low:number, open:number, volume:number, history:series<candle>
  • Params: coin(coin="$CURRENT_MARKET"), timeframe(timeframe="") — empty = inherit graph timeframe; coarser override resamples to HTF bars

data.mid — Mid Price

Live mid from BBO if available; falls back to candle close. coin can be wired (e.g. a ui.coin); spot is true when the coin is a spot pair, and coin echoes the resolved symbol.

  • In: coin:any (optional; falls back to the param)
  • Out: mid:number, spot:boolean, coin:any
  • Params: coin(coin="$CURRENT_MARKET")

data.orderbook — Order Book BBO

Live best-bid/offer snapshot. Empty in backtest; live data in paper/live.

  • Out: bid:number, ask:number, mid:number, spread:number, bidSize:number, askSize:number, imbalance:number
  • Params: coin(coin="$CURRENT_MARKET")

data.position — Position

Current open position for a coin. Paper/live populate liquidationPx, unrealizedPnl, cumFunding from clearinghouseState; backtest only has size + entryPx.

  • In: coin:any (optional; falls back to the param)
  • Out: size:number, signedSize:number, side:number, entryPx:number, positionValue:number, unrealizedPnl:number, liquidationPx:number, marginUsed:number, leverage:number, leverageSetting:number, cumFundingSinceOpen:number, cumFundingAllTime:number
  • Params: coin(coin="$CURRENT_MARKET")

side is 1 (long), -1 (short), or 0 (flat). leverageSetting is the position's leverage, else the account's leverage setting for the coin (NaN when unknown). Spot pairs (e.g. HYPE/USDC) read the wallet's base-token balance.

data.balance — Account Equity

Live perp account equity (mark-to-market). For spot wallet balances use data.spotBalance.

  • Out: equity:number, available:number (free margin: what open positions don't hold)

data.fundingRate — Funding Rate

Latest per-hour funding rate from Hyperliquid (paper/live). The engine debits/credits this rate against open positions each hour. NaN in backtest (no historical funding series is loaded).

  • Out: rate:number (hourly, e.g. 0.0001 = 1 bp/hr), annualized:number (rate × 24 × 365 × 100)
  • Params: coin(coin="$CURRENT_MARKET")

data.fundingCountdown — Funding Countdown

Time until the next hourly funding settlement. Uses the runner's settlement time in paper/live, else derives the next top-of-hour from the tick clock (works in backtest too).

  • Out: seconds:number, minutes:number, nextMs:number (absolute timestamp of next settlement)
  • Params: coin(coin="$CURRENT_MARKET")

data.openInterest — Open Interest

Latest open interest in coin units (paper/live). NaN in backtest — no historical OI series is loaded.

  • Out: oi:number (coin units), notional:number (oi × price, USD)
  • Params: coin(coin="$CURRENT_MARKET")

data.takerPressure — Taker Pressure

Aggressor buy vs sell notional (USD) over a rolling window from the trades tape. pressure is the normalized net in [−1, 1] (+1 all buys, −1 all sells). Paper/live only — NaN in backtest.

  • Out: pressure:number, buy:number (USD), sell:number (USD), net:number (buy − sell), total:number (buy + sell)
  • Params: coin(coin="$CURRENT_MARKET"), window(select="60") — 15s / 30s / 1m / 2m / 5m

data.liqVolume — Liquidation Volume

Share of taker volume that was liquidations over the rolling window. share is liq/total in [0, 1]; liq/total are notional (USD). Paper/live only — NaN in backtest.

  • Out: share:number, liq:number (USD), total:number (USD)
  • Params: coin(coin="$CURRENT_MARKET"), window(select="60") — 15s / 30s / 1m / 2m / 5m

data.spotBalance — Spot Balance

Wallet balance of one spot token (USDC, HYPE, …). available = total − hold (hold = reserved by resting orders). Paper/live only. NaN in backtest.

  • In: token:any (optional; falls back to the param)
  • Out: available:number, total:number, hold:number, entryNtl:number
  • Params: token(string="USDC") (the token symbol as shown in your Balances tab)

data.prices — Prices

Mid, mark, oracle and last price for a coin, plus basis (mark − oracle) and premium %. Mark/oracle come from the perp asset context in paper/live; in backtest they equal the candle close.

  • Out: mid:number, mark:number, oracle:number, last:number, basis:number, premiumPct:number
  • Params: coin(coin="$CURRENT_MARKET")

data.runStats — Run Stats

Trading statistics for this run: fills, entries and closed trades (run and today, UTC), realized PnL, streaks, drawdown from peak equity, day PnL, and time in position for a coin. Backtest counts simulated fills; live counts exchange fills.

  • Out: dayPnl:number, dayPnlPct:number, realizedToday:number, realizedRun:number, entriesToday:number, entriesRun:number, closedTradesToday:number, closedTradesRun:number, consecutiveLosses:number, consecutiveWins:number, lastTradePnl:number, winRate:number, drawdownPct:number, drawdownUsd:number, peakEquity:number, barsInPosition:number, msInPosition:number
  • Params: coin(coin="$CURRENT_MARKET") (the coin for the in-position counters)

Indicator

Technical indicators. Most take a value/candle input and a period param. Every indicator carries the per-node timeframe(timeframe="") override param (omitted from the lists below for brevity — empty means inherit the graph timeframe).

Indicators that operate on a numeric value stream accept either a raw number or a full candle on their value input. When a candle is connected, the source param selects close, open, high, low, or volume (close by default).

Every numeric param listed below is also exposed as an optional input port of the same name (period, mult, …) — wire a ui.number / ui.slider to drive it live; unconnected ports fall back to the param.

Info

Warmup: every indicator emits NaN until its lookback is properly seeded — including composite indicators (MACD, ADX, DEMA, TEMA, Keltner, TSI, TRIX, Ichimoku, PSAR), which use TA-lib-style SMA/Wilder seeding instead of emitting finite-but-wrong values from bar 1. ind.highest/ind.lowest emit NaN (never ±Infinity) on an empty window. Downstream logic sees false from any comparison against NaN, so warmup can never fire a trade.

The sampled indicators (roc, momentum, zscore, stdev, highest, lowest) carry a sampleMode param whose default is barClose — one sample per bar close, so "period N" means N bars identically in backtest, paper, and live. change mode (per-tick sampling on live data) is an explicit opt-in.

See the Indicators page for grouping and notes.

ind.sma — SMA

Simple moving average over the connected value stream.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close")

ind.ema — EMA

Exponential moving average. Seeded with SMA over period bars.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close")

ind.wma — WMA

Linearly-weighted moving average. Most recent value has the largest weight.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close")

ind.dema — DEMA

Double EMA. Reduces lag vs single EMA at the cost of overshoot.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close")

ind.tema — TEMA

Triple EMA. Even less lag than DEMA.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close")

ind.hma — HMA

Hull Moving Average — fast and smooth.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=16), source(select="close")

ind.kama — KAMA

Kaufman Adaptive Moving Average — adjusts smoothing with volatility.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=10), fast(int=2), slow(int=30), source(select="close")

ind.vwma — VWMA

Volume-weighted moving average: sum(price×vol) / sum(vol) over the last N bars. Needs a candle input for volume. source is close, open, high, low or hlc3; with hlc3 (typical price) it is a rolling VWAP over the last N bars.

  • In: candle:candle*
  • Out: out:number
  • Params: period(int=20), source(select="close")

ind.vwapSession — Session VWAP

Volume-weighted average price anchored to the start of each UTC day. Resets at the daily boundary. Uses the typical price (h+l+c)/3.

  • In: candle:candle*
  • Out: out:number

ind.linreg — Linear Regression

Least-squares line fit over the last N values. value = the regression line's endpoint (current bar); slope = per-bar slope.

  • In: value:number|candle*
  • Out: value:number, slope:number
  • Params: period(int=20), source(select="close")

ind.macd — MACD

Moving Average Convergence Divergence. Outputs macd, signal, histogram. SMA-seeded EMAs: macd is NaN until the slow EMA is seeded, signal until signal-many macd values exist.

  • In: value:number|candle*
  • Out: macd:number, signal:number, histogram:number
  • Params: fast(int=12), slow(int=26), signal(int=9), source(select="close")

ind.rsi — RSI

Wilder's RSI on the connected value stream. Exact edge handling: all-gains → 100, all-losses → 0, fully flat (no gains and no losses) → neutral 50.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=14), source(select="close")

ind.stochRsi — Stoch RSI

Stochastic of RSI. Outputs %K and %D.

  • In: value:number|candle*
  • Out: k:number, d:number
  • Params: rsiPeriod(int=14), stochPeriod(int=14), kSmooth(int=3), dPeriod(int=3), source(select="close")

ind.stoch — Stochastic

Stochastic oscillator. Inputs: candle. Outputs %K and smoothed %D (0–100).

  • In: candle:candle*
  • Out: k:number, d:number
  • Params: kPeriod(int=14), kSmooth(int=3), dPeriod(int=3)

ind.cci — CCI

Commodity Channel Index over typical price.

  • In: candle:candle*
  • Out: out:number
  • Params: period(int=20)

ind.willr — Williams %R

Williams %R momentum oscillator (−100 to 0).

  • In: candle:candle*
  • Out: out:number
  • Params: period(int=14)

ind.mfi — Money Flow Index

Volume-weighted RSI. Inputs: candle.

  • In: candle:candle*
  • Out: out:number
  • Params: period(int=14)

ind.tsi — TSI

True Strength Index — double-smoothed momentum.

  • In: value:number|candle*
  • Out: out:number
  • Params: long(int=25), short(int=13), source(select="close")

ind.trix — TRIX

Triple-smoothed EMA rate-of-change (%). Momentum oscillator that filters out short-term noise.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=15), source(select="close")

ind.roc — Rate of Change

Percent change vs N bars ago.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=9), source(select="close"), sampleMode(select="barClose")

ind.momentum — Momentum

Difference vs N bars ago (absolute, not %).

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=10), source(select="close"), sampleMode(select="barClose")

ind.zscore — Z-Score

(value − mean) / stdev over N bars.

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close"), sampleMode(select="barClose")

ind.stdev — Stdev

Population standard deviation over N bars (matches Bollinger).

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), source(select="close"), sampleMode(select="barClose")

ind.realizedVol — Realized Volatility

Standard deviation of log returns over N bars, annualized by the bar timeframe (% per year); perBar is the same in % per bar. A regime gauge: size down or stand aside when it spikes, lean in when it compresses.

  • In: value:number|candle*
  • Out: annualized:number, perBar:number
  • Params: period(int=20), source(select="close")

ind.percentRank — Percent Rank

Where the current value sits inside its own last-N-bar range, 0–100 (100 = highest of the window). Turns any series (volume, ATR, funding, an oscillator) into an adaptive threshold.

  • In: value:number|candle*
  • Out: rank:number
  • Params: period(int=100), source(select="close")

ind.rvol — Relative Volume

Current bar volume divided by the average volume over the last N bars. >1 means above-average activity.

  • In: candle:candle*
  • Out: out:number
  • Params: period(int=20)

ind.highest — Highest

Highest value over the last N bars, optionally excluding the most recent offset bars. offset: 1 gives the *prior* N-bar high — what breakout logic needs so close > highest can be true on a new-high bar. Emits NaN on an empty/unfilled window (never ±Infinity).

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), offset(int=0), source(select="close"), sampleMode(select="barClose")

ind.lowest — Lowest

Lowest value over the last N bars, optionally excluding the most recent offset bars (see ind.highest). Emits NaN on an empty/unfilled window (never ±Infinity).

  • In: value:number|candle*
  • Out: out:number
  • Params: period(int=20), offset(int=0), source(select="close"), sampleMode(select="barClose")

ind.bollinger — Bollinger Bands

SMA basis ± k × stdev. Outputs basis, upper, lower, bandwidth, %b.

  • In: value:number|candle*
  • Out: basis:number, upper:number, lower:number, bandwidth:number, percentB:number
  • Params: period(int=20), mult(number=2), source(select="close")

ind.keltner — Keltner Channels

EMA basis ± k × ATR. Inputs: candle. Outputs basis, upper, lower. SMA-seeded EMA basis + Wilder-seeded ATR — NaN until period bars.

  • In: candle:candle*
  • Out: basis:number, upper:number, lower:number
  • Params: period(int=20), mult(number=2)

ind.donchian — Donchian Channels

Rolling high/low over N bars, optionally excluding the most recent offset bars. Inputs: candle. Outputs upper, lower, mid. offset: 1 gives the prior N-bar channel (breakouts); with offset > 0 all outputs are NaN until period + offset bars are buffered.

  • In: candle:candle*
  • Out: upper:number, lower:number, mid:number
  • Params: period(int=20), offset(int=0)

ind.pivots — Pivots (swing high/low)

Confirmed swing points: a bar is a pivot high when its high tops the left bars before and the right bars after it, so it confirms right bars late (pivot lows likewise). Outputs the last confirmed pivot high/low and a pulse when a new one confirms. Use it for support/resistance and structure breaks.

  • In: candle:candle*
  • Out: high:number, low:number, newHigh:signal<boolean>, newLow:signal<boolean>
  • Params: left(int=5), right(int=5)

ind.atr — ATR

Average True Range (Wilder smoothing). Seeded with the SMA of the first period true ranges.

  • In: candle:candle*
  • Out: out:number
  • Params: period(int=14)

ind.adx — ADX

Average Directional Index. Outputs ADX, +DI, −DI (Wilder). Wilder-seeded: DI is NaN until period bars; ADX needs a further period DX samples (first ADX around bar 2 × period).

  • In: candle:candle*
  • Out: adx:number, plusDi:number, minusDi:number
  • Params: period(int=14)

ind.aroon — Aroon

Aroon up/down/oscillator over N bars.

  • In: candle:candle*
  • Out: up:number, down:number, osc:number
  • Params: period(int=25)

ind.vortex — Vortex

Vortex Indicator — outputs VI+ and VI− over N bars.

  • In: candle:candle*
  • Out: plus:number, minus:number
  • Params: period(int=14)

ind.supertrend — Supertrend

Supertrend overlay. Outputs trend (+1 long, −1 short) and line value. Uses the standard final-band logic: a band ratchets only while price stays on its side and resets to the basic band once the previous close breaks through it (matches TradingView et al.).

  • In: candle:candle*
  • Out: trend:number, line:number
  • Params: period(int=10), mult(number=3)

ind.psar — Parabolic SAR

Welles Wilder PSAR. Outputs sar line + trend (+1/−1). Bar 1 emits NaN/0; the initial trend is derived from the first two closes (Wilder seeding).

  • In: candle:candle*
  • Out: sar:number, trend:number
  • Params: step(number=0.02), max(number=0.2)

ind.obv — OBV

On-Balance Volume — cumulative signed volume.

  • In: candle:candle*
  • Out: out:number

ind.ichimoku — Ichimoku

Ichimoku Cloud — tenkan, kijun, senkouA, senkouB. The senkou outputs are the displaced cloud: the value AT the current bar, computed displacement bars ago (the standard charting definition). They stay NaN until the displacement buffer fills, so no logic runs against a partial cloud.

  • In: candle:candle*
  • Out: tenkan:number, kijun:number, senkouA:number, senkouB:number
  • Params: tenkan(int=9), kijun(int=26), senkouB(int=52), displacement(int=26)

ind.crossover — Crossover

Emits +1 when A crosses above B, −1 when below, 0 otherwise. cross is a numeric event; above is a sustained level (A > B). The cross steps only on bar-close ticks (intra-bar ticks emit cross: 0 and hold the last above); with an HTF override it fires at most once per HTF bucket.

  • In: a:number*, b:number*
  • Out: cross:signal<number>, above:boolean
  • Params: timeframe(timeframe="")

> For a clean boolean up/down event, prefer logic.cross (below).

Logic & Math

Comparisons, boolean ops, arithmetic, formulas, clocks, gates, series logic.

Sampling (sampleMode). Nodes that buffer a stream (math.window, state.memory, and the sampled indicators) carry a sampleMode param. The default is barClose: one sample per bar close, so "period N" means N bars identically in backtest, paper, and live. change opts in to per-tick sampling on live data (counts quote ticks, ≈10 Hz).

logic.compare — Compare

Boolean comparison of two numbers. tolerance makes ==/!= compare within an absolute epsilon (0 = exact float equality) — useful for computed prices that never match exactly. NaN input → false for every op (so != is *not* the negation of == under NaN).

  • In: a:number*, b:number*
  • Out: out:boolean
  • Params: op(select=">") — one of >, >=, <, <=, ==, != — tolerance(number=0)

logic.and — AND

Boolean AND of two inputs.

  • In: a:boolean*, b:boolean*
  • Out: out:boolean

logic.or — OR

Boolean OR.

  • In: a:boolean*, b:boolean*
  • Out: out:boolean

logic.not — NOT

Boolean negation.

  • In: a:boolean*
  • Out: out:boolean

logic.math — Math

Arithmetic between two numbers.

  • In: a:number*, b:number*
  • Out: out:number
  • Params: op(select="+") — one of +, -, *, /, min, max

logic.constant — Constant

Static numeric constant.

  • Out: out:number
  • Params: value(number=0)

logic.text — Text

Outputs a fixed text, e.g. status labels chosen with logic.select and shown with ui.status.

  • Out: out:any
  • Params: text(string="")

logic.equals — Equals

True when a equals b: numbers, text (case-insensitive) or booleans. logic.compare is numeric only; use this to compare a side, a coin or a text value. false when either input is missing.

  • In: a:any*, b:any*
  • Out: out:boolean

logic.select — Select

Outputs a while cond is true, else b. The building block for symmetric long/short logic (pick +size or −size, entry or exit price, …).

  • In: cond:boolean*, a:any*, b:any*
  • Out: out:any

logic.edge — Edge

Pulses true on the tick a boolean rises (or falls) from false. This is the only way to turn a sustained level into a signal<> event.

  • In: in:boolean*
  • Out: out:signal<boolean>
  • Params: edge(select="rising") — rising or falling

logic.cross — Cross

Detects A crossing B in either direction. Each output fires only on the bar the cross happens. Evaluated on bar-close ticks only (Pine semantics) — intra-bar live ticks emit false, so paper/live never see crosses a backtest wouldn't.

  • In: a:number*, b:number*
  • Out: up:signal<boolean> (crossed above), down:signal<boolean> (crossed below), any:signal<boolean> (either)

logic.barssince — Bars Since

Number of bars since the condition was last true (0 on the bar it is true). NaN until it has been true at least once.

  • In: cond:boolean*
  • Out: bars:number

logic.rising — Rising

True when the input has strictly increased over each of the last N bars.

  • In: in:number*
  • Out: out:boolean
  • Params: length(int=1)

logic.falling — Falling

True when the input has strictly decreased over each of the last N bars.

  • In: in:number*
  • Out: out:boolean
  • Params: length(int=1)

logic.valuewhen — Value When

The value of source on the Nth most recent bar where cond was true (occurrence 0 = most recent). NaN until enough occurrences seen.

  • In: cond:boolean*, source:number*
  • Out: out:number
  • Params: occurrence(int=0)

logic.count — Count

How many of the last N bar closes had in true. Use it for N-of-M confirmation (e.g. 3 of the last 5 bars closed above the band) or event frequency. all is true when every bar of a full window was true; any when at least one was.

  • In: in:boolean*
  • Out: count:number, all:boolean, any:boolean
  • Params: window(int=5)

clock.now — Timestamp

Current tick timestamp + decomposed parts. Use for time-of-day gates and session windows.

  • Out: ms:number, seconds:number (second within the current minute, 0–59), minute:number, hour:number, dayOfWeek:number (0 = Sunday … 6 = Saturday), dayOfMonth:number, month:number (1–12), year:number
  • Params: tz(select="utc") — utc or local

clock.bar — Bar Close Clock

Fires true exactly once per bar close at the strategy timeframe (or the optional higher-timeframe override); false on intra-bar live ticks.

  • Out: tick:signal<boolean>
  • Params: timeframe(timeframe="") — empty = strategy timeframe; coarser override fires once per HTF bar close

clock.boundary — New Hour / Day / Week

Fires one pulse on the first bar close of each new hour, day, week (Monday) or month, on any strategy timeframe. The natural clock for DCA, daily rebalancing or session resets. No pulse on the very first tick of a run or during warmup replay.

  • Out: tick:signal<boolean>
  • Params: unit(select="day") (hour, day, week, month), tz(select="utc") (utc or local)

clock.everyMs — Every N ms

Fires on the first tick after each N-millisecond due boundary in ctx.now. Drives intra-bar timers / TWAP without drift. Warmup-safe: the schedule never emits during warmup replay and re-anchors at the warmup→live transition, so a run can't fire the instant it goes live.

  • Out: tick:signal<boolean>
  • Params: ms(int=5000)

clock.everyN — Every N Bars

Fires true once every N bar closes; false on intra-bar live ticks and during warmup replay. A true reset re-anchors the counter: the resetting tick never fires, the resetting bar doesn't count, and the next fire is N bar closes later. A sustained-true reset holds the counter at zero — hold it while flat to turn this into a bars-since-entry time stop.

  • In: n:number — optional override; falls back to the param — reset:boolean
  • Out: tick:signal<boolean>
  • Params: n(int=5)

The counter starts fresh when the live run begins — warmup history does not advance it.

clock.session — Session Window

True while the current time is inside a session window [start, end) on the allowed days, at minute resolution. Overnight sessions (start > end) wrap past midnight; the days filter applies to the day the session starts, so a Fri 22:00→04:00 session stays active into Saturday morning.

  • Out: active:boolean, hour:number, dayOfWeek:number
  • Params: startHour(int=9), endHour(int=17), startMinute(int=0), endMinute(int=0), days(string="1,2,3,4,5") (comma list, 0 = Sunday; empty = all days), tz(select="utc") — utc or local

math.expr — Formula

Arbitrary arithmetic over inputs a, b, c, d (e.g. (a+b)/2, a*b - c). Supports + − × ÷, parentheses, the constants pi and e, and functions min, max, abs, sqrt, pow, log.

  • In: a:number, b:number, c:number, d:number
  • Out: out:number
  • Params: expr(string="a + b")

math.window — Window Reduce

Rolling reduce (sum/mean/max/min/std/range) over the last N values of a stream.

  • In: in:number*
  • Out: out:number
  • Params: size(int=20), op(select="mean"), sampleMode(select="barClose")

math.clamp — Clamp

Constrain a number to [lo, hi].

  • In: in:number*, lo:number, hi:number
  • Out: out:number
  • Params: loFallback(number=0), hiFallback(number=1)

math.abs — Abs

Absolute value.

  • In: in:number*
  • Out: out:number

math.batch — Batch

Adds amount on each tick add is true. When the total reaches min (and hold is false) it pulses out with the total in released, then starts over; pending is what's waiting. Typical use: collect the proceeds of each fill and place one order once they clear Hyperliquid's $10 minimum.

  • In: add:boolean*, amount:number*, hold:boolean
  • Out: out:boolean, released:number, pending:number
  • Params: min(number=12), minIntervalMs(number=0) (least time between two releases; 0 = no limit)

Gates & Timing

Pass-through gating and timing primitives. All are liveStateful — they keep their state across intra-bar ticks (see Core Concepts).

logic.gate — Gate

Pass value through when open is true. When closed, output is the last passed value (hold) or NaN (block).

  • In: value:any*, open:boolean*
  • Out: out:any
  • Params: mode(select="hold") — hold or block

logic.latch — Latch (one-shot)

Rising-edge trigger arms the latch. Once armed, the next finite value passes through to out and the latch disarms. armed exposes the current state.

  • In: trigger:boolean*, value:any*
  • Out: out:any, armed:boolean, fired:boolean
  • Params: edge(select="rising"), passWhen(select="anyValue")

logic.hold — Hold

Outputs the last finite value. Updates only when the input changes to a new finite number; NaN leaves the held value unchanged.

  • In: value:number*
  • Out: out:number

logic.sustain — Sustain

Holds the boolean in true for ms milliseconds after it goes false. Re-triggering during the sustain extends the window.

  • In: in:boolean*
  • Out: out:boolean, remainingMs:number
  • Params: ms(int=1000)

logic.delay — Delay (boolean)

Forwards each true pulse on in after ms milliseconds. The delay can be driven dynamically by the ms input port; the param is the fallback. Warmup-safe: nothing is queued during warmup replay (a historical pulse can never release into the live run).

  • In: in:boolean*, ms:number
  • Out: out:boolean, pending:number
  • Params: msFallback(int=1000)

logic.delayValue — Delay (number)

Holds a numeric value for ms milliseconds before emitting. Useful for staggering size/price signals. ms input overrides the fallback param. hold (default on) keeps emitting the last released value between releases; off emits it only on the release tick (NaN otherwise — a pulse). Warmup-safe like logic.delay.

  • In: in:number*, ms:number
  • Out: out:number, pending:number
  • Params: msFallback(int=1000), hold(boolean=true)

logic.toggle — Toggle (SR latch)

Set/Reset latch: a true set turns out on, a true reset turns it off. When both are true on the same tick, reset wins (safe default: a kill condition always beats an arm condition). The level survives warmup replay — it is state, not a pulse. Use it to turn one-bar pulses into a persistent regime level.

  • In: set:boolean, reset:boolean
  • Out: out:boolean

logic.cooldown — Cooldown

Pass-through pulse suppressor: passes a pulse, then suppresses further pulses for bars bar closes and/or ms milliseconds (both set = until both expire). Prevents signal machine-gunning (e.g. one entry per RSI dip, then quiet for 12 bars). cooling is true while the suppression window is active.

  • In: in:boolean*
  • Out: out:boolean, cooling:boolean
  • Params: bars(int=0) (0 = no bar cooldown), ms(number=0) (0 = no time cooldown)

State

Persistent memory across ticks.

state.memory — Memory

Remembers the previous sample of an input. Outputs current + previous (use logic.valuewhen or math.window for deeper history).

  • In: in:number*
  • Out: current:number, previous:number
  • Params: sampleMode(select="barClose")

state.var — Variable

Stores latest input until overwritten.

  • In: set:number
  • Out: out:number
  • Params: initial(number=0)

state.extreme — Running High / Low

Highest and lowest value seen since reset was last true (e.g. the peak price since entry, for a trailing stop). A true reset clears both.

  • In: value:number*, reset:boolean
  • Out: max:number, min:number

state.first — First Value

Holds the first live value of value (after warmup) for the rest of the run, e.g. the price when the strategy started. A true reset captures again. captured is true once a value is held.

  • In: value:any*, reset:boolean
  • Out: out:any, captured:boolean

Risk

Order gating at the submit barrier. See the gating model.

risk.killSwitch — Kill Switch

When trip is true the latch sticks: every order this tick and thereafter is dropped at the submit barrier, managed orders are deactivated, and tracked resting orders are best-effort cancelled — tripping doesn't just block new orders, it pulls existing ones. The run stays alive (monitoring continues) but disarmed until restarted. Strongly recommended for live runs (not required). liveStateful — the trip survives intra-bar ticks.

  • In: trip:boolean
  • Out: armed:boolean

risk.stop — Stop Strategy

When trip becomes true the entire run halts. Crucially, this tick's orders still submit first (so a same-tick close fires), then the run ends and any resting orders are cancelled. Use it for hard exits — daily loss limit reached, profit target hit, time's up.

  • In: trip:boolean*
  • Out: stopped:boolean

> Distinct from risk.killSwitch: the kill switch disarms trading (drops new intents and cancels resting orders) while keeping the run alive and monitoring; risk.stop ends the run after letting the current tick's orders through.

risk.maxPositionSize — Max Position Size

ok is false when the coin's current position notional has reached maxNotional (USD). Wire ok into risk.preTradeValidate, or read it as a boolean gate. The engine also drops orders on this coin at the submit barrier when they would push the notional past the cap and increase exposure — exposure-reducing and reduce-only orders always pass, so the cap can never trap a position it can't close.

A wired max overrides maxNotional (e.g. an adjustable budget); cap outputs the cap in force. basis picks how the position is measured: mark (size × current price) or cost (size × entry price, for a budget). With thin (Shrink to fit) an order that would pass the cap is shrunk to the part that fits, if that is still a valid order, instead of dropped.

  • In: trigger:boolean, max:number
  • Out: ok:boolean, cap:number
  • Params: coin(coin="$CURRENT_MARKET"), maxNotional(number=5000), basis(select="mark") (mark or cost), thin(boolean=false)

risk.maxFillSize — Max Fill Size

Cap total filled size (coin units) for a market over the run. Each fill accumulates toward maxSize; once reached, no further orders are submitted and the strategy stops (paper/live) or the backtest ends. maxSize 0 means no cap.

  • In: trigger:boolean
  • Out: ok:boolean, filled:number, remaining:number
  • Params: coin(coin="$CURRENT_MARKET"), maxSize(number=0.01)

risk.maxDailyLoss — Max Daily Loss

Blocks new exposure for the rest of the UTC day once the day's PnL (realized + unrealized, against day-start equity) falls below −limit. Closes and reductions still pass. Set a USD limit, a % of day-start equity, or both (the first hit wins; 0 = off). Enforced by the engine at the submit barrier; a new UTC day re-arms it. Wired inputs override the params.

  • In: limitUsd:number, limitPct:number
  • Out: tripped:boolean, dayPnl:number, remaining:number
  • Params: limitUsd(number=0), limitPct(number=2)

risk.maxTradesPerDay — Trade Throttle

Blocks new entries once maxEntries position-opening or adding fills have happened this UTC day (closes still pass; 0 = unlimited). With pauseAfterLosses above 0 it also blocks while the losing streak is at least that many trades. Enforced by the engine at the submit barrier. A false enabled turns it off.

  • In: maxEntries:number, enabled:boolean
  • Out: blocked:boolean, entriesToday:number, remaining:number
  • Params: maxEntries(int=5), pauseAfterLosses(int=0)

risk.preTradeValidate — Pre-Trade Validate

Functional gate. Wire an exec.* node's intent output into intent and a boolean into ok. When ok is false on a tick, the engine drops the intent emitted by that exec node at the submit barrier — not just the forwarded copy. Managed places are gated too. When ok is true (or unwired), the intent passes through unchanged. The engine gates on the node's explicit ok output (a managed exec node's intent port is always empty, so out alone can't distinguish "blocked" from "nothing to forward").

  • In: intent:orderIntent*, ok:boolean
  • Out: out:orderIntent, ok:boolean

Execution

Order emission. All exec.* order nodes carry fireOnBarClose(boolean=true) — they no-op on intra-bar ticks unless set false. Size is floored to the market lot, limit price rounded to the tick, and sub-$10 notionals are rejected (paper/live; backtest also enforces lot/tick/min-notional when market metadata is present). Reduce-only orders skip the $10 minimum so dust positions can always be closed.

The size fallback param is now named sizeFallback; older graphs carrying defaultSize/size still read for back-compat.

fireMode (Trigger). Every fire-driven exec node carries fireMode(select="edge") and fireEveryMinutes(number=60):

  • edge (default, Once when it turns true): fire once per false→true transition of fire. A gate that is simply held true (the classic failure mode of condition wiring) fires ONE order instead of stacking a fresh order every bar-close forever.
  • level (Every bar while true): legacy behaviour: fire on every eligible tick while fire is true. For graphs that pulse fire themselves.
  • first (First time only): one order for the whole run, the first time fire turns true.
  • every (Every N minutes while true): while fire stays true, an order at most every fireEveryMinutes (Repeat every) minutes.

The edge is sampled in the same tick domain the node can fire in (bar-close-gated nodes sample fire only on bar-close ticks, so a pulse that rises intra-bar and holds into the close still fires). The latch survives warmup replay — a signal already true during warmup will not fire at live start. Managed exec.limit is level-driven by design, so fireMode does not apply there.

exec.market — Market Order

Emits a market order intent when fire pulses true. Size it in coins (size) or dollars (notional, converted at the current price); with neither wired, sizeFallback applies. coin can be wired (e.g. a ui.coin). A wired everyMinutes overrides fireEveryMinutes.

Fill outputs. filled is true on the tick a fill of this node's orders lands, with that tick's filledUsd / filledSize; fillSide is +1 (buy) or −1 (sell) of the last fill; filledTotalUsd / filledTotalSize sum the whole run. Other nodes can act on fills, e.g. feed each fill's proceeds into a math.batch that buys another coin.

Retry until filled (retryUnfilled): what didn't fill (a failed or partly filled order, or one that couldn't be sent) is re-sent as a new market order, at most every retryEveryMs, while holdRetries is false. unfilled / unfilledUsd report the fresh shortfall on a tick, retryPendingUsd what is still waiting to be retried; a remainder under the $10 minimum is dropped.

Total cap (maxTotal, coins) / Total cap ($) (maxTotalUsd): stop once this much has filled in total (in-flight orders count); each order is clipped to what's left. 0 = no cap. Wired inputs override the params. capReached turns true once the cap is used up and nothing is in flight.

Use available balance (waitForBalance): each order is sized to what the account can place right now at your Hyperliquid leverage. If that is under the $10 minimum, the node waits instead of failing (waitingBalance is true) and sends as soon as balance frees up, e.g. from profit. Set a large size to always use it all. Reduce-only orders are not affected. inFlight is true while an order of this node is sent and not yet filled or failed.

  • In: fire:boolean*, size:number, side:any, notional:number, coin:any, holdRetries:boolean, maxTotal:number, maxTotalUsd:number, everyMinutes:number
  • Out: intent:orderIntent, filled:boolean, filledUsd:number, filledSize:number, fillSide:number, filledTotalUsd:number, filledTotalSize:number, unfilled:boolean, unfilledUsd:number, retryPendingUsd:number, inFlight:boolean, capReached:boolean, waitingBalance:boolean
  • Params: coin(coin="$CURRENT_MARKET"), side(select="buy"), sizeFallback(number=0.01), reduceOnly(boolean=false), fireOnBarClose(boolean=true), fireMode(select="edge"), fireEveryMinutes(number=60), waitForBalance(boolean=false), retryUnfilled(boolean=false), maxTotalUsd(number=0), maxTotal(number=0), retryEveryMs(number=5000)

exec.twap — TWAP Order

On fire, executes a total size as a series of equal market-order slices spread evenly over a duration (time-weighted average price). The first slice fires immediately; the rest on schedule. A new fire while a TWAP is running is ignored (no restart, no duplicate schedule); in edge mode a held-true fire starts exactly one TWAP and does not auto-restart. The bar-close gate governs starting only: once running, due slices drain on any tick, and coarse cadence catches up by emitting all due slices at once. A rejected slice (min-notional, lot floor) is retried at the next interval, bounded at 5 attempts before being abandoned.

Slices are sized from what really filled: a slice that comes back unfilled or partly filled is made up by the next one, and up to 3 extra slices after the schedule finish a shortfall. A slice waits while an earlier one is still unanswered. A new run starts only once the previous run's orders have settled.

While pause is true, due slices wait and the schedule resumes where it paused, with no catch-up burst (e.g. while price is past a limit). With Stop when fire turns false (stopWhenFireDrops) the remaining slices are cancelled once fire is false, checked on the same ticks as the start. Outputs: the fill outputs of exec.market, plus active (running), paused, progress (0–1 of the total filled) and done (finished and settled).

  • In: fire:boolean*, size:number, side:any, duration:number, slices:number, reduceOnly:boolean, pause:boolean
  • Out: intent:orderIntent, filled:boolean, filledUsd:number, filledSize:number, fillSide:number, filledTotalUsd:number, filledTotalSize:number, active:boolean, paused:boolean, progress:number, done:boolean
  • Params: coin(coin="$CURRENT_MARKET"), side(select="buy"), sizeFallback(number=0.06) (TOTAL size), durationMinutes(number=30), slices(int=6), reduceOnly(boolean=false), fireOnBarClose(boolean=true), stopWhenFireDrops(boolean=false), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.limit — Limit Order

Emits a limit order intent. Size floored, price rounded to tick.

Managed mode (managed: true): the node represents ONE standing resting order the engine keeps reconciled — while fire is true it keeps exactly one order resting at the current price/size (re-pricing via cancel+replace, never duplicating); when fire goes false it cancels the order. Level-driven by design (fireMode n/a). When off (default) the node emits a fresh one-shot order each time it fires.

Managed-mode options: After a fill (afterFill) decides what happens once the resting order has filled: replace places a new one while fire stays true (grid refill), retrigger waits until fire turns false and true again, never places no other order this run. Re-price at bar close only (holdBetweenCloses, with fireOnBarClose off) rests the order as soon as fire turns true and re-prices it only at bar close (mid-bar it only shrinks), avoiding a cancel/replace on every tick. One-shot orders support Use available balance (waitForBalance, see exec.market).

  • In: fire:boolean*, price:number*, size:number, side:any
  • Out: intent:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), side(select="buy"), sizeFallback(number=0.01), postOnly(boolean=true), reduceOnly(boolean=false), managed(boolean=false), fireOnBarClose(boolean=true), holdBetweenCloses(boolean=false), afterFill(select="replace") (replace, retrigger, never), fireMode(select="edge"), fireEveryMinutes(number=60), waitForBalance(boolean=false)

exec.closePosition — Close Position

Closes the current position with a reduce-only market order on fire (no-op when flat). Being reduce-only, it skips the $10 minimum, so sub-$10 dust positions are closable. fraction closes only part of it (0.5 = half, for scale-outs); the fraction input overrides the param.

  • In: fire:boolean*, fraction:number
  • Out: intent:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), fraction(number=1), fireOnBarClose(boolean=true), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.stopMarket — Stop Market

Stop-loss market order. Rests until price crosses stopPx, then fills at market. Reduce-only by default. Whole position (wholePosition) sends Hyperliquid's native entire-position stop: it closes the whole position at trigger time and is cancelled when the position closes (perps only; wire the position size into size for backtest/paper).

  • In: fire:boolean*, stopPx:number*, size:number, side:any
  • Out: intent:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), side(select="sell"), sizeFallback(number=0.01), reduceOnly(boolean=true), fireOnBarClose(boolean=true), wholePosition(boolean=false), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.stopLimit — Stop Limit

Stop-loss that posts a limit order at limitPx once price crosses stopPx. Reduce-only by default.

  • In: fire:boolean*, stopPx:number*, limitPx:number*, size:number, side:any
  • Out: intent:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), side(select="sell"), sizeFallback(number=0.01), reduceOnly(boolean=true), fireOnBarClose(boolean=true), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.takeProfit — Take Profit

Take-profit market order. Rests until price moves favourably to targetPx, then fills at market. Reduce-only by default. Whole position (wholePosition) works as on exec.stopMarket.

  • In: fire:boolean*, targetPx:number*, size:number, side:any
  • Out: intent:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), side(select="sell"), sizeFallback(number=0.01), reduceOnly(boolean=true), fireOnBarClose(boolean=true), wholePosition(boolean=false), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.trailingStop — Trailing Stop

Stop that ratchets to stay trailDistance (price units) from the best price seen, only ever tightening toward the market. Reduce-only. The ratchet is simulated in backtest/paper; live submits a static trigger at the initial offset (Hyperliquid has no native trailing order) — see Backtest Modeling.

  • In: fire:boolean*, trailDistance:number*, size:number, side:any
  • Out: intent:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), side(select="sell"), sizeFallback(number=0.01), fireOnBarClose(boolean=true), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.bracket — Bracket Order

Entry market order plus a protective stop and take-profit, emitted together. Stop + target are reduce-only triggers on the opposite side. The stopPx / targetPx legs are optional (omitted when their input is unwired or non-positive). In backtest/paper the stop and target are OCO — whichever fires first cancels the sibling; live submits them as independent trigger orders (no native OCO), so the surviving leg must be managed.

  • In: fire:boolean*, size:number, side:any, stopPx:number, targetPx:number
  • Out: entry:orderIntent, stop:orderIntent, target:orderIntent
  • Params: coin(coin="$CURRENT_MARKET"), side(select="buy"), sizeFallback(number=0.01), fireOnBarClose(boolean=true), fireMode(select="edge"), fireEveryMinutes(number=60)

exec.targetPosition — Target Position

Reconciles the position to a target signed size (+long / −short / 0 flat) with market orders: opens, adds, reduces, closes or flips as needed. Reductions are reduce-only. Fires at most once per cooldownBars bar closes and ignores adjustments below the market lot or minDelta; minDeltaPct also ignores adjustments smaller than that % of the target (flattening always fires). maxStepUsd (Max per order) splits big adjustments into steps, one per cooldown (0 = all at once). A false enabled pauses it. Spot pairs read the wallet's base-token balance as the current size.

  • In: target:number*, enabled:boolean, coin:any, maxStepUsd:number, minDeltaPct:number
  • Out: intent:orderIntent, delta:number (target − current), current:number
  • Params: coin(coin="$CURRENT_MARKET"), minDelta(number=0), minDeltaPct(number=0), maxStepUsd(number=0), cooldownBars(int=1), fireOnBarClose(boolean=true)

exec.sizeByRisk — Size by Risk

Position size from a fixed dollar risk and a stop distance: size = riskUSD / |entry − stop|. Emits coin-unit size — wire size into an exec node's size input. (Pure compute; no order emitted.)

  • In: riskUSD:number, entryPx:number*, stopPx:number*
  • Out: size:number
  • Params: riskUSDFallback(number=100)

exec.sizeByEquityPct — Size by Equity %

Position size from a percentage of account equity at the current price: size = (equity × pct/100) / price.

  • In: pct:number, price:number*, equity:number
  • Out: size:number, notional:number
  • Params: pctFallback(number=10)

exec.notionalToSize — Notional to Size

Convert a USD notional to a coin-unit size at the current price: size = notional / price.

  • In: notional:number, price:number*
  • Out: size:number
  • Params: notionalFallback(number=100)

Debug

Inspection — probe, log, plot.

debug.probe — Probe

Taps a value; displayed inline and pinned to the bottom panel.

  • In: in:any*
  • Out: out:any
  • Params: label(string="probe")

debug.plot — Plot

Plots a value over time on the chart or in a separate panel. In an indicator graph each plot becomes one chart line.

  • In: value:number*
  • Out: out:number
  • Params: series(string="series"), color(select="primary") — primary/green, warn/amber, danger/red, info/sky, violet — placement(select="overlay") — overlay on price chart or separate panel

debug.log — Log

Logs value to the console when the gate is true.

  • In: value:any*, when:boolean
  • Params: label(string="log")

note — Note

A free-text comment pinned to the canvas. No ports, never runs — pure annotation.

  • Params: text(code="Note"), color(select="amber") — amber, sky, violet, emerald, slate — width(int=220)

Frontend (UI)

End-user form fields and feedback. See the Strategy Frontends page.

Every ui.* node also carries visibleWhenNode(string="") (Visible when node) and visibleWhenEquals(string="true") (…equals), omitted from the lists below: set the first to the id of another ui.* node (e.g. a Toggle Field) and the field shows only while that node's value equals the second. Empty = always visible.

ui.number — Number Field

End-user numeric input. Outputs the entered number, clamped to [min, max] (out-of-range values from the form or CLI never leak past the field's declared bounds). When rendered as the order-form Size block, the RO toggle's state is exposed on the reduceOnly output — wire it into an exec node's reduceOnly input to make the toggle functional.

  • Out: value:number, reduceOnly:boolean
  • Params: label(string="Number"), default(number=0), min(number=0), max(number=1000000), step(number=1), unit(string=""), sizeField(boolean=false) (render as the order form's Size block), order(int=0)

ui.balance — Balance Field

Percent-of-balance slider. Outputs the resolved USD amount: percent × perp account equity (source = equity) or percent × the available wallet balance of a spot token (source = spot). While a spot balance is unknown (backtest, or live before the first wallet sync), value is NaN (not 0), so downstream exec nodes drop the non-finite size instead of silently sizing orders at zero.

  • Out: value:number, percent:number
  • Params: label(string="Amount"), default(number=25), source(select="equity") (equity or spot), token(string="USDC"), order(int=0)

ui.boolean — Toggle Field

End-user on/off switch. Outputs a boolean.

  • Out: value:boolean
  • Params: label(string="Enabled"), default(boolean=false), order(int=0)

ui.string — Text Field

End-user free-text input. Outputs the entered string (e.g. a label, tag, or note to pipe into ui.status).

  • Out: value:any
  • Params: label(string="Text"), default(string=""), placeholder(string=""), order(int=0)

ui.slider — Slider Field

End-user slider over a numeric range. Outputs the selected number, clamped to [min, max].

  • Out: value:number
  • Params: label(string="Slider"), default(number=1), min(number=1), max(number=100), step(number=1), order(int=0)

ui.select — Select Field

End-user dropdown over preset options. Outputs the chosen number or text value. The end-user value is validated against the authored options — an unmatched value falls back to the node default, never passing through.

  • Out: value:any
  • Params: label(string="Option"), options(string="1,2,5,10" | "1m,5m,1h" | "fast=1,slow=2" | "1 minute=1m,1 hour=1h"), default(string="1"), order(int=0)

ui.coin — Coin Field

End-user market picker. Outputs the chosen coin symbol.

  • Out: coin:any
  • Params: label(string="Market"), default(coin="$CURRENT_MARKET"), order(int=0)

ui.side — Side Field

End-user buy/sell or long/short picker. Outputs the chosen order side (buy or sell). Wire into exec.* side input. opposite is the other side (for a hedge or exit leg that should go the other way) and sign is +1 for buy, −1 for sell.

  • Out: side:any, opposite:any, sign:number
  • Params: label(string="Side"), default(select="buy"), labels(select="buy-sell" | "long-short"), order(int=0)

ui.button — Action Button

Fires a one-shot true pulse when the end-user clicks. Wire into exec.*. emitOn picks the tick: barClose (default) holds the press until the next bar-close tick (the pulse survives intra-bar ticks unconsumed, and fires exactly once); instant consumes it on the very next tick — pair with fireOnBarClose: false on the exec node for true click-to-trade.

  • Out: pressed:signal<boolean>
  • Params: label(string="Run"), tone(select="primary") — primary, success, danger — emitOn(select="barClose") — barClose or instant — order(int=0)

ui.timeframe — Timeframe Field

End-user timeframe picker bound to the strategy's graph timeframe (changing it re-sets the candle subscription). The value lives on the graph itself, not in the form values; the output just echoes the resolved timeframe for optional display wiring.

  • Out: timeframe:any
  • Params: label(string="Timeframe"), order(int=0)

ui.readout — Readout

Displays a live numeric value in the strategy frontend. A non-finite input renders as a dash (—) instead of freezing on the last good value.

  • In: value:number*
  • Params: label(string="Value"), format(select="number") — number, usd, pct — order(int=0)

ui.status — Status Badge

Displays a colored status badge from a value in the frontend.

  • In: value:any*
  • Params: label(string="Status"), order(int=0)

ui.gauge — Gauge

Displays a value as a progress bar between min and max. A non-finite input renders as a dash (—).

  • In: value:number*
  • Params: label(string="Gauge"), min(number=0), max(number=100), order(int=0)

Developer

Custom JS + helper markers.

dev.js — JavaScript

Custom JavaScript body with inputs a/b/c/d, persistent state, and ctx (candle/history/bbo/position/equity). Returns a number or { out, plot1, plot2, plot3 }. State commits only on bar close. Scripts are loop-budgeted at 1M loop iterations per tick — past the budget the script is aborted with a runtime error in the Console. Marketplace publish rejects graphs containing dev.js, and file imports containing it are blocked by the untrusted-graph guard (same policy for .borsa.json files as for marketplace forks).

  • In: a:number, b:number, c:number, d:number
  • Out: out:number, plot1:number, plot2:number, plot3:number
  • Params: coin(coin="$CURRENT_MARKET"), source(code=…)

helper.input — Helper Input

Exposes one input port of this graph (turns the graph into a helper). Connect downstream to consume the value from an invoker.

  • Out: out:any
  • Params: portName(string="in"), portType(select="number")

helper.output — Helper Output

Exposes one output port of this graph (turns the graph into a helper). Connect upstream to assign the value visible to invokers.

  • In: in:any*
  • Params: portName(string="out"), portType(select="number")
Node Reference · Docs | Borsa