Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/backtest-kit/uzse-backtest-app/llms.txt

Use this file to discover all available pages before exploring further.

build-candles.ts reads raw trade records from the trade-results MongoDB collection and aggregates them into OHLCV (Open, High, Low, Close, Volume) candlestick documents across 11 timeframes simultaneously. The script processes trades one calendar day at a time, fills every minute of each day with gap candles (carrying the previous close forward at zero volume), and writes the resulting documents to the candle-items collection. Re-running the script is safe — the unique compound index {symbol, interval, timestamp} causes duplicate candles to be silently skipped.

Synopsis

npx tsx scripts/build-candles.ts [symbol] [isin]

Parameters

symbol
string
default:"HMKB"
Display ticker written into the candle-items collection, e.g. HMKB. This is the human-readable name used by charting consumers and does not need to match the ISIN.
isin
string
default:"UZ7011340005"
ISIN code used to look up trade records in trade-results, e.g. UZ7011340005. Must match the symbol field stored by import-trades.ts.

Supported Timeframes

All 11 timeframes from the TIMEFRAMES constant are built in a single pass per day:
IntervalMinutes
1m1
3m3
5m5
15m15
30m30
1h60
2h120
4h240
6h360
8h480
1d1440

Algorithm

The script processes data day by day from the first to the last trade date:
  1. Load trades — queries trade-results for all records matching the given ISIN within the current day’s UTC boundary (00:00:00 to 23:59:59), sorted by time ascending.
  2. Build 1m buckets — aggregates all trades into a minuteMap keyed by the UTC minute timestamp produced by floorToMin(ts, 1).
  3. Walk every minute — iterates every millisecond-aligned minute from dayStartMs to dayStartMs + DAY_MS (1 440 steps). For each minute:
    • If a real trade bucket exists, uses its OHLCV and updates lastClose.
    • Otherwise emits a gap candle: open = high = low = close = lastClose, volume = 0.
  4. Accumulate higher timeframes — each 1m candle is simultaneously folded into all 11 timeframe accumulators using floorToMin(ts, minutes) to align bucket boundaries.
  5. Insert batch — flattens all accumulated candle documents for the day and calls CandleModel.insertMany with ordered: false, allowing partial inserts when some candles already exist.

OHLCV Aggregation Rules

FieldRule
openPrice of the first trade in the period
highMaximum trade price in the period
lowMinimum trade price in the period
closePrice of the last trade in the period
volumeSum of all trade quantities
Gap candles (minutes with no trades) carry open = high = low = close = lastClose and volume = 0. This ensures every timeframe has fully contiguous series with no missing bars, which is required by most charting libraries.

MongoDB

SettingValue
Databasebacktest
Source collectiontrade-results
Target collectioncandle-items
Unique index{ symbol, interval, timestamp } — re-runs skip existing candles
Insert modeinsertMany with ordered: false (partial inserts on duplicates)

Progress Output

The script prints a live-updating line for each day processed:
[day/total] date  trades:N  real_min:N  candles:N  inserted:N  skipped:N
For example:
[1/312] 2023-01-03  trades:27  real_min:19  candles:3014  inserted:3014  skipped:0
[2/312] 2023-01-04  trades:0   real_min:0   candles:3014  inserted:3014  skipped:0
[312/312] 2025-12-31  trades:41  real_min:33  candles:3014  inserted:3014  skipped:0

Done. Inserted: 941182, skipped (duplicates): 0

Example

npx tsx scripts/build-candles.ts HMKB UZ7011340005
Because candle inserts use ordered: false, you can safely re-run build-candles.ts after importing additional trades. Only new candles (those whose {symbol, interval, timestamp} key does not yet exist) will be inserted — all others are skipped without error.

Build docs developers (and LLMs) love