Radiant documentation
Forecasting

Formula syntax

The operators and functions a formula node accepts, and how a formula reads other nodes.

A formula node computes its value from other nodes on the map. A formula is a single expression — not a script, and not a sequence of statements — and it always produces one number or one true/false value.

revenue * margin - fixed_costs

Referring to other nodes

A bare name in a formula is a reference to another node's code. revenue means "the value of the node whose reference code is revenue".

A node's reference code is shown on its card and in the inspector, under its title. Formulas read codes, not titles, so renaming a node does not break a formula.

If a formula names a code that no node has, that node is blocked with Formula node references unknown code "…", and so is anything that reads it. Nothing else on the map is affected.

If two formulas end up depending on each other, directly or through a chain, Radiant reports Dependency cycle detected and names the loop. Every node in the cycle is blocked; the rest of the map still evaluates.

Operators

Arithmetic, in the usual precedence, with parentheses to override it:

OperatorMeaning
+ - * /Add, subtract, multiply, divide
^Raise to a power, right-associative — 2 ^ 3 ^ 2 is 2 ^ (3 ^ 2)
- (prefix)Negate
(revenue - fixed_costs) / months
base_rate ^ 2
-adjustment

Comparisons produce true or false:

OperatorMeaning
< <= > >=Ordering
== !=Equal, not equal

Logic combines them. These are words, not symbols:

OperatorMeaning
and orBoth, either
not (prefix)Negate a condition
growth > 0 and margin >= 0.2
not (risk_flag or blocked)

Comparisons allow for rounding

==, <, > and their relatives compare with a small tolerance, so two values that differ only by floating-point noise count as equal rather than accidentally unequal.

Choosing between values

if(condition, then, otherwise) picks one of two values. There is no ternary ? : — the parser rejects it and tells you to use if instead.

if(growth > 0, revenue * growth, 0)

Only the branch that is taken is evaluated, so the untaken branch cannot cause an error.

Functions

Arithmetic

FunctionArgumentsResult
min(a, b, …)one or moreThe smallest
max(a, b, …)one or moreThe largest
abs(x)1Distance from zero
round(x)1Nearest whole number
floor(x)1Rounded down
ceil(x)1Rounded up
clamp(x, low, high)3x held between low and high

clamp fails if low is greater than high, rather than quietly swapping them.

clamp(forecast, 0, 1)
max(base_case, downside, upside)

Probabilities and odds

These four convert between the ways a chance can be written. They exist because averaging or adjusting a probability is often better done in log-odds than directly.

FunctionArgumentsResult
logit(p)1Log-odds of a probability
inv_logit(x)1Back from log-odds to a probability
odds(p)1Probability as odds
prob(o)1Odds as a probability

Each is strict about its input: logit requires a probability strictly between 0 and 1, odds accepts 0 up to but not including 1, and prob requires non-negative odds. A value outside the range is an error rather than an infinity that quietly spreads through the rest of the map.

inv_logit((logit(analyst_view) + logit(market_view)) / 2)

Reading a distribution

These four ask a question about another node's whole range of simulated outcomes, rather than its single value. The first argument is a node reference, written directly — not an expression.

FunctionArgumentsResult
cdf(node, x)2Share of outcomes at or below x
prob_lt(node, x)2Share of outcomes below x
prob_gt(node, x)2Share of outcomes above x
quantile(node, p)2The value at probability p
prob_gt(revenue, 1000000)
quantile(revenue, 0.9)

These need a simulation

A distribution query reads a whole set of samples, which only a simulation produces. Outside a run there is nothing to read, and the formula reports that it needs a simulation. quantile also requires its probability to be between 0 and 1.

What a formula cannot do

The parser rejects these and says so, rather than interpreting them:

  • Assignments and multiple statements. A formula is one expression.
  • The ternary operator a ? b : c. Use if(a, b, c).
  • Member access like node.value.
  • Arrays, and text values. Numbers and true/false only.

Division by zero is an error, and so is a power with no real result — a negative base with a fractional exponent, for example. Both stop the node rather than producing a silent NaN.