# Prices adjusted for splits and bonus shares > Read a price table as if every split and bonus share had always been there - a chart without the jump on the ex-date - while the table keeps the traded prices. - Category: Time-series - Keywords: ADJUSTED BY, adjusted prices, adjusted close, adjusted candles, adjust, split, stock split, bonus share, bonus shares, right share, corporate action, corporate actions, ex\_date, ex-date, factor, adjustment factor, back-adjusted, unadjusted, PRICES, VOLUMES, NEPSE - See also: [Candles cookbook](candles-cookbook.md), [Trading calendars](calendars.md), [SELECT](select.md), [Views](views.md), [Joins](joins.md) When a company gives a **bonus share** or **splits** its shares, the price falls on the **ex-date**: one old share became more shares, each worth less. A chart of the traded prices then shows a jump that was never a loss. An **adjusted** price divides every price before the ex-date by the same factor, so the history lines up with today's shares. `ADJUSTED BY` reads a table that way. Nothing is written: the table keeps the prices as they were traded, and you choose per query which ones you want. Syntax: ```sql SELECT ... FROM table [alias] ADJUSTED BY actions [PRICES (column, ...)] [VOLUMES (column, ...)] ... ``` ## The table of corporate actions One row per symbol and ex-date. It needs these three columns (other columns are allowed): | Column | Type | Meaning | |---|---|---| | `symbol` | `STRING` or `SYMBOL` | the symbol, as in the price table's `symbol` column | | `ex_date` | `TIMESTAMP` or `DATE` | the first day that trades with the new number of shares | | `factor` | a number | how many shares one old share became: `2` for a 1:1 bonus or a 2-for-1 split, `1.1` for a 10% bonus | **Example: A 10% bonus, then a 2-for-1 split** ```sql CREATE TABLE corp_actions (symbol STRING, ex_date TIMESTAMP, factor DOUBLE, PRIMARY KEY (symbol, ex_date)); INSERT INTO corp_actions VALUES ('CITY', '2026-08-15', 1.1), ('CITY', '2026-09-10', 2); SELECT * FROM corp_actions; ``` Result: `CREATE TABLE corp_actions (` ```text table 'corp_actions' created ``` `INSERT INTO corp_actions VALUES ('CITY', '2026-08-15', 1.1), ('CITY', '2026-09-10', 2)` ```text 2 row(s) inserted ``` `SELECT * FROM corp_actions` | symbol (STRING) | ex_date (TIMESTAMP) | factor (DOUBLE) | |---|---|---| | CITY | 2026-08-15T00:00:00.000Z | 1.1 | | CITY | 2026-09-10T00:00:00.000Z | 2 | (2 rows) ## How a row is adjusted | Row | Prices | Volumes | |---|---|---| | before an ex-date of its symbol | divided by the product of the factors of **every later** ex-date | multiplied by that product | | on or after the symbol's last ex-date | as stored | as stored | | of a symbol with no actions | as stored | as stored | So turnover (price x volume) stays the same, as it should: the money traded did not change. **Example: Daily closes of CITY, as traded and adjusted** ```sql CREATE TABLE corp_actions (symbol STRING, ex_date TIMESTAMP, factor DOUBLE, PRIMARY KEY (symbol, ex_date)); INSERT INTO corp_actions VALUES ('CITY', '2026-08-15', 1.1), ('CITY', '2026-09-10', 2); CREATE TABLE day_bars (time TIMESTAMP, symbol SYMBOL, close DOUBLE, volume LONG) TIMESERIES(time) PARTITION BY MONTH; INSERT INTO day_bars VALUES ('2026-08-14', 'CITY', 880, 100), ('2026-08-15', 'CITY', 800, 110), ('2026-09-09', 'CITY', 820, 90), ('2026-09-10', 'CITY', 410, 200); SELECT b.time, b.close AS traded, a.close AS adjusted, a.volume AS adjusted_volume FROM day_bars b JOIN (SELECT time, close, volume FROM day_bars ADJUSTED BY corp_actions WHERE symbol = 'CITY') a ON a.time = b.time ORDER BY b.time; ``` Result: `CREATE TABLE corp_actions (` ```text table 'corp_actions' created ``` `INSERT INTO corp_actions VALUES ('CITY', '2026-08-15', 1.1), ('CITY', '2026-09-10', 2)` ```text 2 row(s) inserted ``` `CREATE TABLE day_bars (` ```text table 'day_bars' created ``` `INSERT INTO day_bars VALUES ('2026-08-14', 'CITY', 880, 100), ('2026-08-15', 'CITY', 800, 110), ('20` ```text 4 row(s) inserted ``` `SELECT b.time, b.close AS traded, a.close AS adjusted, a.volume AS adjusted_volume FROM day_bars AS ` | time (TIMESTAMP) | traded (DOUBLE) | adjusted (DOUBLE) | adjusted_volume (LONG) | |---|---|---|---| | 2026-08-14T00:00:00.000Z | 880 | 400 | 220 | | 2026-08-15T00:00:00.000Z | 800 | 400 | 220 | | 2026-09-09T00:00:00.000Z | 820 | 410 | 180 | | 2026-09-10T00:00:00.000Z | 410 | 410 | 200 | (4 rows) The 14 August close of 880 was before both ex-dates: 880 / (1.1 x 2) = 400. The 9 September close was only before the split: 820 / 2 = 410. From the split on, prices are as traded. ## Which columns change Without `PRICES (...)` and `VOLUMES (...)`, the columns are found by name: | Kind | Column names | |---|---| | prices (divided) | `open`, `high`, `low`, `close`, `price`, `rate`, `vwap`, `ltp`, `prev_close`, `avg_price` | | volumes (multiplied) | `volume`, `qty`, `quantity`, `shares` | Every other column (the time, the symbol, turnover, counts) is as stored. Name the columns yourself when yours have other names; `()` changes none of that kind: **Example: Only the close, volumes as traded** ```sql CREATE TABLE corp_actions (symbol STRING, ex_date TIMESTAMP, factor DOUBLE, PRIMARY KEY (symbol, ex_date)); INSERT INTO corp_actions VALUES ('CITY', '2026-09-10', 2); CREATE TABLE day_bars (time TIMESTAMP, symbol SYMBOL, close DOUBLE, volume LONG) TIMESERIES(time) PARTITION BY MONTH; INSERT INTO day_bars VALUES ('2026-09-09', 'CITY', 820, 90), ('2026-09-10', 'CITY', 410, 200); SELECT time, close, volume FROM day_bars ADJUSTED BY corp_actions PRICES (close) VOLUMES () ORDER BY time; ``` Result: `CREATE TABLE corp_actions (` ```text table 'corp_actions' created ``` `INSERT INTO corp_actions VALUES ('CITY', '2026-09-10', 2)` ```text 1 row(s) inserted ``` `CREATE TABLE day_bars (` ```text table 'day_bars' created ``` `INSERT INTO day_bars VALUES ('2026-09-09', 'CITY', 820, 90), ('2026-09-10', 'CITY', 410, 200)` ```text 2 row(s) inserted ``` `SELECT time, close, volume FROM day_bars ADJUSTED BY corp_actions PRICES (close) VOLUMES () ORDER BY` | time (TIMESTAMP) | close (DOUBLE) | volume (LONG) | |---|---|---| | 2026-09-09T00:00:00.000Z | 410 | 90 | | 2026-09-10T00:00:00.000Z | 410 | 200 | (2 rows) A whole-number volume column stays a whole number (rounded): 10 shares before a 1.5 bonus are 15. ## Filters, candles and indicators Everything after `FROM t ADJUSTED BY actions` sees the adjusted values: `WHERE close > 100`, `SAMPLE BY`, `GROUP BY`, `sma(close, 20) OVER (...)`. A condition on a column that is not adjusted (`symbol`, `time`) is also run before the adjustment, so `WHERE symbol = 'CITY'` reads one symbol's rows only. **Example: Weekly adjusted candles** ```sql CREATE TABLE corp_actions (symbol STRING, ex_date TIMESTAMP, factor DOUBLE, PRIMARY KEY (symbol, ex_date)); INSERT INTO corp_actions VALUES ('CITY', '2026-09-10', 2); CREATE TABLE day_bars (time TIMESTAMP, symbol SYMBOL, close DOUBLE, volume LONG) TIMESERIES(time) PARTITION BY MONTH; INSERT INTO day_bars VALUES ('2026-09-07', 'CITY', 800, 90), ('2026-09-08', 'CITY', 840, 90), ('2026-09-09', 'CITY', 820, 90), ('2026-09-10', 'CITY', 410, 200), ('2026-09-11', 'CITY', 420, 150); SELECT time, first(close) AS open, max(close) AS high, min(close) AS low, last(close) AS close, sum(volume) AS volume FROM day_bars ADJUSTED BY corp_actions WHERE symbol = 'CITY' SAMPLE BY 7d ALIGN TO CALENDAR; ``` Result: `CREATE TABLE corp_actions (` ```text table 'corp_actions' created ``` `INSERT INTO corp_actions VALUES ('CITY', '2026-09-10', 2)` ```text 1 row(s) inserted ``` `CREATE TABLE day_bars (` ```text table 'day_bars' created ``` `INSERT INTO day_bars VALUES ('2026-09-07', 'CITY', 800, 90), ('2026-09-08', 'CITY', 840, 90), ('2026` ```text 5 row(s) inserted ``` `SELECT time, first(close) AS open, max(close) AS high, min(close) AS low, last(close) AS close, sum(` | time (TIMESTAMP) | open (DOUBLE) | high (DOUBLE) | low (DOUBLE) | close (DOUBLE) | volume (LONG) | |---|---|---|---|---|---| | 2026-09-03T00:00:00.000Z | 400 | 420 | 400 | 410 | 540 | | 2026-09-10T00:00:00.000Z | 410 | 420 | 410 | 420 | 350 | (2 rows) - **Speed:** the adjusted rows are worked out for each query; filter by symbol and time when the table is big. - **Views:** `CREATE VIEW adjusted_days AS SELECT ... FROM day_bars ADJUSTED BY corp_actions` keeps the adjusted reading under a name. - **Rights:** the query reads both tables, so it needs the right to read both. ## Errors | Message | Why | |---|---| | `table '...' needs the columns symbol, ex_date and factor` | the actions table has other column names | | `table '...' has no time column` | the price table needs `TIMESERIES(time)` (or `TIMESTAMP(col)`) to know which rows come first | | `has no column symbol` | the price table needs a `symbol` column to match the actions by | | `ADJUSTED BY works on a table of this database` | not on a subquery, a function, a view, `AS OF` or another database's table |