# Volume indicators
> Is money flowing in or out? On-balance volume, the A/D line, Chaikin money flow and oscillator, MFI, force index and ease of movement.
- Category: Functions — technical indicators
- Keywords: volume, money flow, buying pressure, selling pressure, accumulation, distribution, divergence, on-balance volume, on balance volume, OBV, accumulation/distribution, accumulation distribution, A/D line, AD, Chaikin money flow, CMF, Chaikin oscillator, Chaikin A/D oscillator, ADOSC, money flow index, MFI, volume RSI, force index, Elder force index, EFI, ease of movement, EMV, EOM, stock market
- See also: [Technical indicators](functions-indicators.md), [Volatility indicators and bands](functions-indicators-volatility.md), [Momentum indicators](functions-indicators-momentum.md), [Trend indicators](functions-indicators-trend.md), [Aggregate functions (vwap)](functions-aggregate.md#vwap), [Window and series functions](functions-window.md)
**Volume** is how many shares were traded in a candle. A price move on big volume means many people agree with it; a move on small volume is weak. **Volume indicators** mix the price and the volume to show if money is flowing **into** a share (buying pressure, "accumulation") or **out of** it (selling pressure, "distribution").
A common use is a **divergence**: the price goes sideways or down, but the volume indicator goes up. That can mean big buyers are collecting the share before the price rises.
These functions are **series functions**, like [sma and atr](functions-indicators.md). The same rules apply:
- **Order:** without `OVER`, all result rows are one series in time order. With **many symbols**, always write `OVER (PARTITION BY symbol ORDER BY time)`.
- **Period:** `n` is how many candles the indicator looks back, a whole number of 1 or more.
- **WHERE runs first**, and candles made with `SAMPLE BY` must be made in a subquery: see the rules in [technical indicators](functions-indicators.md). `obv` and `ad` are running totals, so their numbers depend on the first row the series has.
- **Warm-up:** at the start of a series there are not enough candles yet, and the result is `NULL`:
| Function (default periods) | First value on candle | NULL candles at the start |
|---|---|---|
| `obv(close, volume)` | 1 | 0 |
| `ad(high, low, close, volume)` | 1 | 0 |
| `cmf(high, low, close, volume, n)` (n = 20) | n | n - 1 |
| `adosc(high, low, close, volume, fast, slow)` (3, 10) | the bigger of fast and slow | one less |
| `mfi(high, low, close, volume, n)` (n = 14) | n + 1 | n |
| `efi(close, volume, n)` (n = 13) | n + 1 | n |
| `eom(high, low, volume, n, divisor)` (14, 100000000) | n + 1 | n |
- **NULL values:** `cmf` and `eom` look at a window of candles: a NULL makes the windows that hold it `NULL`, and then the values come back. `obv`, `ad`, `adosc`, `mfi` and `efi` are built step by step: one NULL makes this and **every later** value `NULL`. Leave NULL candles out with `WHERE`.
- **Same as TA-Lib:** `obv`, `ad`, `adosc` and `mfi` give the same numbers as TA-Lib (OBV, AD, ADOSC, MFI). `cmf` (Chaikin), `efi` (Elder) and `eom` (Arms) are not in TA-Lib; they use the usual formulas written on each entry.
- **Returns** `DOUBLE`.
**Example: Volume indicators of CITY's one-minute candles (the last 5 minutes)** (sample data: sample)
```sql
SELECT time, close, volume, obv, ad, cmf20, adosc, mfi14, efi13
FROM (SELECT time, close, volume,
obv(close, volume) AS obv,
round(ad(high, low, close, volume)) AS ad,
round(cmf(high, low, close, volume), 3) AS cmf20,
round(adosc(high, low, close, volume)) AS adosc,
round(mfi(high, low, close, volume), 1) AS mfi14,
round(efi(close, volume)) AS efi13
FROM ochl_minutes
WHERE symbol = 'CITY') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | volume (DOUBLE) | obv (DOUBLE) | ad (DOUBLE) | cmf20 (DOUBLE) | adosc (DOUBLE) | mfi14 (DOUBLE) | efi13 (DOUBLE) |
|---|---|---|---|---|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 504.9 | 1000 | 13660 | -17532 | -0.031 | -513 | 34.3 | -46 |
| 2026-09-10T09:13:00.000Z | 505.1 | 460 | 14660 | -17389 | -0.06 | -560 | 47.3 | -20 |
| 2026-09-10T09:12:00.000Z | 504.8 | 390 | 14200 | -17542 | 0.02 | -662 | 50.5 | -47 |
| 2026-09-10T09:11:00.000Z | 504.5 | 1350 | 13810 | -17932 | -0.041 | -646 | 47.7 | -74 |
| 2026-09-10T09:10:00.000Z | 504.6 | 1340 | 15160 | -16582 | 0.013 | -159 | 59.3 | -64 |
(5 rows)
**Example: A NULL volume: cmf comes back, obv stays NULL** (sample data: sample)
Setup:
```sql
CREATE TABLE candles (time TIMESTAMP, high DOUBLE, low DOUBLE, close DOUBLE, volume DOUBLE) TIMESERIES(time) PARTITION BY DAY;
INSERT INTO candles VALUES ('2026-09-01', 11, 9, 10, 100), ('2026-09-02', 12, 10, 11, 200), ('2026-09-03', 13, 11, 12, NULL),
('2026-09-04', 14, 12, 13, 400), ('2026-09-05', 13, 11, 12, 500), ('2026-09-06', 15, 12, 14, 600);
```
```sql
SELECT time, close, volume, obv(close, volume) AS obv, round(cmf(high, low, close, volume, 2), 3) AS cmf2
FROM candles
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | volume (DOUBLE) | obv (DOUBLE) | cmf2 (DOUBLE) |
|---|---|---|---|---|
| 2026-09-01T00:00:00.000Z | 10 | 100 | 100 | NULL |
| 2026-09-02T00:00:00.000Z | 11 | 200 | 300 | 0 |
| 2026-09-03T00:00:00.000Z | 12 | NULL | NULL | NULL |
| 2026-09-04T00:00:00.000Z | 13 | 400 | NULL | NULL |
| 2026-09-05T00:00:00.000Z | 12 | 500 | NULL | 0 |
| 2026-09-06T00:00:00.000Z | 14 | 600 | NULL | 0.182 |
(6 rows)
## obv
Syntax:
```sql
obv(close, volume)
obv(close, volume) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**On-balance volume**: a running total of the volume. When the close is higher than the previous close, the candle's volume is **added**; when it is lower, the volume is **subtracted**; when it is the same, nothing changes. When OBV rises, the volume is bigger on up days than on down days: buyers are stronger. Only the direction of OBV matters, not the number itself.
| Argument | Type | Meaning |
|---|---|---|
| `close` | number | the candle's close |
| `volume` | number | the candle's volume |
- **Returns:** `DOUBLE`.
- **NULL:** never `NULL` at the start. **Careful:** a NULL `close` or `volume` makes this and every later value `NULL`.
- **Notes:** like TA-Lib, the first candle's OBV is its own volume. Because it is a running total, the number depends on where the series starts; compare its **changes**, not its level.
**Example: Up day: add the volume. Down day: subtract it** (sample data: sample)
```sql
SELECT time, close, volume, obv(close, volume) AS obv
FROM ochl_day
WHERE symbol = 'VOLT' AND time >= '2026-09-01'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | volume (DOUBLE) | obv (DOUBLE) |
|---|---|---|---|
| 2026-09-01T00:00:00.000Z | 169 | 58850 | 58850 |
| 2026-09-02T00:00:00.000Z | 171.5 | 21770 | 80620 |
| 2026-09-03T00:00:00.000Z | 176 | 145930 | 226550 |
| 2026-09-06T00:00:00.000Z | 180.1 | 78550 | 305100 |
| 2026-09-07T00:00:00.000Z | 176.9 | 135560 | 169540 |
| 2026-09-08T00:00:00.000Z | 182.4 | 136770 | 306310 |
| 2026-09-09T00:00:00.000Z | 187.3 | 94410 | 400720 |
| 2026-09-10T00:00:00.000Z | 191.2 | 41470 | 442190 |
(8 rows)
**Example: Divergence: price change and OBV change since 23 August** (sample data: sample)
```sql
SELECT symbol, count(*) AS days,
round(last(close) - first(close), 1) AS price_change,
last(obv) - first(obv) AS obv_change
FROM (SELECT time, symbol, close,
obv(close, volume) OVER (PARTITION BY symbol ORDER BY time) AS obv
FROM ochl_day
WHERE time >= '2026-08-23') t
GROUP BY symbol
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | days (LONG) | price_change (DOUBLE) | obv_change (DOUBLE) |
|---|---|---|---|
| CITY | 15 | -3 | 226090 |
| COAST | 15 | -14.9 | 65740 |
| VOLT | 15 | 10.2 | -246280 |
(3 rows)
## ad, ad_line
Syntax:
```sql
ad(high, low, close, volume)
ad_line(high, low, close, volume)
ad(high, low, close, volume) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **accumulation/distribution line** (A/D line). For every candle it finds where the close is inside the candle, with the **money flow multiplier** `((close - low) - (high - close)) / (high - low)`: `1` when the close is at the high, `-1` when it is at the low, `0` in the middle. The A/D line is the running total of `multiplier × volume`. A rising line means the closes are near the highs on big volume: accumulation. `ad_line` is the same function.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `volume` | number | the candle's volume |
- **Returns:** `DOUBLE`.
- **NULL:** never `NULL` at the start. **Careful:** a NULL argument makes this and every later value `NULL`.
- **Notes:** a candle with `high = low` adds nothing. Unlike `obv`, it does not look at the previous close, only at the candle itself. The same as TA-Lib AD.
**Example: The multiplier of each day and the A/D line of CITY** (sample data: sample)
```sql
SELECT time, high, low, close, volume,
round(((close - low) - (high - close)) / (high - low), 3) AS multiplier,
round(ad(high, low, close, volume)) AS ad_line
FROM ochl_day
WHERE symbol = 'CITY' AND time >= '2026-09-03'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | high (DOUBLE) | low (DOUBLE) | close (DOUBLE) | volume (DOUBLE) | multiplier (DOUBLE) | ad_line (DOUBLE) |
|---|---|---|---|---|---|---|
| 2026-09-03T00:00:00.000Z | 500.9 | 495.5 | 497.9 | 101000 | -0.111 | -11222 |
| 2026-09-06T00:00:00.000Z | 500.1 | 496 | 499 | 74950 | 0.463 | 23511 |
| 2026-09-07T00:00:00.000Z | 501 | 495 | 498.7 | 117080 | 0.233 | 50829 |
| 2026-09-08T00:00:00.000Z | 501.1 | 497.5 | 497.5 | 34400 | -1 | 16429 |
| 2026-09-09T00:00:00.000Z | 512.4 | 504 | 504.6 | 182390 | -0.857 | -139905 |
| 2026-09-10T00:00:00.000Z | 507 | 500.7 | 504.9 | 183040 | 0.333 | -78892 |
(6 rows)
**Example: A/D line of both symbols at the end of each day (one-minute candles)** (sample data: sample)
```sql
SELECT time, symbol, close, ad_line
FROM (SELECT time, symbol, close,
round(ad_line(high, low, close, volume) OVER (PARTITION BY symbol ORDER BY time)) AS ad_line
FROM ochl_minutes) t
WHERE time IN ('2026-09-09T09:14:00Z', '2026-09-10T09:14:00Z')
ORDER BY symbol, time;
```
Result:
| time (TIMESTAMP) | symbol (SYMBOL) | close (DOUBLE) | ad_line (DOUBLE) |
|---|---|---|---|
| 2026-09-09T09:14:00.000Z | CITY | 504.6 | -14867 |
| 2026-09-10T09:14:00.000Z | CITY | 504.9 | -17532 |
| 2026-09-09T09:14:00.000Z | COAST | 380.1 | -19693 |
| 2026-09-10T09:14:00.000Z | COAST | 376.1 | -9254 |
(4 rows)
## cmf
Syntax:
```sql
cmf(high, low, close, volume)
cmf(high, low, close, volume, n)
cmf(high, low, close, volume, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**Chaikin money flow**: over the last `n` candles, the sum of `multiplier × volume` divided by the sum of the volume (the multiplier is the one of [ad](functions-indicators-volume.md#ad)). It is between `-1` and `1`. **Above 0** means buying pressure (closes near the highs); **below 0** means selling pressure. Many traders look for values above `0.1` or below `-0.1`.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `volume` | number | the candle's volume |
| `n` | whole number, 1 or more | how many candles; default `20` |
- **Returns:** `DOUBLE` from -1 to 1.
- **NULL:** `NULL` for the first `n - 1` candles, and when the total volume of the window is `0`. A NULL argument makes the `n` results whose window holds it `NULL`; after that CMF comes back.
- **Notes:** a candle with `high = low` counts with multiplier `0` (its volume still counts in the total).
**Example: Buying or selling pressure? 5-day CMF of every symbol** (sample data: sample)
```sql
SELECT time, symbol, close, cmf5,
CASE WHEN cmf5 > 0 THEN 'buying' WHEN cmf5 < 0 THEN 'selling' END AS pressure
FROM (SELECT time, symbol, close,
round(cmf(high, low, close, volume, 5) OVER (PARTITION BY symbol ORDER BY time), 3) AS cmf5
FROM ochl_day
WHERE time >= '2026-08-23') t
WHERE time >= '2026-09-08'
ORDER BY symbol, time;
```
Result:
| time (TIMESTAMP) | symbol (SYMBOL) | close (DOUBLE) | cmf5 (DOUBLE) | pressure (STRING) |
|---|---|---|---|---|
| 2026-09-08T00:00:00.000Z | CITY | 497.5 | -0.072 | selling |
| 2026-09-09T00:00:00.000Z | CITY | 504.6 | -0.274 | selling |
| 2026-09-10T00:00:00.000Z | CITY | 504.9 | -0.114 | selling |
| 2026-09-08T00:00:00.000Z | COAST | 377.2 | 0.337 | buying |
| 2026-09-09T00:00:00.000Z | COAST | 380.1 | -0.077 | selling |
| 2026-09-10T00:00:00.000Z | COAST | 376.1 | -0.267 | selling |
| 2026-09-08T00:00:00.000Z | VOLT | 182.4 | 0.238 | buying |
| 2026-09-09T00:00:00.000Z | VOLT | 187.3 | 0.283 | buying |
| 2026-09-10T00:00:00.000Z | VOLT | 191.2 | 0.254 | buying |
(9 rows)
**Example: 20-minute CMF at the last minute of the day** (sample data: sample)
```sql
SELECT symbol, close, cmf20
FROM (SELECT time, symbol, close,
round(cmf(high, low, close, volume) OVER (PARTITION BY symbol ORDER BY time), 3) AS cmf20
FROM ochl_minutes) t
WHERE time = '2026-09-10T09:14:00Z'
ORDER BY symbol;
```
Result:
| symbol (SYMBOL) | close (DOUBLE) | cmf20 (DOUBLE) |
|---|---|---|
| CITY | 504.9 | -0.031 |
| COAST | 376.1 | -0.015 |
(2 rows)
## adosc, chaikin_osc
Syntax:
```sql
adosc(high, low, close, volume)
adosc(high, low, close, volume, fast, slow)
chaikin_osc(high, low, close, volume, fast, slow)
adosc(high, low, close, volume, fast, slow) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **Chaikin oscillator** (Chaikin A/D oscillator): the fast EMA of the [A/D line](functions-indicators-volume.md#ad) minus its slow EMA. It shows if the A/D line speeds up or slows down, like [MACD](functions-indicators-momentum.md#macd) does for the price. When it crosses **above 0**, buying pressure is growing (a buy signal); below 0, selling pressure is growing. `chaikin_osc` is the same function.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `volume` | number | the candle's volume |
| `fast` | whole number, 1 or more | the fast EMA period; default `3` |
| `slow` | whole number, 1 or more | the slow EMA period; default `10` |
- **Returns:** `DOUBLE`, in the unit of `volume`.
- **NULL:** the first value is on candle `max(fast, slow)`: candle 10 with the defaults. **Careful:** a NULL argument makes this and every later value `NULL`.
- **Notes:** like TA-Lib ADOSC, both EMAs start at the first A/D value (not at an SMA like [ema](functions-indicators.md#ema)), so the first values still depend on the start. Load more candles than you show.
**Example: Chaikin oscillator of CITY (the last 5 minutes)** (sample data: sample)
```sql
SELECT time, close, osc
FROM (SELECT time, close, round(adosc(high, low, close, volume)) AS osc
FROM ochl_minutes
WHERE symbol = 'CITY') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | osc (DOUBLE) |
|---|---|---|
| 2026-09-10T09:14:00.000Z | 504.9 | -513 |
| 2026-09-10T09:13:00.000Z | 505.1 | -560 |
| 2026-09-10T09:12:00.000Z | 504.8 | -662 |
| 2026-09-10T09:11:00.000Z | 504.5 | -646 |
| 2026-09-10T09:10:00.000Z | 504.6 | -159 |
(5 rows)
**Example: Signals: the oscillator crosses 0** (sample data: sample)
```sql
SELECT time, close, round(osc) AS osc, CASE WHEN osc > 0 THEN 'BUY' ELSE 'SELL' END AS signal
FROM (SELECT time, close, osc, lag(osc) AS prev_osc
FROM (SELECT time, close, chaikin_osc(high, low, close, volume, 3, 10) AS osc
FROM ochl_minutes
WHERE symbol = 'CITY' AND time IN '2026-09-10') a) b
WHERE (osc > 0 AND prev_osc <= 0) OR (osc < 0 AND prev_osc >= 0)
ORDER BY time
LIMIT 6;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | osc (DOUBLE) | signal (STRING) |
|---|---|---|---|
| 2026-09-10T05:39:00.000Z | 505.2 | 420 | BUY |
| 2026-09-10T05:52:00.000Z | 506.3 | -310 | SELL |
| 2026-09-10T06:10:00.000Z | 505.9 | 211 | BUY |
| 2026-09-10T06:13:00.000Z | 505.4 | -162 | SELL |
| 2026-09-10T06:16:00.000Z | 505.6 | 311 | BUY |
| 2026-09-10T06:21:00.000Z | 505.2 | -189 | SELL |
(6 rows)
## mfi
Syntax:
```sql
mfi(high, low, close, volume)
mfi(high, low, close, volume, n)
mfi(high, low, close, volume, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **money flow index**, a number from 0 to 100. It is often called the "volume RSI": it works like [rsi](functions-indicators.md#rsi), but with money instead of price. Each candle has a **typical price** `(high + low + close) / 3` and a **money flow** `typical price × volume`. The money flow counts as positive when the typical price is higher than the previous one, negative when it is lower. MFI is `100 × positive / (positive + negative)` over the last `n` candles. Above **80** traders call the share overbought; below **20**, oversold.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `volume` | number | the candle's volume |
| `n` | whole number, 1 or more | how many candles; default `14` |
- **Returns:** `DOUBLE` from 0 to 100.
- **NULL:** `NULL` for the first `n` candles (the first candle has no previous typical price). **Careful:** a NULL argument makes this and every later value `NULL`.
- **Notes:** the same as TA-Lib MFI. Only falls in the window give `0`; only rises give `100`. When the total money flow of the window is less than 1 (for example no volume), MFI is `0`.
**Example: Overbought (80 or more) and oversold (20 or less) days, 5-day MFI** (sample data: sample)
```sql
SELECT time, symbol, close, mfi5,
CASE WHEN mfi5 >= 80 THEN 'overbought' ELSE 'oversold' END AS zone
FROM (SELECT time, symbol, close,
round(mfi(high, low, close, volume, 5) OVER (PARTITION BY symbol ORDER BY time), 1) AS mfi5
FROM ochl_day
WHERE time >= '2026-08-23') t
WHERE mfi5 >= 80 OR mfi5 <= 20
ORDER BY time, symbol;
```
Result:
| time (TIMESTAMP) | symbol (SYMBOL) | close (DOUBLE) | mfi5 (DOUBLE) | zone (STRING) |
|---|---|---|---|---|
| 2026-08-30T00:00:00.000Z | VOLT | 173 | 0 | oversold |
| 2026-08-31T00:00:00.000Z | CITY | 505.7 | 14.3 | oversold |
| 2026-08-31T00:00:00.000Z | VOLT | 172.6 | 10.9 | oversold |
| 2026-09-01T00:00:00.000Z | CITY | 502.4 | 0 | oversold |
| 2026-09-01T00:00:00.000Z | VOLT | 169 | 12.2 | oversold |
| 2026-09-02T00:00:00.000Z | CITY | 496.4 | 0 | oversold |
| 2026-09-03T00:00:00.000Z | CITY | 497.9 | 18.6 | oversold |
| 2026-09-06T00:00:00.000Z | VOLT | 180.1 | 83.8 | overbought |
| 2026-09-07T00:00:00.000Z | COAST | 379.2 | 86.3 | overbought |
| 2026-09-08T00:00:00.000Z | COAST | 377.2 | 87 | overbought |
| 2026-09-09T00:00:00.000Z | COAST | 380.1 | 89.6 | overbought |
(11 rows)
**Example: MFI 14 against RSI 14 of COAST (the last 5 minutes)** (sample data: sample)
```sql
SELECT time, close, mfi14, rsi14
FROM (SELECT time, close,
round(mfi(high, low, close, volume), 1) AS mfi14,
round(rsi(close, 14), 1) AS rsi14
FROM ochl_minutes
WHERE symbol = 'COAST') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | mfi14 (DOUBLE) | rsi14 (DOUBLE) |
|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 376.1 | 68.9 | 59.4 |
| 2026-09-10T09:13:00.000Z | 376.2 | 58.8 | 60.9 |
| 2026-09-10T09:12:00.000Z | 376 | 54 | 59 |
| 2026-09-10T09:11:00.000Z | 375.5 | 43.1 | 53.9 |
| 2026-09-10T09:10:00.000Z | 375.6 | 36.4 | 55.2 |
(5 rows)
**Example: high, low, close and volume are all needed** (sample data: sample; shows an error)
```sql
SELECT mfi(high, low, close) FROM ochl_day;
```
Result:
Error: `line 1, column 8: function 'mfi' takes 4 to 5 argument(s), got 3`
## efi, force_index
Syntax:
```sql
efi(close, volume)
efi(close, volume, n)
force_index(close, volume, n)
efi(close, volume, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**Elder's force index**: the **force** of one candle is `(close - previous close) × volume`, so a big move on big volume is a big force. `efi` is the [ema](functions-indicators.md#ema) of the force over `n` candles. Above 0, buyers are in control; below 0, sellers. `force_index` is the same function.
| Argument | Type | Meaning |
|---|---|---|
| `close` | number | the candle's close |
| `volume` | number | the candle's volume |
| `n` | whole number, 1 or more | the EMA period; default `13` (traders also use `2` for short signals) |
- **Returns:** `DOUBLE`, in price × shares (for example dollars × shares).
- **NULL:** `NULL` for the first `n` candles (the first candle has no force). **Careful:** a NULL argument makes this and every later value `NULL`.
- **Notes:** the EMA starts with the plain average of the first `n` forces, like `ema`.
**Example: The force of each day and its 3-day EMA (VOLT)** (sample data: sample)
```sql
SELECT time, close, volume,
round((close - lag(close)) * volume) AS force,
round(efi(close, volume, 3)) AS efi3
FROM ochl_day
WHERE symbol = 'VOLT' AND time >= '2026-08-30'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | volume (DOUBLE) | force (DOUBLE) | efi3 (DOUBLE) |
|---|---|---|---|---|
| 2026-08-30T00:00:00.000Z | 173 | 93050 | NULL | NULL |
| 2026-08-31T00:00:00.000Z | 172.6 | 48550 | -19420 | NULL |
| 2026-09-01T00:00:00.000Z | 169 | 58850 | -211860 | NULL |
| 2026-09-02T00:00:00.000Z | 171.5 | 21770 | 54425 | -58952 |
| 2026-09-03T00:00:00.000Z | 176 | 145930 | 656685 | 298867 |
| 2026-09-06T00:00:00.000Z | 180.1 | 78550 | 322055 | 310461 |
| 2026-09-07T00:00:00.000Z | 176.9 | 135560 | -433792 | -61666 |
| 2026-09-08T00:00:00.000Z | 182.4 | 136770 | 752235 | 345285 |
| 2026-09-09T00:00:00.000Z | 187.3 | 94410 | 462609 | 403947 |
| 2026-09-10T00:00:00.000Z | 191.2 | 41470 | 161733 | 282840 |
(10 rows)
**Example: 13-minute force index of COAST (the last 5 minutes)** (sample data: sample)
```sql
SELECT time, close, volume, force13
FROM (SELECT time, close, volume, round(force_index(close, volume)) AS force13
FROM ochl_minutes
WHERE symbol = 'COAST') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | volume (DOUBLE) | force13 (DOUBLE) |
|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 376.1 | 1060 | 104 |
| 2026-09-10T09:13:00.000Z | 376.2 | 1320 | 140 |
| 2026-09-10T09:12:00.000Z | 376 | 1150 | 119 |
| 2026-09-10T09:11:00.000Z | 375.5 | 680 | 43 |
| 2026-09-10T09:10:00.000Z | 375.6 | 1200 | 61 |
(5 rows)
## eom, ease_of_movement
Syntax:
```sql
eom(high, low, volume)
eom(high, low, volume, n)
eom(high, low, volume, n, divisor)
ease_of_movement(high, low, volume, n, divisor)
eom(high, low, volume, n, divisor) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**Ease of movement** (Richard Arms): how easily the price moves. A big positive value means the price went **up easily**, on little volume; a big negative value means it went down easily. Values near 0 mean the price needed a lot of volume to move. `ease_of_movement` is the same function.
For each candle: the **distance** is `(high + low) / 2 - (previous high + previous low) / 2`, the **box ratio** is `(volume / divisor) / (high - low)`, and the candle's value is `distance / box ratio`. `eom` is the [sma](functions-indicators.md#sma) of these values over `n` candles.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `volume` | number | the candle's volume |
| `n` | whole number, 1 or more | how many candles; default `14` |
| `divisor` | number, not 0 | scales the volume; default `100000000` |
- **Returns:** `DOUBLE`. The size depends on `divisor`; only the sign and the changes matter.
- **NULL:** `NULL` for the first `n` candles (the first candle has no previous high and low). A candle with volume `0` or a NULL value makes the windows that hold it `NULL`; after that EOM comes back.
- **Notes:** a candle with `high = low` counts as `0`. The default divisor is made for very big volumes; with small volumes (hundreds or thousands of shares per minute) a divisor like `10000` gives easier numbers. It does not change the sign.
**Example: EOM 14 of COAST with the default divisor and with 10000 (the last 5 minutes)** (sample data: sample)
```sql
SELECT time, close, eom_default, eom_10000
FROM (SELECT time, close,
round(eom(high, low, volume), 1) AS eom_default,
round(eom(high, low, volume, 14, 10000), 3) AS eom_10000
FROM ochl_minutes
WHERE symbol = 'COAST') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | eom_default (DOUBLE) | eom_10000 (DOUBLE) |
|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 376.1 | 460.9 | 0.046 |
| 2026-09-10T09:13:00.000Z | 376.2 | -634.5 | -0.063 |
| 2026-09-10T09:12:00.000Z | 376 | 3122.5 | 0.312 |
| 2026-09-10T09:11:00.000Z | 375.5 | 395.9 | 0.04 |
| 2026-09-10T09:10:00.000Z | 375.6 | -1109.3 | -0.111 |
(5 rows)
**Example: Did VOLT rise easily? 3-day EOM** (sample data: sample)
```sql
SELECT time, close, volume, eom3,
CASE WHEN eom3 > 0 THEN 'up easily' WHEN eom3 < 0 THEN 'down easily' END AS reading
FROM (SELECT time, close, volume, round(ease_of_movement(high, low, volume, 3, 10000), 2) AS eom3
FROM ochl_day
WHERE symbol = 'VOLT' AND time >= '2026-08-23') t
WHERE time >= '2026-09-01'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | volume (DOUBLE) | eom3 (DOUBLE) | reading (STRING) |
|---|---|---|---|---|
| 2026-09-01T00:00:00.000Z | 169 | 58850 | -0.97 | down easily |
| 2026-09-02T00:00:00.000Z | 171.5 | 21770 | 1.09 | up easily |
| 2026-09-03T00:00:00.000Z | 176 | 145930 | 1.13 | up easily |
| 2026-09-06T00:00:00.000Z | 180.1 | 78550 | 3.88 | up easily |
| 2026-09-07T00:00:00.000Z | 176.9 | 135560 | 2.21 | up easily |
| 2026-09-08T00:00:00.000Z | 182.4 | 136770 | 2.14 | up easily |
| 2026-09-09T00:00:00.000Z | 187.3 | 94410 | 1.71 | up easily |
| 2026-09-10T00:00:00.000Z | 191.2 | 41470 | 3.2 | up easily |
(8 rows)
**Example: The divisor must not be 0** (sample data: sample; shows an error)
```sql
SELECT eom(high, low, volume, 14, 0) FROM ochl_day;
```
Result:
Error: `line 1, column 8: eom: the divisor must not be 0`