Skip to content

Research notes

Crypto candle resampling: aggregate OHLCV with gap checks

A five-minute candle can have plausible prices and still be incomplete. Check the field arithmetic, exact source slots and availability before using it.

An ivory accordion ribbon passes through a ceramic frame into broader folds, with a lime seam on one narrow fold.
Illustration for research notes; examples in this article are educational.

To resample crypto OHLCV candles, group compatible lower-timeframe bars into explicitly aligned windows, then take the first open, maximum high, minimum low, last close and summed volume. Validate the expected source intervals before accepting each result. A bucket can contain plausible prices while missing a minute, starting halfway through the download, or still developing. Keep those states separate from a complete, finalized candle.

The arithmetic is short. The harder part is proving that the group contains the observations its label promises. This walkthrough builds five-minute candles from one-minute candles and carries that proof alongside the numbers.

Define the aggregation contract

Start with one venue, instrument, market type and price basis. Trade-price spot candles and perpetual mark-price candles describe different inputs. Group each source separately; combining their rows first creates a candle without a clear market identity.

The source interval must fit inside the target boundaries. Aligned one-minute candles can construct five-minute candles. Two-minute candles beginning at 09:00 cannot exactly construct a 09:00–09:05 window, because the 09:04–09:06 source candle crosses its endpoint. Recover finer data rather than allocating half that candle's range or volume.

Write down the following contract before running a resampler:

Contract fieldThis article's exampleWhy it matters
Source timestampOne-minute opening instant in UTCDetermines which interval a row represents
Target boundariesEvery five minutes from UTC midnightMakes repeated downloads group identically
MembershipStart included, end excludedAssigns a boundary observation exactly once
Output labelTarget opening instantSeparates the label from the availability time
Volume unitBase-asset units for one fictional pairMakes addition meaningful
EligibilityExact source coverage, finalized inputs, elapsed targetPrevents incomplete results becoming signals

For the window beginning at 09:00, membership is [09:00, 09:05). Its five source opens are 09:00, 09:01, 09:02, 09:03 and 09:04. The source candle opening at 09:05 belongs to the next window.

In pandas, closed controls which edge belongs to a bin, while label controls the timestamp attached to the result. origin and offset control alignment for fixed frequencies. Setting a right-edge label alone does not move a boundary observation into a different group. pandas resampling parameters

Changing a chart clock raises a different question. The candle time-zone guide explains when displayed labels change and when the underlying aggregation window changes. Here, every timestamp and boundary stays in UTC.

Work through complete, gapped and developing buckets

The following prices and volumes are hypothetical. Assume a collector checks its dataset at 09:11:30 UTC on one fictional trading day. Every supplied row below is finalized and has already arrived. The 09:07 row is missing for an unknown reason; the current 09:11 minute is excluded because it is unfinished.

Minute opening UTCOpenHighLowCloseBase volume
09:00100102991012
09:011011041001033
09:021031061021051
09:0310510598994
09:0499104991042
09:051041051021032
09:061031071031061
09:081061081041053
09:091051061011022
09:101021041011031

The first bucket opens at 100, reaches a high of 106 and a low of 98, then closes at 104. Its volume is 2 + 3 + 1 + 4 + 2 = 12 base units. All five expected source intervals are present, finalized and available after the target has ended.

Applying the same arithmetic to the other rows gives these observed summaries:

Target UTC windowObserved OHLCVFinalized source slotsState at 09:11:30
09:00–09:05100 / 106 / 98 / 104 / 125 of 5Complete and eligible
09:05–09:10104 / 108 / 101 / 102 / 84 of 5Elapsed, unknown gap; withhold
09:10–09:15102 / 104 / 101 / 103 / 11 of 5Developing; withhold

The second row is a summary of observed inputs, not a verified five-minute candle. The missing minute might change its range and volume. Its known final close does not repair the missing price path. The third row is developing normally: several required minutes have not finished yet. Waiting may complete the third bucket; waiting alone cannot recover the second bucket's historical gap.

If the download had started at 09:02, the first target would also be incomplete, containing only three of its five required minutes. Preserve the 09:00 boundary and mark partial coverage. Moving the origin to the first downloaded row would quietly change the requested series.

Aggregate each OHLCV field explicitly

Do not average the five closes to obtain the new close. In the first bucket, their arithmetic mean is (101 + 103 + 105 + 99 + 104) / 5 = 102.4; the correct final close remains 104.

Likewise, applying .ohlc() to only the close column returns the opening, highest, lowest and closing values among those closes. In this example it produces 101 / 105 / 99 / 104, losing the true opening price and both extremes. pandas documents .ohlc() as an aggregation of the supplied values, excluding missing values. pandas OHLC operation

For an already normalized, sorted table of finalized one-minute candles, use one source identity, a UTC DatetimeIndex of candle opens, unique aligned timestamps and finite numeric OHLCV. The aggregation stage can be written as:

r = df.resample(
    "5min",
    origin="epoch",
    closed="left",
    label="left",
)
out = r.agg({
    "open": "first",
    "high": "max",
    "low": "min",
    "close": "last",
})
v = r["volume"]
out["volume"] = v.sum(
    min_count=5
)

Here, five valid volume inputs are required; pandas returns an unavailable sum when the group has fewer than min_count valid values. This protects the example's volume column from a four-minute subtotal masquerading as a complete total. It does not validate timestamp alignment, eliminate duplicates or prove finality. pandas resampled-sum requirements

The code still calculates observed prices for incomplete groups. Attach the state ledger and mask or exclude their OHLCV from consumers requiring completed candles. Use a different expected count when the source or target interval changes.

Prove coverage using identities and expected slots

A row count of five is insufficient. Four genuine minute records plus a duplicate also make five rows. Compare the exact set of source opening instants with the expected grid and require one canonical finalized record per slot. Reject off-grid timestamps rather than rounding them into place.

For example, Binance Spot identifies REST klines by their opening time within the requested symbol and interval. Preserve those request dimensions in your local identity key. Binance Spot kline identity

Live updates introduce another duplication risk. Binance Spot's kline stream reports updates to the current candle, with x indicating whether it is closed. These are updates to one candle's state. Summing the volume field across every received snapshot would count the evolving candle repeatedly. Maintain one record per source candle and apply a documented update policy before aggregating distinct intervals. Binance Spot kline stream fields

An unknown absent interval remains unknown. If the source deliberately omits no-trade intervals, establish that convention and collection completeness before defining synthetic rows. The missing-candle and zero-volume worksheet handles that classification. A synthetic row needs a construction flag even when it makes the calendar regular.

Check price consistency as well: each low must be no greater than either open or close, each high no less, and volume nonnegative. Reject missing or nonfinite fields before first/last/max/min operations can skip them and conceal a damaged constituent.

Preserve volume units and decision-time availability

Sum compatible base volume separately from compatible quote volume. Binance Spot's kline payload distinguishes these fields; neither should be added to the other. Binance Spot volume field definitions

The base, quote and contract volume guide explains the unit checks needed before aggregating across instruments or feeds.

Do not multiply the new close by total base volume and call it measured quote turnover. Transactions occurred at different prices. Similarly, averaging minute VWAPs equally discards their volume weights. These additional fields require their own sufficient inputs and aggregation rules.

Other market observations can require a different operation entirely. A funding rate has settlement timing and a rate basis; the funding-rate aggregation guide explains why carrying this OHLCV recipe over to funding history can change the quantity being measured.

Finally, an output labeled 09:00 is not known at 09:00. Its five-minute close depends on the interval ending at 09:05 and the subsequent arrival of the necessary finalized inputs. Retain the latest required arrival time alongside the target end. The multi-timeframe timing guide shows how that availability condition controls which candle a decision may use.

Keep a validation receipt for each output window

Before using a reconstructed candle, save its source identity, target endpoints, expected source slots, observed finalized slots, missing slots, synthetic flags and availability time. Record the aggregation-policy version so a future backfill or correction can be compared with the original result.

Validate one boundary case, one missing interval and one developing bucket whenever changing the input adapter or interval rule. Reversing source-row order must not change a result after chronological sorting. Replaying the same finalized record must not increase volume. Removing 09:07 must withhold the second example bucket, while the passage of 09:15 alone must not mark the third complete without its required inputs.

Use the complete first bucket as a small reconciliation fixture: 100 / 106 / 98 / 104 / 12, with exactly five distinct finalized minute slots. Keep the numerical result and its eligibility receipt together; a candle is usable only when both agree.

Keep the context with the example.

Each worked example is illustrative, with its sources and assumptions in the article. Read our editorial standards for authorship, verification and corrections.