GRVT: where its trading screen loses time, and what to fix

Measured from the Netherlands in a logged-in Chrome, October 2026.

In short. On a warm reload the screen is trade-ready after 4.8 s (median of 5 loads), 16 of 27 perpetual trading screens, possible rank 12 to 21; after an uncached reload 6.0 s. The largest bottleneck: The HTML takes 0.6 s to its first byte and is 1.1 MB. First change to try: 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.

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 reloadmedian 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 reloadmedian 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
Standing16 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 ready5 of 5 warm, 5 of 5 uncached
Notesrecent 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.

0 s1 s2 s3 s4 s
Order form3.0 s
Order book3.6 s +0.6 s
Price4.6 s +1.0 s
Chart4.6 s +0.0 s
Trade-ready4.8 s +0.2 s settling

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.

HostRoleCDN edge seenConnectResponse
grvt.iopageCloudflare Amsterdam14 ms285 ms
market-data.grvt.iomarket dataCloudflare Amsterdam13 ms254 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.

WhenWhat
0.64 sHTML 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 sFirst wave of 36 scripts (the largest 1.4 MB unpacked).
1.16 sPage header shows the market; first paint at 1.18 s.
2.09–2.18 sTwo feeds open (tradesiren, trades.grvt.io), and a rewards request (reward.grvt.io) that takes 1.5 s.
2.40–2.72 sSecond wave of 30 scripts (up to 0.9 MB unpacked).
3.39 sThe 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 sWalletConnect registry, 1.2 MB unpacked.
4.89 sOrder book shows.
6.13 sPrice shows, 1.2 s after the book.
6.88–8.17 sA market-data.grvt.io request takes 1.3 s.
8.26 sChart 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.

WhenWhat
0.6 sHTML arrives
2.2 sFirst market-data request
2.5 sFirst frame sent on a live feed
2.8 sFirst frame received on a live feed
2.8 sOrder form ready
4.8 sPrice ready
4.8 sChart ready
4.8 sOrder book ready
4.9 sTrade-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

0 s0 s1 s1 s2 s2 s3 s3 s4 s4 s5 s5 s6 s6 s7 s7 s8 s8 sGR-11grvt.io190 kB589 ms2grvt.io/0wksd7w43h336.js172 kB450 ms3grvt.io/28pn8-yq9bar8.js122 kB387 ms4grvt.io/0ti4nzz9c4i_2.js333 kB515 ms5grvt.io/client.base.min.js12 kB962 ms6market-data.grvt.io1 kB661 ms7edge.grvt.io635 B1037 ms8edge.grvt.io228 B1032 ms9edge.grvt.io139 B1030 ms10edge.grvt.io175 B1029 ms11edge.grvt.io582 B1028 ms12edge.grvt.io2 kB1079 ms13edge.grvt.io540 B1076 ms14edge.grvt.io182 B1072 ms15edge.grvt.io144 B1072 ms16trades.grvt.io965 B630 ms17reward.grvt.io1 kB1485 ms18trades.grvt.io281 B632 ms19trades.grvt.io232 B636 ms20trades.grvt.io970 B640 ms21trades.grvt.io119 B648 ms22trades.grvt.io182 B662 ms23trades.grvt.io856 B843 ms24edge.grvt.io706 B996 ms25edge.grvt.io917 B993 ms26market-data.grvt.io298 B912 ms27grvt.io747 B872 ms28grvt.io3 kB822 ms29edge.grvt.io117 B808 ms30grvt.io/1xzm580wmggid.js153 kB291 ms31grvt.io/3nm1zph9dex3o.js170 kB290 ms32grvt.io/2kw2-lcij6raw.js235 kB286 ms33grvt.io/1lxa5upwe3r5_.js114 kB283 ms34grvt.io/03mziicv7jx69.js210 kB312 ms35market-data.grvt.io200 B719 ms36edge.grvt.io295 B757 ms37edge.grvt.io76 B729 ms38trades.grvt.io404 B673 ms39grvt.io356 B655 ms40grvt.io230 B657 ms41grvt.io233 B657 ms42trades.grvt.io748 B656 ms43trades.grvt.io149 B656 ms44grvt.io607 ms45grvt.io607 ms46grvt.io3 kB943 ms47edge.grvt.io117 B772 ms48edge.grvt.io186 B823 ms49edge.grvt.io179 B763 ms50edge.grvt.io1 kB796 ms51explorer-api.walletconnec…173 kB302 ms52edge-cdn.grvt.io2 kB779 ms53edge-cdn.grvt.io2 kB810 ms54edge-cdn.grvt.io2 kB784 ms55grvt.io2 kB812 ms56tradesiren.grvt.io86 B1110 ms57edge.grvt.io5 kB980 ms58grvt.io236 B614 ms59grvt.io3 kB855 ms60grvt.io3 kB854 ms61grvt.io762 ms62grvt.io634 ms63auth.privy.io2 kB106 ms64grvt.io3 kB660 ms65cca-lite.coinbase.com634 ms66cca-lite.coinbase.com630 ms67edge.grvt.io613 B1673 ms68trades.grvt.io210 B1138 ms69trades.grvt.io581 B1126 ms70trades.grvt.io128 B1126 ms71market-data.grvt.io30 kB1294 msprice 6.1 sorder book 4.9 schart 8.3 s
Download this figure as an image:

Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached

GR-1The HTML takes 0.6 s to its first byte and is 1.1 MB.grade B
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.
GR-2The market-data feed opens last, at 3.39 s.grade C
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.
GR-3The price shows 1.2 s after the order book.grade C
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.
GR-4The chart is drawn right after a 1.3 s candle request that starts late.grade C
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.
GR-5edge.grvt.io/query is called 17 times before the page is ready.grade C
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
GR-6Other pages are fetched at start-up, and positions are read twice.grade B
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
GR-7Analytics, survey, support and wallet scripts load before the chart.grade B
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
GR-8In most loads trade-ready comes up to 0.8 s after the panels are in: the chart is still animating.grade B
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.