# Guide to Writing Expressions

Canonical page: https://www.adaptabletools.com/docs/adaptable-ql-expression-writing

- 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](https://www.adaptabletools.com/docs/dev-guide-adaptable-state-initial-state/index.md)) or built in the UI, without typing, in the [Query Builder](https://www.adaptabletools.com/docs/ui-query-builder/index.md)

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](https://www.adaptabletools.com/docs/dev-guide-adaptable-state-initial-state/index.md).

Expressions do not have to be typed by hand - they can be built in the [Query Builder](https://www.adaptabletools.com/docs/ui-query-builder/index.md) or [Expression Editor](https://www.adaptabletools.com/docs/ui-expression-editor/index.md) (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      |

- 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](https://www.adaptabletools.com/docs/adaptable-ql-expression-standard/index.md), [Aggregation](https://www.adaptabletools.com/docs/adaptable-ql-expression-aggregation/index.md) and [Observable](https://www.adaptabletools.com/docs/adaptable-ql-expression-observable/index.md)
- `PERCENT_CHANGE` is dual-named: `PERCENT_CHANGE([Price], 'INCREASE')` is [Relative Change](https://www.adaptabletools.com/docs/adaptable-ql-expression-relative-change/index.md) (parentheses); `PERCENT_CHANGE{[Price], WITHIN(1h)}` is [Observable](https://www.adaptabletools.com/docs/adaptable-ql-expression-observable/index.md) (curly braces)

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                                        | <CodeStep step={1} example="[Price]" /> |
| 2   | Expression Functions (and operators) | What to do with those values                                     | <CodeStep step={2} example="MIN" />     |
| 3   | `AND` / `OR`                                          | Join multiple boolean clauses                                    | <CodeStep step={3} example="AND" />     |
| 4   | Input values                                          | Literals you type in                                             | <CodeStep step={4} example="50" />      |
| 5   | `WHERE`                                               | Restrict which rows an Aggregation or Observable Expression sees | <CodeStep step={5} example="WHERE" />   |

- An Expression can include as many of each element as required
- Not every element is present in every Expression

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

```ts [[2, 1, "MIN"], [1, 1, "BloombergBid"], [1, 1, "MarkitBid"],[2, 1, ">"],[4, 1, "50"],[3, 1, "OR"], [1, 1, "Currency"],  [4, 1, "USD"]]
 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:

```ts [[2, 1, "SUM"], [1, 1, "PnL"], [2, 1, ">"], [4, 1, "5M"], [5, 1, "WHERE"], [1, 1, "Currency"], [4, 1, "USD"], [3, 1, "AND"], [1, 1, "Book"], [4, 1, "Rates"]]
 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`
```

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](https://www.adaptabletools.com/docs/dev-guide-columns-adaptable-column/index.md) is referenced by its `columnId` in square brackets. AdapTableQL reads that cell's value when the Expression evaluates.

```ts [[1, 2, "Price"],[4, 2, "100"]]
// Reference Price Column (in a value-producing Expression)
[Price] * 100;
```

```ts [[1, 2, "Currency"],[4, 2, "USD"]]
// Reference Currency Column (in a Boolean Expression)
[Currency] = 'USD';
```

There is no limit on how many Columns an Expression can include:

```ts [[2, 2, "PRODUCT"],[1, 2, "OrderCost"],[1, 2, "ItemCost"],[1, 2, "PackageCost"]]
// 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:

```ts [[1, 2, "OrderCost"],[1, 2, "ItemCost"]]
// Compare 2 columns
[OrderCost] > [ItemCost];
```

- 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`.

- Overviews (Settings Panel, wizards) show the Column Header by default
- Set `displayColumnFriendlyNamesForExpressions` to _false_ if you want Ids there too
- The [Expression Editor](https://www.adaptabletools.com/docs/ui-expression-editor/index.md) always inserts Ids

Always reference a Column with square brackets, e.g. `[Price]`

### `FIELD`

A [Field](https://www.adaptabletools.com/docs/adaptable-ql-expression-advanced-row-data/index.md) is a value in the row data that is **not** a Column.

Unlike with Column, there is no square-bracket shortcut — always use `FIELD`:

```ts [[2, 1, "FIELD"], [1, 1, "'Price'"],[4, 1, "100"]]
FIELD('Price') * 100;
```

### `$SCOPE`

[$SCOPE](https://www.adaptabletools.com/docs/adaptable-ql-expression-advanced-scope-function/index.md) stands in for the current Column when the Module has [Column Scope](https://www.adaptabletools.com/docs/dev-guide-columns-scope/index.md) (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):

```ts [[2, 1, "IS_POSITIVE"], [1, 1, "$SCOPE"]]
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](https://www.adaptabletools.com/docs/adaptable-ql-expression-functions/index.md) are the core of every Expression.

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.

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

```ts [[2, 2, "AVG"], [1, 2, "bloombergBid"], [1, 2, "markitBid"], [1, 2, "indicativeBid"]]
// Standard — average of 3 cells in this row (parentheses)
AVG([bloombergBid], [markitBid], [indicativeBid]);
```

```ts [[2, 2, "AVG"], [1, 2, "Price"]]
// 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):

```ts [[2, 2, "ADD_DAYS"], [2, 2, "CURRENT_DAY"], [4, 2, "5"]]
// The date 5 days from now
ADD_DAYS(CURRENT_DAY(), 5);
```

```ts [[2, 2, "ADD_DAYS"], [2, 2, "CURRENT_DAY"], [4, 2, "5"], [2, 2, "<"], [1, 2, "TradeDate"]]
// Is TradeDate in the next 5 days
ADD_DAYS(CURRENT_DAY(), 5) < [TradeDate];
```

Other typical Functions:

```ts [[2, 1, "COALESCE"], [1, 1, "bloombergPrice"], [1, 1, "indicativePrice"], [1, 1, "markitPrice"]]
COALESCE([bloombergPrice], [indicativePrice], [markitPrice]);
```

```ts [[2, 1, "CONCAT"], [1, 1, "currency"], [4, 1, "'-'"], [1, 1, "country"]]
CONCAT([currency], '-', [country]);
```

```ts [[2, 1, "MAX"], [1, 1, "ItemCost"], [1, 1, "OrderCost"], [1, 1, "InvoicedCost"], [1, 1, "PackageCost"], [2, 1, "*"], [4, 1, "10"]]
MAX([ItemCost], [OrderCost], [InvoicedCost], [PackageCost] * 10);
```

- See the full list of functions in the [Expression Functions catalogue](https://www.adaptabletools.com/docs/adaptable-ql-expression-functions/index.md)
- Hide Functions you do not want by [Reducing Expression Complexity](https://www.adaptabletools.com/docs/adaptable-ql-expression-managing/index.md)
- Developers can add [Custom Expression Functions](https://www.adaptabletools.com/docs/adaptable-ql-expression-functions-custom/index.md) to supplement those provided by AdapTable

Date and working-day Functions use the [Holiday Calendar](https://www.adaptabletools.com/docs/dev-guide-tutorial-holiday-calendars/index.md): `IS_WORKDAY`, `IS_HOLIDAY`, `IS_NEXT_WORKDAY`, `IS_PREV_WORKDAY`, `WORKDAY` and `NETWORKDAYS`. Write dates with `DATE('2026-12-31')`, not a quoted string.

If an Expression Function you would like to use is missing, please email <a href='mailto:support@adaptabletools.com'>Adaptable Tools Support</a> 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:

```ts [[2, 1, "GT"], [1, 1, "Price"],[4, 1, "100"], [1, 3, "Price"], [2, 3, ">"],[4, 3, "100"]]
GT([Price], 100);
// is the same as
[Price] > 100;
```

Functions and operators can be mixed:

```ts [[2, 1, "MAX"], [1, 1, "Price"], [1, 1, "Cost"], [2, 1, ">"], [4, 1, "100"]]
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')`    |

```ts [[1, 1, "Currency"], [2, 1, "IN", 11], [4, 1, "USD"], [4, 1, "EUR"], [3, 1, "AND"], [1, 1, "Counterparty"], [2, 1, "NOT_CONTAINS"], [4, 1, "Test"]]
[Currency] IN ('USD', 'EUR') AND [Counterparty] NOT_CONTAINS 'Test'
```

## 3. Combining clauses

Boolean Expressions can contain multiple clauses, joined with `AND` or `OR`:

```ts [[1, 1, "Price"], [2, 1, ">"], [3, 1, "AND"], [1, 1, "Currency"], [2, 1, "="] ,[4, 1, "USD"]]
 [Price] > 100 AND [Currency] = 'USD'
```

```ts [[1, 1, "Price"], [2, 1, ">"], [3, 1, "OR"], [1, 1, "Currency"], [2, 1, "="],[4, 1, "USD"]]
 [Price] > 100 OR [Currency] = 'USD'
```

There is no limit on the number of clauses.

`&` 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:

```ts [[1, 1, "Price"], [2, 1, ">"], [3, 1, "AND"] , [1, 1, "Currency"], [2, 1, "="], [3, 1, "OR"], [1, 1, "ItemCost"], [2, 1, "<"],[1, 1, "OrderCost"]]
 ([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`:

```ts [[1, 1, "Price"], [2, 1, ">"], [4, 1, "100"], [3, 1, "OR"], [1, 1, "Currency"], [4, 1, "EUR"], [3, 1, "AND"], [1, 1, "Status"], [4, 1, "Open"]]
 [Price] > 100 OR [Currency] = 'EUR' AND [Status] = 'Open'
// is the same as
 [Price] > 100 OR ([Currency] = 'EUR' AND [Status] = 'Open')
```

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

```ts [[2, 1, "CASE"], [1, 1, "Price", 12], [4, 1, "100"], [4, 1, "High"], [1, 1, "Price", 43], [4, 1, "10", 52], [4, 1, "Medium"], [4, 1, "Low"]]
 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:

```ts [[2, 1, "CASE"], [1, 1, "Currency"], [4, 1, "USD"], [4, 1, "Dollar"], [4, 1, "EUR"], [4, 1, "Euro"], [4, 1, "Other"]]
 CASE [Currency] WHEN 'USD' THEN 'Dollar' WHEN 'EUR' THEN 'Euro' ELSE 'Other' END
```

See [Logic in Expressions](https://www.adaptabletools.com/docs/adaptable-ql-expression-advanced-logic/index.md) for more on `IF` and `CASE`

**Example: AdapTableQL: Multi-Clause Boolean Expressions**

Using Boolean Expressions with multiple Clauses

- A [Grid Filter](https://www.adaptabletools.com/docs/handbook-grid-filter/index.md) for rows where `Language` is _JavaScript_ **and** either `Github Watchers` > 20,500 **or** `Github Stars` > 8500
- A custom report in [Export](https://www.adaptabletools.com/docs/handbook-exporting/index.md) — `MIT JavaScript` — where `License` is _MIT License_ **and** `Language` is _JavaScript_
- A [Format Column Condition](https://www.adaptabletools.com/docs/handbook-column-formatting-conditions/index.md) where `Github Stars` > 50,000 **or** `License` is _MIT License_
- An [Alert](https://www.adaptabletools.com/docs/handbook-alerting/index.md) when `Open Issues` > 50 **and** `Language` is _JavaScript_

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Standard Expressions with multiple clauses',
  initialState: {
    Dashboard: {
      Tabs: [
        {
          Name: 'Demo',
          Toolbars: ['GridFilter', 'Export', 'Alert'],
        },
      ],
    },
    StatusBar: {
      StatusBars: [
        {
          Key: 'Center Panel',
          StatusBarPanels: ['Export', 'Alert', 'GridFilter'],
        },
      ],
    },
    Theme: {CurrentTheme: 'dark'},

    Export: {
      CurrentReport: 'MIT JavaScript',
      CurrentFormat: 'JSON',
      Reports: [
        {
          Name: 'MIT JavaScript',
          ReportColumnScope: 'ScopeColumns',
          ReportRowScope: 'ExpressionRows',
          Scope: {
            ColumnIds: ['name', 'language', 'topics', 'license', 'updated_at'],
          },
          Expression: "[language] = 'JavaScript' AND [license] = 'MIT License'",
        },
      ],
    },
    FormatColumn: {
      FormatColumns: [
        {
          Name: 'style-all',
          Style: {
            BackColor: 'var(--ab-color-palette-26)',
            ForeColor: 'var(--ab-color-palette-13)',
          },
          Scope: {
            All: true,
          },
          Rule: "[license] = 'MIT License' OR [github_stars] > 50000 ",
        },
      ],
    },
    Alert: {
      AlertDefinitions: [
        {
          Name: 'alert-Warning-43',
          MessageType: 'Warning',
          Scope: {
            All: true,
          },
          Rule: '[open_issues_count] > 50  AND [language] = "JavaScript"',
          AlertProperties: {
            DisplayNotification: true,
          },
        },
      ],
    },
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          TableColumns: [
            'name',
            'language',
            'open_issues_count',
            'license',
            'github_watchers',
            'github_stars',
            'created_at',
            'has_wiki',
            'updated_at',
            'pushed_at',
            'closed_issues_count',
            'open_pr_count',
            'closed_pr_count',
            'has_projects',
            'has_pages',
            'week_issue_change',
          ],
          Name: 'Standard Layout',
          GridFilter: {
            Expression:
              '[language]="JavaScript" AND ([github_watchers] > 20500 OR [github_stars] > 8500)',
          },
          AutoSizeColumns: true,
        },
      ],
    },
  },
};
```

## 4. Input values

Input values are literals you type — they are not read from the grid:

```ts [[1, 1, "Price"], [2, 1, ">"], [4, 1, "100"]]
[Price] > 100;
```

```ts [[1, 1, "Currency"], [2, 1, "="], [4, 1, "USD"]]
[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]`      |

- 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])`

Text comparisons ignore case by default, so `[Currency] = 'usd'` matches `USD` - see [Case Sensitivity](https://www.adaptabletools.com/docs/adaptable-ql-expression-managing/index.md#case-sensitivity)

## 5. `WHERE`

`WHERE` adds a Standard (Boolean) Expression to an [Aggregation](https://www.adaptabletools.com/docs/adaptable-ql-expression-aggregation/index.md) or [Observable](https://www.adaptabletools.com/docs/adaptable-ql-expression-observable/index.md) 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)

- `WHERE` is not used on Standard Expressions (Grid Filter, Format Column etc.)
- Those Expressions already run per row — the whole Expression *is* the filter

```ts [[2, 1, "SUM"],[1, 1, "PnL"],[2, 1, ">"],[4, 1, "5M"],[5, 1, "WHERE"],[1, 1, "Country"],[2, 1, "IN"],[4, 1, "US"],[4, 1, "UK"]]
 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:

```ts [[2, 1, "SUM"],[1, 1, "PnL"],[2, 1, ">"],[4, 1, "5M"],[5, 1, "WHERE"],[1, 1, "Currency"],[4, 1, "USD"],[3, 1, "AND"],[1, 1, "RequiredDate"],[2, 1, "ADD_DAYS"],[2, 1, "CURRENT_DAY"]]
 SUM{[PnL]} > '5M' WHERE [Currency] = 'USD' AND [RequiredDate] < ADD_DAYS(CURRENT_DAY(), 30)
```

Placed inside the braces, each Aggregation can have its own filter:

```ts [[2, 1, "SUM", 1], [1, 1, "Notional", 6], [5, 1, "WHERE", 16], [1, 1, "Side", 23], [4, 1, "Buy"], [2, 1, "SUM", 40], [1, 1, "Notional", 45], [5, 1, "WHERE", 55], [1, 1, "Side", 62], [4, 1, "Sell"]]
 SUM{[Notional] WHERE [Side] = 'Buy'} - SUM{[Notional] WHERE [Side] = 'Sell'}
```

- [Aggregation Expressions](https://www.adaptabletools.com/docs/adaptable-ql-expression-aggregation/index.md) — `WHERE` on aggregated Alerts, with a demo
- [Observable Expressions](https://www.adaptabletools.com/docs/adaptable-ql-expression-observable/index.md) — `WHERE` on watched row / grid changes

## Common Mistakes

The [Expression Editor](https://www.adaptabletools.com/docs/ui-expression-editor/index.md) 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                                                                    |

- [Relative Change](https://www.adaptabletools.com/docs/adaptable-ql-expression-relative-change/index.md) Expressions are always boolean
- Advanced Functions (`QUERY`, `VAR`, `IF`, `CASE`, `IFERROR`, `IFNA`, `NA`, `FIELD`, `$SCOPE`) are covered under [Advanced Expressions](https://www.adaptabletools.com/docs/adaptable-ql-expression-advanced/index.md)
