ApeX: 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), 18 of 27 perpetual trading screens, possible rank 13 to 20; after an uncached reload 5.1 s. The largest bottleneck: A 9.3 MB main script. First change to try: Split index-BYeI0AHq.js by route and move wallet, chain and other-page code out of the trading page's first load.

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.6 s–4.9 s; loads: 4.9 s, 4.6 s, 4.8 s, 4.8 s, 4.9 s
Uncached reloadmedian 5.1 s, range 4.7 s–5.2 s; loads: 5.1 s, 5.2 s, 5.1 s, 4.7 s, 5.0 s
Standing18 of 27 by median; possible rank 13 to 20. 2.4 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

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
Price3.5 s
Order book3.5 s +0.0 s
Order form3.5 s +0.0 s
Recent trades3.6 s +0.1 s
Account panel3.6 s +0.0 s
Chart4.2 s +0.5 s
Trade-ready4.8 s +0.7 s settling

Stack

React, built with Vite; TradingView chart library (library.js); two Ethereum libraries (viem and ethers) plus the MetaMask SDK and WalletConnect; Google reCAPTCHA; AppsFlyer.

Delivery

omni.apex.exchange served by nginx over HTTP/2; HTML no-cache; app files cached 30 days; 45 static files without a cache-control header; three fonts shipped as uncompressed TTF.

HostRoleCDN edge seenConnectResponse
omni.apex.exchangepage—12 ms174 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.18 sHTML arrives.
0.34–0.76 sindex-BYeI0AHq.js downloads: 3.1 MB compressed, 9.3 MB unpacked; with it viem (455 KB unpacked), ethers (431 KB) and CSS. The react chunk later starts 1.55 s of main-thread work.
0.77 sreCAPTCHA (recaptcha__nl.js, 829 KB unpacked) loads; it is loaded a second time at 1.41 s.
1.16 sMetaMask SDK (539 KB unpacked).
1.86 sPage header shows the market.
1.91–2.64 sA 3.1 MB response from omni.apex.exchange sent without compression, probably the zklink WebAssembly module.
2.02 sWalletConnect wallet registry (1.2 MB unpacked); fetched again at 3.68 s, taking 1.7 s.
2.15–2.75 sThe chart library (library.js, 2.4 MB unpacked).
2.22 sFirst paint.
4.51 sPrice and order book show.
6.74 sChart drawn, 2.2 s after the book: trade-ready in this load. 19 long tasks took 3.2 s of main thread.

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.4 sFirst market-data request
2.0 sFirst frame sent on a live feed
2.6 sFirst frame received on a live feed
3.5 sPrice ready
3.5 sOrder book ready
3.5 sRecent trades ready
3.5 sOrder form ready
3.5 spositions ready
3.5 sassets ready
3.5 smargin ready
4.1 sChart ready
4.8 sTrade-ready

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

Request waterfalldiagnostic capture 1 October, uncached

69 of the 318 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.

PageScriptImageData requestwaiting for the serverdownloading

0 s0 s1 s1 s2 s2 s3 s3 s4 s4 s5 s5 s6 s6 s1omni.apex.exchange4 kB19 msAX-12omni.apex.exchange/index-…3.1 MB422 ms3www.gstatic.com/recaptcha…357 kB506 ms4omni.apex.exchange/metama…196 kB123 ms5recaptcha.net29 kB423 ms6www.gstatic.com/recaptcha…357 kB422 ms7l2dex-image-static.dev.ap…2 kB628 ms8omni.apex.exchange3.1 MB727 ms9eth.pro.apex.exchange709 B1134 ms10omni.apex.exchange836 B665 ms11omni.apex.exchange889 B899 ms12omni.apex.exchange2 kB506 ms13matic.pro.apex.exchange722 B1128 ms14omni.apex.exchange1 kB633 ms15omni.apex.exchange1 kB492 ms16omni.apex.exchange892 B892 ms17omni.apex.exchange2 kB539 ms18omni.apex.exchange846 B509 ms19omni.apex.exchangeopen20omni.apex.exchange41 kB608 ms21auth.privy.io1 kB637 ms22omni.apex.exchange/4618.e…37 kB488 ms23omni.apex.exchange/librar…747 kB597 ms24base.pro.apex.exchange950 B939 ms25base.pro.apex.exchange955 B937 ms26omni.apex.exchange4 kB1284 ms27explorer-api.walletconnec…173 kB1678 ms28omni.apex.exchange1675 ms29omni.apex.exchange/index-…106 kB936 ms30omni.apex.exchange/index-…35 kB937 ms31omni.apex.exchange986 B1020 ms32omni.apex.exchange789 B871 ms33omni.apex.exchange795 B873 ms34omni.apex.exchange2 kB984 ms35omni.apex.exchange3 kB931 ms36omni.apex.exchange2 kB891 ms37omni.apex.exchange797 B1336 ms38omni.apex.exchange1 kB890 ms39omni.apex.exchange815 B890 ms40omni.apex.exchange808 B929 ms41omni.apex.exchange835 B889 ms42omni.apex.exchange800 B887 ms43omni.apex.exchange3 kB970 ms44omni.apex.exchange798 B957 ms45omni.apex.exchange796 B996 ms46omni.apex.exchange1 kB996 ms47omni.apex.exchange2 kB1002 ms48omni.apex.exchange806 B994 ms49omni.apex.exchange865 B994 ms50omni.apex.exchange880 B1019 ms51omni.apex.exchange809 B996 ms52omni.apex.exchange808 B994 ms53auth.privy.io2 kB14 ms54omni.apex.exchange815 B886 ms55base.pro.apex.exchange667 B1096 ms56base.pro.apex.exchange671 B1092 ms57matic.pro.apex.exchange696 B1183 ms58static-pro.apex.exchange219 kB54 ms59omni.apex.exchange4 kB710 ms60omni.apex.exchange7 kB577 ms61static-pro.apex.exchange196 kB48 ms62static-pro.apex.exchange242 kB49 ms63pulse.walletconnect.org141 B755 ms64api.web3modal.org366 B756 ms65pulse.walletconnect.org141 B715 ms66api.web3modal.org366 B634 ms67omni.apex.exchange734 ms68matic.pro.apex.exchange661 B658 ms69matic.pro.apex.exchange657 B658 msprice 4.5 sorder book 4.5 schart 6.7 s
Download this figure as an image:

Bottlenecks and fixes, largest firstdiagnostic capture 1 October, uncached

AX-1A 9.3 MB main script.grade B
Evidence
/assets/index-BYeI0AHq.js is 3.1 MB compressed and 9.3 MB unpacked (0.34–0.76 s); 1.55 s of main-thread work starts in the react chunk; 19 long tasks add up to 3.2 s before trade-ready. On the 3 October warm reload, with the files cached, two tasks still block for 327 ms (at 0.60 s) and 314 ms (at 2.31 s). In the warm campaign loads the first panels show at 2.8 to 2.9 s at the earliest.
Gap observed
3.2 s of main-thread time across 19 long tasks
Holds back
Start-up as a whole; which panel waits for this work is not established.
Waterfall rows
row 2: 3.1 MB received, 9.3 MB unpacked, max-age=2592000
Fix
Split index-BYeI0AHq.js by route and move wallet, chain and other-page code out of the trading page's first load.
Where
Vite build (manualChunks, dynamic imports).
Verify
DevTools → Coverage: unused bytes in index-*.js on the trade page.
AX-2A 3.1 MB file is sent without compression; it is probably a WebAssembly module.grade B
Evidence
1 October capture: omni.apex.exchange sends 3.1 MB over the wire for one response at 1.91–2.64 s, with no content-encoding. The timed loads name a file of exactly that size: /assets/zklink-sdk-web_bg-xmAq9B20.wasm (3,110,973 bytes, 2.65–3.19 s uncached). The match is by size and host, from a different load. On a warm reload the file comes from the cache. The two cross-load matches here are by size and host. Another response, probably /api/v3/symbols, is 757 KB unpacked (41 KB compressed, marked no-cache).
Gap observed
0.73 s from requesting the 3.1 MB file to its completion
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Serve .wasm files with brotli or gzip (WebAssembly usually shrinks to less than half), and load the zklink SDK when a signature is needed, not at start-up.
Where
nginx compression types (add application/wasm); the import of the zklink SDK.
Verify
DevTools → Network, filter "wasm": content-encoding br or gzip; the request starts after the first panels show.
AX-3Duplicate and overlapping libraries at start-up.grade B
Evidence
1 October capture: reCAPTCHA is loaded twice (849 KB unpacked, 357 KB compressed each, at 0.77 and 1.41 s), WalletConnect's wallet list twice (1.2 MB each; the first at 2.02 s, before first paint at 2.22 s, the repeat at 3.68 s), and two Ethereum libraries (viem and ethers) plus the MetaMask SDK (0.55 MB) load during start-up. The timed loads also show /api/v3/stable-token-price and /spot-tokens-price each requested twice 10 ms apart, and data-api.polymarket.com/value twice.
Measured
two 849 KB reCAPTCHA loads, two 1.2 MB wallet-list loads, and overlapping Ethereum libraries
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Load reCAPTCHA and the WalletConnect registry once, only when needed; keep one Ethereum library.
Where
Captcha and wallet provider initialisation.
Verify
DevTools → Network: one recaptcha__ and one explorer-api request per load, none before the first price paint.
AX-4The chart is drawn 2.2 s after price and book.grade C
Evidence
1 October capture: price and book at 4.51 s, chart at 6.74 s, although library.4350c821a355451bc08e.js finished downloading at 2.75 s. The timed loads show the candle request (/api/v3/klines) going out late: 6.24–6.43 s uncached with the chart at 6.58 s, and 5.86–6.06 s warm with the chart at 6.25 s; in the 3 October recording 3.72–3.92 s with the chart at 4.12 s. Each time the chart follows the candles by about 0.2 s. In the 1 October capture 22 chart script requests also fail at 3.3–3.7 s and are requested again. All 5 warm campaign loads have the chart last (median 4.2 s). In 4 of the 10 ranked loads the chart canvas appears, disappears again for 0.6 to 0.75 s (for example from 2.65 to 3.24 s in one warm load) and comes back before the candles pass, which suggests the chart widget is built, torn down and built again.
Gap observed
2.23 s between price/book and the chart
Holds back
Chart (median 4.2 s in the timed loads).
Fix
Send the /api/v3/klines request with the other market data at start-up; the chart appears about 0.2 s after it answers. Check whether the chart widget is mounted twice.
Where
Chart datafeed and mount conditions.
Verify
DevTools → Network, filter "klines": the request starts before 2 s.
AX-5Trade-ready comes 0.7 s after the chart has its data: the chart is still animating.grade B
Evidence
All 10 ranked loads have the chart last, and in every one the chart panel still has a running animation between the chart having its data and trade-ready (0.64 to 0.75 s on the warm loads). Recorded warm load: chart at 4.12 s, trade-ready at 4.80 s, all 7 samples in between; in one a font is also still loading.
Gap observed
0.64 to 0.75 s between the chart and trade-ready in the five warm loads
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
AX-6Three fonts are uncompressed TTF, 147 KB each.grade B
Evidence
1 October capture: HarmonyOS_Sans_Regular.ttf, Medium and Bold from omni.apex.exchange are 147 KB each (440 KB together), sent without compression, at 1.27 and 1.93 s. The page already uses one WOFF2 font (35 KB).
Measured
three TTF fonts of 147 KB each
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Ship the three HarmonyOS Sans files as WOFF2 subsets.
Where
The font files and @font-face rules in the app build.
Verify
DevTools → Network, filter "font": all fonts are woff2, each well under 100 KB.
Likely saving
Small on a reload; about 0.3 MB less on a first visit
AX-7Four large images load while the chart is starting.grade C
Evidence
1 October capture: four images from static-pro.apex.exchange of 241, 218, 195 and 151 KB (0.8 MB together) are requested at 4.7 and 5.0 s and arrive by 5.05 s, between the price (4.51 s) and the chart (6.74 s). Their paths are masked, so the capture does not say what they show.
Measured
0.8 MB of images between the price and the chart
Holds back
Not established: the captures do not show which panel waits for this.
Fix
Load the images after trade-ready, or where they are displayed, and serve them at display size in a modern format.
Where
The image assets on static-pro.apex.exchange requested at start-up.
Verify
DevTools → Network, filter "static-pro": no image over 100 KB before the chart is drawn.
Likely saving
Small

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 (this page's stored loads were scored again afterwards with one change: the order form is checked on Market, with one input field, as it was during 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.