Trading analytics dashboard
A private dashboard to watch and control my trading bots across several broker accounts.
The bots run on their own and trade on several cTrader accounts. Checking them meant reading logs. I needed equity, daily P&L and bot state in one screen. I also needed to pause a bot without opening a terminal.
Three decisions that shaped it.
- 01
Static export, all data fetched in the browser
Why. The dashboard ships as plain files with no Node server to run. Every request carries the session cookie to the bots' API.
Trade-off. The auth guard is a client-side redirect. The API has to enforce every check, and each screen starts with a skeleton.
- 02
SSE events trigger refetches, polling is the fallback
Why. A closed trade or new snapshot refreshes the right card at once. Polling every 15 to 60 seconds covers a dropped stream.
Trade-off. Each component that listens opens its own EventSource. The overview holds three at once.
- 03
The monthly report is a print stylesheet
Why. The report renders as a normal page. An A4 landscape @page rule and window.print() turn it into a PDF.
Trade-off. No automated PDF. Someone has to open the page and print it.
Trading analytics dashboard
A private dashboard to watch and control my trading bots across several broker accounts.
- Role
- Internal tool: designed and built for my own trading bots
- Stack
- Next.js 15, React 19, TypeScript, Recharts, Tailwind CSS 4
The bots run on their own and trade on several cTrader accounts. Checking them meant reading logs. I needed equity, daily P&L and bot state in one screen. I also needed to pause a bot without opening a terminal.
Three decisions that shaped it.
Static export, all data fetched in the browser
Why. The dashboard ships as plain files with no Node server to run. Every request carries the session cookie to the bots' API.
Trade-off. The auth guard is a client-side redirect. The API has to enforce every check, and each screen starts with a skeleton.
SSE events trigger refetches, polling is the fallback
Why. A closed trade or new snapshot refreshes the right card at once. Polling every 15 to 60 seconds covers a dropped stream.
Trade-off. Each component that listens opens its own EventSource. The overview holds three at once.
The monthly report is a print stylesheet
Why. The report renders as a normal page. An A4 landscape @page rule and window.print() turn it into a PDF.
Trade-off. No automated PDF. Someone has to open the page and print it.