browser trading clients are harder than they look. A socket that keeps dying, a chart that has to redraw without stuttering, an order form where stale state costs money instead of just looking bad. The public WebTrader pages at Tradesired describe a client that has answered most of the boring parts the way I would have wanted them answered. What follows is a read of the published material against what the category actually demands.
Why this is a hard build in the first place
Look at a trading screen and you see a chart with some buttons round it. Fine.
Then you try to ship one.
What you're actually shipping is four systems wearing one page as a disguise. A transport layer that has to stay subscribed to a symbol set across a connection that will absolutely die in a lift. A rendering layer redrawing a price series while somebody drags the viewport with a trackpad. An order layer where a value 400ms stale isn't a visual glitch, it's money leaving. And then the layout, because all of it has to fit whatever the user opened it on, which in my case is a 14-inch ThinkPad at 110% zoom, since I'm 29 and already squinting.
Native clients get to cheat. They own their process and keep a socket open while minimised.
A web client gets a tab. That's the whole allowance.
Three panels, and what each one is defending
The WebTrader page labels its blocks plainly: "Everything Within Easy Reach" for the interface, "Streamlined Order Management" for execution, "Advanced Market Visualization" for charting. The split underneath maps onto three of the four systems above, which reads more like an architecture diagram than a feature backlog.
Instrument navigation down one side, ticket where your hand already is, chart taking the rest. None of it is new, and converging on the shape the category settled two decades ago was right: a novel layout in a trading client is a tax you charge users for your own entertainment. The four session clocks in the header, SYD, TOK, LON and NY, are the one genuinely thoughtful touch: free to build, and they kill a timezone conversion I used to do badly at half seven in the morning.
Progressive disclosure is what keeps three panels survivable. The page advertises 80+ indicators, and eighty is a fine number to own and an awful number to render at once. The usual answer is a small default set with the rest one menu behind it, which also keeps first paint cheap.
What happens when you reload the tab?
My standard test for any browser app holding state that matters.
Desktop terminals get restarted weekly. A browser tab gets reloaded constantly: muscle-memory Cmd+R, an extension taking the renderer down, a laptop that slept under Kottbusser Tor and came back evicted. Each is a state-restoration problem, and the state splits into two piles wanting opposite handling.
- Server-side truth: positions, balance, open orders, fill history. Re-fetch on load, and don't paint a cached copy first, because a position that blinks in two seconds late teaches somebody to distrust the screen permanently.
- UI state: watchlist, timeframe, indicator set, the trendlines you drew at 2am and still believe in. Keep it in
localStorageand the XAUUSD setup you spent twenty minutes on exists on one machine, which is version drift wearing a hat. Keep it in a server-side profile and the account looks the same on the work laptop.
And that's the strongest argument for shipping a trading client as a web app. Nobody is sitting on a build from March, which deletes a whole class of support ticket before anybody writes it.
Reconnects
Price streams drop. That isn't a defect, it's Tuesday. Tradesired publishes execution under 30ms with no requotes alongside a 99.9% uptime SLA, and I like that those are two separate numbers: uptime is availability, execution is the round trip on an order. Quoting them apart is the accurate way to quote them, and plenty of shops roll both into one figure and hope nobody asks. What separates a solid client from a haunted one is the next four seconds.
let attempt = 0;
function connect() {
const ws = new WebSocket(FEED_URL);
ws.addEventListener("open", () => {
attempt = 0;
ws.send(JSON.stringify({ op: "subscribe", symbols: watchlist, since: lastSeq }));
});
ws.addEventListener("close", () => {
const delay = Math.min(30000, 500 * 2 ** attempt++);
setTimeout(connect, delay * (0.5 + Math.random()));
});
}
The jitter on that last line matters more than the backoff does. Leave it out and a dropped region reconnects in one synchronised wave: a thundering herd built out of your own users.
The other half of the problem is what the client asks for once the socket is back up. Resubscribing cold is simpler to reason about and cheap to test. Replaying from the last acknowledged sequence, which is what since: lastSeq is gesturing at, costs bookkeeping on both ends and behaves better on a regional train with two bars. Most clients end up doing one for prices and the other for order events, because a missed tick is cosmetic and a missed fill is not.
Charts: three modes, one series
Line, bar and candlestick, per the platform page. Three ways of drawing one series, and the choice changes what you notice. Pull XAUUSD up on a four-hour candle and a compressed range shows as a run of small bodies; flip the same series to line and that compression smooths into a curve you'd never notice. Bar view splits the difference, and nobody I know uses it. Underneath the design argument sits a rendering one: past a few hundred continuously updating elements a chart built from DOM nodes pays style recalculation and layout on every tick, while canvas lets you own the pixels and redraw only the dirty rectangle, so cost tracks what changed. That is why almost every serious web chart you have used is a canvas with a thin DOM shell holding the axes. It is also why the timeframe switcher is the one control worth making feel instant.
What you get back for shipping in a browser
There is one genuinely fiddly bit, and it is worth knowing about before you build.
Hidden tabs get throttled. Chrome clamps timers in a backgrounded tab, so anything sitting on setInterval drifts, and while the socket usually keeps delivering, alert logic layered on top of it needs its own answer. document.visibilityState is how a client finds out and forces a resync on the way back. Solved problem, once you know to solve it.
And for that one piece of bookkeeping you get a very large return. No installer. No version drift. Nobody sitting on a build from March, nobody running the thing from a USB stick on a machine you have never heard of, nobody opening a ticket you can only close by asking which build they are on. For a retail trading client that is plainly the right side of the bargain, and running WebTrader in the browser is the decision the rest of the platform hangs off.
The part that isn't an engineering problem
One thing worth saying out loud, because it sits underneath every screen described above: negative balance protection is published in the account table itself, as a column of its own, rather than as a footnote somewhere below it. A protection term printed in the same table as everything else is a product decision rather than a UI one, and it is the kind I notice.
The thing I keep coming back to, though, is the session clocks. SYD, TOK, LON, NY. Four labels and a timer, almost certainly the cheapest component on the page, and the only one I noticed twice.
Top comments (0)