DSL operator & function catalog
This is the complete set of callable names in the Signal language: constants, config builders, transforms, rolling aggregations, operators, and accessors. Every name below resolves in the compiler today. Each entry states its qualified name, its parameters, and what it returns, so you can look up an exact signature without leaving the page.
For a category-by-category tour that teaches when to reach for each family, read operators and functions. This page is the flat catalog; that page is the guided walk.
How to read an entry
Each entry names a callable, its parameters, and its return type. A worked entry:
rolling_mean(
x: Signal,
periods: Int,
*,
min_periods: Optional[Int] = null,
ignore: "null" | "nan" | "both" = "both",
) -> Signalxperiodsmin_periodsnullnull requires a full window.ignore"null" | "nan" | "both""both"Read it this way:
- Qualified name. A callable is either a bare name (
rolling_mean,where) or a dotted accessor method (option.iv,bar.close,time.hour). Dotted names group the market and calendar accessors underbar,option, andtime. - The
*separator. A bare*in a signature marks the boundary between positional parameters and keyword-only parameters. Parameters before the*(x,periods) may be passed positionally or by name. Parameters after it (min_periods,ignore) must always be named. - Return type. Every entry returns one of: a Signal, a scalar (the two constants), a config object (
SamplingConfigorBucketingConfig), or a Table (ContractTable,BookTable,PositionTable, orLegTable). A standalone signal program must end on a Signal-valued expression. - Argument order. Positional arguments come before named arguments. Once a named argument appears, every later argument must also be named.
Every code example on this page is a whole runnable program that ends on a Signal:
// 20-day annualized realized volatility
daily_close = bar.close(sampling=sampling(period="day", time="close"))
returns = log_change(daily_close, 1)
rolling_std(returns, 20) * sqrt(252)Constants
Two scalar constants. Both resolve to their raw numeric value where used; neither is a Signal.
math.pi3.141592653589793math.e2.718281828459045Config & import builders
Three builders. The first two produce config objects consumed by an accessor's sampling= / bucketing= argument; the third imports a saved signal.
sampling(*, period, multiplier, offset, time, times, days, weeks, months) -> SamplingConfig
bucketing(*, period, agg, multiplier, offset, partial) -> BucketingConfig
signal(slug: String) -> Signalsampling(...)returns aSamplingConfigfor point-in-time sampling.bucketing(...)returns aBucketingConfigfor window aggregation.signal("slug")imports a previously saved signal by its slug and returns its value on the importing program's spine. Writingsignal("realized-vol-20d")is equivalent to writing that signal's expression in place.
The two config builders are folded at compile time and are passed to an accessor call, not evaluated on their own. Full parameter tables, the period and time enumerations, and the cascading defaults live in the data accessor reference.
Signal resampling
Two functions apply a config object to an already-computed Signal, the Signal-level counterparts to passing sampling= / bucketing= on an accessor.
sample(x: Signal, sampling: SamplingConfig) -> Signal
bucket(x: Signal, bucketing: BucketingConfig) -> Signalsample(x, sampling)applies aSamplingConfigtox: point-in-time selection with backward as-of fill.bucket(x, bucketing)applies aBucketingConfigtox: a window aggregate over observed rows, one row per bucket, labeled at the window's right endpoint.
sampling and bucketing are mutually exclusive on a single accessor call. Use sample() / bucket() when the series is a derived expression rather than a raw accessor result.
// Daily close of a derived intraday ratio
ratio = bar.close() / bar.open()
sample(ratio, sampling(period="day", time="close"))Element-wise transforms
Twenty-five pointwise functions. Each maps input to output row by row; null and NaN propagate unchanged.
abs(x)|x|.sign(x)1.0 if x > 0, -1.0 if x < 0, 0.0 if x == 0.sqrt(x)NaN for x < 0.root(x, power)x ** (1 / power). power is a positive integer.log(x, base="e")"e", 2, or 10. NaN for x <= 0.lognp(x, n=1, base="e")log_base(x + n). The offset keeps zero-valued inputs off the domain boundary.exp(x)e ** x. Overflow to +inf becomes NaN.floor(x)ceil(x)round(x, decimals=0)decimals places, half-to-even (banker's rounding).clip(x, lower=null, upper=null)[lower, upper]. Either bound may be omitted.sin(x)x in radians.cos(x)x in radians.tan(x)NaN near the singularities at pi/2 + k*pi.arcsin(x)[-pi/2, pi/2]. NaN for |x| > 1.arccos(x)[0, pi]. NaN for |x| > 1.arctan(x)(-pi/2, pi/2). Defined for all reals.deg2rad(x)rad2deg(x)sinh(x)NaN.cosh(x)NaN.tanh(x)(-1, 1).arcsinh(x)arccosh(x)NaN for x < 1.arctanh(x)NaN for |x| >= 1.Domain errors return NaN rather than raising: sqrt(-1), log(0), and arcsin(2) all produce NaN, as does an overflowing exp.
Most of these take a single argument. The ones that do not:
root(x, power)takes the root degree as a second positional argument.log(x, base)andlognp(x, n, base)take the base as"e",2, or10;lognpalso takes the offsetn(default1).round(x, decimals)takes the decimal count and uses banker's rounding.clip(x, lower, upper)raises iflower > upperwhen both are set.
Missing-data & conditional
Five functions for handling null / NaN and for row-wise selection.
fill_missing(x: Signal, value: Signal, fill: "null" | "nan" | "both" = "nan") -> Signal
coalesce(*args: Signal, exhausted: "null" | "nan" = "null") -> Signal
where(cond: Signal, arg1: Signal, arg2: Signal) -> Signal
is_missing(x: Signal, kind: "null" | "nan" | "both") -> Signal
replace_where(x: Signal, cond: Signal, value: Signal) -> Signalfill_missing(x, value, fill)replaces missing observations ofxwithvalue.fillselects which kind to target. Whenvalueis itself missing at a row, that row is left unchanged.coalesce(*args, exhausted)is variadic and takes one or more Signals. It returns the first non-missing value across its arguments, left to right, at each timestamp;exhaustedchooses the fallback when every argument is missing at a row.is_missing(x, kind)returns Float641.0wherexis missing and0.0otherwise. The output is nevernullorNaN.replace_where(x, cond, value)returnsxwith truthy-condrows replaced byvalue. AnullorNaNincondkeeps the originalxat that row.
where(cond, arg1, arg2) is a row-wise select, not a mask. It returns arg1 where cond is truthy (any non-zero value) and arg2 otherwise. Either branch can be any Float64 value, not only 1.0 / 0.0. A null cond yields null; a NaN cond yields NaN.
// Cap 30-DTE mid IV at its own 20-day average
iv = option.iv(30, sampling=sampling(period="day", time="close"))
avg = rolling_mean(iv, 20)
where(iv > avg, avg, iv)Like the comparison and logical operators, is_missing returns Float64 1.0 / 0.0, never a boolean column.
Lag, diff & change
Four functions that reference a prior observation. Each requires a timeseries and periods >= 1. The first periods observations are null because no history exists yet.
lag(x: Signal, periods: Int) -> Signal
diff(x: Signal, periods: Int) -> Signal
pct_change(x: Signal, periods: Int) -> Signal
log_change(x: Signal, periods: Int, base: "e" | 2 | 10 = "e") -> Signallag(x, periods)shiftsxforward byperiodspositions:result[i] = x[i - periods].diff(x, periods)returnsx - lag(x, periods).pct_change(x, periods)returnsx / lag(x, periods) - 1.log_change(x, periods, base)returnslog(x / lag(x, periods), base).
pct_change and log_change divide by the lagged value, so a zero denominator yields NaN; log_change also yields NaN when the ratio is zero or negative.
Rolling aggregations
Sixteen windowed functions. Each aggregates a trailing observation-count window of x.
rolling_sum(x, periods)rolling_mean(x, periods)rolling_median(x, periods)rolling_min(x, periods)rolling_max(x, periods)rolling_std(x, periods)ddof.rolling_var(x, periods)ddof. Equal to rolling_std(...) ** 2.rolling_skew(x, periods)null below 3 valid observations.rolling_kurt(x, periods)null below 4 valid observations.rolling_count(x, periods)rolling_zscore(x, periods)(x - rolling_mean) / rolling_std. Adds ddof. NaN when the std is zero.rolling_demean(x, periods)x - rolling_mean(x, periods).rolling_rank(x, periods)[0, 1].rolling_winsorize(x, periods)[lower_q, upper_q] window quantiles.rolling_quantile(x, periods, q)q-th window quantile. q is a positional third argument in [0, 1].rolling_ewma(x, periods)Shared keyword arguments. Every rolling function except rolling_count takes:
periods(required, positional): window size in observations.min_periods(defaultnull): minimum valid observations required to emit a value;nullrequires the full window of sizeperiods. Belowmin_periodsvalid values, the output isnull.ignore(default"both", also"null"/"nan"): which missing kinds to skip when forming the window.
rolling_std, rolling_var, and rolling_zscore add ddof (default 0, population; 1 for sample). rolling_winsorize adds lower_q / upper_q. rolling_ewma sets its decay through exactly one of half_life, alpha, or center_of_mass; with none supplied, periods is used as the span.
Windows count observations, not calendar time. On the default 1-minute spine, rolling_mean(iv, 20) averages 20 minutes, not 20 days. Sample the series to daily first when you want a day-count window. See the execution model for why.
// WRONG: option.iv(30) is a 1-minute series, so 252 is 252 minutes
rolling_rank(option.iv(30), 252)// RIGHT: sample to daily first, then 252 observations is ~1 year
iv_daily = option.iv(30, sampling=sampling(period="day", time="close"))
rolling_rank(iv_daily, 252)Arithmetic, comparison & logical operators
+ - * / **> >= < <= == !=&& / and, || / or-, ! / notComparison and logical operators return Float64 1.0 / 0.0, not booleans. Because the result stays numeric, masks compose by multiplication (AND) and addition (OR):
// Enter only when 1-year IV rank is elevated and price is above its 20-day mean
iv_daily = option.iv(30, sampling=sampling(period="day", time="close"))
close_daily = bar.close(sampling=sampling(period="day", time="close"))
(rolling_rank(iv_daily, 252) > 0.50) && (close_daily > rolling_mean(close_daily, 20))Division of a finite numerator by zero yields NaN. Both operands align on the union spine with backward as-of fill before the operator applies; null and NaN propagate, and NaN takes precedence over null.
Operator precedence
Highest to lowest. Use parentheses to override.
**-, !, not* /+ -> >= < <=== !=&& / and|| / orAccessors
Three accessor families pull market and calendar data onto the active spine:
bar.*:open,high,low,close,volume.option.*:delta,gamma,theta,vega,rho,iv, pluscontract, which returns aContractTable(see below).time.*: calendar attributes and session math (day_of_week,hour,minute_of_day,trading_days,session_fraction, and the rest).
Each of these returns a Signal and accepts sampling= / bucketing= to derive a coarser frequency, with one exception: option.contract returns a ContractTable and takes no sampling= / bucketing=. Full parameter tables, target-selection arguments (dte, delta, moneyness), and per-symbol coverage live in the data accessor reference.
Backtest-context accessors
Four callables return Tables rather than Signals: book(), position(), leg(slug), and option.contract(...). Each is a registered callable in the compiler. You get a usable value by projecting one numeric column with field access, which yields a Signal you can feed to any operator.
Three of them read live engine state and are gated by the evaluation context, so they resolve only in the slots where their backing state exists:
book(): whole-account state. Available in entry signals, exit signals, and adjustment triggers.position(): the structure under evaluation, aggregated across its legs. Available in adjustment triggers only.leg(slug): one leg of the position, addressed by its strategy slug. Available in adjustment triggers, where it reads the live held leg, and in a leg's contract selection, where it resolves the referenced leg's run-start candidate.
option.contract(...) is the contract-selection accessor: the per-minute selected contract, returned as a ContractTable. It reads market data and is not context-gated the way the three state accessors are.
Using a state accessor outside its slot raises an availability error that names the accessor and the context.
// Adjustment trigger: roll when the short put's delta breaches 0.40
leg("short_put").delta > 0.40// Margin defense: fire when account excess liquidity drops below $5,000
book().excess_liquidity < 5000Column inventories and per-context availability live in the data accessor reference; for how these slot into a strategy, see signals inside a backtest.
Known naming pitfalls
The short names mean, std, rank, zscore, min, max, median, sum, and var are not registered, and there is no length= window keyword. Use the rolling_* names with periods: write rolling_mean(x, 20), not mean(x, length=20). A bare accessor without call parentheses is not a value: write bar.close(), not bar.close. Assign a name before you reference it, and never reassign it.