- Evidence
- Recorded load: about 20 requests go out at 1.67 s and each waits 0.7–1.0 s for the server; more rounds follow at 2.9 s and 3.35 s. The last one finishes at 4.13 s and the market socket sends its first frame at 4.14 s; price and book follow at 4.80 s. In the campaign the order form and wallet show at 3.3 to 3.4 s and price and book not before 4.9 s. Logged-out recording with a fresh browser profile, 3 October, which keeps the addresses: the first round mixes the market requests (futures/api/inquire/initialMarketData, futures/api/inquire/market, futures/api/config/marketCategory) with requests the trading panels do not need first (api/country/all, which takes 1.48 s, api/informationFromIp, api/regulatoryDisclaimer/banner, api/languages, tracking/api/v1/events); a later round fetches futures/api/inquire/initial/market (1.2 MB unpacked), futures/api/v2.3/market/risk_limit (0.3 MB) and api/init/typesPrice twice.
- Gap observed
- 2.47 s from the first API batch to the market-socket subscription
- Holds back
- Price (median 5.2 s in the timed loads), book (median 5.2 s in the timed loads).
- Fix
- Send initialMarketData, inquire/market and the socket subscription first. Move country/all, informationFromIp, regulatoryDisclaimer/banner, languages and tracking events until after the price shows, and request typesPrice once.
- Where
- Feed client start-up and the start-up request sequence.
- Verify
- DevTools → Network → WS: first frame sent before 1 s. DevTools → Network, filter "api.btse.com": the market requests are the first ones.
- Likely saving
- ~1.5 s
BTSE: 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 7.0 s, range 6.8 s–7.2 s; loads: 7.2 s, 7.2 s, 6.9 s, 7.0 s, 6.8 s |
| Uncached reload | median 7.0 s, range 6.6 s–7.3 s; loads: 7.3 s, 7.0 s, 6.9 s, 6.6 s, 7.3 s |
| Standing | 25 of 27 by median; possible rank 24 to 25. 4.6 s behind the fastest, Thalex (2.4 s): 2.9 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: outside viewport |
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
Not captured on 1 October; from the 3 October recorded warm load only.
Delivery
www.btse.com through CloudFront.
| Host | Role | CDN edge seen | Connect | Response |
|---|---|---|---|---|
| www.btse.com | page | CloudFront Amsterdam | 13 ms | 35 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 orderrecorded warm reload 3 October
From one warm reload recorded with Chrome's performance trace on 3 October 2026. Recording slows the page, so read the order and the gaps, not the totals. Some host names were masked when the recording was saved.
| When | What |
|---|---|
| 0.79 s | HTML arrives, among the slowest in the test. |
| 1.67–2.69 s | About 20 API requests go out together; each waits 0.7–1.0 s for the server. |
| 2.92–4.13 s | Two more rounds of API requests; the last one finishes at 4.13 s. |
| 3.45 s | Order form and wallet panel show. |
| 4.14 s | The page sends its first frame on the market socket (ws.btse.com), right after the last API round; first message at 4.37 s. |
| 4.80 s | Price and order book show. |
| 5.88 s | Chart drawn. |
| 6.92 s | Trade-ready in the recorded load. Long tasks before the first panel: 1.5 s, the longest 575 ms. |
Request waterfallrecorded warm reload 3 October
62 of the 369 requests (the page itself, the longest and the largest) started before trade-ready in the warm reload recorded on 3 October, 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.
PageScriptData requestwaiting for the serverdownloading
Bottlenecks and fixes, largest firstrecorded warm reload 3 October
- Evidence
- 788 ms to the first byte of the document in the recorded load, 833 ms in the logged-out recording. There the response says cache-control "public, max-age=60, s-maxage=604800" with age 244994 (2.8 days) and "x-cache: Error from cloudfront": the CDN is set up to keep the page for 7 days, but reports an error and still takes 0.8 s. That pattern fits a CDN that tries the origin, fails and falls back to its stored copy; the recording does not prove it.
- Gap observed
- 0.788 s to the document first byte
- Holds back
- Every panel: nothing is shown before this ends.
- Fix
- Look up why CloudFront reports "Error from cloudfront" for the futures page and fix the origin or the error handling, so the edge answers from its stored copy without waiting for the origin.
- Where
- CloudFront distribution for www.btse.com: origin health and the cache behaviour of the futures page.
- Verify
- DevTools → Network → document → Headers: "x-cache: Hit from cloudfront"; Timing: waiting for server response under 100 ms.
- Likely saving
- ~0.7 s
- Evidence
- 8 long tasks, the longest 575 ms at 1.73 s, before the order form shows at 3.45 s. The browser attributes the longest one to BTSE's own scripts. The largest start-up scripts, by name from the logged-out recording: fTPyrBIP.js (3.5 MB unpacked), B-0vxubZ.js (2.0 MB), BOOzV_OZ.js (1.3 MB), app.DI3xhnWo.js (1.0 MB) and chunk-echarts.K2WzvGbX.js (0.65 MB), all requested in the first 1.5 s.
- Gap observed
- 1.5 s of main-thread time before the first panel
- Holds back
- Every panel: nothing is shown before this ends.
- Fix
- Profile start-up and split the 575 ms task; check whether chunk-echarts (0.65 MB) and the non-trading parts of fTPyrBIP.js are needed before the first panel, and load them later if not.
- Where
- Bundler output for the futures page.
- Verify
- DevTools → Performance: no task over 200 ms before the first panel.
- Likely saving
- Up to ~1 s more, after BT-1
- Evidence
- All 10 campaign loads: between the last panel and trade-ready the only readiness condition failing is an animation running in the chart panel, for 0.62 to 1.22 s on the warm loads (medians: last panel 5.9 s, trade-ready 7.0 s). Recorded load: 1.05 s, with 15 animated elements in the chart; the panels do not move, fonts are loaded and no image is pending.
- Gap observed
- 0.62 to 1.22 s between the last panel 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 ~1 s
- Evidence
- Campaign warm medians: price and book 5.2 s, chart 5.9 s; recorded load: 4.80 s and 5.88 s. Logged-out recording: the chart library (charting_library/bundles/library.8fdacc60e5256d6fcc84.js, 2.8 MB unpacked) is requested at 6.19 s, after the price at 5.47 s, and /chart/markets/list is requested twice at 6.18 s, taking 0.55 and 0.97 s.
- Gap observed
- 0.7 s between the price and book median and the chart median (two separate medians, not a wait measured inside one load)
- Holds back
- Chart (median 5.9 s in the timed loads).
- Fix
- Request library.js and chart/markets/list together with the first market requests, not after the price.
- Where
- Chart component loading.
- Verify
- DevTools → Network, filter "charting_library": library.js starts before 2 s.
- Likely saving
- ~0.7 s
- Evidence
- Logged-out recording with a fresh browser profile, 3 October: wsdk.js from webdevicejs.com, loaded by a BTSE script (B-0vxubZ.js) at 1.7 s, runs inside one 2.32 s task from 2.39 to 4.71 s; sampled profiling places the time in wsdk.js itself. While it runs, 15 API responses that had arrived by 2.9 s are not handed to the page until about 5.0 s, and the futures socket created at 1.86 s completes its handshake only at 4.99 s. The price shows at 5.5 s. In the logged-in recording, all third-party scripts together used 0.5 s, so the timed screen does not pay this.
- Gap observed
- 2.3 s of main-thread time in the fresh-profile device-check task
- Holds back
- Start-up as a whole; which panel waits for this work is not established.
- Fix
- Load the device check after the trading screen is usable, or only before the actions that need it, such as sign-in and orders.
- Where
- The wsdk.js loader in B-0vxubZ.js (called from app.DI3xhnWo.js).
- Verify
- DevTools → Performance in a new browser profile: no wsdk.js task before the price shows.
- Likely saving
- Over 2 s, first visits only
- Evidence
- The page is on www.btse.com and the API on api.btse.com, so the browser first sends an OPTIONS request (a CORS preflight) for each address. Logged-out recording with a fresh browser profile, 3 October: 26 OPTIONS requests in all; 16 at 2.10–2.25 s take 0.26 to 0.54 s each before the real requests can be sent, and 6 more at 5.42 s take 0.25 s each. The logged-in recording masks request methods, so it does not show whether a returning visitor pays the same.
- Gap observed
- 0.26 to 0.54 s for each permission request before the real request can be sent
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Send Access-Control-Max-Age: 7200 on api.btse.com so the browser remembers the permission, or serve the API under www.btse.com so no permission request is needed.
- Where
- CORS response headers of api.btse.com, or the routing of /api under www.btse.com.
- Verify
- DevTools → Network, filter "method:OPTIONS": none on a reload.
- Likely saving
- About 0.3 s per API round; the market data needs up to three rounds
- Evidence
- Logged-out recording: www.btse.com/i18n/en.json is 1.1 MB unpacked (274 KB compressed) with cache-control "no-cache", so the browser asks the server about it on every load, while the scripts and styles are cached for a year. Four .woff fonts of about 120 KB each are cached for one day; the newer woff2 format is typically about 30% smaller.
- Measured
- a 1.1 MB translation file marked no-cache and four fonts of about 120 KB
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Give en.json a content hash in its name and the same one-year lifetime as the scripts; ship the fonts as woff2.
- Where
- Build output for /i18n and /static/font; their cache-control headers.
- Verify
- DevTools → Network: en.json and the fonts show "(disk cache)" on a reload; font type woff2.
- Likely saving
- Small on a reload; about 0.1 MB less on a first visit
- Evidence
- Logged-out recording: pixel.mediamathrdrt.com/scripts/cg_btse.js and coinzillatag.com/lib/performance.js at 0.84 s, googletagmanager.com/gtm.js at 1.39 s, all before any trading panel. The recording shows no completed response for the coinzilla and Tag Manager scripts. Their main-thread cost was not measured.
- Measured
- three advertising and tag scripts in the first 1.4 s
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Load the advertising and tag scripts after trade-ready.
- Where
- The script tags for mediamath, coinzilla and Google Tag Manager.
- Verify
- DevTools → Network, filter "mediamath", "coinzilla", "googletagmanager": no requests before the price shows.
- Likely saving
- Not measured
- Evidence
- 3 October recorded warm load: 538 forced layout recalculations costing 399 ms in total, the most of any screen in this test. The recording masks the callers. The logged-out follow-up recording does not reproduce it (41 small ones), so it belongs to the logged-in screen with its account panels.
- Measured
- 538 forced layout recalculations, 399 ms, in the recorded warm load
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Find the code that reads element sizes right after writing to the DOM (DevTools → Performance flags each one as a forced reflow) and batch the reads.
- Where
- The components of the logged-in futures screen that measure themselves while rendering.
- Verify
- DevTools → Performance on a logged-in reload: forced-reflow warnings under 50, total under 50 ms.
- Likely saving
- Up to ~0.4 s of main thread on a warm reload
- Evidence
- Logged-out follow-up recording: one task of 752 ms at 7.26 s, before the order book shows at 9.60 s in that load. The CPU profile places 582 ms of it in setTimeout and 84 ms in clearTimeout; every sampled setTimeout call runs through the Sentry wrapper in chunk-sentry.DKA24Cnk.js and Vue component set-up in B-0vxubZ.js, and the clearTimeout calls come from tooltip code in fTPyrBIP.js (clearTooltipTimer, destroyToolTip). So the page creates and destroys a very large number of tooltip timers while its components mount, and each one is wrapped by Sentry.
- Measured
- a 752 ms task, 666 ms of it in setTimeout and clearTimeout, in the follow-up recording
- Holds back
- Not established: the captures do not show which panel waits for this.
- Fix
- Count how many tooltip components mount at start-up and give them one shared timer; turn off Sentry's timer instrumentation if it is not needed.
- Where
- The tooltip component in fTPyrBIP.js and the Sentry integration (setTimeout wrapping) in chunk-sentry.js.
- Verify
- DevTools → Performance: no task over 200 ms attributed to setTimeout or clearTimeout.
- Likely saving
- Up to ~0.7 s on a first visit; unknown on a warm reload
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.