A real-time, web-based stock ticker app with configurable user profiles, multi-style charts, an anomaly dashboard (spikes / drops / volume surges), and per-symbol news.
- Real-time quotes — live trades streamed from Finnhub over websocket, with 15s REST polling as fallback
- Charts — Candles, Bars, Line, Area, and Baseline views across 1D / 5D / 1M / 6M / 1Y / 5Y timeframes (TradingView lightweight-charts)
- Anomaly dashboard — scans the last trading session (5-min bars) for statistically significant price spikes, drops, and volume surges (z-score based) across your whole watchlist
- News — last 7 days of company news for the selected symbol
- Profiles — multiple local user profiles, each with its own watchlist, default chart type, and timeframe (stored in browser localStorage)
- Ticker tape — scrolling marquee of your watchlist
- Install Node.js 18 or newer.
- Get a free API key at finnhub.io.
- In this folder:
then edit
copy .env.example .env.envand paste your key intoFINNHUB_API_KEY=. - Install dependencies:
npm install
npm run dev
Then open http://localhost:5173. This starts the Express backend (port 3001) and Vite dev server together.
The app is already structured for it:
npm run build # builds the frontend into /dist
npm start # Express serves /dist + the API + websocket on one port
So any Node host (Render, Railway, Fly.io, AWS, Azure App Service…) just needs npm install && npm run build && npm start with the FINNHUB_API_KEY env var set. The API key stays server-side — it's never exposed to the browser.
Browser (React + lightweight-charts)
│ /api/* (REST: quotes, candles, news, search)
│ /ws (websocket: live trades)
▼
Express server (server/index.js)
├─ Finnhub — real-time quotes, websocket trades, company news, symbol search
└─ Yahoo Finance — historical candles (Finnhub candles are paid-only)
Responses are cached briefly in memory to stay inside Finnhub's free-tier rate limit (60 calls/min).
- Live websocket trades only flow while US markets are open; outside market hours prices come from REST quotes.
- Profiles are stored in the browser (localStorage). When you move to the cloud and want real accounts, swap
src/lib/profiles.jsfor an API backed by a database.