Guide to Writing Expressions

View as Markdown

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 and WHERE
  • 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:

QuestionExpressionUsed In
Open USD trades over 1 million[Status] = 'Open' AND [Currency] = 'USD' AND [Notional] > 1000000Grid 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])} * 100Calculated Column
Price above the average for its Desk[Price] > AVG{[Price], GROUP_BY([Desk])}Format Column
Rates Desk PnL falls below -500KSUM{[PnL]} < '-500K' WHERE [Desk] = 'Rates'Alert
Price has moved more than 5% in the last hourPERCENT_CHANGE{[Price], WITHIN(1h)} > 5Observable Alert
Price has not updated for 30 secondsNO_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_CHANGE is 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:

#ElementWhat it isExample
1Columns (and Fields / $SCOPE)Values taken from the row[Price]
2Expression Functions (and operators)What to do with those valuesMIN
3AND / ORJoin multiple boolean clausesAND
4Input valuesLiterals you type in50
5WHERERestrict which rows an Aggregation or Observable Expression seesWHERE

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 / OR are 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 displayColumnFriendlyNamesForExpressions to 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)}

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

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:

FunctionShortcutExample
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:

KeywordExample
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):

OrderOperators
1NOT / !
2^
3* / %
4+ -
5Comparisons: = != < <= > >= IN CONTAINS etc.
6AND
7OR
8WHERE
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

  • NOT applies 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' END

It can also match a single value against a list of options:

 CASE [Currency] WHEN 'USD' THEN 'Dollar' WHEN 'EUR' THEN 'Euro' ELSE 'Other' END

Find Out More

See Logic in Expressions for more on IF and CASE

AdapTableQL: Multi-Clause Boolean Expressions
Fork
  • A Grid Filter for rows where Language is JavaScript and either Github Watchers > 20,500 or Github Stars > 8500
  • A custom report in Export — MIT JavaScript — where License is MIT License and Language is JavaScript
  • A Format Column Condition where Github Stars > 50,000 or License is MIT License
  • An Alert when Open Issues > 50 and Language is JavaScript
Loading demo…

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:

TypeWrite AsExample
NumberDigits, without quotes100, -5.5
TextIn single or double quotes'USD', "USD"
BooleanTRUE or FALSE[IsLive] = TRUE
DateThe DATE function, with an ISO dateDATE('2026-12-31')
DurationA number with ms, s, m, h or d30s, 5m, 1h (Observable Expressions)
ListValues in parentheses, separated by commas('USD', 'EUR', 'GBP') (with IN)
BlankNULL()[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

  • WHERE is 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

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:

WrittenProblemWrite Instead
Price > 100Columns need square brackets[Price] > 100
[Order Id] = 5Use the Column Id, not the Header[orderId] = 5
[Price] == 100Equality is a single =[Price] = 100
[Price] > 0 && [Qty] > 0Use 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 bracesSUM{[PnL]}
ROW_AGE{} > 30Durations need a unitROW_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:

ReturnTypical Modules
BooleanGrid Filter, Column Filter, Alerts, Format Column, Flashing Cell, Export, Plus Minus, Badge Style
Any ValueCalculated Column, Named Query

Note