Derive: 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 5.1 s (median of 5 loads), 20 of 27 perpetual trading screens, possible rank 13 to 23; after an uncached reload 8.3 s. The largest bottleneck: Market-data requests to api.lyra.finance take 1.6–2.2 s, and its sockets 0.9–1.6 s to connect. First change to try: Give the public market-data responses a short cache lifetime and let the CDN use it, and measure where the 2 s goes (connection, server, queue) before moving servers.

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 5.1 s, range 4.7 s–5.8 s; loads: 5.8 s, 4.7 s, 5.1 s, 5.2 s, 4.8 s
Uncached reloadmedian 8.3 s, range 7.1 s–8.9 s; loads: 8.7 s, 7.3 s, 8.3 s, 8.9 s, 7.1 s
Standing20 of 27 by median; possible rank 13 to 23. 2.6 s behind the fastest, Thalex (2.4 s): 2.1 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

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 s5 s
Price1.7 s
Order form2.5 s +0.8 s
Recent trades3.2 s +0.7 s
Account panel3.2 s +0.0 s
Order book3.3 s +0.2 s
Chart4.2 s +0.9 s
Trade-ready5.1 s +0.9 s settling

Stack

Next.js on Vercel; TradingView chart library (library.js); WalletConnect / web3modal, Alchemy RPC; Intercom.

Delivery

HTML from Vercel marked private, no-store, so never cached; market data from api.lyra.finance, whose responses the CDN does not cache (cf-cache-status: DYNAMIC), although some declare a short lifetime; the chart library is served with max-age=0, while the app's own script chunks are cached for a year.

HostRoleCDN edge seenConnectResponse
app.derive.xyzpage—13 ms28 ms
api.lyra.financemarket dataCloudflare Amsterdam13 ms260 ms
api.derive.xyzmarket dataCloudflare Amsterdam14 ms256 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.02 sHTML arrives (Vercel).
0.18–0.88 sNext.js chunks (up to 401 KB unpacked each).
0.50 sPage header shows the market.
1.10–3.28 sREST calls to api.lyra.finance take 1.6–2.2 s each.
1.24 sThree separate sockets to api.lyra.finance open; their handshakes finish at 2.15, 2.51 and 2.87 s (0.9–1.6 s); first messages at 2.66–3.28 s.
1.32 sFirst paint.
2.94–4.62 sA 657 KB (unpacked) api.lyra.finance response takes 1.7 s.
3.02 sPrice and order book show.
3.69 sThe chart library (library.js, 2.5 MB unpacked, max-age=0) is requested.
5.21 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.2 sHTML arrives
1.2 sFirst market-data request
1.4 sPrice ready
1.9 sOrder form ready
2.2 sFirst frame sent on a live feed
2.2 sbalances ready
2.6 sFirst frame received on a live feed
3.0 sRecent trades ready
3.2 sOrder book ready
3.6 sChart ready
4.6 sTrade-ready

282 requests before trade-ready; 10 long main-thread tasks (over 50 ms) before trade-ready, longest 100 ms; 78 script requests before the first panel; 0 requests over HTTP/1.1.

Request waterfalldiagnostic capture 1 October, uncached

67 of the 262 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 requestwaiting for the serverdownloading

0 s0 s1 s1 s2 s2 s3 s3 s4 s4 s5 s5 s1app.derive.xyz17 kB715 ms2app.derive.xyz/d287739a-2…56 kB377 ms3app.derive.xyz/1b5a735e-d…91 kB430 ms4app.derive.xyz/1919-89483…68 kB436 ms5app.derive.xyz/5372-931fc…4 kB497 ms6app.derive.xyz/655-0435e7…91 kB480 ms7app.derive.xyz/2945-cb717…98 kB495 ms8app.derive.xyz/2656-28efb…12 kB499 ms9app.derive.xyz/4704-b4d46…9 kB488 ms10app.derive.xyz/6071-2085d…11 kB495 ms11app.derive.xyz/9281-4a07d…48 kB499 ms12app.derive.xyz/4557-9a455…9 kB497 ms13app.derive.xyz/7622-4b72f…72 kB499 ms14app.derive.xyz/3881-0e4e2…13 kB496 ms15app.derive.xyz/1109-3fc4b…23 kB479 ms16app.derive.xyz/1414-3b9d9…22 kB488 ms17app.derive.xyz/page-e29a6…52 kB497 ms18app.derive.xyz/7768.08ddc…109 kB84 ms19api.lyra.finance354 B874 msDV-120api.lyra.finance12 kB1664 ms21api.lyra.finance5 kB837 msDV-122api.lyra.finance2 kB1650 msDV-123api.lyra.finance2 kB1648 msDV-124api.lyra.finance4 kB2150 ms25app.derive.xyz8 kB659 msDV-126api.lyra.finance179 B1610 msDV-127api.lyra.finance259 B1698 ms28app.derive.xyz8 kB1043 ms29api.lyra.finance189 B1223 ms30api.lyra.finance162 B1213 ms31api.lyra.finance390 B1216 ms32api.lyra.finance135 B1207 ms33api.lyra.finance180 B980 ms34api.lyra.finance177 B741 ms35api.lyra.finance120 B738 ms36api.lyra.finance153 B1193 ms37api.lyra.finance145 B744 ms38api.lyra.finance120 B729 ms39app.derive.xyz8 kB661 ms40app.derive.xyz8 kB793 ms41app.derive.xyz8 kB666 ms42api.lyra.finance14 kB1131 msDV-143api.lyra.finance12 kB1677 ms44app.derive.xyz8 kB705 ms45app.derive.xyzopen46app.derive.xyz8 kB638 ms47app.derive.xyz/1795.95b1b…114 kB99 ms48app.derive.xyz8 kB839 ms49app.derive.xyz/2069.b02dd…66 kB57 ms50app.derive.xyz8 kB1497 ms51rpc.derive.xyz444 B560 ms52rpc.derive.xyz369 B554 ms53app.derive.xyz8 kB919 ms54api.lyra.finance172 B1175 ms55api.lyra.finance131 B1160 ms56app.derive.xyz8 kB887 ms57app.derive.xyzopen58app.derive.xyz/library.25…641 kB164 ms59app.derive.xyz8 kB803 ms60app.derive.xyz8 kB893 ms61app.derive.xyzopen62app.derive.xyz8 kB509 ms63app.derive.xyz8 kB488 ms64api.lyra.finance179 B609 ms65api.lyra.finance175 B612 ms66api.lyra.finance132 B609 ms67api.lyra.finance14 kB593 msprice 3.0 sorder book 3.0 schart 5.2 s
Download this figure as an image:

Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached

DV-1Market-data requests to api.lyra.finance take 1.6–2.2 s, and its sockets 0.9–1.6 s to connect.grade B
Evidence
1 October capture: six REST requests to api.lyra.finance start at 1.10–1.15 s and finish at 2.75–3.28 s; the slowest waits 1.96 s for its first byte. Its three sockets need 0.92, 1.28 and 1.63 s to connect. Price and book wait until 3.02 s. Three of the six carry short cache lifetimes (max-age 300, 180 and 10 s) and the others none; every one is marked cf-cache-status: DYNAMIC, so the CDN passes each request to the origin. The capture does not show where the origin is.
Gap observed
0.9 to 1.6 s for each of three socket connections; 1.6 to 2.2 s for each REST call
Holds back
Price (median 1.7 s in the timed loads), book (median 3.3 s in the timed loads).
Waterfall rows
row 20: 12 kB received, 135 kB unpacked, max-age=300; row 22: 2 kB received, 5 kB unpacked, max-age=10; row 23: 2 kB received, 13 kB unpacked, no cache header; row 24: 4 kB received, 43 kB unpacked, max-age=180; row 26: 179 B received, no cache header; row 27: 259 B received, 270 B unpacked, no cache header; row 43: 12 kB received, 657 kB unpacked, max-age=30
Fix
Give the public market-data responses a short cache lifetime and let the CDN use it, and measure where the 2 s goes (connection, server, queue) before moving servers.
Where
Cloudflare cache rules for api.lyra.finance public endpoints; api.lyra.finance hosting.
Verify
DevTools → Network → Headers on a public api.lyra.finance request: cf-cache-status HIT; Timing: waiting for server response under 200 ms.
DV-2The chart library is requested after price and book, and revalidated on every visit.grade B
Evidence
1 October capture: /tradingview/bundles/library.257d05210b16f5ddbfc2.js (2.5 MB unpacked) is requested at 3.69 s, after price and book at 3.02 s; chart at 5.21 s. The file name already carries a content hash, yet it is served with max-age=0, must-revalidate, so the browser asks the server about it on every visit; its stylesheet has the same header. The app's own script chunks are served with max-age=31536000, immutable. In the slowest warm campaign load (5.8 s) the chart canvas appears at 2.95 s, disappears from 3.16 to 3.77 s and comes back at 3.94 s before the chart passes at 4.37 s, which suggests the chart widget is built, torn down and built again.
Gap observed
0.67 s between price/book and the chart-library request
Holds back
Chart (median 4.2 s in the timed loads).
Fix
Serve /tradingview/bundles/ with cache-control: max-age=31536000, immutable, like the app's own chunks, add a preload link for library.js on the trading route, and check whether the chart widget is mounted twice.
Where
Vercel headers for /tradingview/bundles/; the chart loader and the HTML head.
Verify
DevTools → Network: library.js shows "(disk cache)" on reload and starts before 1.5 s.
DV-3Three sockets to the same API.grade B
Evidence
Three WebSocket connections to api.lyra.finance open at 1.24 s, each paying its own slow handshake.
Gap observed
0.9 to 1.6 s for each of three separate socket handshakes
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Multiplex all subscriptions over one socket.
Where
Feed client.
Verify
DevTools → Network → WS: one api.lyra.finance socket.
DV-4A 657 KB response overlaps the wait for the chart.grade C
Evidence
An api.lyra.finance response from 2.94 to 4.62 s (657 KB unpacked, 12 KB compressed, max-age=30) overlaps the wait for the chart, drawn at 5.21 s. By host and order in the timed loads it is probably /public/get_instruments; that match is not measured.
Gap observed
1.68 s for the 657 KB API response
Holds back
Probably the chart (median 4.2 s in the timed loads): the timing points to it, but the captures do not show that this panel waits for it.
Fix
Request only the instruments the first screen needs, or send the request after the chart has started loading.
Where
The /public/get_instruments call to api.lyra.finance (probable match).
Verify
DevTools → Network, filter "get_instruments": response size and start time.
DV-5The three private order lists are each requested twice, 15 ms apart.grade C
Evidence
All 20 older timed loads send /private/get_open_orders twice; in the loads checked in detail, /private/get_trigger_orders and /private/get_algo_orders are also each sent twice, about 15 ms apart (uncached: 2.590 and 2.603 s for open orders), each taking about 0.75 s. The request bodies are not recorded, so the two could differ in their parameters.
Measured
three order-list requests sent twice each
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Share one request per order list between the components that need it.
Where
The callers of get_open_orders, get_trigger_orders and get_algo_orders.
Verify
DevTools → Network, filter "private/get_": one request per address on a load.
Likely saving
Small: three fewer requests of about 0.75 s each
DV-6Thirteen tracking requests and two slow app requests run before the page is ready.grade C
Evidence
1 October timed loads: 13 requests to app.derive.xyz/api/ingest between 2.9 and 4.6 s (uncached) and between 2.5 and 4.3 s (warm), all before trade-ready. /api/intents takes 1.48 s to return 13 bytes and /api/rewards/fees 1.0 s for 83 bytes. Intercom's widget script loads at 1.80 s and opens its own socket at 3.34 s. Whether any panel waits for these is not visible from outside.
Measured
13 tracking requests before trade-ready; 1.48 s for a 13-byte response
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Batch the /api/ingest events and send them after trade-ready; load Intercom after trade-ready; check why /api/intents and /api/rewards/fees take over a second.
Where
The analytics client behind /api/ingest; the Intercom loader; the handlers for /api/intents and /api/rewards/fees.
Verify
DevTools → Network, filter "ingest" and "intercom": no requests before the chart is drawn.
Likely saving
Unknown
DV-7Trade-ready comes 0.8 s after the panels are in on a warm load and 1.9 to 2.6 s on an uncached one: the chart is still animating.grade B
Evidence
All 10 campaign loads: in the wait before trade-ready, counted from when the page's main parts are in place, the chart panel still has a running animation: 0.76 to 0.87 s on the 5 warm loads and 1.90 to 2.60 s on the 5 uncached ones (in the slowest, the main parts are in place at 6.30 s and trade-ready comes at 8.89 s). The chart itself passes its check a little later than that starting point, so the wait after the chart is shorter. Recorded warm load: chart at 3.57 s, trade-ready at 4.65 s, with the animation running in all 11 samples in between.
Gap observed
0.76 to 0.87 s before trade-ready in the five warm loads and 1.9 to 2.6 s in the five uncached loads, counted from when the page's main parts are in place; 1.07 s after the chart in the recorded load
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.8 s warm and ~2 s uncached

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.