Skip to content

Formula Language Guide

Formulas are the key to customizing your FinZot plan. They replace static numbers with dynamic logic, allowing the plan to react to changing conditions over time.

If you’re comfortable in Excel or Google Sheets, this syntax will feel familiar. Each formula is a standalone expression that returns one of two things:

  • A Number: Used for any amount, such as a custom income or expense.
  • A Boolean (TRUE/FALSE): Used to trigger milestones or control conditional logic.

For a complete list of all available planner fields (like net_worth or age_primary), see the Planner Context Reference.

  • Numbers: All numbers are 64-bit floats. You can write them in several ways:
    • 123 (whole number)
    • 1_000_000 (underscores as visual separators, ignored by the engine)
    • 45.67 (decimal)
    • 10% (percentage, becomes 0.1)
    • .75% (leading decimal, becomes 0.0075)
  • Booleans: Simple TRUE or FALSE values.
  • Variables: These are fields from the planner, like invested_assets or total_debt.
  • Comments: Use // to add a comment. The engine will ignore everything after it on that line.
    • invested_assets * 1.1 // Add a 10% raise
  • Case Insensitivity: All variables, function names, and keywords (IF, AND, TRUE, etc.) are case-insensitive. age_primary is the same as Age_Primary.

The language supports standard arithmetic, comparison, and logical operators.

CategoryOperatorsDescription
Grouping( ... )Controls the order of operations.
Exponent^Exponentiation (e.g., 2 ^ 3 is 8).
Arithmetic*, /Multiplication and Division.
+, -Addition and Subtraction.
Comparison=, !=, <>Equal, Not Equal.
>, >=, <, <=Greater Than, Less Than (or Equal).
LogicalNOTInverts a boolean (NOT TRUE is FALSE).
ANDTRUE if both sides are TRUE.
ORTRUE if at least one side is TRUE.

The AND and OR operators are “short-circuiting.” This means they stop evaluating as soon as they have an answer.

Operators follow standard precedence (like PEMDAS/BODMAS). ^ is evaluated first, then * and /, then + and -. AND is evaluated before OR.

When in doubt, use parentheses () to make your logic clear.

((taxable_balance + roth_balance) >= 1_500_000) AND (NOT debt_free)

Functions are powerful tools for more complex logic.

These functions let you choose a value based on one or more tests.

  • IF(condition, then_value, else_value) Returns then_value if the condition is TRUE, otherwise returns else_value.

    // Spend 5k on vacation if is over 65, otherwise 2k
    IF(age_primary >= 65, 5000, 2000)
  • IFS(condition1, value1, [condition2, value2], ...) Evaluates pairs of conditions and values. It returns the value for the first condition that is TRUE. If no conditions are true, it returns 0.0.

    Tip: Use TRUE as your final condition to create a default “else” value.

    IFS(
    net_worth > 5_000_000, 0.02,
    net_worth > 1_000_000, 0.01,
    TRUE, 0.0
    )
  • CASE WHEN condition1 THEN value1 ... [ELSE else_value] END A readable, SQL-style alternative to IFS. It returns the THEN value for the first WHEN condition that is met. If no conditions match, it returns the ELSE value or 0.0 if ELSE is omitted.

    // Set a spending goal based on the plan year
    CASE
    WHEN calendar_year <= 2040 THEN 75_000
    WHEN calendar_year <= 2045 THEN 80_000
    ELSE 85_000
    END

These functions are identical to the AND, OR, and NOT operators but can be more familiar for excel users.

  • AND(expr1, expr2, ...): Returns TRUE if all arguments are TRUE.
  • OR(expr1, expr2, ...): Returns TRUE if any argument is TRUE.
  • NOT(expr): Inverts a single boolean expression.
AND(
age_primary > 50,
net_worth > 1_000_000,
debt_free
)
  • MIN(num1, num2, ...): Returns the smallest number from the list.
  • MAX(num1, num2, ...): Returns the largest number from the list.
    // Calculate net savings, but ensure it's never less than zero
    MAX(0, net_worth - total_debt)
    // Cap a withdrawal at 50,000
    MIN(FUTURE_DOLLARS(40000), 50000)
  • ABS(number): Returns the absolute (non-negative) value of a number.
    // Find the difference between two balances, regardless of which is larger
    ABS(roth_balance - traditional_balance)
  • POWER(base, exponent): Raises a number to a power. (Identical to the ^ operator).
    // Calculate 1.03 compounded for (age - 20) periods
    POWER(1.03, age_primary - 20)

These advanced functions look at data from previous years, enabling you to model trends, smooth volatility, or create time-based triggers.

IMPORTANT: The first argument to LAG, PCT_CHANGE, and all ROLLING_ functions must be a raw planner field (e.g., net_worth). You cannot use an expression (e.g., net_worth - total_debt).

  • LAG(variable, periods) Retrieves the value of a variable from a specific number of periods (years) ago. LAG(net_worth, 0) returns the current year’s value.

    // Calculate this year's savings by comparing current and prior net worth
    net_worth - LAG(net_worth, 1)
  • PCT_CHANGE(variable, periods) Calculates the percentage change between the current value and the value from periods ago: (current - past) / past. periods must be 1 or greater. It returns 0.0 if the past value is zero or invalid.

    // Trigger a milestone if net worth dropped more than 20% in the last year
    PCT_CHANGE(net_worth, 1) < -0.20

These functions compute a single statistic by looking at a “window” of the last periods years (inclusive of the current year).

  • ROLLING_SUM(variable, periods): Total sum over the window.
  • ROLLING_AVG(variable, periods): Simple average over the window.
  • ROLLING_MIN(variable, periods): Smallest value in the window.
  • ROLLING_MAX(variable, periods): Largest value in the window.

These are powerful when combined with CASE to create “guardrails” or smooth out volatile values.

// Set a variable withdrawal rate based on the 3-year average of net worth
CASE
WHEN ROLLING_AVG(invested_assets, 3) < 1_000_000 THEN 0.035 // 3.5% rate
WHEN ROLLING_AVG(invested_assets, 3) < 3_000_000 THEN 0.040 // 4.0% rate
ELSE 0.045 // 4.5% rate
END

Tip: Rolling Functions in Early Years

Rolling functions still run in the first few years of a plan, but they operate on a partial window.

  • ROLLING_AVG(net_worth, 3) in Year 2 will give you the average of Year 1 and Year 2.
  • ROLLING_AND(debt_free, 3) will return TRUE in Year 1 and Year 2 by default.

These functions look at a window of boolean values.

  • ROLLING_AND(bool_variable, periods): TRUE if the variable was TRUE for all years in the window.
  • ROLLING_OR(bool_variable, periods): TRUE if the variable was TRUE for at least one year in the window.
// Trigger a milestone if...
OR(
// 1. The 3-year minimum net worth ever dropped below 1M
ROLLING_MIN(net_worth, 3) < 1_000_000,
// 2. OR not yet debt-free
NOT debt_free
)
// Go on a 5,000 vacation, but only if been debt-free for 3 straight years
IF(ROLLING_AND(debt_free, 3), 5000, 0)