DocsThe Signal Language (DSL)Operators and functions
The Signal Language (DSL)Reference

Operators and functions

This page groups the Signal language's callable names by category so you know what exists and how to reach for it. The exhaustive per-function signatures and examples live in the DSL operator & function catalog.

Updated Jul 20266 min read

How to read this page

The Signal language exposes a fixed, complete set of callables: roughly 70 functions, two constants, and the Table accessors documented with the data accessors. This page groups them by what they do; the catalog is the exhaustive lookup.

Four naming rules prevent most compile failures. Learn them before anything else.

  • Full names only. Use rolling_mean, never mean; rolling_std, never std; rolling_rank, never rank. The short forms are not registered and fail to compile.
  • Rolling and lag windows count observations, not calendar time. The window argument is periods, and it counts rows on the input's current spine. rolling_mean(x, 20) on a 1-minute series averages 20 minutes. Sample to daily first for "20 days" (see the execution model).
  • Comments are //. There is no block-comment form.
  • Accessors are called with parentheses. Write bar.close(), never bar.close.

Operators and precedence

The operators split into four groups.

  • Arithmetic: + - * / **
  • Comparison: > >= < <= == !=
  • Logical: && (alias and), || (alias or)
  • Unary: -, ! (alias not)

Every comparison and every logical result is a Float64 Signal of 1.0 (true) or 0.0 (false), never a boolean. This keeps the value column uniform and lets you compose comparisons with arithmetic. Dividing a finite numerator by zero yields NaN, consistent with IEEE 754.

Precedence runs from highest to lowest. Use parentheses to override it.

Level
Operators
Associativity
1 (highest)
**
right-to-left
2
unary -, !, not
right-to-left
3
*, /
left-to-right
4
+, -
left-to-right
5
>, >=, <, <=
left-to-right
6
==, !=
left-to-right
7
&&, and
left-to-right
8 (lowest)
||, or
left-to-right

So (iv - rv) / rv is a percent spread, while iv - rv / rv subtracts rv / rv from iv. And (iv > 0.3) && (delta > 0.5) needs the parentheses, because relational binds tighter than && only within each side.

Element-wise transforms

Each transform maps a Signal to a Signal row by row, with signature (Signal) -> Signal unless noted. null and NaN propagate unchanged; any ±inf result becomes NaN. See the catalog for per-function arguments.

  • abs: absolute value.
  • sign: 1.0 if positive, -1.0 if negative, 0.0 at zero.
  • sqrt: principal square root; NaN for negative inputs.
  • root(x, power): x ** (1 / power); power is a positive integer.
  • log(x, base="e"|2|10): logarithm in the requested base; NaN for x <= 0.
  • lognp(x, n=1, base="e"|2|10): log(x + n), shifting the input off the domain boundary at zero (useful for series like volume that include zeroes).
  • exp: e ** x; overflow to +inf becomes NaN.
  • floor, ceil: round toward negative and positive infinity.
  • round(x, decimals=0): banker's rounding (half-to-even).
  • clip(x, lower=None, upper=None): clamp to [lower, upper]; either bound may be omitted; raises if lower > upper.
  • Trig and hyperbolic: sin cos tan arcsin arccos arctan deg2rad rad2deg sinh cosh tanh arcsinh arccosh arctanh. Trig inputs are in radians.

Missing-data and conditional

These functions read and act on the two kinds of missing value the language tracks, null versus NaN.

  • fill_missing(x, value, fill="null"|"nan"|"both"): replace missing observations of x with value. Default fill="nan". A row where value is itself missing is skipped.
  • coalesce(*args, exhausted="null"|"nan"): first non-null, non-NaN value across the arguments, left to right per row. Variadic, at least one argument. Default exhausted="null" chooses the fallback when every argument is missing.
  • where(cond, arg1, arg2): row-wise select arg1 where cond is truthy, else arg2. Any non-zero numeric value is truthy; a null cond yields null, a NaN cond yields NaN.
  • is_missing(x, kind="null"|"nan"|"both"): 1.0 where x is missing, 0.0 otherwise. The output is never null or NaN itself.
  • replace_where(x, cond, value): x with truthy-cond rows replaced by value. A null or NaN cond keeps the original x.

Lag, diff, and change

Each of these requires a timeseries and an integer periods >= 1. The first periods rows are null because there is not enough history, and all four are no-lookahead by construction.

  • lag(x, periods): x shifted forward periods observations: result[i] = x[i - periods].
  • diff(x, periods): x - lag(x, periods).
  • pct_change(x, periods): x / lag(x, periods) - 1; a lagged value of exactly zero yields NaN.
  • log_change(x, periods, base="e"|2|10): log(x / lag(x, periods), base); a non-positive ratio yields NaN.
// One-period log return of the daily close
daily_close = bar.close(sampling=sampling(period="day", time="close"))
log_change(daily_close, 1)

Rolling aggregations

Every rolling function takes a timeseries and an observation-count periods. They share four keyword arguments:

  • periods (required): window size in observations.
  • min_periods=None: minimum valid observations to emit a value. None requires the full periods window, so the output is null until the row index reaches periods - 1.
  • ignore="both": which missing kinds to exclude from the window: "null", "nan", "both" (default), or "neither". Under "null", a NaN in the window propagates to a NaN output; under "nan", a null propagates to a null output; "neither" propagates both.
  • ddof (on rolling_std, rolling_var, rolling_zscore only): 0 for population (default), 1 for sample.

The functions:

Function
Notes
rolling_sum
Window total.
rolling_mean
Arithmetic mean.
rolling_median
50th percentile; resistant to outliers.
rolling_min, rolling_max
Window extremes.
rolling_std, rolling_var
Dispersion; var equals std ** 2.
rolling_skew
Third standardized moment; null below 3 valid observations.
rolling_kurt
Fisher excess kurtosis (normal is 0); null below 4 valid observations.
rolling_count
Count of non-missing observations; always emits, output Float64.
rolling_zscore
(x - rolling_mean) / rolling_std; NaN when the rolling std is 0.
rolling_demean
x - rolling_mean(x, periods).
rolling_rank
Percentile rank in [0, 1]; a window of size 1 emits 0.5.
rolling_winsorize
Clip to a trailing [lower_q, upper_q] quantile range.
rolling_quantile(x, periods, q)
q-th quantile: 0 is the min, 0.5 the median, 1 the max.
rolling_ewma
Exponentially weighted mean. Set decay with exactly one of half_life, alpha, or center_of_mass; with none, the span is periods (alpha = 2 / (periods + 1)).

This is where the observation-count window bites. A "trailing year" is 252 daily observations, not 252 of whatever the accessor returns by default.

// WRONG: option.iv(30) is a 1-minute series, so 252 counts 252 minutes, not a year
rolling_rank(option.iv(30), 252)
// RIGHT: sample to daily first, then 252 observations is about one year
iv_daily = option.iv(30, sampling=sampling(period="day", time="close"))
rolling_rank(iv_daily, 252)

The window counts observations on the input's spine: 252 on a daily series is roughly a year; on a 1-minute series it is 252 minutes.

Resampling functions and builders

Two builders describe a resampling, and two apply functions run it against an already-computed Signal.

  • sampling(*, period=None, multiplier=1, offset=None, time=None, times=None, days=None, weeks=None, months=None): build a point-in-time sampling config.
  • bucketing(*, period, agg="last", multiplier=1, offset=None, partial=true): build a window-aggregation config.
  • sample(x, sampling): apply a sampling to Signal x. Sampling selects rows and forward-fills the last known value onto the target spine (as-of).
  • bucket(x, bucketing): apply a bucketing to Signal x. Bucketing aggregates the observed rows in each window into one row, labeled at the window's right endpoint.

sampling and bucketing are mutually exclusive on a single accessor call. The execution model covers the point-in-time versus window-aggregation semantics, and how each interacts with the union spine.

// Daily-close last value of a derived intraday ratio
ratio = bar.close() / bar.open()
bucket(ratio, bucketing(period="day", agg="last"))

Constants

Two bare-name constants resolve to a scalar at compile time and can be used anywhere a scalar is expected.

  • math.pi: 3.141592653589793
  • math.e: 2.718281828459045

Defaults and edge cases

These behaviours shape any result the functions above produce, so read a number against them.

  • Dividing a finite numerator by zero yields NaN (IEEE 754).
  • Any ±inf result is converted to NaN. This covers overflow in exp, sinh, cosh, and trig singularities in tan.
  • With min_periods=None, a rolling output is null until the row index reaches periods - 1.
  • Windows are observation-count on the input's current spine, not calendar time.

Two calling rules apply across every signature. Positional arguments must precede named arguments. A bare * in a signature marks the start of keyword-only arguments, so everything after it must be passed by name.

Was this page helpful?