Guide to Writing Expressions
Summary
- An AdapTableQL Expression is a short, human-readable string - it reads almost like the business question it answers
- One language is used everywhere: Grid Filters, Column Filters, Styles, Alerts, Calculated Columns, Reports and more
- Expressions are built from just 5 elements: Columns, Functions (and operators),
AND/OR, input values andWHERE - Aggregation and Observable Functions use curly braces (
SUM{[PnL]},HIGHEST{[Price], WITHIN(1h)}); Standard Functions use parentheses (MIN([Bid], [Ask])) - Expressions can be handwritten (e.g. in Initial Adaptable State) or built in the UI, without typing, in the Query Builder
An AdapTableQL Expression is a human-readable string which AdapTable evaluates against the data in the grid.
This page shows how to write one - it is both a quick tour of what Expressions can do and a reference for developers who provide Expressions in Initial Adaptable State.
Hint
Expressions do not have to be typed by hand - they can be built in the Query Builder or Expression Editor (which validates as you type), and then copied into Initial State
Reads Like the Question
Each Expression reads very close to the business question it answers:
| Question | Expression | Used In |
|---|---|---|
| Open USD trades over 1 million | [Status] = 'Open' AND [Currency] = 'USD' AND [Notional] > 1000000 | Grid Filter |
| Trades maturing in the next 30 days | [MaturityDate] < ADD_DAYS(CURRENT_DAY(), 30) | Format Column |
| Counterparties with 'Bank' in the name | [Counterparty] CONTAINS 'Bank' | Grid Filter |
| Notional in USD | [Notional] * [FxRate] | Calculated Column |
| Label each trade by size | [Notional] > 1000000 ? 'Large' : 'Small' | Calculated Column |
| Each row's share of its Desk's PnL (%) | [PnL] / SUM{[PnL], GROUP_BY([Desk])} * 100 | Calculated Column |
| Price above the average for its Desk | [Price] > AVG{[Price], GROUP_BY([Desk])} | Format Column |
| Rates Desk PnL falls below -500K | SUM{[PnL]} < '-500K' WHERE [Desk] = 'Rates' | Alert |
| Price has moved more than 5% in the last hour | PERCENT_CHANGE{[Price], WITHIN(1h)} > 5 | Observable Alert |
| Price has not updated for 30 seconds | NO_CHANGES{[Price], WITHIN(30s)} | Flashing Cell |
Note
- The same Expression works in any Module which accepts that kind of Expression - so a condition written for a Grid Filter can equally drive a Style or an Alert
- How an Expression is evaluated depends on its type - see Standard, Aggregation and Observable
PERCENT_CHANGEis dual-named:PERCENT_CHANGE([Price], 'INCREASE')is Relative Change (parentheses);PERCENT_CHANGE{[Price], WITHIN(1h)}is Observable (curly braces)
Caution
In the examples on this page, lines starting with // are explanations only - they are not part of the Expression
Anatomy of an Expression
Expressions can be complex, but they are built from a small set of elements:
| # | Element | What it is | Example |
|---|---|---|---|
| 1 | Columns (and Fields / $SCOPE) | Values taken from the row | [Price] |
| 2 | Expression Functions (and operators) | What to do with those values | MIN |
| 3 | AND / OR | Join multiple boolean clauses | AND |
| 4 | Input values | Literals you type in | 50 |
| 5 | WHERE | Restrict which rows an Aggregation or Observable Expression sees | WHERE |
Hint
- An Expression can include as many of each element as required
- Not every element is present in every Expression
Note
- Strictly speaking there are really only 2 elements: Expression Functions and input values
- Columns, operators and
AND/ORare themselves Functions, treated separately here so the colours stay readable
This boolean Expression uses 1–4:
MIN([BloombergBid], [MarkitBid]) > 50 OR [Currency] = 'USD'
// 1 — Columns: `BloombergBid`, `MarkitBid`, `Currency`
// 2 — Functions / operators: `MIN`, `>`
// 3 — Logical: `OR`
// 4 — Input values: `50`, `'USD'`This Aggregation Expression adds WHERE so all 5 appear:
SUM{[PnL]} > '5M' WHERE [Currency] = 'USD' AND [Book] = 'Rates'
// 1 — Columns: `PnL`, `Currency`, `Book`
// 2 — Functions / operators: `SUM`, `>`
// 3 — Logical: `AND`
// 4 — Input values: `'5M'`, `'USD'`, `'Rates'`
// 5 — `WHERE`Hint
When compared with an Aggregation, a number can use a K, M or B suffix, e.g. '5M' is 5,000,000, '1.5B' is 1,500,000,000 and '-500K' is -500,000
The type pages use these same colours for their examples.
1. Columns
A Column is referenced by its columnId in square brackets. AdapTableQL reads that cell's value when the Expression evaluates.
// Reference Price Column (in a value-producing Expression)
[Price] * 100;// Reference Currency Column (in a Boolean Expression)
[Currency] = 'USD';There is no limit on how many Columns an Expression can include:
// Multiply 3 columns together
PRODUCT([OrderCost], [ItemCost], [PackageCost]);And AdapTable allows you to use Columns in both left and right side of a comparison Expression:
// Compare 2 columns
[OrderCost] > [ItemCost];Note
- A Column is optional in Standard Expressions, but one is usually used
- Some Aggregation and Observable Expressions require a Column
The stored Expression always uses the Column Id, not the Header — e.g. [orderId], not Order Id.
Hint
- Overviews (Settings Panel, wizards) show the Column Header by default
- Set
displayColumnFriendlyNamesForExpressionsto false if you want Ids there too - The Expression Editor always inserts Ids
Caution
Always reference a Column with square brackets, e.g. [Price]
FIELD
A Field is a value in the row data that is not a Column.
Unlike with Column, there is no square-bracket shortcut — always use FIELD:
FIELD('Price') * 100;$SCOPE
$SCOPE stands in for the current Column when the Module has Column Scope (Format Column, Alert, Flashing Cell, Column Filter, Plus Minus, and Named Queries referenced from those Modules).
Treat it conceptually like a Column (which is what it repeatedly gets translated to at evaluation time):
IS_POSITIVE($SCOPE);It is not valid in a Grid Filter, as there is no current Column; a Named Query can use it, but only when referenced from a Module with Column Scope.
2. Expression Functions
Expression Functions are the core of every Expression.
Caution
Aggregation and Observable Expressions require Functions in order to be valid
Function names - and keywords such as AND, OR and WHERE - are not case sensitive, but by convention they are written in capitals (as are Custom Functions).
Many Functions take arguments, which are placed inside brackets.
Note
- AdapTableQL uses different types of brackets depending on the type of the Expression:
- Standard Functions use parentheses:
SUM(1, 2, 3),AVG([Bid], [Ask]) - Aggregation Functions use curly braces:
SUM{[PnL]},AVG{[Price], GROUP_BY([Currency])} - Observable Functions use curly braces:
HIGHEST{[price], WITHIN(1h)}
- Standard Functions use parentheses:
Arguments are often Columns:
// Standard — average of 3 cells in this row (parentheses)
AVG([bloombergBid], [markitBid], [indicativeBid]);// Aggregation — average of the Price column across rows (curly braces)
AVG{[Price]};They can also be literals, or other Functions (the inner Function must return the right data type):
// The date 5 days from now
ADD_DAYS(CURRENT_DAY(), 5);// Is TradeDate in the next 5 days
ADD_DAYS(CURRENT_DAY(), 5) < [TradeDate];Other typical Functions:
COALESCE([bloombergPrice], [indicativePrice], [markitPrice]);CONCAT([currency], '-', [country]);MAX([ItemCost], [OrderCost], [InvoicedCost], [PackageCost] * 10);Find Out More
- See the full list of functions in the Expression Functions catalogue
- Hide Functions you do not want by Reducing Expression Complexity
- Developers can add Custom Expression Functions to supplement those provided by AdapTable
Hint
Date and working-day Functions use the Holiday Calendar: IS_WORKDAY, IS_HOLIDAY, IS_NEXT_WORKDAY, IS_PREV_WORKDAY, WORKDAY and NETWORKDAYS. Write dates with DATE('2026-12-31'), not a quoted string.
Feedback
If an Expression Function you would like to use is missing, please email Adaptable Tools Support with suggested details
Operator shortcuts
Some of the most common Functions, particularly those which perform comparisons, also have operator shortcuts:
| Function | Shortcut | Example |
|---|---|---|
EQ | = | [Currency] = 'USD' |
NEQ | != | [Status] != 'Closed' |
GT / GTE / LT / LTE | > >= < <= | [Price] > 100 |
SUM / SUB / PRODUCT / DIV / POW / MOD | + - * / ^ % | [PackageCost] + [OrderCost] |
NOT | ! | !IS_BLANK([Comments]) |
IF | ? : | [PnL] > 0 ? 'Profit' : 'Loss' |
So these pairs are the same Expression:
GT([Price], 100);
// is the same as
[Price] > 100;Functions and operators can be mixed:
MAX([Price], [Cost]) > 100;Keyword Operators
Some Functions can also be written as a word between their two values, which reads more naturally:
| Keyword | Example |
|---|---|
CONTAINS / NOT_CONTAINS | [Counterparty] CONTAINS 'Bank' |
STARTS_WITH / ENDS_WITH | [Ticker] STARTS_WITH 'US' |
IN / NOT_IN | [Currency] IN ('USD', 'EUR', 'GBP') |
[Currency] IN ('USD', 'EUR') AND [Counterparty] NOT_CONTAINS 'Test'3. Combining clauses
Boolean Expressions can contain multiple clauses, joined with AND or OR:
[Price] > 100 AND [Currency] = 'USD' [Price] > 100 OR [Currency] = 'USD'There is no limit on the number of clauses.
Hint
& and | can be used as shorthand for AND and OR
Use parentheses when mixing AND and OR — they are sometimes required, and always easier to read:
([Price] > 100) AND (([Currency] = 'USD') OR ([ItemCost] < [OrderCost]))Order of Evaluation
Without parentheses, operators are applied in this order (highest first):
| Order | Operators |
|---|---|
| 1 | NOT / ! |
| 2 | ^ |
| 3 | * / % |
| 4 | + - |
| 5 | Comparisons: = != < <= > >= IN CONTAINS etc. |
| 6 | AND |
| 7 | OR |
| 8 | WHERE |
| 9 | ? : |
So AND is applied before OR:
[Price] > 100 OR [Currency] = 'EUR' AND [Status] = 'Open'
// is the same as
[Price] > 100 OR ([Currency] = 'EUR' AND [Status] = 'Open')Caution
NOTapplies only to the value straight after it, so wrap a comparison in parentheses:NOT([Status] = 'Closed')NOT [Status] = 'Closed'means(NOT [Status]) = 'Closed'(and the Expression Editor warns about it)
Choosing Between Values
CASE returns a different value depending on which condition is true - ideal for Calculated Columns:
CASE WHEN [Price] > 100 THEN 'High' WHEN [Price] > 10 THEN 'Medium' ELSE 'Low' ENDIt can also match a single value against a list of options:
CASE [Currency] WHEN 'USD' THEN 'Dollar' WHEN 'EUR' THEN 'Euro' ELSE 'Other' ENDFind Out More
See Logic in Expressions for more on IF and CASE
- A Grid Filter for rows where
Languageis JavaScript and eitherGithub Watchers> 20,500 orGithub Stars> 8500 - A custom report in Export —
MIT JavaScript— whereLicenseis MIT License andLanguageis JavaScript - A Format Column Condition where
Github Stars> 50,000 orLicenseis MIT License - An Alert when
Open Issues> 50 andLanguageis JavaScript
4. Input values
Input values are literals you type — they are not read from the grid:
[Price] > 100;[Currency] = 'USD';Each type of value is written as follows:
| Type | Write As | Example |
|---|---|---|
| Number | Digits, without quotes | 100, -5.5 |
| Text | In single or double quotes | 'USD', "USD" |
| Boolean | TRUE or FALSE | [IsLive] = TRUE |
| Date | The DATE function, with an ISO date | DATE('2026-12-31') |
| Duration | A number with ms, s, m, h or d | 30s, 5m, 1h (Observable Expressions) |
| List | Values in parentheses, separated by commas | ('USD', 'EUR', 'GBP') (with IN) |
| Blank | NULL() | [Qty] = 0 ? NULL() : [PnL] / [Qty] |
Hint
- To include a quote in some text, either use the other kind of quote (
"O'Brien") or double it ('O''Brien') - To test whether a cell is empty, use
IS_BLANK([Comments])
Note
Text comparisons ignore case by default, so [Currency] = 'usd' matches USD - see Case Sensitivity
5. WHERE
WHERE adds a Standard (Boolean) Expression to an Aggregation or Observable Expression.
Only rows that match the clause take part in the aggregation or the observation.
It can go in 2 places:
- After the Expression - it applies to every Aggregation in the Expression
- Inside the braces - it applies only to that one Aggregation (or Observable Function)
Note
WHEREis not used on Standard Expressions (Grid Filter, Format Column etc.)- Those Expressions already run per row — the whole Expression is the filter
SUM{[PnL]} > '5M' WHERE [Country] IN ('US', 'UK')
// 2 / 1 / 4 — the main Aggregation Expression
// 5 — `WHERE`
// 1 / 4 — the additional (standard Boolean) clause (same colours as everywhere else)The clause can be as rich as any other Boolean Expression:
SUM{[PnL]} > '5M' WHERE [Currency] = 'USD' AND [RequiredDate] < ADD_DAYS(CURRENT_DAY(), 30)Placed inside the braces, each Aggregation can have its own filter:
SUM{[Notional] WHERE [Side] = 'Buy'} - SUM{[Notional] WHERE [Side] = 'Sell'}Find Out More
- Aggregation Expressions —
WHEREon aggregated Alerts, with a demo - Observable Expressions —
WHEREon watched row / grid changes
Common Mistakes
The Expression Editor flags these as you type (often with a one-click fix), but they are worth knowing when writing Expressions by hand:
| Written | Problem | Write Instead |
|---|---|---|
Price > 100 | Columns need square brackets | [Price] > 100 |
[Order Id] = 5 | Use the Column Id, not the Header | [orderId] = 5 |
[Price] == 100 | Equality is a single = | [Price] = 100 |
[Price] > 0 && [Qty] > 0 | Use AND / OR (not && / ||) | [Price] > 0 AND [Qty] > 0 |
[Price] > '100' | Numbers are not quoted | [Price] > 100 |
[TradeDate] > '2026-01-01' | Dates need the DATE function | [TradeDate] > DATE('2026-01-01') |
SUM([PnL]) | Aggregating a Column needs curly braces | SUM{[PnL]} |
ROW_AGE{} > 30 | Durations need a unit | ROW_AGE{} > 30s |
NOT [Status] = 'Closed' | NOT applies only to [Status] | NOT([Status] = 'Closed') |
Return Value
Each Module decides whether the Expression must return boolean or a value of any type:
| Return | Typical Modules |
|---|---|
| Boolean | Grid Filter, Column Filter, Alerts, Format Column, Flashing Cell, Export, Plus Minus, Badge Style |
| Any Value | Calculated Column, Named Query |
Note
- Relative Change Expressions are always boolean
- Advanced Functions (
QUERY,VAR,IF,CASE,IFERROR,IFNA,NA,FIELD,$SCOPE) are covered under Advanced Expressions