Manual · 4 of 5
Data & integrity
Sources and coverage
- Coins
- BTC · ETH
- Bar
- 15 minutes, UTC
- History
- since 1 Jan 2020
- Bars per coin
- ≈ 234,000 per feed
- Feeds
- 6 selectable + 1 internal
- Indicators
- 110 per feed
- Public columns
- 833
- New bar
- ≈ 3 s after each quarter hour
| Prefix | Exchange | Market | Currency | Notes |
|---|---|---|---|---|
bn_spot | Binance | Spot | USDT | The default feed. Only Binance feeds carry taker-buy and trade-count columns. |
bn_perp | Binance | Futures (USDT-margined perpetual) | USDT | Taker-buy and trade-count columns available. |
okx_spot | OKX | Spot | USDT | |
okx_perp | OKX | Futures (perpetual swap) | USDT | |
cb_spot | Coinbase | Spot | USD | No quote_volume column. |
up_spot | Upbit | Spot | KRW | Prices are Korean won. 100000000 means ₩100,000,000. |
up_krwusdt | Upbit (internal) | KRW/USDT rate | KRW | Not selectable in the app; powers x_krw_premium. History starts 2024-06-07. |
Each feed is collected directly from its exchange as 15-minute candles (open, high, low, close, volume and, where the exchange provides them, quote volume, trade count and taker-buy volume). Every indicator is computed per feed from that feed's own candles, so up_spot_rsi is the RSI of Upbit's KRW price, not a copy of Binance's.
The bar timeline
A bar is named by its start time. The 09:00 bar covers 09:00:00 to 09:14:59 UTC and becomes final at 09:15:00. That is the moment its close, high, low and volume stop changing — and the first moment any indicator that uses it can be computed honestly.
| Clock (UTC) | Event | What is known |
|---|---|---|
| 09:00 – 09:14:59 | bar 09:00 forms | its open is fixed; high / low / close / volume keep changing |
| 09:15:00 | bar 09:00 closes, bar 09:15 opens | the 09:00 candle is final; indicators that end at 09:00 can now be computed |
| ≈ 09:15:03 | the 09:15 row is published | date = 20260901.0915 · indicators = values as of the 09:00 close · raw OHLCV of 09:15 fill in when that bar closes |
A new row is published a few seconds after each quarter hour. Its indicator columns are complete immediately (they depend only on closed bars); its raw candle columns describe the bar that has just opened and are filled when that bar closes 15 minutes later.
The one rule: only completed bars
On any row, every indicator column holds the value computed through the previous completed bar. In the engine this is a single global setting, output_lag_bars = 1, applied once to the published columns.
- The row for the bar that opened at 00:00 UTC on 1 Sep 2026 shows
bn_spot_rsi≈ 44. - That RSI was computed from closes up to the 23:45 bar of 31 Aug — the last close known at 00:00:00. That close, 78,581.29, is what
p:bn_spot_closereturns on the same row. - The row's own close, 78,754.74, was not known until 00:15 and did not enter the RSI. It is there as
bn_spot_close, the raw candle, so you can measure what the bar did after the setup.
date,entity,bn_spot_close,p:bn_spot_close,round(bn_spot_rsi) as rsi_prev_bar@entity=BTC and date=20260901.0000Try this →
How to think about a row
Why look-ahead and repainting are fatal
Look-ahead bias
Look-ahead bias is using information in a signal that was not available at the time the signal fires. The classic accident: computing RSI with the current bar's close and then filtering “RSI below 30”. Every matched bar is one whose close has already fallen — you selected it because of the move you are about to measure. The backtest looks brilliant, and no trader could have acted on it, because at the open the close did not exist.
The same leak hides in innocent places: dividing an indicator by the row's current close, using a rolling maximum that includes the current bar, or a calendar flag derived from the local clock at processing time. Each one lets a value on the row depend on the future of that row.
Repainting
Repainting is when a value changes after you first saw it. Live indicators drawn on a forming candle do this constantly: a “cross” appears at 09:07, vanishes at 09:12, and the chart replayed later shows only the final version. Anything that was evaluated on unfinished bars is history rewritten after the fact: what you see now is not what you would have seen then.
Why it matters for TPSL
- A pattern search is only evidence if every matched bar was matchable at the time. Otherwise the statistics on the result page describe hindsight, not a repeatable setup.
- Win rates, average moves, run-up and drawdown are all computed from the bars after the setup. If the setup already contains the outcome, those numbers are circular.
- Deep Analysis searches thousands of conditions automatically. A single leaking column would let it “discover” rules that are guaranteed to fail live.
How the pipeline enforces it
| Guarantee | What it means |
|---|---|
| Fixed definitions | The 110 indicators are defined once in a versioned registry (parameters, windows, normalisation). A query cannot change them, so two people running the same condition get the same rows. |
| Completed bars only | The engine that computes indicators only ever sees closed candles. An unfinished bar is never an input. |
| One lag, applied once | The published value on a row is the indicator computed through the previous bar. The shift is applied exactly once, at the very end of the pipeline, to the published column — internal intermediate series are not shifted, which prevents accidental double lags. |
| Normalisation at the same bar | dev % and % of close divide by the close of the bar the indicator belongs to, inside the pipeline. Dividing by the row's own close at query time would mix bar t−1 with bar t — a one-bar leak. |
| No gap filling | A missing candle stays missing. Windows restart after a gap instead of bridging it with invented values. |
| Event windows exclude the current bar | Breakouts and spikes compare a bar against the bars before it; the reference window is shifted so a bar can never 'break' its own high. |
| Calendar from the slot | day, day_progress and the other calendar columns are derived from the bar's UTC start time, never from a clock, so backfilled history and live rows agree. |
| Immutable generations | History is published in versioned, append-only generations. A correction creates a new generation with a new identifier; nothing is edited in place. Each response carries the generation it came from. |
| Forward references are explicit | The only way to see the future is nN:. It is meant for measuring outcomes after a setup. A condition that contains nN: is a hindsight study, and Deep Analysis refuses it as an input. |
The practical consequence: a value you see on a row today is the value that was published when that row appeared, and the value it will still have next year. Rerunning a query never changes old rows; it only appends new bars.
Missing values
TPSL never invents a number. When a value cannot be computed honestly it is stored as null, shown as “—” in tables, and it never matches a comparison.
| Source | Where it happens | Typical size |
|---|---|---|
| Warm-up | An indicator with a 30-bar window has no value for the first 29 bars of the history — and again for 29 bars after any gap. | sma is null on 0.27 % of Binance spot bars; adaptive_dma (768 bars) on 5 % |
| Exchange gaps | Maintenance windows and outages: Binance spot 154 missing bars, Coinbase ≈ 106, Upbit ≈ 550 (yearly maintenance on 31 Dec), OKX and Binance perpetual just the current bar. | the missing bar itself and the warm-up after it |
| No event yet | bars_since_* columns are null when no spike / breakout happened inside the last 672 bars. | ≈ 8–9 % of bars for the volume-spike columns |
| Not on this feed | Binance-only columns on other feeds; quote_volume on Coinbase. | the column simply does not exist — the engine reports an unknown column |
| KRW/USDT history | The Upbit KRW/USDT rate starts on 7 June 2024, so x_krw_premium is null before that. | 66 % of the history |
| Future bars | nN: references are null until the bar exists; forward columns on the latest rows are null. | the last N rows of a query |
Because a null never matches, you do not need guards in conditions. Averages and win rates on the result page exclude null cells from both the sum and the count.
Known extreme prints
The full-history survey found no data defect, but a few genuine exchange prints dominate the extremes of some columns. They are real trades the exchange served, kept as-is.
- 13 Mar 2020, 02:30 UTC, Binance perpetual ETH — a high of 323 against a close of 118 during the crash rebound. It sets the historical maximum of range, trange, plus_dm, midprice, range_l2 and sar on that feed for the following bars.
- 19 May 2021, 13:00 UTC, Binance perpetual ETH — a low of 1,400.73 (open 2,332.92), the maximum of minus_dm.
- Early 2020 on OKX perpetual — thin volume, including a few zero-volume bars, which leaves the volume-based regression columns null a little longer on that feed.
- Upbit prices are KRW: the maximum close in the raw tables (≈ 180,000,000) is ₩180 million, not a dollar price.
bn_perp_range<20 or restrict the dates. The result page's medians are already robust to a handful of outliers.Units and rounding
- Price-level indicators are stored as a percent distance from the close (dev %); price differences as a percent of the close; returns and volatilities are ratios multiplied by 100. This makes every indicator comparable across $3,900 and $126,000 BTC, across exchanges, and across USDT, USD and KRW quotes.
- Oscillators, ranks, correlations and bar counts keep their natural scale.
- Raw candle columns and the cumulative volume lines (ad, adosc, obv) stay in exchange units.
- All published values are rounded to four decimals — one part in a million of the price, about the tick resolution of BTC/USD. Internal intermediate values are kept at full precision, so an indicator built on another indicator never sees a rounded input.
Result, run-up and drawdown
The result page adds a few columns computed by the engine from raw candles, all anchored on the row's own bar:
| Column | Definition | Moment |
|---|---|---|
| Result | (close − open) ÷ open × 100 of the row's bar | signal bar |
| Close / Prev, High / Prev, Low / Prev | the bar's close, high and low divided by the previous close | signal bar |
| +15 m, +1 h, +4 h, +1 d | the close 1 / 4 / 16 / 96 bars later divided by this close (n1:, n4:, n16:, n96:) | outcome |
| Run-up +1 h / +4 h | the highest high of the next 4 / 16 bars divided by this close — the best exit you could have had | outcome |
| Drawdown +1 h / +4 h | the lowest low of the next 4 / 16 bars divided by this close — the worst point you would have sat through | outcome |
The Outcome summary turns these into win rates, averages, medians and extremes over the matched bars, and shows the median and 10th-percentile run-up and drawdown as reference lines for a take-profit and a stop-loss. They are descriptive history: a setup that averaged +0.4 % after the fact is not a promise about the next occurrence.
Reproducibility
- The same condition returns the same historical rows every time; only newly arrived bars are added.
- The “Executed query” line on the result page is the exact text that ran — copy it, share it, rerun it.
- Every indicator definition is fixed and versioned; the version is stamped on each response. A definition change would ship as a new generation, never as a silent rewrite.
- Plain-language questions are translated into this same query language before running, so a translated question is as reproducible as a hand-written condition.
Continue with the Examples, or go back to the query language.