- Evidence
- Marked no-store (Cloudflare DYNAMIC): 638 ms before the first byte on 1 October, 593 ms on 3 October; that time includes the connection and the network, not only the server. The document /exchange/perpetual is 1.1 MB unpacked (190 KB compressed) and takes until 1.39 s to arrive. Two loads do not prove every load is the same.
- Gap observed
- 0.64 s to the HTML first byte on 1 October; 0.59 s on 3 October
- Holds back
- Every panel: nothing is shown before this ends.
- Waterfall rows
- row 1: 190 kB received, 1.1 MB unpacked, no-store
- Fix
- Cache the trading page shell at the edge and fill user data on the client; look at what makes the HTML 1.1 MB and send less of it up front.
- Where
- Next.js rendering of /exchange/perpetual; Cloudflare cache rules.
- Verify
- DevTools → Network → document: waiting for server response under 200 ms and document size well under 1.1 MB.
GRVT: where its trading screen loses time, and what to fix
Measured from the Netherlands in a logged-in Chrome, October 2026.
Three measurements appear on this page, and their times differ. Rank and results come from the campaign's timed reloads. The diagnostic capture and the recorded reload are single loads made with recording switched on, which slows the page: read them for the order of events and the gaps, not for totals.
Resultscampaign: 5 warm and 5 uncached reloads
| Warm reload | median 4.8 s, range 4.3 s–5.1 s; loads: 5.1 s, 4.8 s, 4.3 s, 4.8 s, 5.0 s |
| Uncached reload | median 6.0 s, range 5.3 s–6.8 s; loads: 5.7 s, 6.0 s, 5.3 s, 6.7 s, 6.8 s |
| Standing | 16 of 27 by median; possible rank 12 to 21. 2.3 s behind the fastest, Thalex (2.4 s): 2.0 times as long. The possible rank is the range the measurements leave open: it counts only the exchanges that clearly beat this one and those it clearly beats. Exchanges whose ranges overlap cannot be told apart. |
| Loads ready | 5 of 5 warm, 5 of 5 uncached |
| Notes | recent trades not measured: inactive tab |
Where the time goescampaign medians, warm reload
Panels in the order they first showed real, usable data on a warm reload. The coloured part of each row is the time that panel added after the one before it. The last row is trade-ready: every panel is ready and the page has stopped moving.
Stack
Next.js, server-rendered; TradingView chart library (grvt.io/library.js); WalletConnect; PostHog, Plausible, Sentry and Intercom.
Delivery
HTML marked private, no-store and not cached at the CDN (Cloudflare DYNAMIC); app files immutable for a year; market data from market-data.grvt.io and trades.grvt.io.
| Host | Role | CDN edge seen | Connect | Response |
|---|---|---|---|---|
| grvt.io | page | Cloudflare Amsterdam | 14 ms | 285 ms |
| market-data.grvt.io | market data | Cloudflare Amsterdam | 13 ms | 254 ms |
Median of up to 10 attempts from the test machine. Response time includes the server's own time, so it is not a distance.
What happened, in orderdiagnostic capture 1 October, uncached
From one uncached diagnostic load recorded on 1 October 2026 (cache disabled, browser extensions active), so totals are slower than the campaign's timed loads; file names, sizes, headers, order of events and main-thread times are what the findings rely on. A main-thread time given for a script counts all the work that starts in that script, including code it calls in other files, so it shows where start-up work begins, not what one library costs. Endpoint paths were masked when the capture was saved.
| When | What |
|---|---|
| 0.64 s | HTML starts to arrive after 0.64 s; the document is 1.1 MB unpacked (190 KB compressed) and finishes at 1.39 s. The 3 October recorded load: 0.59 s. |
| 0.81–1.33 s | First wave of 36 scripts (the largest 1.4 MB unpacked). |
| 1.16 s | Page header shows the market; first paint at 1.18 s. |
| 2.09–2.18 s | Two feeds open (tradesiren, trades.grvt.io), and a rewards request (reward.grvt.io) that takes 1.5 s. |
| 2.40–2.72 s | Second wave of 30 scripts (up to 0.9 MB unpacked). |
| 3.39 s | The market-data feed opens, last of the three; handshake at 4.28 s, first message at 4.61 s; 1,522 messages before trade-ready. |
| 3.46 s | WalletConnect registry, 1.2 MB unpacked. |
| 4.89 s | Order book shows. |
| 6.13 s | Price shows, 1.2 s after the book. |
| 6.88–8.17 s | A market-data.grvt.io request takes 1.3 s. |
| 8.26 s | Chart drawn: trade-ready in this load. |
Recorded warm reload, 3 October
One warm reload recorded with Chrome's performance trace. Recording slows the page, so read the order and the gaps, not the total.
| When | What |
|---|---|
| 0.6 s | HTML arrives |
| 2.2 s | First market-data request |
| 2.5 s | First frame sent on a live feed |
| 2.8 s | First frame received on a live feed |
| 2.8 s | Order form ready |
| 4.8 s | Price ready |
| 4.8 s | Chart ready |
| 4.8 s | Order book ready |
| 4.9 s | Trade-ready |
364 requests before trade-ready; 8 long main-thread tasks (over 50 ms) before trade-ready, longest 128 ms; 151 script requests before the first panel; 0 requests over HTTP/1.1.
Request waterfalldiagnostic capture 1 October, uncached
71 of the 242 requests (the page itself, the longest and the largest) started before market data was ready in the 1 October uncached capture, in start order. The outlined part of a bar is waiting for the server; the solid part is downloading. Rows are numbered so a request can be pointed to. The numbers on the right are the size received and the total time. Vertical lines mark when a panel had data in this capture. Shaded rows are named in a finding below, tagged with its ID.
PageScriptData requestImagewaiting for the serverdownloading
Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached
- Evidence
- 1 October capture: the tradesiren feed is created at 2.09 s and the trades feed at 2.18 s; the market-data feed only at 3.39 s, with its handshake at 4.28 s and first message at 4.61 s. The timed loads show what runs in between: reward.grvt.io/api/v1/epochs (2.14–3.42 s) and market-data.grvt.io/lite/v1/trading_sessions (2.04–2.89 s). The order does not prove the market-data feed waits for them.
- Gap observed
- 1.3 s between creating the first trades socket and creating the market-data socket
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Create the market-data socket at the same time as tradesiren, and check whether it waits for /api/v1/epochs or trading_sessions.
- Where
- Feed client start-up order.
- Verify
- DevTools → Network → WS: the market-data.grvt.io socket is created before 2.2 s.
- Evidence
- 1 October capture: book at 4.89 s, price at 6.13 s, although the market-data feed delivered its first message at 4.61 s. In the campaign the price and the chart share the last place in all 5 warm loads (both 4.6 s median).
- Gap observed
- 1.24 s between the order book and the price
- Holds back
- Price (median 4.6 s in the timed loads).
- Fix
- Render the price from the market-data feed's first ticker message.
- Where
- Price component data source.
- Verify
- DevTools → Performance: price paint within 200 ms of the first market-data message.
- Evidence
- 1 October capture: a market-data.grvt.io request from 6.88 to 8.17 s (122 KB unpacked); the chart is drawn at 8.26 s, 0.09 s later. The timed loads name the likely request, market-data.grvt.io/lite/v1/kline: 6.31–7.32 s uncached and 4.02–4.99 s warm, about 1.0 s each. The name comes from different loads than the capture, matched by host and order.
- Gap observed
- 1.29 s for the market-data request before the chart
- Holds back
- Probably the chart (median 4.6 s in the timed loads): the timing points to it, but the captures do not show that this panel waits for it.
- Fix
- Send the /lite/v1/kline request at start-up, in parallel with the feeds, and check why it takes 1.0 s.
- Where
- Chart datafeed; the /lite/v1/kline endpoint.
- Verify
- DevTools → Network, filter "kline": the request starts before 2 s.
- Evidence
- 1 October timed loads: 17 requests to https://edge.grvt.io/query before trade-ready, from 2.1 to 4.3 s uncached and from 1.5 to 4.1 s warm; single calls take up to 1.0 s (1.62–2.64 s warm). The request bodies are not recorded, so the calls may ask for different things.
- Measured
- 17 requests to one query endpoint before trade-ready
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Combine the start-up queries into one or a few requests.
- Where
- The client for edge.grvt.io/query (batching).
- Verify
- DevTools → Network, filter "edge.grvt.io/query": 5 or fewer requests before the chart is drawn.
- Likely saving
- Unknown: it depends on which queries the panels wait for
- Evidence
- All 20 older timed loads request trades.grvt.io/lite/v1/positions twice (2.53 and 3.30 s in one) and fetch /exchange/strategies two to four times; /exchange/earn-on-equity is fetched twice in the loads checked in detail (2.57 and 3.08 s). The responses differ in size, so these may not be identical requests.
- Measured
- two page prefetches and one positions request, each sent twice
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Fetch other pages after trade-ready, and check whether the two positions requests can be one.
- Where
- The code that fetches /exchange/strategies and /exchange/earn-on-equity during start-up; the callers of /lite/v1/positions.
- Verify
- DevTools → Network, filter "earn-on-equity" and "positions": no page prefetch before the chart is drawn; one positions request.
- Likely saving
- Small
- Evidence
- 1 October capture: Plausible at 1.88 s, PostHog config at 3.21 s and its surveys script (105 KB unpacked) at 3.68 s, Intercom at 3.88 s, and 31 Privy scripts (3.4 MB unpacked) from 4.13 s; the chart is drawn at 8.26 s. In the 3 October recording Intercom used 66 ms of main thread.
- Measured
- about 3.5 MB (unpacked) of analytics, survey, support and wallet script before the chart
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Load surveys, Intercom and the Privy wallet scripts after trade-ready or on first use.
- Where
- The loaders for PostHog surveys, Intercom and Privy.
- Verify
- DevTools → Network, filter "posthog", "intercom", "privy": no requests before the chart is drawn.
- Likely saving
- Unknown: about 3.5 MB less script to process before the chart
- Evidence
- Campaign: in 4 of the 5 warm loads and 4 of the 5 uncached loads the chart panel still has a running animation in the wait before trade-ready, counted from when the page's main parts are in place (up to 0.74 s warm, 0.81 s uncached). One warm load has no wait at all, and in one uncached load the chart is missing candles for 0.34 s instead.
- Gap observed
- up to 0.74 s before trade-ready in four of five warm loads, counted from when the page's main parts are in place
- Holds back
- Trade-ready itself: the page counts as ready only when nothing in a panel is still animating or loading.
- Fix
- Record a reload with DevTools → Animations and find what still animates in the chart after the candles are drawn, for example a toolbar loading spinner or a fade-in; end it when the data is in.
- Where
- The chart panel and its loading states.
- Verify
- DevTools → Animations during a reload: nothing in the chart container animates after the candles appear.
- Likely saving
- Up to ~0.7 s
Grade B: the capture shows the problem directly. Grade C: the order of events in the capture points to it; the dependency is for the team to confirm. The order is our judgement of each finding's effect on trade-ready, largest first; it is not sorted by gap. A gap is the time between two observed events or the time one piece of work takes; gaps overlap and do not add up. Where a finding is about size or count and no time was measured, the row says what was measured instead. Holds back names the panel the finding delays. Nothing here is graded higher, because no fix was confirmed by changing a page and measuring again.
Savings are estimates from the timeline, not measured after a change. They overlap, so they do not add up; the summary gives the combined estimate.
How this was measured
Timings: 5 warm and 5 uncached reloads in a seeded random order across all exchanges, each judged against a checklist frozen for this page before the campaign; trade-ready is the first moment every checklist panel shows real data, nothing is still loading, and the layout holds still for 0.5 s. The findings on this page come from separate recorded loads, which are never part of the timings. Full method.