Formula DSL Guide

Write custom factors with a safe, time-aware formula language

FactorBench formulas are numeric, composable, and evaluated by the Rust runner on point-in-time data. Use them to rank, filter, and backtest strategies with full transparency.

price / eps.ttm(0)momentum(252, 21)rank(roe.ttm(0))price(date_of_last_earnings + 1)zscore(fcf.ttm(0) / revenue.ttm(0))

Syntax at a glance

The DSL supports numeric expressions, time-aware metric lookups, and a small set of safe functions. Identifiers are case-insensitive and ignore punctuation, but use the canonical names below for clarity.

Core rules

  • Use metric.ttm(k) or metric.mrq(k) to access fundamentals.
  • k=0 is the latest period, k=4 is ~1 year ago.
  • Price is available as price or price(date_expr).
  • Cross-sectional transforms: rank(expr), zscore(expr), z(expr).
  • Reuse saved formulas with formula:<id> in selection steps.

Grammar (simplified)

expr     ::= term (('+' | '-') term)*
term     ::= factor (('*' | '/') factor)*
factor   ::= power ('^' power)*
power    ::= unary
unary    ::= ('+' | '-') unary | primary
primary  ::= NUMBER | ident | call | '(' expr ')'

ident    ::= name '.' basis '(' k ')' | price | date_vars
call     ::= func_name '(' expr (',' expr)* ')'

Metrics and time bases

Use .ttm(k) for trailing twelve months or .mrq(k) for most recent quarter values. For ratio fields (like pe or grossMargin),.ttm() returns the latest ratio value rather than summing quarters.

Special date variables:
date_of_last_earnings, last_earnings_date,date_of_last_earnings_announcement, last_earnings_announcement_date
Use inside price() as epoch-day numbers, or pass an ISO date string like"2024-12-31".
price(date_of_last_earnings + 1)
price("2024-12-31")

Per-share and flow metrics (TTM)

epsrevenueocffcfdpsebitebitdagrossProfit

Margins and returns (TTM ratios)

grossMarginoperatingMarginnetMarginroeroaroicincomeQuality

Valuation and ratio fields (non-additive)

pepegforwardPegpbpspocfpfcfevToEbitdaevToSalesdividendYielddividendPayout

Balance sheet (MRQ)

totalAssetstotalDebtcashEqsharesbook_value_per_share

Liquidity (MRQ)

currentRatioquickRatiocashRatio

Leverage and efficiency

debtToEquitydebtToAssetsnetDebtToEBITDAreceivablesTurnoverinventoryTurnoverdaysSalesOutstanding

Tip: You can also reference raw column names from the fundamentals dataset (case-insensitive). The parser normalizes punctuation to underscores, so book_value_per_share andbookValuePerShare both resolve in most cases.

Functions and transforms

Functions operate per ticker. Transforms like rank() and zscore()compute across the current universe.

Price and technical functions

  • price() - Price on or before a date (defaults to the as-of price).
  • sma(n) - Simple moving average of price over n trading days.
  • momentum(n[, skip]) - Price return over n trading days, optionally skipping recent days.
  • volatility(n) - Std dev of daily returns over n trading days.
  • rolling_max(n) - Highest price over the last n trading days.

Cross-sectional transforms

  • rank(expr) - Percentile rank across the current universe (0..1, higher is better).
  • zscore(expr) - Z-score across the current universe (mean 0, std 1).
  • z(expr) - Alias for zscore().

Numeric helpers

  • min(a, b, ...) - Minimum of arguments (use to cap values).
  • max(a, b, ...) - Maximum of arguments (use to floor values).
  • round(x) - Round to the nearest integer.
  • floor(x) - Round down to an integer.
  • ceil(x) - Round up to an integer.

Formula examples

Mix and match metrics, functions, and transforms to build your factor screens.

Valuation

P/E Ratio
price / eps.ttm(0)
P/B Ratio
price / book_value_per_share.mrq(0)
EV/EBITDA (Approx)
(price * shares.mrq(0) + totalDebt.mrq(0) - cashEq.mrq(0)) / ebitda.ttm(0)
Dividend Yield
dps.ttm(0) / price

Growth and quality

EPS YoY Growth
(eps.ttm(0) - eps.ttm(4)) / eps.ttm(4)
Revenue YoY Growth
(revenue.ttm(0) - revenue.ttm(4)) / revenue.ttm(4)
ROIC Trend
roic.ttm(0) - roic.ttm(4)
Income Quality
incomeQuality.ttm(0)

Technical and events

12M-1M Momentum
momentum(252, 21)
Price vs 200D SMA
price / sma(200)
52-Week High Distance
1 - price / rolling_max(252)
Earnings Reaction
price(date_of_last_earnings_announcement + 1) / price(date_of_last_earnings_announcement - 1) - 1

Limitations and data behavior

Keep these guardrails in mind when composing formulas.

  • No boolean operators or conditionals inside formulas. Use selection filters to apply thresholds.
  • Division by zero or missing metrics returns NaN; screens can drop N/As using the selection settings.
  • Formulas are numeric-only and evaluated per ticker; transforms apply cross-sectionally.
  • TTM fields sum up to four quarters (except ratio metrics, which use the latest reported value).

Need help building a factor?

Ask the FactorBench assistant or explore the preset formula library in the app.

Open FactorBench