Every calculation on BimaStack is written in one small, spreadsheet-like language. Values, conditions, functions, dates, currency and rounding, with examples from real rate cards.
The BimaStack team · · 7 min read
Every calculation on BimaStack, from a base premium to an age loading to “is this traveller over 80”, is written in one small language. If you can write an Excel formula, you can write one of these. This guide walks through everything it can do, with examples from real rate cards. The reference has the exact rules.
A formula is one expression
A formula is a single expression, read like a spreadsheet cell: no statements, no braces, no programming. It uses the names of the values it is given (the vehicle’s value, the driver’s age, the running total so far) and returns one result.
A name refers to a value the formula is given. Names are letters, digits and underscores, and may be dotted, like rate.value. A choice from a fixed list (a cover type, a zone) arrives as text, so you compare it with == "PSV".
Arithmetic
Plus, minus, times and divide, with brackets, in the usual order: brackets first, then * and /, then + and -. A minus in front of a value negates it.
Compare with == (equals), != (not equal), <, <=, > and >=. Combine with the words and, or and not. A comparison gives yes or no.
Conditions
driverAge >= 18 and driverAge <= 65
usage == "PSV" or usage == "COMMERCIAL"
not winterSports
One thing to know: comparisons don’t chain. Write 18 <= age and age <= 65, not 18 <= age <= 65. The editor tells you if you try.
if: choose a value
if(condition, value when yes, value when no) works exactly like Excel’s IF. Both answers must be the same kind (two numbers, or two texts). Only the branch that applies is worked out, so if(count == 0, 0, total / count) never divides by zero.
An age factor from a travel rate card: nested ifs, read top to bottom
The smaller or larger of two numbers: floors and caps
max(premium, 7500)
round(x, places, mode)
Rounds to 0–10 places by a named mode: "HALF_EVEN", "HALF_UP", "HALF_DOWN", "UP", "DOWN", "CEILING", "FLOOR"
round(premium, 2, "HALF_EVEN")
ageInYears(birth, onDate)
Completed years between two dates (yyyy-MM-dd)
ageInYears(dateOfBirth, coverStart)
daysBetween(from, to)
Days from one date to another, not counting the first
daysBetween(coverFrom, coverTo) + 1
convert(amount, from, to)
Converts between currencies at the published rate
convert(limit, "USD", "KES")
prorate(amount, from, to, type)
Scales an annual amount to the cover period: "FULL_TERM" or "PRORATED" (days ÷ 365)
prorate(premium, coverFrom, coverTo, "PRORATED")
Rounding is usually left to the end. Use round when a rule says to round first: each traveller’s premium before adding them up, or up to the next 5 shillings with round(x / 5, 0, "CEILING") * 5. A currency conversion uses one rate for the whole calculation, so two conversions of the same pair always agree.
let: name a step
Start a formula with one or more lets to name a part of it and use it after. Each ends with a semicolon, and the last line is the result.
A loading per child above the four included
let extraChildren = max(childCount - 4, 0);
extraChildren * 1500
Days of cover, counting both ends or not, from the table’s own convention
let days = if(convention == "INCLUSIVE",
daysBetween(coverFrom, coverTo) + 1,
daysBetween(coverFrom, coverTo));
days >= bandFrom and days <= bandTo
A let can’t reuse a name already given, so a formula always means one thing when read from the top.
Mistakes are caught before you save
Every formula is checked as you type, and one with an error can’t be saved. The check runs in four stages, and each message points at the exact place:
Stage
Catches
Example message
Syntax
A missing bracket, a stray symbol
Unexpected '!'. Use 'not' for negation.
Reference
A name or function that doesn’t exist here, with a suggestion
Unknown variable 'driverAg'. Suggestion: Did you mean 'driverAge'?
Type
Adding text to a number, a wrong number of arguments
if()'s two branches must have the same type, found NUMBER and STRING.
Risk (a warning)
Something that will fail when it runs
Division by a literal zero will always fail at evaluation time.
A risk warning doesn’t stop you saving; the first three do. What no check can catch is a formula that is valid but means the wrong thing (10% of the sum insured when you meant 10% of the running total), which is what trying it is for.
Try it
Give the formula sample values and see the result, worked out by the same engine that prices live quotes. Then try it inside its workflow, against your real rate tables, and read the result of every step.
What it deliberately leaves out
No loops or lists. Working over a list of travellers or rate rows is a workflow’s job: it runs your formula once per item. One formula stays one readable line of logic.
No lookups. Rates come from rate tables, read by a workflow and handed to the formula as a value.
No side effects. A formula computes a value and nothing else, so the same inputs always give the same answer.
Where formulas live
You write a formula inside a workflow step, or once under a name to be reused by many workflows. Either way it is saved as a version that never changes: an edit makes a new version, and a quote always records which version priced it. Built-in calculations such as reading a rate table are written in this same language, so nothing is hidden from you.
Write your first formula
Register your company and open the formula editor.