Skip to content

Hyperliquid has published half a day since 22 September and we summed it as a whole one - #2814

Merged
Flotapponnier merged 1 commit into
devfrom
fix/hl-feed-truncation-detection
Oct 5, 2026
Merged

Flotapponnier merged 1 commit into
devfrom
fix/hl-feed-truncation-detection

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Reported as "our stats don't match CoinMarketMan, sometimes +50% diff". They don't, and the cause is upstream.

What is wrong

Hyperliquid's per-builder fills export stops partway through each day. Trust Wallet (0x5af1…5f71), last fill in the published file:

day fills last fill
10 Sep 20,946 23:59:57 whole
21 Sep 33,681 23:59:54 whole
22 Sep 14,890 12:27:11 truncated
25 Sep 13,906 12:13:31 truncated
30 Sep 9,985 12:11:42 truncated
2 Oct 14,365 12:10:58 truncated

Every day since 22 September ends within three minutes of 12:11 UTC. Re-downloading gives an identical md5, so this is the published artefact, not a partial fetch.

Proof it is a subset, not a different universe

Checked against the live userFillsByTime API for one user on 2 October:

  • CSV: 953 fills · API: 1,299 carrying a builder fee
  • Exact match on (second, coin, px, sz): 953 matched, 0 CSV fills unmatched, 346 API fills with no CSV counterpart
  • Implied fee rate 0.0950% on both sides — Trust Wallet's own rate, so the extra fills are its own and not another builder's
  • By hour: 04–11 kept 100%, hour 12 kept 47%, hours 13–15 kept nothing

So our numbers were a faithful read of an upstream that had quietly halved. Fees and volume were understated together, the implied rates stayed correct, and nothing in the pipeline could tell. That also explains the shape of the report: users were only ~12% low (morning traders are all present) while volume was ~39% low, and the ratio varied per builder with their users' trading hours — which is why it flips the top two places rather than scaling everything.

What this adds

The cohort's furthest fill per day is collected inside the pass that already walks those days, so the check costs no extra parsing. One builder going quiet in the evening proves nothing; the whole cohort going quiet at the same minute is the feed being cut short.

  • hl_frontend_day_coverage_hours_v2 — hours the headline day reached
  • hl_frontend_day_truncated_v2 — 1 when under 23
  • hl_frontend_truncated_days_window_v2 — such days inside the 30-day window

Threshold 23 h leaves an hour of slack for a genuinely quiet evening across all 104 builders, which has never happened in the mirror, while catching the widest real truncation (12.45 h).

Figures stay published. The answer to a truncated upstream is to say so, not to drop the day and let the window silently shorten.

Not an option: the node

It is running on SGP, but it has not advanced since 3 October 09:10, looping on could not read abci state: early eof and abci_stream timed out. It holds 10 of 24 hours for that day and nothing for the 4th. Resyncing still needs the requester-pays S3 snapshot.

Follow-ups this does not do

  • Report it to Hyperliquid. Only they can fix the source, and it affects every consumer of that feed.
  • Backfill the missing halves via userFillsByTime, seeded from the CSV user list. Proven to work above; recovers the afternoon fills of users we already see, though not users who traded only after the cut. Sizeable: ~1,400 users for one builder for one day.
  • Surface the coverage on the page once the metric has data.

Verified

go build, go vet, go test ./..., gofmt clean; 3 new tests pinned to the measured days above; pnpm validate 235 specs.

… it as a whole one

Reported as "our stats don't match CoinMarketMan, sometimes +50% diff".
They do not, and the cause is upstream.

Hyperliquid's per-builder fills export stops partway through each day.
Trust Wallet (0x5af1…5f71), last fill in the published file:

  10 Sep  23:59:57   whole
  21 Sep  23:59:54   whole
  22 Sep  12:27:11   truncated
  25 Sep  12:13:31   truncated
  30 Sep  12:11:42   truncated
   2 Oct  12:10:58   truncated

Every day since 22 September ends within three minutes of 12:11 UTC. The
file is stable across re-downloads (identical md5), so this is the
published artefact and not a partial fetch.

Checked against the live info API for one user on 2 October: the CSV holds
953 fills, the API 1,299 carrying a builder fee, and every CSV fill is
present in the API with none the other way. The export is a strict subset.
Both sides imply the same 0.0950% fee rate, which is Trust Wallet's, so the
extra fills are its own and not another builder's. By hour: 04-11 kept
100%, hour 12 kept 47%, hours 13-15 kept nothing.

So our figures were a faithful read of an upstream that had quietly halved.
Fees and volume were understated together, the rates stayed right, and
nothing in the pipeline could tell.

This publishes the coverage instead of hiding it. The cohort's furthest
fill per day is already collected during the pass that builds the windows,
so the check costs no extra parsing: one builder going quiet in the evening
proves nothing, the whole cohort going quiet at the same minute is the feed
being cut short.

  hl_frontend_day_coverage_hours_v2      hours the headline day reached
  hl_frontend_day_truncated_v2           1 when it is under 23
  hl_frontend_truncated_days_window_v2   such days inside the 30-day window

Figures stay published. The answer to a truncated upstream is to say so,
not to drop the day and let the window silently shorten.

The node is not an alternative: it is running on SGP but has not advanced
since 3 October 09:10, looping on "could not read abci state: early eof",
with 10 of 24 hours for that day and nothing for the 4th.
@Flotapponnier
Flotapponnier merged commit a2fd50f into dev Oct 5, 2026
2 checks passed
@Flotapponnier
Flotapponnier deleted the fix/hl-feed-truncation-detection branch October 5, 2026 14:10
Flotapponnier added a commit that referenced this pull request Oct 7, 2026
)

* Hyperliquid has published half a day since 22 September and we summed it as a whole one (#2814)

Reported as "our stats don't match CoinMarketMan, sometimes +50% diff".
They do not, and the cause is upstream.

Hyperliquid's per-builder fills export stops partway through each day.
Trust Wallet (0x5af1…5f71), last fill in the published file:

  10 Sep  23:59:57   whole
  21 Sep  23:59:54   whole
  22 Sep  12:27:11   truncated
  25 Sep  12:13:31   truncated
  30 Sep  12:11:42   truncated
   2 Oct  12:10:58   truncated

Every day since 22 September ends within three minutes of 12:11 UTC. The
file is stable across re-downloads (identical md5), so this is the
published artefact and not a partial fetch.

Checked against the live info API for one user on 2 October: the CSV holds
953 fills, the API 1,299 carrying a builder fee, and every CSV fill is
present in the API with none the other way. The export is a strict subset.
Both sides imply the same 0.0950% fee rate, which is Trust Wallet's, so the
extra fills are its own and not another builder's. By hour: 04-11 kept
100%, hour 12 kept 47%, hours 13-15 kept nothing.

So our figures were a faithful read of an upstream that had quietly halved.
Fees and volume were understated together, the rates stayed right, and
nothing in the pipeline could tell.

This publishes the coverage instead of hiding it. The cohort's furthest
fill per day is already collected during the pass that builds the windows,
so the check costs no extra parsing: one builder going quiet in the evening
proves nothing, the whole cohort going quiet at the same minute is the feed
being cut short.

  hl_frontend_day_coverage_hours_v2      hours the headline day reached
  hl_frontend_day_truncated_v2           1 when it is under 23
  hl_frontend_truncated_days_window_v2   such days inside the 30-day window

Figures stay published. The answer to a truncated upstream is to say so,
not to drop the day and let the window silently shorten.

The node is not an alternative: it is running on SGP but has not advanced
since 3 October 09:10, looping on "could not read abci state: early eof",
with 10 of 24 hours for that day and nothing for the 4th.

* the biggest day on /products was measuring feed coverage, not activity (#2864)

Hyperliquid's public per-builder export cuts days off around 12:10 UTC, and
intermittently rather than from one clean break: 15, 16 and 18 September were
already short while 17, 19, 20 and 21 ran to 23:5x, and 3 October ran full in
the middle of an unbroken run of short days. Seventeen of the thirty days
ending 6 October were short. On the days it did carry in full, the hours
before 13:00 hold 43% of the notional and 44% of the fees.

Nothing on the page said so, and three things followed from that.

The biggest day was wrong rather than low. A maximum over days of unequal
length ranks coverage: fomo's peak published as 21 September at $72,233
because 21 September is one of the few whole days in the window, while 28
September carried more than that before noon alone and summed to $47,480.
CoinMarketMan, on a complete feed, puts the peak on 6 October. The scan now
skips days the feed cut short and the card says "Biggest complete day", since
a short day's total is a lower bound and not comparable to a whole one.

The window sums were right arithmetic on an incomplete feed and read as half
the answer. fomo's 30d volume is $1.489B against CoinMarketMan's $2.823B, and
day-by-day correction brings it to $2.409B, which accounts for the gap bar
15%. Those figures stay as measured rather than scaled to a guess: what
changes is that /hyperliquid and the #hl section now carry the coverage above
them. A null coverage reading discloses too, because "we did not measure it"
and "the feed is whole" are different claims.

The truncated-day count itself understated the problem. It only looked at days
whose files were still inside the mirror window and skipped the rest, so it
reported 12 of 30 where the archive held 17. Coverage is now recorded per day
in state and kept after the files go away, with a companion gauge for how many
days were actually measured so the note never invents a denominator.

Also the timeframe control the leaderboard was missing: the harness has
published 24h, 7d and 30d all along and the hub only ever read 30d. "24h" is
labelled "feed day" because it is the last complete UTC day, not a rolling
window.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* the coverage check was a max, so one late file hid a cut-off day (#2866)

Hyperliquid's export cuts a day at one instant for the whole cohort, so the
per-builder last fills cluster inside a few minutes. Measured across the
archive: on 6 October 2026 every qualifying builder's file ends between 11:58
and 12:09, on 23 September between 12:01 and 12:13, on 21 September between
23:31 and 23:59.

The day's coverage was taken as the maximum of those. The reasoning was sound
as far as it went, one builder going quiet in the evening proves nothing about
the feed, but a maximum is maximally sensitive to an outlier in the other
direction: a single file reaching past the cutoff lifts the day over the 23h
threshold and the day reads whole. 23 September recorded 19.9 hours and 8
September recorded 24 exactly that way, and the window count came out at 12
short days where the median finds 18.

So the reduction is now the median over builders with at least 100 fills that
day, which leaves 29 to 53 samples a day in the archive. It is robust in both
directions: a few quiet builders cannot drag it down, and one straggler cannot
lift it. Under 8 samples it falls back to the max, which can only call a short
day whole and never the reverse.

The disclosure note reads the gauge, so it corrects itself. The figures it
sits above do not change: they were always the sum of what the feed published.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant