From 316f0ee9ea396f936885052c341d15ee833f9b65 Mon Sep 17 00:00:00 2001 From: javier Date: Tue, 5 May 2026 12:29:50 +0200 Subject: [PATCH] Improve array functions page examples and fix style issues Replace synthetic ARRAY literal examples with queries against the demo market_data table for most functions. Remove CREATE TABLE/INSERT blocks for aggregate examples in favor of SAMPLE BY queries on live data. Fix alphabetical ordering (stddev before sum), correct NaN references to NULL, remove parenthetical dashes, and drop references to the not-yet-available array_agg function. --- documentation/query/functions/array.md | 478 +++++++++++++++---------- 1 file changed, 297 insertions(+), 181 deletions(-) diff --git a/documentation/query/functions/array.md b/documentation/query/functions/array.md index 9e547bb8a..53f5dc866 100644 --- a/documentation/query/functions/array.md +++ b/documentation/query/functions/array.md @@ -16,9 +16,9 @@ don't contribute to either count or sum. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_avg(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]); @@ -28,17 +28,26 @@ SELECT array_avg(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]); | --------- | | 1.5 | +**Average bid price across all 40 order book levels:** + +```questdb-sql demo title="array_avg - average bid price" +SELECT symbol, array_avg(bids[1]) AS avg_bid_price +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + ## array_build `array_build(nArrays, size, filler1 [, filler2, ...])` constructs a `DOUBLE` array at runtime, where the length and contents can vary per row. -Use `array_build` when the `ARRAY[...]` literal syntax is not enough — for +Use `array_build` when the `ARRAY[...]` literal syntax is not enough, for example when you need to: -- **Fill an array with a scalar** — create a zero vector, or fill every +- **Fill an array with a scalar** - create a zero vector, or fill every position with a computed value (like `array_max`) -- **Stack arrays into a 2D matrix** — combine several 1D arrays into a single +- **Stack arrays into a 2D matrix** - combine several 1D arrays into a single `DOUBLE[][]` for downstream array operations #### Parameters @@ -46,8 +55,8 @@ example when you need to: | Parameter | Description | |-----------|-------------| | `nArrays` | How many sub-arrays the output has. Must be an integer literal (not a column, expression, or bind variable). `1` produces a 1D `DOUBLE[]`. `2` or more produces a 2D `DOUBLE[][]` with `nArrays` sub-arrays, each of length `size`. | -| `size` | Length of each sub-array. Can be an `INT`/`LONG` value, or a `DOUBLE[]` array — in which case the array's element count is used as the length. | -| `filler1 .. fillerN` | One filler per sub-array (exactly `nArrays` fillers required). A **scalar** (`DOUBLE`/`INT`/`LONG`) is repeated for every position. A **`DOUBLE[]` array** is copied position-by-position: if shorter than `size`, remaining positions are `NaN`; if longer, excess elements are ignored. | +| `size` | Length of each sub-array. Can be an `INT`/`LONG` value, or a `DOUBLE[]` array whose element count is used as the length. | +| `filler1 .. fillerN` | One filler per sub-array (exactly `nArrays` fillers required). A **scalar** (`DOUBLE`/`INT`/`LONG`) is repeated for every position. A **`DOUBLE[]` array** is copied position-by-position: if shorter than `size`, remaining positions are `NULL`; if longer, excess elements are ignored. | All arguments except `nArrays` can be constants, declared variables, column references, or expressions evaluated per row. @@ -55,7 +64,7 @@ references, or expressions evaluated per row. #### Return type - `DOUBLE[]` when `nArrays` is `1` -- `DOUBLE[][]` when `nArrays` is `2` or more — the first dimension has length +- `DOUBLE[][]` when `nArrays` is `2` or more, where the first dimension has length `nArrays`, the second has length `size` The output is always 1D or 2D. Passing a large `nArrays` (e.g. 100) produces a @@ -73,7 +82,7 @@ SELECT array_build(1, 3, 0) FROM long_sequence(1); | -------------- | | [0.0,0.0,0.0] | -**Variable-length fill — size from a column:** +**Variable-length fill - size from a column:** ```questdb-sql demo title="array_build - variable-length fill" SELECT x, array_build(1, x::int, -1) FROM long_sequence(3); @@ -106,7 +115,7 @@ it is repeated in every position. **Copy an existing array:** When both `size` and the filler are the same `DOUBLE[]`, the filler is copied -position-by-position — effectively cloning the array: +position-by-position, effectively cloning the array: ```questdb-sql demo title="array_build - copy an array" SELECT array_build(1, bids[1], bids[1]) @@ -116,18 +125,17 @@ LIMIT 1; The result is a new `DOUBLE[]` with the same length and values as `bids[1]`. -**NaN padding when the filler array is shorter than `size`:** +**NULL padding when the filler array is shorter than `size`:** -```questdb-sql demo title="array_build - NaN padding" +```questdb-sql demo title="array_build - NULL padding" SELECT array_build(1, 5, ARRAY[10.0, 20.0, 30.0]) FROM long_sequence(1); ``` -| array_build | -| ------------------------- | -| [10.0,20.0,30.0,NaN,NaN] | +| array_build | +| ------------------------------ | +| [10.0,20.0,30.0,null,null] | -Positions beyond the filler's length are filled with `NaN` (which QuestDB -treats as `NULL` for `DOUBLE` values). +Positions beyond the filler's length are filled with `NULL`. **2D array with scalar fill:** @@ -150,8 +158,10 @@ FROM market_data LIMIT 1; ``` -Returns a `DOUBLE[][]` where the first row contains bid prices and the second -row contains ask prices, both taken from the order book snapshot. +Here `bids[1]` appears twice: in the `size` position it provides the output +length (40 elements), and as the first filler it supplies the bid prices. +`asks[1]` is the second filler. The result is a `DOUBLE[2][40]` where the first +row contains bid prices and the second row contains ask prices. **Stack multiple array columns into a 2D array:** @@ -182,8 +192,8 @@ copied position-by-position into its respective sub-array. evaluates to `NULL`, the result is a `NULL` array. - Fillers must be scalars or 1D `DOUBLE[]` arrays. Multi-dimensional array fillers are not accepted. -- `NaN` values inside a filler array are copied as-is. A `NULL` filler array - fills the entire row with `NaN`. +- `NULL` values inside a filler array are copied as-is. A `NULL` filler array + fills the entire row with `NULL`. - Both `nArrays` and `size` are capped at 268,435,455. The total element count (`nArrays × size`) must also fit within memory limits. @@ -194,9 +204,9 @@ elements do not contribute to the count. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT @@ -208,6 +218,15 @@ SELECT | ---| --- | | 2 | 0 | +**Count the number of price levels on each side of the book:** + +```questdb-sql demo title="array_count - order book levels" +SELECT symbol, array_count(bids[1]) AS bid_levels, array_count(asks[1]) AS ask_levels +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + ## array_cum_sum `array_cum_sum(array)` returns a 1D array of the cumulative sums over the array, @@ -217,9 +236,9 @@ elements behave as if they were zero. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_cum_sum(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]); @@ -229,6 +248,17 @@ SELECT array_cum_sum(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]); | ---------------------- | | ARRAY[1.0,2.0,4.0,6.0] | +**Cumulative bid volume (depth of book):** + +```questdb-sql demo title="array_cum_sum - cumulative book depth" +SELECT symbol, array_cum_sum(bids[2]) AS cumulative_bid_volume +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +Each position shows the total volume available at that level and above. + ## array_elem_avg `array_elem_avg(array1, array2 [, ...])` or `array_elem_avg(array)` returns an @@ -243,11 +273,11 @@ Uses Kahan compensated summation to minimize floating-point rounding errors. **Multi-argument mode:** -- `array1`, `array2` [, `...`] — two or more `DOUBLE[]` arrays +- `array1`, `array2` [, `...`] - two or more `DOUBLE[]` arrays **Aggregate mode:** -- `array` — a `DOUBLE[]` column +- `array` - a `DOUBLE[]` column #### Examples @@ -259,31 +289,18 @@ SELECT array_elem_avg(ARRAY[10.0, 20.0, 30.0], ARRAY[30.0, 40.0, 50.0]); | ------------------ | | [20.0,30.0,40.0] | -**Aggregate — average bid sizes per symbol each hour:** +**Aggregate - average bid volume per level each hour:** -```questdb-sql -CREATE TABLE book_sizes ( - ts TIMESTAMP, symbol SYMBOL, bid_sizes DOUBLE[] -) TIMESTAMP(ts) PARTITION BY DAY; - -INSERT INTO book_sizes VALUES - ('2025-06-01T09:30:00', 'AAPL', ARRAY[100.0, 200.0, 150.0]), - ('2025-06-01T09:30:05', 'AAPL', ARRAY[120.0, 180.0, 160.0, 90.0]), - ('2025-06-01T09:30:00', 'MSFT', ARRAY[500.0, 400.0]), - ('2025-06-01T09:30:05', 'MSFT', ARRAY[600.0, 300.0]); - -SELECT ts, symbol, array_elem_avg(bid_sizes) -FROM book_sizes +```questdb-sql demo title="array_elem_avg - average bid volume per level" +SELECT timestamp, symbol, array_elem_avg(bids[2]) AS avg_bid_volume +FROM market_data +WHERE symbol = 'EURUSD' + AND timestamp IN '$today' SAMPLE BY 1h; ``` -| ts | symbol | array_elem_avg | -| --------------------------- | ------ | ------------------------ | -| 2025-06-01T09:00:00.000000Z | AAPL | [110.0,190.0,155.0,90.0] | -| 2025-06-01T09:00:00.000000Z | MSFT | [550.0,350.0] | - -The `AAPL` group has snapshots with different book depths. The fourth position -only has one contributing row. +Each position in the result holds the average volume at that book level across +all snapshots in the hour. ## array_elem_max @@ -297,11 +314,11 @@ different-length, and multi-dimensional behavior as `array_elem_min`. **Multi-argument mode:** -- `array1`, `array2` [, `...`] — two or more `DOUBLE[]` arrays +- `array1`, `array2` [, `...`] - two or more `DOUBLE[]` arrays **Aggregate mode:** -- `array` — a `DOUBLE[]` column +- `array` - a `DOUBLE[]` column #### Examples @@ -324,20 +341,17 @@ SELECT array_elem_max( | ---------------------------------- | | [[4.0,8.0,7.0],[5.0,8.0,9.0]] | -**Aggregate — best bid prices per symbol each hour:** - -Using the `book` table from the [array_elem_min](#array_elem_min) example: +**Aggregate - best bid price at each level over the hour:** -```questdb-sql -SELECT ts, symbol, array_elem_max(bid_prices) -FROM book +```questdb-sql demo title="array_elem_max - best bid per level" +SELECT timestamp, symbol, array_elem_max(bids[1]) AS best_bid_per_level +FROM market_data +WHERE symbol = 'EURUSD' + AND timestamp IN '$today' SAMPLE BY 1h; ``` -| ts | symbol | array_elem_max | -| --------------------------- | ------ | -------------------------- | -| 2025-06-01T09:00:00.000000Z | AAPL | [150.5,149.5,149.0,148.0] | -| 2025-06-01T09:00:00.000000Z | MSFT | [420.0,419.5] | +Each position holds the highest bid price seen at that book level during the hour. ## array_elem_min @@ -367,17 +381,17 @@ number of dimensions. **Multi-argument mode:** -- `array1` — a `DOUBLE[]` array -- `array2` — a `DOUBLE[]` array -- `...` — additional `DOUBLE[]` arrays (optional) +- `array1` - a `DOUBLE[]` array +- `array2` - a `DOUBLE[]` array +- `...` - additional `DOUBLE[]` arrays (optional) **Aggregate mode:** -- `array` — a `DOUBLE[]` column +- `array` - a `DOUBLE[]` column #### Examples -**Multi-argument — element-wise minimum of two arrays:** +**Multi-argument - element-wise minimum of two arrays:** ```questdb-sql SELECT array_elem_min(ARRAY[1.0, 5.0, 3.0], ARRAY[4.0, 2.0, 6.0]); @@ -387,7 +401,7 @@ SELECT array_elem_min(ARRAY[1.0, 5.0, 3.0], ARRAY[4.0, 2.0, 6.0]); | --------------- | | [1.0,2.0,3.0] | -**Multi-argument — arrays of different lengths:** +**Multi-argument - arrays of different lengths:** ```questdb-sql SELECT array_elem_min( @@ -402,7 +416,7 @@ SELECT array_elem_min( The fourth position has only one contributing value. -**Multi-argument — NULL elements are skipped:** +**Multi-argument - NULL elements are skipped:** ```questdb-sql SELECT array_elem_min(ARRAY[100.0, null], ARRAY[null, 200.0]); @@ -415,33 +429,19 @@ SELECT array_elem_min(ARRAY[100.0, null], ARRAY[null, 200.0]); Each position takes the minimum over the values that are present (1 value each here, not 2). -**Aggregate — worst bid prices per symbol each hour:** +**Aggregate - worst bid price at each level over the hour:** -```questdb-sql -CREATE TABLE book ( - ts TIMESTAMP, symbol SYMBOL, bid_prices DOUBLE[] -) TIMESTAMP(ts) PARTITION BY DAY; - -INSERT INTO book VALUES - ('2025-06-01T09:30:00', 'AAPL', ARRAY[150.0, 149.5, 149.0]), - ('2025-06-01T09:30:05', 'AAPL', ARRAY[150.5, 149.0, 148.5, 148.0]), - ('2025-06-01T09:30:00', 'MSFT', ARRAY[420.0, 419.5]), - ('2025-06-01T09:30:05', 'MSFT', ARRAY[419.0, 418.5]); - -SELECT ts, symbol, array_elem_min(bid_prices) -FROM book +```questdb-sql demo title="array_elem_min - worst bid per level" +SELECT timestamp, symbol, array_elem_min(bids[1]) AS worst_bid_per_level +FROM market_data +WHERE symbol = 'EURUSD' + AND timestamp IN '$today' SAMPLE BY 1h; ``` -| ts | symbol | array_elem_min | -| --------------------------- | ------ | -------------------------- | -| 2025-06-01T09:00:00.000000Z | AAPL | [150.0,149.0,148.5,148.0] | -| 2025-06-01T09:00:00.000000Z | MSFT | [419.0,418.5] | - -The `AAPL` group has snapshots with different book depths. The fourth position -only has one contributing row. +Each position holds the lowest bid price seen at that book level during the hour. -**Multi-argument — 2D arrays:** +**Multi-argument - 2D arrays:** ```questdb-sql SELECT array_elem_min( @@ -468,11 +468,11 @@ Uses Kahan compensated summation to minimize floating-point rounding errors. **Multi-argument mode:** -- `array1`, `array2` [, `...`] — two or more `DOUBLE[]` arrays +- `array1`, `array2` [, `...`] - two or more `DOUBLE[]` arrays **Aggregate mode:** -- `array` — a `DOUBLE[]` column +- `array` - a `DOUBLE[]` column #### Examples @@ -487,24 +487,17 @@ SELECT array_elem_sum( | ----------------- | | [11.0,22.0,33.0] | -**Aggregate — total filled quantities per level across a session:** - -```questdb-sql -CREATE TABLE fills ( - ts TIMESTAMP, symbol SYMBOL, fill_qtys DOUBLE[] -) TIMESTAMP(ts) PARTITION BY DAY; - -INSERT INTO fills VALUES - ('2025-06-01T09:30:00', 'AAPL', ARRAY[100.0, 200.0]), - ('2025-06-01T09:30:05', 'AAPL', ARRAY[150.0, 50.0]), - ('2025-06-01T09:30:10', 'AAPL', ARRAY[250.0, 300.0]); +**Aggregate - total bid volume per level over the hour:** -SELECT array_elem_sum(fill_qtys) FROM fills; +```questdb-sql demo title="array_elem_sum - total volume per level" +SELECT timestamp, symbol, array_elem_sum(bids[2]) AS total_bid_volume_per_level +FROM market_data +WHERE symbol = 'EURUSD' + AND timestamp IN '$today' +SAMPLE BY 1h; ``` -| array_elem_sum | -| --------------- | -| [500.0,550.0] | +Each position holds the sum of all bid volumes seen at that level during the hour. ## array_max @@ -514,9 +507,9 @@ contains no finite values, the function returns `NULL`. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_max(ARRAY[ [1.0, 5.0], [3.0, 2.0] ]); @@ -526,6 +519,15 @@ SELECT array_max(ARRAY[ [1.0, 5.0], [3.0, 2.0] ]); | --------- | | 5.0 | +**Best bid and best ask from the full order book arrays:** + +```questdb-sql demo title="array_max - best bid and ask" +SELECT symbol, array_max(bids[1]) AS best_bid, array_min(asks[1]) AS best_ask +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + ## array_min `array_min(array)` returns the minimum value from all the array elements. `NULL` @@ -534,9 +536,9 @@ contains no finite values, the function returns `NULL`. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_min(ARRAY[ [1.0, 5.0], [3.0, 2.0] ]); @@ -546,6 +548,15 @@ SELECT array_min(ARRAY[ [1.0, 5.0], [3.0, 2.0] ]); | --------- | | 1.0 | +**Worst price on each side of the book:** + +```questdb-sql demo title="array_min - deepest book levels" +SELECT symbol, array_min(bids[1]) AS worst_bid, array_max(asks[1]) AS worst_ask +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + ## array_position `array_position(array, elem)` returns the position of `elem` inside the 1D `array`. If @@ -554,8 +565,8 @@ position of the first `NULL` element, if any. #### Parameters -- `array` — the 1D array -- `elem` — the element to look for +- `array` - the 1D array +- `elem` - the element to look for #### Examples @@ -569,6 +580,17 @@ SELECT | -- | ---- | | 1 | NULL | +**Verify best_bid is always the first element of bids[1]:** + +```questdb-sql demo title="array_position - find best_bid in the book" +SELECT symbol, array_position(bids[1], best_bid) AS best_bid_position +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +Returns `1` for every row, confirming `best_bid` equals `bids[1][1]`. + ## array_reverse **Syntax:** @@ -580,8 +602,8 @@ array_reverse(array) -> DOUBLE[] Reverses the element order within the innermost dimension of a `DOUBLE[]` array. Unlike [array_sort](#array_sort), which reorders elements by value, `array_reverse` preserves whatever ordering the array already has and flips it. -This is useful when elements are ordered by an external criterion - such as -ingestion timestamp, another column's values, or a prior sort - and you need +This is useful when elements are ordered by an external criterion (such as +ingestion timestamp, another column's values, or a prior sort) and you need the opposite direction. For multi-dimensional arrays, each sub-array is reversed independently. @@ -609,19 +631,17 @@ SELECT array_reverse(ARRAY[1.0, 2.0, 3.0]); | :------------ | | [3.0, 2.0, 1.0] | -**Reverse time-ordered prices to get latest-first:** +**Reverse bid prices to get worst-to-best order:** -```questdb-sql demo title="array_reverse - latest prices first" -SELECT timestamp, - array_reverse(array_agg(price)) AS prices_latest_first -FROM trades -WHERE symbol = 'BTC-USDT' - AND timestamp IN '$now - 5s..$now' -SAMPLE BY 1s; +```questdb-sql demo title="array_reverse - bids worst to best" +SELECT symbol, array_reverse(bids[1]) AS bids_worst_to_best +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; ``` -`array_agg` collects prices in timestamp order. Wrapping with `array_reverse` -puts the most recent price first in each bucket. +`bids[1]` stores prices best-first (descending). Reversing gives ascending order +from the deepest level to top of book. **Reverse each row of a 2D array independently:** @@ -636,7 +656,6 @@ SELECT array_reverse(ARRAY[[1.0, 2.0], [3.0, 4.0]]); #### See also - [array_sort](#array_sort) - Sort array elements by value -- [Aggregation functions](/docs/query/functions/aggregation/) - `array_agg` collects row values into an array ## array_sort @@ -649,10 +668,8 @@ array_sort(array, descending, nullsFirst) -> DOUBLE[] ``` Sorts the elements of a `DOUBLE[]` array by value along the innermost -dimension. Use it when values collected by -[array_agg](/docs/query/functions/aggregation/) are in timestamp -order but you need them ordered by value instead - for example, to find the -median, compute percentiles, or prepare input for +dimension. Useful when you need elements ordered by value, for example to find +the median, compute percentiles, or prepare input for [insertion_point](#insertion_point). For multi-dimensional arrays, each sub-array is sorted independently. @@ -672,20 +689,18 @@ is NULL. Empty arrays return empty arrays. #### Examples -**Sort prices by value within each time bucket:** +**Sort ask prices descending:** -```questdb-sql demo title="array_sort - sort collected prices by value" -SELECT timestamp, - array_agg(price) AS prices_by_time, - array_sort(array_agg(price)) AS prices_by_value -FROM trades -WHERE symbol = 'BTC-USDT' - AND timestamp IN '$now - 5s..$now' -SAMPLE BY 1s; +```questdb-sql demo title="array_sort - asks descending" +SELECT symbol, array_sort(asks[1], true) AS asks_desc +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; ``` -`array_agg` collects prices in timestamp order. `array_sort` re-orders them -from lowest to highest within each bucket. +`asks[1]` is stored ascending (best ask first). Sorting descending gives the +same result as `array_reverse` here, but `array_sort` works on unsorted arrays +too. **Descending sort:** @@ -723,26 +738,6 @@ SELECT array_sort(ARRAY[[3.0, 1.0, 2.0], [6.0, 4.0, 5.0]]); - [array_reverse](#array_reverse) - Reverse array element order - [insertion_point](#insertion_point) - Binary search on a sorted array -- [Aggregation functions](/docs/query/functions/aggregation/) - `array_agg` collects row values into an array - -## array_sum - -`array_sum(array)` returns the sum of all the array elements. `NULL` elements -behave as if they were zero. - -#### Parameter - -- `array` — the array - -#### Example - -```questdb-sql -SELECT array_sum(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]); -``` - -| array_sum | -| --------- | -| 6.0 | ## array_stddev @@ -753,9 +748,9 @@ non-finite values (NaN, Infinity) are ignored. If the array contains fewer than #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_stddev(ARRAY[ [1.0, 2.0], [3.0, 4.0] ]); @@ -765,6 +760,17 @@ SELECT array_stddev(ARRAY[ [1.0, 2.0], [3.0, 4.0] ]); | ------------ | | 1.29099445 | +**Price dispersion across bid levels:** + +```questdb-sql demo title="array_stddev - bid price dispersion" +SELECT symbol, array_stddev(bids[1]) AS bid_price_dispersion +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +A higher value means prices are more spread out across the 40 book levels. + ## array_stddev_pop `array_stddev_pop(array)` returns the population standard deviation of all the @@ -775,9 +781,9 @@ returns `NULL`. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_stddev_pop(ARRAY[ [1.0, 2.0], [3.0, 4.0] ]); @@ -787,6 +793,15 @@ SELECT array_stddev_pop(ARRAY[ [1.0, 2.0], [3.0, 4.0] ]); | ---------------- | | 1.11803399 | +**Population stddev of ask volumes (treating the 40 levels as the full population):** + +```questdb-sql demo title="array_stddev_pop - ask volume dispersion" +SELECT symbol, array_stddev_pop(asks[2]) AS ask_volume_stddev +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + ## array_stddev_samp `array_stddev_samp(array)` returns the sample standard deviation of all the @@ -797,9 +812,9 @@ the function returns `NULL`. #### Parameter -- `array` — the array +- `array` - the array -#### Example +#### Examples ```questdb-sql SELECT array_stddev_samp(ARRAY[ [1.0, 2.0], [3.0, 4.0] ]); @@ -809,6 +824,43 @@ SELECT array_stddev_samp(ARRAY[ [1.0, 2.0], [3.0, 4.0] ]); | ----------------- | | 1.29099445 | +**Sample stddev of bid prices across levels:** + +```questdb-sql demo title="array_stddev_samp - bid price sample stddev" +SELECT symbol, array_stddev_samp(bids[1]) AS bid_price_stddev +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +## array_sum + +`array_sum(array)` returns the sum of all the array elements. `NULL` elements +behave as if they were zero. + +#### Parameter + +- `array` - the array + +#### Examples + +```questdb-sql +SELECT array_sum(ARRAY[ [1.0, 1.0], [2.0, 2.0] ]); +``` + +| array_sum | +| --------- | +| 6.0 | + +**Total bid-side liquidity (sum of all bid volumes):** + +```questdb-sql demo title="array_sum - total bid volume" +SELECT symbol, array_sum(bids[2]) AS total_bid_volume +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + ## dim_length `dim_length(array, dim)` returns the length of the n-dimensional array along @@ -816,10 +868,10 @@ dimension `dim`. #### Parameters -- `array` — the array -- `dim` — the dimension (1-based) whose length to get +- `array` - the array +- `dim` - the dimension (1-based) whose length to get -#### Example +#### Examples Get the length of the array along the 1st dimension. @@ -831,6 +883,20 @@ SELECT dim_length(ARRAY[42, 42], 1); | ------------ | | 2 | +**Inspect the structure of the order book arrays:** + +```questdb-sql demo title="dim_length - order book dimensions" +SELECT + dim_length(bids, 1) AS num_sub_arrays, + dim_length(bids, 2) AS levels_per_sub_array +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +Returns `2` and `40`: the bids array has 2 sub-arrays (prices, volumes), each +with 40 levels. + ## dot_product `dot_product(left_array, right_array)` returns the dot-product of the two @@ -839,10 +905,10 @@ arrays, which must be of the same shape. The result is equal to #### Parameters -- `left_array` — the left array -- `right_array` — the right array +- `left_array` - the left array +- `right_array` - the right array -#### Example +#### Examples ```questdb-sql SELECT dot_product( @@ -855,6 +921,19 @@ SELECT dot_product( | ----------- | | 54.0 | +**Volume-weighted average bid price (VWAP):** + +```questdb-sql demo title="dot_product - VWAP of bid side" +SELECT symbol, + dot_product(bids[1], bids[2]) / array_sum(bids[2]) AS vwap_bid +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +The dot product of prices and volumes divided by total volume gives the +volume-weighted average price across all 40 bid levels. + ## flatten `flatten(array)` flattens all the array's elements into a 1D array, in row-major @@ -862,9 +941,9 @@ order. #### Parameters -- `array` — the array +- `array` - the array -#### Example +#### Examples Flatten a 2D array. @@ -876,6 +955,18 @@ SELECT flatten(ARRAY[[1, 2], [3, 4]]); | ----------------- | | [1.0,2.0,3.0,4.0] | +**Flatten the 2D bids array into a single 1D array (prices then volumes):** + +```questdb-sql demo title="flatten - flatten order book" +SELECT symbol, flatten(bids) AS bids_flat +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +The result is a 1D array of 80 elements: the 40 bid prices followed by the 40 +bid volumes. + ## insertion_point Finds the insertion point of the supplied value into a sorted 1D array. The @@ -891,9 +982,9 @@ an unsorted array is unspecified. #### Parameters -- `array` — the 1D array -- `value` — the value whose insertion point to look for -- `ahead_of_equal` (optional, default `false`) — when true (false), returns the +- `array` - the 1D array +- `value` - the value whose insertion point to look for +- `ahead_of_equal` (optional, default `false`) - when true (false), returns the insertion point before (after) any elements equal to `value` #### Examples @@ -909,6 +1000,18 @@ SELECT | -- | -- | -- | | 3 | 3 | 2 | +**Find where the best bid would slot into the ask book:** + +```questdb-sql demo title="insertion_point - bid in ask book" +SELECT symbol, insertion_point(asks[1], best_bid) AS bid_in_ask_book +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +Since `asks[1]` is sorted ascending and the best bid is below the best ask, +this returns `1` because the bid would go before all ask levels. + ## matmul `matmul(left_matrix, right_matrix)` performs matrix multiplication. This is an @@ -980,11 +1083,11 @@ It fills the holes created by shifting with `fill_value`, the default being #### Parameters -- `array` — the array -- `distance` — the shift distance -— `fill_value` — the value to place in empty slots after shifting +- `array` - the array +- `distance` - the shift distance +- `fill_value` - the value to place in empty slots after shifting -#### Example +#### Examples ```questdb-sql SELECT shift(ARRAY[ [1.0, 2.0], [3.0, 4.0] ], 1); @@ -1010,6 +1113,19 @@ SELECT shift(ARRAY[ [1.0, 2.0], [3.0, 4.0] ], -1, 10.0); | ---------------------------- | | ARRAY[[2.0,10.0],[4.0,10.0]] | +**Level-to-level price differences in the bid book:** + +```questdb-sql demo title="shift - price differences between levels" +SELECT symbol, bids[1] - shift(bids[1], 1, 0.0) AS bid_level_diffs +FROM market_data +WHERE symbol = 'EURUSD' +LIMIT -3; +``` + +Subtracting the shifted array from the original reveals the price drop between +consecutive bid levels. The first element shows the full price (shifted in `0.0` +as fill), subsequent elements show the tick-by-tick decrease. + ## transpose `transpose(array)` transposes an array, reversing the order of its coordinates.