# Momentum indicators
> MACD, stochastic, stochastic RSI, Williams %R, CCI, rate of change, momentum and TRIX on candles, with the same results as TA-Lib.
- Category: Functions — technical indicators
- Keywords: momentum, oscillator, overbought, oversold, crossover, signal line, divergence, TA-Lib, MACD, moving average convergence divergence, signal line, histogram, stochastic, stochastic oscillator, slow stochastic, %K, %D, fast stochastic, STOCHF, stochastic RSI, StochRSI, STOCHRSI, Williams %R, WILLR, %R, commodity channel index, CCI, rate of change, ROC, ROCP, ROCR, MOM, TRIX, triple exponential average, stock market
- See also: [Technical indicators](functions-indicators.md), [Volatility indicators and bands](functions-indicators-volatility.md), [Trend indicators](functions-indicators-trend.md), [Volume indicators](functions-indicators-volume.md), [Returns and risk](functions-returns-risk.md), [Window and series functions](functions-window.md)
**Momentum** is the speed of a price move. A momentum indicator tells how strong a rise or a fall is, and when it gets weaker. Many of them are **oscillators**: numbers that swing between fixed levels (like 0 and 100). Near the top level a share is called **overbought** (it rose a lot and may fall back); near the bottom level **oversold**.
These are **series functions**, like [sma, ema, rsi and atr](functions-indicators.md), with the same rules:
- **Order:** without `OVER`, all result rows are one series in time order. With **many symbols**, always write `OVER (PARTITION BY symbol ORDER BY time)`.
- **Periods** are whole numbers of candles, written as constants (`macd(close, 12, 26, 9)`). You can leave out the ones at the end: they get the usual defaults.
- **Warm-up:** the first rows are `NULL` until there are enough candles. With the default periods:
| Function | Default periods | First value on candle | In general |
|---|---|---|---|
| `macd`, `macd_signal`, `macd_hist` | 12, 26, 9 | 34 | slow + signal - 1 |
| `stoch_k`, `stoch_d` | 14, 3, 3 | 18 | k + k_slowing + d - 2 |
| `stochf_k` | 14 | 14 | k |
| `stochrsi_k`, `stochrsi_d` | 14, 5, 3 | 21 | rsi_period + k + d - 1 |
| `willr` | 14 | 14 | n |
| `cci` | 20 | 20 | n |
| `roc`, `rocp`, `rocr`, `mom` | 10 | 11 | n + 1 |
| `trix` | 30 | 89 | 3n - 1 |
- **WHERE runs first**, and an indicator cannot be in the same `SELECT` as `SAMPLE BY` or `GROUP BY`: make the candles in a subquery (see [technical indicators](functions-indicators.md)).
- **NULL prices:** `macd`, `stochrsi` and `trix` are built from EMAs or Wilder averages. One NULL price makes that row and **every later** row `NULL`: leave NULL prices out with `WHERE close IS NOT NULL`. The others only look at a window of candles: they are NULL while the window holds the NULL, and then come back.
- **Same as TA-Lib:** the formulas and the first row with a value match TA-Lib.
- **Returns** `DOUBLE`.
- One series function cannot be inside another (`ema(trix(close, 30), 9)`): compute the first one in a subquery.
**Example: Momentum of CITY's one-minute candles: MACD, stochastic, Williams %R and CCI (the last 5)** (sample data: sample)
```sql
SELECT time, close, macd, stoch_k, willr, cci
FROM (SELECT time, close,
round(macd(close), 3) AS macd,
round(stoch_k(high, low, close), 1) AS stoch_k,
round(willr(high, low, close), 1) AS willr,
round(cci(high, low, close), 1) AS cci
FROM ochl_minutes
WHERE symbol = 'CITY') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | macd (DOUBLE) | stoch_k (DOUBLE) | willr (DOUBLE) | cci (DOUBLE) |
|---|---|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 504.9 | -0.093 | 35.2 | -66.7 | -6.3 |
| 2026-09-10T09:13:00.000Z | 505.1 | -0.091 | 29.3 | -55.6 | 16 |
| 2026-09-10T09:12:00.000Z | 504.8 | -0.109 | 21.5 | -72.2 | -57.3 |
| 2026-09-10T09:11:00.000Z | 504.5 | -0.099 | 28.9 | -84.2 | -80.9 |
| 2026-09-10T09:10:00.000Z | 504.6 | -0.054 | 38.8 | -78.9 | -57.7 |
(5 rows)
## macd, macd_signal, macd_hist
Syntax:
```sql
macd(x [, fast [, slow [, signal]]])
macd_signal(x [, fast [, slow [, signal]]])
macd_hist(x [, fast [, slow [, signal]]])
macd(x, fast, slow, signal) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**MACD** (moving average convergence divergence) shows the trend and its strength with three lines:
- `macd`: the **MACD line**, `EMA(fast) - EMA(slow)` of the price. Above 0, the short-term average is above the long-term one: the price is going up.
- `macd_signal`: the **signal line**, an EMA of the MACD line over `signal` candles.
- `macd_hist`: the **histogram**, `macd - macd_signal`. It grows when the move gets stronger.
When the MACD line crosses **above** the signal line (the histogram turns from minus to plus), traders read it as a buy signal; crossing **below** is a sell signal.
| Argument | Type | Meaning |
|---|---|---|
| `x` | number | the price, usually `close` |
| `fast` | whole number, 1 or more | the short EMA period; default `12` |
| `slow` | whole number, 1 or more | the long EMA period; default `26` |
| `signal` | whole number, 1 or more | the EMA period of the signal line; default `9` |
- **Returns:** `DOUBLE`, in the unit of the price (for example dollars).
- **NULL:** all three are `NULL` for the first `slow + signal - 2` candles (33 with the defaults), like TA-Lib. **Careful:** one NULL price makes this and every later value `NULL`.
- **Notes:** when `fast` is bigger than `slow`, the two are swapped (like TA-Lib). Both EMAs start on candle `slow`, like TA-Lib's `MACD`; a chart that starts the fast EMA earlier shows slightly different first values. Load much more history than you show.
**Example: MACD, signal and histogram of CITY's one-minute candles (the last 5)** (sample data: sample)
```sql
SELECT time, close, macd, signal, hist
FROM (SELECT time, close,
round(macd(close), 3) AS macd,
round(macd_signal(close), 3) AS signal,
round(macd_hist(close), 3) AS hist
FROM ochl_minutes
WHERE symbol = 'CITY') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | macd (DOUBLE) | signal (DOUBLE) | hist (DOUBLE) |
|---|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 504.9 | -0.093 | -0.062 | -0.031 |
| 2026-09-10T09:13:00.000Z | 505.1 | -0.091 | -0.054 | -0.037 |
| 2026-09-10T09:12:00.000Z | 504.8 | -0.109 | -0.045 | -0.063 |
| 2026-09-10T09:11:00.000Z | 504.5 | -0.099 | -0.029 | -0.069 |
| 2026-09-10T09:10:00.000Z | 504.6 | -0.054 | -0.012 | -0.042 |
(5 rows)
**Example: MACD crossovers: buy and sell signals of COAST on 10 September** (sample data: sample)
```sql
SELECT time, close, hist,
CASE WHEN hist > 0 THEN 'BUY' ELSE 'SELL' END AS signal
FROM (SELECT time, close, hist, lag(hist) AS prev_hist
FROM (SELECT time, close, round(macd_hist(close), 3) AS hist
FROM ochl_minutes
WHERE symbol = 'COAST') a) b
WHERE time IN '2026-09-10'
AND ((hist > 0 AND prev_hist <= 0) OR (hist < 0 AND prev_hist >= 0))
ORDER BY time
LIMIT 6;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | hist (DOUBLE) | signal (STRING) |
|---|---|---|---|
| 2026-09-10T05:35:00.000Z | 380.8 | -0.026 | SELL |
| 2026-09-10T05:53:00.000Z | 380 | 0.02 | BUY |
| 2026-09-10T06:21:00.000Z | 380.7 | -0.041 | SELL |
| 2026-09-10T06:36:00.000Z | 380.4 | 0.03 | BUY |
| 2026-09-10T06:43:00.000Z | 379.4 | -0.031 | SELL |
| 2026-09-10T06:45:00.000Z | 380.2 | 0.013 | BUY |
(6 rows)
**Example: Short periods on daily candles: MACD 3, 6, 3 of every symbol on the last day** (sample data: sample)
```sql
SELECT time, symbol, close, macd, signal, hist
FROM (SELECT time, symbol, close,
round(macd(close, 3, 6, 3) OVER (PARTITION BY symbol ORDER BY time), 2) AS macd,
round(macd_signal(close, 3, 6, 3) OVER (PARTITION BY symbol ORDER BY time), 2) AS signal,
round(macd_hist(close, 3, 6, 3) OVER (PARTITION BY symbol ORDER BY time), 2) AS hist
FROM ochl_day
WHERE time >= '2026-08-23') t
WHERE time IN '2026-09-10'
ORDER BY symbol;
```
Result:
| time (TIMESTAMP) | symbol (SYMBOL) | close (DOUBLE) | macd (DOUBLE) | signal (DOUBLE) | hist (DOUBLE) |
|---|---|---|---|---|---|
| 2026-09-10T00:00:00.000Z | CITY | 504.9 | 1.02 | 0.2 | 0.83 |
| 2026-09-10T00:00:00.000Z | COAST | 376.1 | 0.61 | 0.83 | -0.22 |
| 2026-09-10T00:00:00.000Z | VOLT | 191.2 | 3.77 | 2.93 | 0.85 |
(3 rows)
## stoch_k, stoch_d
Syntax:
```sql
stoch_k(high, low, close [, k [, k_slowing [, d]]])
stoch_d(high, low, close [, k [, k_slowing [, d]]])
stoch_k(high, low, close, k, k_slowing, d) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **slow stochastic oscillator**, from 0 to 100. It tells where the close is inside the range of the last `k` candles: 100 is at the highest high, 0 at the lowest low.
- The raw value is `(close - lowest low) / (highest high - lowest low) × 100` over the last `k` candles.
- `stoch_k` (**%K**) is the SMA of the raw value over `k_slowing` candles.
- `stoch_d` (**%D**) is the SMA of %K over `d` candles: a slower line.
Above **80** is overbought, below **20** oversold. When %K crosses above %D in the oversold zone, traders read it as a buy signal.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `k` | whole number, 1 or more | candles for the highest high and lowest low; default `14` |
| `k_slowing` | whole number, 1 or more | the SMA period of %K; default `3` |
| `d` | whole number, 1 or more | the SMA period of %D; default `3` |
- **Returns:** `DOUBLE` from 0 to 100.
- **NULL:** both are `NULL` for the first `k + k_slowing + d - 3` candles (17 with the defaults). Like TA-Lib, %K waits for %D, so `d` also changes when %K starts. A NULL price gives NULL while it is inside a window; then the values come back.
- **Notes:** when the highest high equals the lowest low (a flat market), the raw value is `0`. The same as TA-Lib's `STOCH` with SMA smoothing.
**Example: Slow stochastic 14, 3, 3 of COAST's one-minute candles (the last 5)** (sample data: sample)
```sql
SELECT time, close, k, d,
CASE WHEN k >= 80 THEN 'overbought' WHEN k <= 20 THEN 'oversold' ELSE 'normal' END AS zone
FROM (SELECT time, close,
round(stoch_k(high, low, close), 1) AS k,
round(stoch_d(high, low, close), 1) AS d
FROM ochl_minutes
WHERE symbol = 'COAST') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | k (DOUBLE) | d (DOUBLE) | zone (STRING) |
|---|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 376.1 | 87.3 | 87.2 | overbought |
| 2026-09-10T09:13:00.000Z | 376.2 | 88.6 | 82.5 | overbought |
| 2026-09-10T09:12:00.000Z | 376 | 85.8 | 71.2 | overbought |
| 2026-09-10T09:11:00.000Z | 375.5 | 73.1 | 52.8 | normal |
| 2026-09-10T09:10:00.000Z | 375.6 | 54.6 | 38.4 | normal |
(5 rows)
**Example: Stochastic 5, 3, 3 on daily candles, every symbol** (sample data: sample)
```sql
SELECT time, symbol, close, k, d
FROM (SELECT time, symbol, close,
round(stoch_k(high, low, close, 5, 3, 3) OVER (PARTITION BY symbol ORDER BY time), 1) AS k,
round(stoch_d(high, low, close, 5, 3, 3) OVER (PARTITION BY symbol ORDER BY time), 1) AS d
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) | k (DOUBLE) | d (DOUBLE) |
|---|---|---|---|---|
| 2026-09-08T00:00:00.000Z | CITY | 497.5 | 37 | 30.7 |
| 2026-09-09T00:00:00.000Z | CITY | 504.6 | 43 | 37.1 |
| 2026-09-10T00:00:00.000Z | CITY | 504.9 | 50.4 | 43.5 |
| 2026-09-08T00:00:00.000Z | COAST | 377.2 | 81.5 | 77.3 |
| 2026-09-09T00:00:00.000Z | COAST | 380.1 | 70.7 | 78.2 |
| 2026-09-10T00:00:00.000Z | COAST | 376.1 | 45.6 | 65.9 |
| 2026-09-08T00:00:00.000Z | VOLT | 182.4 | 75 | 73.3 |
| 2026-09-09T00:00:00.000Z | VOLT | 187.3 | 78.4 | 75.8 |
| 2026-09-10T00:00:00.000Z | VOLT | 191.2 | 91 | 81.4 |
(9 rows)
**Example: %K crosses above %D: buy signals of CITY's one-minute candles on 10 September** (sample data: sample)
```sql
SELECT time, close, k, d
FROM (SELECT time, close, k, d, lag(k) AS prev_k, lag(d) AS prev_d
FROM (SELECT time, close,
round(stoch_k(high, low, close), 1) AS k,
round(stoch_d(high, low, close), 1) AS d
FROM ochl_minutes
WHERE symbol = 'CITY') a) b
WHERE time IN '2026-09-10' AND k > d AND prev_k <= prev_d AND k < 30
ORDER BY time
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | k (DOUBLE) | d (DOUBLE) |
|---|---|---|---|
| 2026-09-10T05:18:00.000Z | 504.4 | 18.9 | 16.3 |
| 2026-09-10T05:30:00.000Z | 504.5 | 26.2 | 25.5 |
| 2026-09-10T06:06:00.000Z | 505.5 | 15.6 | 13.8 |
| 2026-09-10T06:37:00.000Z | 504.5 | 11.5 | 10 |
| 2026-09-10T06:48:00.000Z | 503.2 | 8.4 | 6.4 |
(5 rows)
## stochf_k
Syntax:
```sql
stochf_k(high, low, close [, k])
stochf_k(high, low, close, k) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **fast stochastic** %K: the raw stochastic value without smoothing, `(close - lowest low) / (highest high - lowest low) × 100` over the last `k` candles. It reacts at once, so it jumps more than [stoch_k](#stoch_k).
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `k` | whole number, 1 or more | how many candles; default `14` |
- **Returns:** `DOUBLE` from 0 to 100.
- **NULL:** `NULL` for the first `k - 1` candles. A NULL price gives NULL while it is inside the window; then the values come back.
- **Notes:** `0` when the highest high equals the lowest low. The same as the fast %K of TA-Lib's `STOCHF`. For the fast %D, take the `sma` of it over 3 candles in an outer query.
**Example: Fast %K and fast %D (SMA 3 of fast %K) of VOLT** (sample data: sample)
```sql
SELECT time, close, fast_k, round(sma(fast_k, 3), 1) AS fast_d
FROM (SELECT time, close, round(stochf_k(high, low, close, 5), 1) AS fast_k
FROM ochl_day
WHERE symbol = 'VOLT' AND time >= '2026-08-23') t
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | fast_k (DOUBLE) | fast_d (DOUBLE) |
|---|---|---|---|
| 2026-08-23T00:00:00.000Z | 181 | NULL | NULL |
| 2026-08-24T00:00:00.000Z | 178.5 | NULL | NULL |
| 2026-08-25T00:00:00.000Z | 176.8 | NULL | NULL |
| 2026-08-26T00:00:00.000Z | 174.2 | NULL | NULL |
| 2026-08-27T00:00:00.000Z | 173.3 | 11.2 | NULL |
| 2026-08-30T00:00:00.000Z | 173 | 18.5 | NULL |
| 2026-08-31T00:00:00.000Z | 172.6 | 17.2 | 15.6 |
| 2026-09-01T00:00:00.000Z | 169 | 15.3 | 17 |
| 2026-09-02T00:00:00.000Z | 171.5 | 48.8 | 27.1 |
| 2026-09-03T00:00:00.000Z | 176 | 87 | 50.4 |
| 2026-09-06T00:00:00.000Z | 180.1 | 77.1 | 71 |
| 2026-09-07T00:00:00.000Z | 176.9 | 57.8 | 74 |
| 2026-09-08T00:00:00.000Z | 182.4 | 89.9 | 74.9 |
| 2026-09-09T00:00:00.000Z | 187.3 | 87.5 | 78.4 |
| 2026-09-10T00:00:00.000Z | 191.2 | 95.4 | 90.9 |
(15 rows)
## stochrsi_k, stochrsi_d
Syntax:
```sql
stochrsi_k(x [, rsi_period [, k [, d]]])
stochrsi_d(x [, rsi_period [, k [, d]]])
stochrsi_k(x, rsi_period, k, d) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **stochastic RSI**, from 0 to 100. It puts the stochastic formula on the [RSI](functions-indicators.md#rsi) values instead of the prices: where is today's RSI inside the range of the last `k` RSI values? It moves faster than the RSI and reaches overbought (above 80) and oversold (below 20) more often.
- `stochrsi_k` (%K): `(RSI - lowest RSI) / (highest RSI - lowest RSI) × 100` over the last `k` RSI values.
- `stochrsi_d` (%D): the SMA of %K over `d` candles.
| Argument | Type | Meaning |
|---|---|---|
| `x` | number | the price, usually `close` |
| `rsi_period` | whole number, 1 or more | the RSI period; default `14` |
| `k` | whole number, 1 or more | how many RSI values for the range; default `5` |
| `d` | whole number, 1 or more | the SMA period of %D; default `3` |
- **Returns:** `DOUBLE` from 0 to 100.
- **NULL:** both are `NULL` for the first `rsi_period + k + d - 2` candles (20 with the defaults). **Careful:** the RSI inside is a Wilder average, so one NULL price makes this and every later value `NULL`.
- **Notes:** `0` when the RSI did not change over the `k` values. The same as TA-Lib's `STOCHRSI` (fast %K and fast %D).
**Example: Stochastic RSI 14, 5, 3 of CITY's one-minute candles (the last 5)** (sample data: sample)
```sql
SELECT time, close, rsi14, k, d
FROM (SELECT time, close,
round(rsi(close, 14), 1) AS rsi14,
round(stochrsi_k(close), 1) AS k,
round(stochrsi_d(close), 1) AS d
FROM ochl_minutes
WHERE symbol = 'CITY') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | rsi14 (DOUBLE) | k (DOUBLE) | d (DOUBLE) |
|---|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 504.9 | 47.6 | 69.4 | 82.4 |
| 2026-09-10T09:13:00.000Z | 505.1 | 50.1 | 100 | 59.3 |
| 2026-09-10T09:12:00.000Z | 504.8 | 46.2 | 77.8 | 25.9 |
| 2026-09-10T09:11:00.000Z | 504.5 | 42 | 0 | 3.6 |
| 2026-09-10T09:10:00.000Z | 504.6 | 43 | 0 | 3.6 |
(5 rows)
## willr, williams_r
Syntax:
```sql
willr(high, low, close [, n])
williams_r(high, low, close [, n])
willr(high, low, close, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**Williams %R**, from **-100 to 0**. Like the stochastic, it shows where the close is inside the range of the last `n` candles, but counted down from the top: `0` is at the highest high, `-100` at the lowest low. Above **-20** is overbought, below **-80** oversold. `williams_r` is the same function.
`%R = (highest high - close) / (highest high - lowest low) × -100`
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `n` | whole number, 1 or more | how many candles; default `14` |
- **Returns:** `DOUBLE` from -100 to 0.
- **NULL:** `NULL` for the first `n - 1` candles. A NULL price gives NULL while it is inside the window; then the values come back.
- **Notes:** `%R = fast stochastic - 100`. When the highest high equals the lowest low, the result is `0` (like TA-Lib's `WILLR`).
**Example: Williams %R 5 of every symbol on the last three days** (sample data: sample)
```sql
SELECT time, symbol, close, wr,
CASE WHEN wr >= -20 THEN 'overbought' WHEN wr <= -80 THEN 'oversold' ELSE 'normal' END AS zone
FROM (SELECT time, symbol, close,
round(williams_r(high, low, close, 5) OVER (PARTITION BY symbol ORDER BY time), 1) AS wr
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) | wr (DOUBLE) | zone (STRING) |
|---|---|---|---|---|
| 2026-09-08T00:00:00.000Z | CITY | 497.5 | -60.9 | normal |
| 2026-09-09T00:00:00.000Z | CITY | 504.6 | -44.8 | normal |
| 2026-09-10T00:00:00.000Z | CITY | 504.9 | -43.1 | normal |
| 2026-09-08T00:00:00.000Z | COAST | 377.2 | -22.9 | normal |
| 2026-09-09T00:00:00.000Z | COAST | 380.1 | -53.9 | normal |
| 2026-09-10T00:00:00.000Z | COAST | 376.1 | -86.3 | oversold |
| 2026-09-08T00:00:00.000Z | VOLT | 182.4 | -10.1 | overbought |
| 2026-09-09T00:00:00.000Z | VOLT | 187.3 | -12.5 | overbought |
| 2026-09-10T00:00:00.000Z | VOLT | 191.2 | -4.6 | overbought |
(9 rows)
**Example: %R is the fast stochastic minus 100** (sample data: sample)
```sql
SELECT time, close,
round(willr(high, low, close, 5), 2) AS willr5,
round(stochf_k(high, low, close, 5) - 100, 2) AS fast_k_minus_100
FROM ochl_day
WHERE symbol = 'COAST' AND time >= '2026-09-03'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | willr5 (DOUBLE) | fast_k_minus_100 (DOUBLE) |
|---|---|---|---|
| 2026-09-03T00:00:00.000Z | 373.2 | NULL | NULL |
| 2026-09-06T00:00:00.000Z | 375.4 | NULL | NULL |
| 2026-09-07T00:00:00.000Z | 379.2 | NULL | NULL |
| 2026-09-08T00:00:00.000Z | 377.2 | NULL | NULL |
| 2026-09-09T00:00:00.000Z | 380.1 | -53.89 | -53.89 |
| 2026-09-10T00:00:00.000Z | 376.1 | -86.31 | -86.31 |
(6 rows)
## cci
Syntax:
```sql
cci(high, low, close [, n])
cci(high, low, close, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **commodity channel index**. It measures how far the **typical price** `(high + low + close) / 3` is from its average of the last `n` candles, compared with the usual distance. Most values are between **-100 and +100**. Above +100 the price is unusually high (a strong rise, or overbought); below -100 unusually low.
`CCI = (typical price - SMA of the typical price) / (0.015 × mean deviation)`. The **mean deviation** is the average distance of the `n` typical prices from their SMA.
| Argument | Type | Meaning |
|---|---|---|
| `high` | number | the candle's high |
| `low` | number | the candle's low |
| `close` | number | the candle's close |
| `n` | whole number, 1 or more | how many candles; default `20` |
- **Returns:** `DOUBLE` (no fixed range).
- **NULL:** `NULL` for the first `n - 1` candles. A NULL price gives NULL while it is inside the window; then the values come back.
- **Notes:** `0` when all typical prices of the window are equal. The same as TA-Lib's `CCI`.
**Example: CCI 20 of COAST's one-minute candles (the last 5)** (sample data: sample)
```sql
SELECT time, close, cci20,
CASE WHEN cci20 > 100 THEN 'above +100' WHEN cci20 < -100 THEN 'below -100' ELSE 'inside' END AS channel
FROM (SELECT time, close, round(cci(high, low, close), 1) AS cci20
FROM ochl_minutes
WHERE symbol = 'COAST') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | cci20 (DOUBLE) | channel (STRING) |
|---|---|---|---|
| 2026-09-10T09:14:00.000Z | 376.1 | 148.5 | above +100 |
| 2026-09-10T09:13:00.000Z | 376.2 | 135.6 | above +100 |
| 2026-09-10T09:12:00.000Z | 376 | 129.4 | above +100 |
| 2026-09-10T09:11:00.000Z | 375.5 | 45.1 | inside |
| 2026-09-10T09:10:00.000Z | 375.6 | 43.4 | inside |
(5 rows)
**Example: CCI 5 on daily candles, every symbol on the last day** (sample data: sample)
```sql
SELECT symbol, close, cci5
FROM (SELECT time, symbol, close,
round(cci(high, low, close, 5) OVER (PARTITION BY symbol ORDER BY time), 1) AS cci5
FROM ochl_day) t
WHERE time IN '2026-09-10'
ORDER BY cci5 DESC;
```
Result:
| symbol (SYMBOL) | close (DOUBLE) | cci5 (DOUBLE) |
|---|---|---|
| VOLT | 191.2 | 108.6 |
| CITY | 504.9 | 56.2 |
| COAST | 376.1 | -38 |
(3 rows)
## roc, rocp, rocr
Syntax:
```sql
roc(x [, n])
rocp(x [, n])
rocr(x [, n])
roc(x, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **rate of change**: how much the price changed against the price `n` candles ago. The three functions give the same change in three forms:
| Function | Formula | 10% rise gives |
|---|---|---|
| `roc` | `(x / x n candles ago - 1) × 100` | `10` (percent) |
| `rocp` | `(x - x n candles ago) / x n candles ago` | `0.1` (a fraction) |
| `rocr` | `x / x n candles ago` | `1.1` (a ratio) |
Above 0 (above 1 for `rocr`), the price is higher than `n` candles ago. When ROC gets smaller while the price still rises, the move is losing speed.
| Argument | Type | Meaning |
|---|---|---|
| `x` | number | the price, usually `close` |
| `n` | whole number, 1 or more | how many candles back; default `10` |
- **Returns:** `DOUBLE`.
- **NULL:** `NULL` for the first `n` candles, when this or the old price is NULL, and when the old price is `0`.
- **Notes:** the same as TA-Lib's `ROC`, `ROCP` and `ROCR`. `roc(close, 1)` is the same as [pct_change](functions-window.md#pct-change); for returns see [returns and risk](functions-returns-risk.md).
**Example: 5-day rate of change of CITY in three forms** (sample data: sample)
```sql
SELECT time, close,
round(roc(close, 5), 2) AS roc5_pct,
round(rocp(close, 5), 4) AS rocp5,
round(rocr(close, 5), 4) AS rocr5
FROM ochl_day
WHERE symbol = 'CITY' AND time >= '2026-08-23'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | roc5_pct (DOUBLE) | rocp5 (DOUBLE) | rocr5 (DOUBLE) |
|---|---|---|---|---|
| 2026-08-23T00:00:00.000Z | 507.9 | NULL | NULL | NULL |
| 2026-08-24T00:00:00.000Z | 510.3 | NULL | NULL | NULL |
| 2026-08-25T00:00:00.000Z | 513.3 | NULL | NULL | NULL |
| 2026-08-26T00:00:00.000Z | 513.2 | NULL | NULL | NULL |
| 2026-08-27T00:00:00.000Z | 505.9 | NULL | NULL | NULL |
| 2026-08-30T00:00:00.000Z | 503.1 | -0.95 | -0.0095 | 0.9905 |
| 2026-08-31T00:00:00.000Z | 505.7 | -0.9 | -0.009 | 0.991 |
| 2026-09-01T00:00:00.000Z | 502.4 | -2.12 | -0.0212 | 0.9788 |
| 2026-09-02T00:00:00.000Z | 496.4 | -3.27 | -0.0327 | 0.9673 |
| 2026-09-03T00:00:00.000Z | 497.9 | -1.58 | -0.0158 | 0.9842 |
| 2026-09-06T00:00:00.000Z | 499 | -0.81 | -0.0081 | 0.9919 |
| 2026-09-07T00:00:00.000Z | 498.7 | -1.38 | -0.0138 | 0.9862 |
| 2026-09-08T00:00:00.000Z | 497.5 | -0.98 | -0.0098 | 0.9902 |
| 2026-09-09T00:00:00.000Z | 504.6 | 1.65 | 0.0165 | 1.0165 |
| 2026-09-10T00:00:00.000Z | 504.9 | 1.41 | 0.0141 | 1.0141 |
(15 rows)
**Example: Which symbol rose most in the last 5 trading days?** (sample data: sample)
```sql
SELECT symbol, close, roc5
FROM (SELECT time, symbol, close,
round(roc(close, 5) OVER (PARTITION BY symbol ORDER BY time), 2) AS roc5
FROM ochl_day) t
WHERE time IN '2026-09-10'
ORDER BY roc5 DESC;
```
Result:
| symbol (SYMBOL) | close (DOUBLE) | roc5 (DOUBLE) |
|---|---|---|
| VOLT | 191.2 | 8.64 |
| CITY | 504.9 | 1.41 |
| COAST | 376.1 | 0.78 |
(3 rows)
## mom, momentum
Syntax:
```sql
mom(x [, n])
momentum(x [, n])
mom(x, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
The **momentum**: the price now minus the price `n` candles ago, in the unit of the price. Above 0, the price is higher than `n` candles ago. `momentum` is the same function.
| Argument | Type | Meaning |
|---|---|---|
| `x` | number | the price, usually `close` |
| `n` | whole number, 1 or more | how many candles back; default `10` |
- **Returns:** `DOUBLE`, in the unit of the price.
- **NULL:** `NULL` for the first `n` candles, and when this or the old price is NULL.
- **Notes:** the same as TA-Lib's `MOM`. To compare shares with different prices, use [roc](#roc) (percent) instead.
**Example: 10-minute momentum of COAST (the last 5)** (sample data: sample)
```sql
SELECT time, close, mom10
FROM (SELECT time, close, round(momentum(close), 2) AS mom10
FROM ochl_minutes
WHERE symbol = 'COAST') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | mom10 (DOUBLE) |
|---|---|---|
| 2026-09-10T09:14:00.000Z | 376.1 | 1.5 |
| 2026-09-10T09:13:00.000Z | 376.2 | 1.8 |
| 2026-09-10T09:12:00.000Z | 376 | 1.2 |
| 2026-09-10T09:11:00.000Z | 375.5 | 0.1 |
| 2026-09-10T09:10:00.000Z | 375.6 | 0.2 |
(5 rows)
**Example: The first n rows have no old price** (sample data: sample)
```sql
SELECT time, close, round(mom(close, 3), 1) AS mom3, round(close - lag(close, 3), 1) AS same
FROM ochl_day
WHERE symbol = 'VOLT' AND time >= '2026-09-03'
ORDER BY time;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | mom3 (DOUBLE) | same (DOUBLE) |
|---|---|---|---|
| 2026-09-03T00:00:00.000Z | 176 | NULL | NULL |
| 2026-09-06T00:00:00.000Z | 180.1 | NULL | NULL |
| 2026-09-07T00:00:00.000Z | 176.9 | NULL | NULL |
| 2026-09-08T00:00:00.000Z | 182.4 | 6.4 | 6.4 |
| 2026-09-09T00:00:00.000Z | 187.3 | 7.2 | 7.2 |
| 2026-09-10T00:00:00.000Z | 191.2 | 14.3 | 14.3 |
(6 rows)
## trix
Syntax:
```sql
trix(x [, n])
trix(x, n) OVER ([PARTITION BY ...] [ORDER BY ...])
```
**TRIX**: the change from one candle to the next, in percent, of a **triple-smoothed EMA** (the EMA of the EMA of the EMA of the price). The triple smoothing removes the small moves, so TRIX shows only the bigger trend. Above 0 the trend is up, below 0 down. Traders often add a signal line (a 9-candle EMA of TRIX) and watch the crossings.
| Argument | Type | Meaning |
|---|---|---|
| `x` | number | the price, usually `close` |
| `n` | whole number, 1 or more | the period of the three EMAs; default `30` |
- **Returns:** `DOUBLE` (percent: `0.05` means +0.05% per candle).
- **NULL:** `NULL` for the first `3n - 2` candles (88 with the default). **Careful:** one NULL price makes this and every later value `NULL`.
- **Notes:** the same as TA-Lib's `TRIX`. It needs many candles: use one-minute candles or a long daily history.
**Example: TRIX 30 of CITY's one-minute candles (the last 5)** (sample data: sample)
```sql
SELECT time, close, trix30
FROM (SELECT time, close, round(trix(close), 4) AS trix30
FROM ochl_minutes
WHERE symbol = 'CITY') t
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | close (DOUBLE) | trix30 (DOUBLE) |
|---|---|---|
| 2026-09-10T09:14:00.000Z | 504.9 | 0.0047 |
| 2026-09-10T09:13:00.000Z | 505.1 | 0.005 |
| 2026-09-10T09:12:00.000Z | 504.8 | 0.0053 |
| 2026-09-10T09:11:00.000Z | 504.5 | 0.0056 |
| 2026-09-10T09:10:00.000Z | 504.6 | 0.0059 |
(5 rows)
**Example: TRIX and its signal line: leave the warm-up rows out before the EMA** (sample data: sample)
```sql
SELECT time, trix15, round(ema(trix15, 9), 4) AS signal
FROM (SELECT time, trix15
FROM (SELECT time, round(trix(close, 15), 4) AS trix15
FROM ochl_minutes
WHERE symbol = 'COAST') a
WHERE trix15 IS NOT NULL) b
ORDER BY time DESC
LIMIT 5;
```
Result:
| time (TIMESTAMP) | trix15 (DOUBLE) | signal (DOUBLE) |
|---|---|---|
| 2026-09-10T09:14:00.000Z | -0.0001 | -0.0035 |
| 2026-09-10T09:13:00.000Z | -0.0015 | -0.0044 |
| 2026-09-10T09:12:00.000Z | -0.0028 | -0.0051 |
| 2026-09-10T09:11:00.000Z | -0.0039 | -0.0057 |
| 2026-09-10T09:10:00.000Z | -0.0045 | -0.0061 |
(5 rows)