Live Demo: https://real-time-stock-screener-blond.vercel.app
A state-of-the-art, high-performance financial screener terminal resembling professional Bloomberg/TradingView interfaces. Designed to track, filter, and analyze 5,000 stocks from the Indian equity market universe in real-time with sub-5ms filter processing speeds and sub-25ms end-to-end WebSocket render latency.
Follow these step-by-step instructions to clone, configure, and run the screener locally:
- Node.js: Version 20 or higher is required.
- npm: Version 10 or higher.
git clone https://github.com/yash-pratap-create/ZethetaScreener.git
cd ZethetaScreenerInstall all package dependencies including dev tools:
npm installStarts the Next.js development server with a live simulated WebSocket stream:
npm run devOpen http://localhost:3000 in your browser to view the live dashboard terminal.
Validate the logic and performance requirements:
- Run Unit & Performance Tests:
npm run test - Run Test Coverage:
Note: The test suite enforces strict execution time limits (<200ms for filtering, <150ms for sorting) across 5,000 mock records.
npm run test:coverage
To test the optimized production bundle locally:
npm run build
npm run startTo run the Storybook server and view isolated UI stories:
npm run storybookOpen http://localhost:6006 to explore stories for UI primitives, cells, filters, and charts.
The project runs out-of-the-box with simulated data generators. However, the following standard environment variables can be configured:
| Variable Name | Type | Default | Description |
|---|---|---|---|
PORT |
Number | 3000 |
Port for the Next.js server. |
NODE_ENV |
String | development |
The application environment (development, production, test). |
No API keys are required for local development as the backend dynamically generates a simulated universe of 5,000 Indian stocks and simulates WebSocket price streams.
Here is a summary of all commands available in package.json:
npm run dev: Starts the dev server with Hot Module Replacement (HMR).npm run build: Builds an optimized production-ready bundle.npm run start: Starts the built production server.npm run lint: Runs ESLint to check for code issues.npm run lint:fix: Runs ESLint and automatically resolves fixable lint errors.npm run format: Formats the codebase using Prettier.npm run test: Runs unit and performance benchmarks with Vitest.npm run test:watch: Runs Vitest in watch mode.npm run test:coverage: Generates a test coverage report using Istanbul/V8.npm run test:e2e: Runs Playwright end-to-end integration tests.npm run storybook: Launches the Storybook development dashboard.npm run build-storybook: Compiles Storybook into a static web app for hosting.
The terminal is built to handle high-frequency data updates without UI locking, utilizing a virtualized grid, cost-ordered predicate compiler, and RAF (requestAnimationFrame) batching.
The component tree balances layout responsibility and memoization boundaries to minimize re-renders:
graph TD
A[ScreenerPage app/page.tsx] --> B[RealtimeProvider]
B --> C[Topbar layout/Topbar]
B --> D[MarketPulseBar / StatsRibbon]
B --> E[ErrorBoundary DataGrid]
E --> F[ScreenerView]
F --> G[ErrorBoundary FilterPanel]
G --> H[FilterSidebar FilterPanel/FilterSidebar]
H --> H1[Preset Buttons]
H --> H2[FilterRuleRow array]
F --> I[ErrorBoundary DataGrid]
I --> J[StockGrid DataGrid/StockGrid]
J --> J1[Sticky Table Header]
J --> J2[Virtualized Tbody]
J2 --> K[Cell Renderers Price/Change/Vol/RSI]
F --> L[Suspense Chart]
L --> M[ChartModal Chart/ChartModal]
M --> M1[Lightweight Charts Canvas]
M --> M2[RSI Sub-chart]
Real-time state and UI filters flow through separate stores and merge during grid rendering:
sequenceDiagram
autonumber
participant WS as Mock WebSocket Stream (RAF)
participant RQ as React Query (Universe API)
participant RT as realtimeStore (Zustand)
participant FL as filterStore (Zustand)
participant Hook as useStockScreener Hook
participant Engine as filterEngine (Compiler)
participant Grid as Virtualized StockGrid
RQ->>Hook: Fetches 5,000 base stocks once
Note over WS, RT: Batched updates via requestAnimationFrame
WS->>RT: Accumulate and flush price updates (sub-50ms)
RT->>Hook: Notify state update
Hook->>Hook: Merge base stock specs + live price updates
FL->>Hook: Active filter rules / preset group
Hook->>Engine: Run merged stocks through compiled predicates
Engine-->>Hook: Output filtered stock array (sub-5ms)
Hook->>Grid: Feed filtered list
Grid->>Grid: Render viewport rows (~20 out of 5,000)
The platform incorporates a simulated REST API backend generating Indian stock market mock universes, technical indicators, and historical OHLCV candles.
Retrieves a paginated and filtered list of the active stock universe.
- Query Params:
page: Page index (default:1)limit: Rows per page (default:100)search: Search query filtering by symbol, company, or sector.
- Response:
{ "data": [ { "symbol": "TCS", "companyName": "Tata Consultancy Services Ltd", "sector": "Information Technology", "lastPrice": 3845.2, "changePercent": 1.25, "marketCap": 1395000, "pe": 28.4, "pb": 12.5, "roe": 39.5, "rsi14": 58.2, "macdSignal": "Bullish", "indexMembership": ["NIFTY50", "NIFTY500"] } ], "pagination": { "page": 1, "limit": 100, "total": 5000 } }
Fetches historical candle bars for chart plotting.
- Parameters:
symbol(e.g.TCS) - Query Params:
timeframe(1D,1W,1M, etc.) - Response:
[ { "time": 1717315200, "open": 3810, "high": 3860, "low": 3805, "close": 3845, "volume": 120000 } ]
Gets live averages of prime indices (NIFTY 50, SENSEX, NIFTY MIDCAP).
- Decision: Next.js App Router (Client Component heavy).
- Trade-Off: Although a screener dashboard behaves like a Single Page App (SPA) once loaded, Next.js was selected to enable SEO-optimized symbol detail pages, server-side landing shell generation, and simple API routing. The main screener itself is a Client Component shell wrapping local stores.
- Decision: Zustand + Immer for local client-side state.
- Trade-Off: Redux brings significant boilerplate, while Zustand is extremely lightweight (~1KB). Combined with Immer, we mutate deep properties (such as real-time price updates and cell flashes) cleanly without manual copying, preventing garbage collection spikes.
- Decision: TanStack Table v8 for data grid logic combined with TanStack Virtual.
- Trade-Off: Standard tables crash when rendering 5,000 rows with real-time updates. By virtualizing the viewport, we maintain a constant DOM size of ~20 visible rows, achieving consistent >58 FPS scroll rates. This introduces some complexity around focus management and keyboard accessibility, which we resolved by implementing explicit cell indexing and shortcut listeners.
- Decision: TradingView's Lightweight Charts.
- Trade-Off: Lightweight Charts is written in canvas, keeping CPU usage low even with thousands of ticks. While it is less customizable for generic layouts compared to D3, it provides the industry-standard financial chart UI out-of-the-box, including built-in zoom, drag, and timescale sync.
- Decision: Cost-ordered compiled predicate chain.
- Trade-Off: Evaluating 10+ rules on 5,000 items on every state tick using standard filters takes >30ms, which causes UI micro-stutters. By pre-compiling rules into a flat function and placing high-selectivity boolean/numeric filters first, we short-circuit failing matches instantly, reducing filtering time to 3–8ms.
Our chart implementation calculates all technical indicators purely from scratch in src/lib/indicators.ts:
- Simple Moving Average (SMA): $$\text{SMA}n = \frac{1}{n} \sum{i=0}^{n-1} P_{t-i}$$
- Exponential Moving Average (EMA): $$\text{EMA}t = (P_t \times k) + (\text{EMA}{t-1} \times (1 - k)), \quad k = \frac{2}{n + 1}$$
- Bollinger Bands: $$\text{Middle Band} = \text{SMA}{20}, \quad \text{Upper/Lower} = \text{SMA}{20} \pm (2 \times \sigma_{20})$$
-
Relative Strength Index (RSI):
$$\text{RSI} = 100 - \frac{100}{1 + \text{RS}}, \quad \text{RS} = \frac{\text{SMMA}(\text{Gain}, 14)}{\text{SMMA}(\text{Loss}, 14)}$$ - Volume Profile: Distributes historical volumes into 24 equally spaced price-range buckets between the high and low bounds of the period, separating buy and sell concentrations.
- Simulated Data Boundary:
- Limitation: The WebSocket stream and API use a Geometric Brownian Motion (GBM) generator in the Next.js API route instead of connecting to a real broker feed.
- Future Plan: Add support for actual broker API connections (e.g. Zerodha Kite Connect, Angel One SmartAPI).
- Offline Mode:
- Limitation: Cache relies on React Query, but custom presets and watchlists are stored in
localStoragewhich can be cleared by the browser. - Future Plan: Integrate IndexedDB or a SQLite WASM layer for offline local database storage.
- Limitation: Cache relies on React Query, but custom presets and watchlists are stored in
- Multi-Chart Viewports:
- Limitation: Currently, only one chart modal can be opened at a time.
- Future Plan: Implement a multi-pane charting dashboard matching professional TradingView layouts where up to 4 charts can be tracked simultaneously.



