Skip to main content
Hyperliquid doesn’t hand you a PnL curve; it hands you fills. Every fill that reduces a position carries the profit or loss that closing it realized, so a trader’s whole performance history is derivable from userFillsByTime plus userFunding. This page builds the derivation: realized PnL over time, fees, funding, win rate, volume, and a per-coin breakdown.
userFills, userFillsByTime, userFunding, and candleSnapshot are all served by GoldRush. The waterfall routes to it automatically, so you only need ?provider=goldrush if you’re pinning for consistency or debugging.

1. Fetch the fill history

userFillsByTime takes a millisecond-epoch window. endTime is optional and defaults to now.
Each fill carries everything the derivation needs:
The two load-bearing fields:
  • closedPnl is the realized profit or loss from the portion of the position that this fill closed. It’s "0.0" on fills that only open or increase a position, because nothing was realized. Only closing fills carry a non-zero value.
  • fee is what you paid to trade, always positive, and always deducted. A negative fee (a maker rebate) appears as a negative value, which the arithmetic below handles without a special case.

Handling the 2000-fill cap

Hyperliquid caps a fills response at 2000 rows, keeping the most recent. For an active trader over a long window, that’s a truncated view, and nothing in the response tells you it happened. You have to infer it:
To reconstruct a full history, page backwards: fetch a window, and if you hit the cap, fetch again ending just before the oldest fill you received.
Don’t de-duplicate on tid by itself. It’s a trade id, and fills that aren’t trades don’t have one. A spot dust conversion is emitted with tid: 0 and hash: "0x000…0". Key a map on tid and an account with 42 dust conversions collapses to a single row, quietly undercounting volume, fill count, and per-coin activity.
Each page is a billed request, so an unbounded backfill over a very active address can be expensive. Bound the loop: cap the number of pages, or show the truncated window with a “showing the most recent 2000 fills” note and let the user opt in to the full history.

2. Derive the numbers

With the fills in hand, everything else is arithmetic. Sort oldest-first, walk once, and accumulate.
Three definitions in that function are worth stating explicitly, because they’re the ones people get wrong: Realized PnL is net of fees. Σ(closedPnl − fee). Hyperliquid’s closedPnl is gross, so it doesn’t deduct what you paid to trade. Report gross PnL as “realized PnL” and your numbers will consistently overstate performance, by more the more actively the address trades. Volume is notional, Σ|px × sz|. The absolute value matters: sz can be negative depending on direction, and a trader’s volume is a magnitude, not a signed quantity. Win rate is wins ÷ closing fills, not wins ÷ all fills. Every position takes at least one opening fill that realizes nothing, so dividing by the total fill count silently halves the win rate of anyone who opens and closes in single fills, and distorts it unpredictably for everyone else. Get the denominator from dir, not from closedPnl. The tempting shortcut (“a closing fill is one where closedPnl ≠ 0”) is wrong in both directions. A position closed exactly at its entry price realizes nothing and would be dropped from the denominator, inflating the win rate of anyone who scratches trades. And a spot dust conversion also carries closedPnl: 0 despite touching no perp position at all. dir states what the fill actually did, so it answers the question directly.
This derivation covers realized PnL only: settled facts from closed positions. It deliberately excludes unrealized PnL on positions still open. If your dashboard shows a combined figure, compute the unrealized half separately from live positions and mids (see the wallet tracker), and label the two clearly. Mixing them into one number produces a figure that jumps around with the market and reconciles against nothing.

3. Add funding

Perp traders pay or receive funding continuously, and over a long holding period it can dominate trading PnL. It’s a separate ledger from fills:
delta.usdc is signed: negative is funding you paid, positive is funding you received. So the accumulation is a plain sum, no sign flip:
Keep funding on its own line rather than folding it into realized PnL. They answer different questions (one is trading skill, the other is carry cost) and a trader whose strategy is profitable but whose funding bill exceeds it needs to see both.

4. Add price context

A PnL curve is easier to read against the market it was earned in. candleSnapshot supplies the backdrop. This is the one type in the series with a nested request body. Its parameters go inside a req object rather than at the top level:
Candles arrive oldest-first. The keys are terse: t open time, T close time, s coin, i interval, o/h/l/c the OHLC prices, v volume, n trade count. Match the interval to the window so the chart stays readable rather than returning thousands of points:

5. Assemble the surface

A reasonable set of headline tiles from that: realized PnL (net of fees), funding, fees paid, volume, win rate, and fill count, with the cumulative series as the chart and byCoin as a sortable table underneath.
Surface truncated in the UI. A user comparing a 90-day figure against their own records will otherwise conclude your numbers are wrong, when in fact the window was capped. One line, “showing the most recent 2000 fills”, turns a bug report into understood behavior.

Next

Market explorer moves from one address to the whole venue.