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, Volatility indicators and bands, Momentum indicators, Trend indicators, Aggregate functions (vwap), Window and series functions
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. The same rules apply:
- Order: without
OVER, all result rows are one series in time order. With many symbols, always writeOVER (PARTITION BY symbol ORDER BY time). - Period:
nis how many candles the indicator looks back, a whole number of 1 or more. - WHERE runs first, and candles made with
SAMPLE BYmust be made in a subquery: see the rules in technical indicators.obvandadare 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:
cmfandeomlook at a window of candles: a NULL makes the windows that hold itNULL, and then the values come back.obv,ad,adosc,mfiandefiare built step by step: one NULL makes this and every later valueNULL. Leave NULL candles out withWHERE. - Same as TA-Lib:
obv,ad,adoscandmfigive the same numbers as TA-Lib (OBV, AD, ADOSC, MFI).cmf(Chaikin),efi(Elder) andeom(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)
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:
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);
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:
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
NULLat the start. Careful: a NULLcloseorvolumemakes this and every later valueNULL. - 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)
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)
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:
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
NULLat the start. Careful: a NULL argument makes this and every later valueNULL. - Notes: a candle with
high = lowadds nothing. Unlikeobv, 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)
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)
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:
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). 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:
DOUBLEfrom -1 to 1. - NULL:
NULLfor the firstn - 1candles, and when the total volume of the window is0. A NULL argument makes thenresults whose window holds itNULL; after that CMF comes back. - Notes: a candle with
high = lowcounts with multiplier0(its volume still counts in the total).
Example: Buying or selling pressure? 5-day CMF of every symbol (sample data: sample)
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)
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:
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 minus its slow EMA. It shows if the A/D line speeds up or slows down, like 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 ofvolume. - NULL: the first value is on candle
max(fast, slow): candle 10 with the defaults. Careful: a NULL argument makes this and every later valueNULL. - Notes: like TA-Lib ADOSC, both EMAs start at the first A/D value (not at an SMA like 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)
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)
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:
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, 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:
DOUBLEfrom 0 to 100. - NULL:
NULLfor the firstncandles (the first candle has no previous typical price). Careful: a NULL argument makes this and every later valueNULL. - Notes: the same as TA-Lib MFI. Only falls in the window give
0; only rises give100. When the total money flow of the window is less than 1 (for example no volume), MFI is0.
Example: Overbought (80 or more) and oversold (20 or less) days, 5-day MFI (sample data: sample)
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)
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)
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:
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 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:
NULLfor the firstncandles (the first candle has no force). Careful: a NULL argument makes this and every later valueNULL. - Notes: the EMA starts with the plain average of the first
nforces, likeema.
Example: The force of each day and its 3-day EMA (VOLT) (sample data: sample)
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)
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:
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 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 ondivisor; only the sign and the changes matter. - NULL:
NULLfor the firstncandles (the first candle has no previous high and low). A candle with volume0or a NULL value makes the windows that hold itNULL; after that EOM comes back. - Notes: a candle with
high = lowcounts as0. The default divisor is made for very big volumes; with small volumes (hundreds or thousands of shares per minute) a divisor like10000gives 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)
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)
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)
SELECT eom(high, low, volume, 14, 0) FROM ochl_day;
Result:
Error: line 1, column 8: eom: the divisor must not be 0