DOCS/CONCEPTS

Data quality

Every venue is captured twice, from two regions, and merged by the venue's own sequence numbers. Anything still missing after the merge is written to a table you can query. Corrections are published as new revisions, and old files are never edited.

Dual-region capture

Each venue stream is captured independently in Tokyo (ap-northeast-1, primary) and Ireland (eu-west-1, secondary). From Phase 2, N. Virginia (us-east-1) is added as a third region. The two capture sessions share nothing: separate hosts, separate AWS accounts, separate network paths and separate connections to the venue. A venue disconnect, a regional network event or a bad deploy on one side leaves the other side recording.

01CaptureEach region writes every venue message, byte for byte, to the raw tier with its own ts_capture. Both copies are kept.
02Check sequenceEach region checks continuity of the venue's sequence numbers on every message and marks breaks.
03MergeThe merger orders both streams by seq and keeps one row per message. Its ts_capture is the earliest receive time across the regions, and region names the region that received it first. Messages only the secondary saw are marked region = euw1.
04Record what is leftSequence ranges missing from both regions become rows in gaps. The book is resynced from a fresh venue snapshot.
05Normalize and publishThe merged stream is normalized to the schema. Daily files are finalized and published by 02:00 UTC the next day.

Live and historical merges

The live stream cannot wait for Ireland on every message without adding about 105 ms of delay for a Tokyo-hosted venue. It forwards the first copy of each message to reach it, which for a Tokyo-hosted venue is almost always the primary. When the primary detects a sequence break, the stream holds that symbol for up to 750 ms waiting for the secondary to supply the missing messages, then either emits them (marked region = euw1) or emits a gap notice and resyncs.

The historical merge runs again after the UTC day closes, with no time limit, over both raw copies. It can fill messages that reached Ireland after the live holdback expired. Historical files are therefore at least as complete as the live stream was, and the gaps table lists only what the final merge could not fill.

Sequence checks

A gap you cannot see is worse than a gap you can. We check continuity with the strongest signal each venue provides:

VenueSequence fieldContinuity rule
HyperliquidBlock height, event indexEvery block height present, consecutive. seq encodes height and position in block.
BinanceU, u, puSpot: first U = previous u + 1. Futures: pu = previous u.
Bybitu, sequ = previous u + 1; u = 1 marks a venue-side reset.
OKXseqId, prevSeqIdprevSeqId = previous seqId, plus the book checksum.
Coinbasesequence+1 per product.
Deribitchange_id, prev_change_idprev_change_id = previous change_id.
KrakenBook checksumCRC32 checksum on every book update.

For trade streams without a sequence, we compare trade ids where the venue assigns them contiguously, and cross-check trade counts against the venue's REST history after the day closes. Every venue other than Hyperliquid is planned, subject to a data agreement with the exchange; the rules above for those venues follow their public API documentation and will be confirmed with each venue before capture starts.

The gaps table

Every interval that survives the merge without data gets a row in gaps. Gaps are recorded and never filled: we do not interpolate, forward-fill or synthesize rows, and order books are never backfilled from other sources. Intervals where data is present but a quality bound was missed, such as a capture clock outside its error budget, are not gaps; they are published with the daily quality metrics below.

ColumnTypeDescription
gap_idstringStable id.
venue, dataset, symbolenum, enum, stringWhat was affected. symbol is null when a whole venue stream was down.
seq_start, seq_endint64Missing sequence range, where the stream is sequenced.
ts_start, ts_endint64Last good and first good ts_capture around the gap.
reasonenumseq_break, checksum_mismatch, venue_outage or disconnect (table below).
regions_missinglist<string>Regions that missed it. A row exists only when every capturing region did.
resyncboolTrue when the book was rebuilt from a fresh venue snapshot after the gap.
detected_byenumHow the gap was found: seq, checksum, region_compare, snapshot_compare or trade_reconcile.
detected_atint64When our pipeline recorded the gap.
revisionint32Revision in which the row first appeared.
notestringPlain-text detail, such as the venue's maintenance notice id.
reasonMeaning
seq_breakThe venue's sequence skipped and neither region received the missing messages.
checksum_mismatchA venue checksum failed at capture, so the book state since the last good check cannot be trusted.
venue_outageThe venue stopped publishing, including announced maintenance (the notice reference is in note).
disconnectOur connections in every capturing region dropped at the same time while the venue may still have been publishing.

All columns are on Schema reference. Every Parquet file also states how many gap rows overlap it in its fdc.gap_count metadata, so a pipeline can check completeness without a query.

import pandas as pd
from fdc import Client

fdc = Client()
gaps = pd.DataFrame(fdc.gaps(venue="hyperliquid", symbol="BTC-PERP",
                             start="2026-06-01", end="2026-07-01"))

# drop trades that fall inside any gap before computing a statistic
trades = fdc.trades(venue="hyperliquid", symbol="BTC-PERP",
                    start="2026-06-01", end="2026-07-01").to_pandas()
for g in gaps.itertuples():
    trades = trades[~trades.ts_capture.between(g.ts_start, g.ts_end)]
ONE ROW
{"gap_id":"gap_hl_20260611_0007","venue":"hyperliquid","dataset":"book_l3",
 "symbol":null,"reason":"venue_outage",
 "ts_start":1781179412804113201,"ts_end":1781179431266048873,
 "seq_start":1020315471708160,"seq_end":1020315634237439,
 "regions_missing":["apne1","euw1"],"resync":true,"detected_by":"seq","detected_at":1781179413560220514,
 "revision":1,"note":"API nodes stopped publishing; both regions disconnected within 40 ms."}

The gaps table is available on every plan, including test keys.

Venue maintenance

Venues announce maintenance windows through their own channels. We track announcements from each venue's status page and API channels, and treat the window like any other interruption: data before and after is kept, the interval itself is recorded.

  • Feed down, trading down. One gaps row per affected dataset with reason = venue_outage and the venue's notice reference in note. instruments gets a halted row for the window.
  • Feed up, trading halted. No gap, because the feed is complete. The instruments status change to halted or reduce_only tells you the book was not live.
  • After the window. The book restarts from a venue snapshot, and the gap row has resync = true. Venues sometimes cancel resting orders during maintenance; the first snapshot after the window shows the book they restored, and the difference is not reported as cancel events.
  • Funding. If a venue skips or delays a funding settlement during maintenance, the settled row appears when the venue publishes it, with ts_funding as the venue states. A skipped settlement has no row.

Revisions

Published data is never modified in place. When we correct something, we publish a new revision of each affected file and record it in the changelog. Code that read revision 1 last month can read revision 1 again today and get the same bytes.

What causes a revision

  • A normalization bug: a field mapped incorrectly for a venue or a period.
  • Recovered data: messages found in the raw tier after the daily merge, for example from a delayed regional upload.
  • Venue corrections: trades the venue later busts or amends, and reference data the venue republishes.

Schema changes are a separate mechanism with their own rules; see the versioning section of Schema reference.

How a revision is published

  • The file at the canonical S3 path is replaced by the new revision, with fdc.revision incremented in its metadata.
  • Earlier revisions stay readable as S3 object versions of the same key for 24 months, and through the revision parameter on REST and in the SDK.
  • The changelog entry lists the dataset, venues, symbols, date range, new revision number, cause, and counts of rows added, removed and changed.
  • Account contacts get an email on the day of publication. The gaps table is revised the same way: a gap filled by recovered data is removed in the new revision and remains readable in the earlier one.
CHANGELOG ENTRY FORMAT
DATA REVISION  book_l2 · hyperliquid · all symbols
Range          2026-02-03 to 2026-02-05 (UTC dates of ts_capture)
Revision       1 -> 2
Cause          Hourly snapshots omitted levels with size below 1e-8.
Rows           +18,442 added · 0 removed · 0 changed
Action         Re-download if you replay from hourly snapshots in this range.

To pin a revision in a pipeline that must be reproducible, pass it explicitly:

PYTHON
fdc.download(dataset="book_l2", venue="hyperliquid", symbol="BTC-PERP",
             start="2026-02-03", end="2026-02-06", revision=1, path="./pinned")

Quality metrics

We compute the following per venue, dataset and UTC day, and publish them on Status. The same numbers drive our internal alerts.

MetricDefinitionTarget
Sequence completenessShare of expected sequence numbers present after the final merge.≥ 99.999%
Gap timeSeconds covered by gaps rows.≤ 1 s/day outside venue events
Dual-capture overlapShare of messages received by both regions. Low overlap means we were running on one leg.≥ 99.9%
Secondary fill rateShare of rows supplied by the secondary region.reported
Checksum pass rateShare of venue-checksummed messages that passed validation at capture.100%
Clock error, p99Capture host clock error bound. Intervals above budget are listed with start and end times.< 50 µs
Capture latencyts_capture − ts_exchange, primary region, p50 and p99.reported
Publication timeWhen the day's files were finalized.by 02:00 UTC
DAILY QUALITY · HYPERLIQUID · BTC-PERPSYNTHETIC
Metrictradesbook_l2book_l4
Sequence completeness100.000%100.000%99.9996%
Gap time0 s0 s0.4 s
Dual-capture overlap99.982%99.979%99.977%
Secondary fill rate0.0031%0.0044%0.0047%
Clock error, p9911 µs11 µs11 µs
Capture latency p50 / p9918.4 / 77.8 ms17.9 / 76.1 ms18.2 / 77.0 ms
ONE UTC DAY · FINAL MERGEILLUSTRATIVE VALUES

If a metric misses its target, the affected day is annotated on Status and in the file metadata. To report something that looks wrong in the data, write to daniel@financedatacloud.com with the venue, symbol and a seq or time range; confirmed issues are fixed through the revision process above.