Technical Architecture Document β€” v2.0

The Ultimate
Indian Market Terminal

A complete deep-dive into the React + Vite frontend, dual-watchlist system, 5 quantitative scatter models, automated data pipeline, and JAMstack deployment powering a professional-grade Indian stock market terminal.

Investment Watchlist132 Stocks
MTF Watchlist216 Stocks
Quant Models5
Custom Indices14
ArchitectureJAMstack
Monthly Cost$0 / mo

Table of Contents

  1. System Overview
  2. Dual Watchlist System
  3. Pipeline Flowchart
  4. Data Ingestion Layer
  5. Index Math Engine
  6. Heatmap Engine
  7. Quantitative Models
  8. Automation & CI/CD
  9. Frontend Architecture
  10. File Structure
  11. Technology Stack
  12. How to Run Locally
🏗
System Overview
The big picture β€” what this system does and how
348
Total Stocks Tracked
14
Custom Indices
5
Quant Models
3
Dashboard Views
6
Timeframes
$0
Monthly Cost
🎯 What This System Does

This is a fully serverless, zero-cost professional trading terminal for Indian markets. It tracks two separate watchlists β€” a curated Alpha 132 investment watchlist and an MTF swing-trading watchlist of 216 margin-eligible stocks β€” and synthesizes institutional-grade analytics for each.

The system answers two core questions: "How do my sector bets look as equal-weighted index charts?" and "Which stocks are showing quantitative signals worth acting on right now?"

Everything is computed by a Python script, committed as static JSON to GitHub, auto-deployed via Cloudflare Pages, and rendered in a React + Vite browser app β€” no backend server, no database, no cost.

πŸ’‘ Core Design Philosophy

JAMstack architecture β€” Python computes data offline, commits static JSON to GitHub, and the React frontend fetches it directly. No runtime server. The "backend" is a scheduled GitHub Action + Python script running twice daily.

πŸ”‘ Key Design Decisions

β€’ Dual watchlist β€” Investment + MTF mode, switchable at runtime
β€’ Delta cache β€” only fetches new trading days each run
β€’ Equal-weighting β€” no stock dominates by market cap
β€’ React + Vite β€” full TypeScript component tree, zero global state libraries

πŸ”€
Dual Watchlist System
Alpha 132 investment mode vs MTF swing-trading mode
πŸ—οΈ Architecture Overview

The dashboard supports two completely independent watchlists that can be switched in real-time using the navbar toggle. Each watchlist has its own set of computed JSON files and its own data pipeline processing path. The React app uses a WatchlistId type ('investment' | 'mtf') as the top-level state that drives all data fetching and view availability.

πŸ“Š Alpha 132 β€” Investment Watchlist

Purpose: Long-term portfolio management and sector analysis

β€’ 132 hand-picked Indian small- and mid-cap stocks
β€’ Synthesizes 14 equal-weighted indices (1 master + 13 sector)
β€’ All 3 views available: Index Chart Heatmap Quant
β€’ Data source files: indices_summary.json, heatmap_data.json, quant_models.json

⚑ MTF Stocks β€” Swing Trading Watchlist

Purpose: Swing trade candidate selection using 3.5x+ margin stocks

β€’ 216 stocks eligible for MTF (Margin Trade Funding) at β‰₯3.5x margin
β€’ No index chart view (not tracked as index constituents)
β€’ Available views: Heatmap Quant
β€’ Data source files: mtf_heatmap_data.json, mtf_quant_models.json
β€’ Clicking a heatmap tile navigates directly to Quant Models with that stock selected

πŸ”„ Runtime Switching Behavior

When the user clicks the Alpha 132 / MTF Stocks toggle in the navbar:

1. App.tsx calls handleWatchlistChange() which updates the watchlist state
2. If switching to MTF and current view is 'chart', the view auto-redirects to 'heatmap'
3. Both useIndexData and useQuantData hooks re-fire their refresh() callbacks, fetching the watchlist-specific JSON files
4. The selected quant ticker is reset to null to clear cross-watchlist state
5. The navbar center tab group dynamically shows/hides the "Index Chart" button based on watchlist

MTF Stock Selection Philosophy: The MTF watchlist is used to identify which stock to buy for a swing trade. The Quant Models + Heatmap combination lets you quickly spot momentum outliers and mean-reversion setups across 216 stocks simultaneously β€” a workflow that would take hours manually.
πŸ”„
End-to-End Pipeline Flowchart
How data flows from Yahoo Finance to your browser
TRIGGER
⏰
Cron Schedule
07:30 UTC (1 PM IST)
10:10 UTC (3:40 PM IST)
Mon–Fri only
πŸ–±οΈ
Manual Dispatch
GitHub UI β†’ Actions β†’
Run workflow
Any time
↓
github.com webhook
CI/CD
🐧
GitHub Actions Runner (ubuntu-latest)
Checkout repo β†’ Python 3.11 β†’ pip install yfinance pandas numpy β†’ Execute build_indices.py --watchlist all
↓
subprocess call
INGEST
πŸ“‹
Load Both Watchlists
Stocks List.json (132)
MTF Stocks.json (216)
SYMBOL_OVERRIDES map
πŸ’Ύ
Delta Cache Check
Load data/history.json
Find last cached date
Fetch only new days
πŸ“‘
yfinance API
Fetch OHLCV from
Yahoo Finance
Rate limited: 1.5–3s
↓
raw OHLCV DataFrames
COMPUTE
πŸ“
Normalize Base 1000
Investment only
Each stock indexed to 1000
on Day 0 of its history
βš–οΈ
Equal-Weighted Indices
1 Master + 13 Sectors
Synthesized OHLC
Multi-timeframe returns
πŸ”¬
5 Quant Models
EMA Β· Momentum Β· Kinematic
Ornstein-Uhlenbeck Β· Fractal
Both watchlists
↓
computed JSON objects
OUTPUT
πŸ“
Investment JSONs
indices_summary Β· heatmap_data
sector_*.json Γ— 13
quant_models.json
πŸ“‚
MTF JSONs
mtf_heatmap_data
mtf_quant_models
Both in data/ + public/data/
↓
git commit β†’ git push β†’ origin/main
DEPLOY
☁️
Cloudflare Pages (Global CDN)
Auto-deploys on every push to main Β· Serves Vite-built static bundle from 300+ edge locations Β· No server needed
↓
HTTPS fetch()
FRONTEND
πŸ“ˆ
Index Chart View
TradingView Lightweight
Charts v5 Β· OHLC
Investment mode only
🟩
Market Treemap
D3-hierarchy squarified
Both watchlists
6 timeframe modes
πŸ”¬
Quant Models
5 scatter models
OLS regression overlay
Interactive watchlist table
πŸ“‘
Data Ingestion Layer
How we pull and cache 348 stocks from Yahoo Finance
πŸ”„ Delta Caching Strategy

Rather than re-downloading full history every run, the pipeline uses a delta cache approach:

1. On first run β†’ download 1 year of daily OHLCV data for every stock and save to data/history.json
2. On subsequent runs β†’ check the last date in cache per ticker, fetch only new trading days
3. Merge new rows into existing cache and re-save
4. The history.json file is excluded from the public/data/ sync (it can be very large and is not consumed by the frontend)

This reduces typical update time from ~15 minutes down to under 2 minutes.

⚠️ Anti-Ban Rate Limiting

Yahoo Finance throttles aggressive scrapers. The ingestion loop adds a random 1.5–3.0 second delay between each ticker fetch to avoid 429 errors.

time.sleep(random.uniform(1.5, 3.0))
πŸ”§ Ticker Symbol Overrides

Some stocks have mismatches between NSE symbol and Yahoo Finance ticker. A SYMBOL_OVERRIDES dict handles these edge cases:

"TRIL" β†’ "TARIL.NS" "INDEGENE" β†’ "INDGN.NS" "PRICOL" β†’ "PRICOLLTD.NS" "EMS" β†’ "EMSLIMITED.NS" "KPGEL" β†’ "KPGEL.BO" "LTIM" β†’ "540005.BO" # MTF override
πŸ“ Stock Metadata Formats

Stocks List.json (Investment) and MTF Stocks.json (Swing) both share this schema:

{ "ticker": "ZOMATO", "name": "Zomato Ltd", "sector": "Consumer Goods & Retail", "exchange": "NSE" }

The exchange field determines the suffix: NSE β†’ .NS BSE β†’ .BO

The --watchlist CLI flag controls which file is processed: --watchlist all (default in CI), --watchlist investment, or --watchlist mtf.

πŸ“
Index Math Engine
Equal-weighted index synthesis with Base 1000 normalization (Investment watchlist only)
πŸ“Œ The Core Formula

Every stock's price history is normalized to a base of 1000 on the first day it appears. This removes price bias β€” a β‚Ή5 stock and a β‚Ή5000 stock contribute equally.

Normalized Price(t) = (Raw Close(t) / Raw Close(Day 0)) Γ— 1000

The Index Value on any day is the arithmetic mean of all normalized prices in that sector:

Index(t) = Ξ£ [Normalized_i(t)] / N   (where N = number of stocks)

Since all stocks start at 1000 on Day 0, the index also starts at 1000 β€” exactly like how Nifty started at 1000 on November 3, 1995.

πŸ•―οΈ Synthesized OHLC Candlesticks

Each day's candle is synthesized:
β€’ Open = mean of all stocks' normalized open
β€’ High = mean of all stocks' normalized high
β€’ Low = mean of all stocks' normalized low
β€’ Close = mean of all stocks' normalized close

This gives a realistic candle with wicks and bodies, just like a real index candle.

πŸ“† Multi-Timeframe Returns

6 return windows computed per stock & index:
1D 1 trading day back
1W 5 trading days back
1M 21 trading days back
3M 63 trading days back
6M 126 trading days back
1Y full year (first record)

🏭 Sector Qualification Rule

A sector only gets its own sub-index if it has 4 or more stocks. Smaller sectors are included in the Master Index but don't get a standalone chart.

Result: 1 Master Index + 13 Sector Sub-Indices = 14 total indices across all 132 investment stocks.

🟩
Heatmap Engine
Finviz-style squarified treemap built with D3-hierarchy β€” works for both watchlists
πŸ—ΊοΈ Treemap Layout Algorithm

The heatmap uses D3's treemap() with the squarify tiling algorithm β€” the same technique used by Finviz and Bloomberg. It recursively subdivides a rectangle into child rectangles with aspect ratios as close to 1:1 as possible.

Tile size = absolute value of % return (or equal in Equal-Sizing mode). Bigger movers = bigger tiles.
Tile color = diverging red–green scale:

≀ βˆ’5%
βˆ’3%
βˆ’1%
~0%
+1%
+3%
β‰₯ +5%
⚑ Interactive Controls

β€’ Timeframe toggle β€” 1D / 1W / 1M / 3M / 6M / 1Y
β€’ Sector filter β€” show one sector at a time
β€’ Search β€” live ticker/name search
β€’ Return filter β€” click a stat chip to filter by return bucket
β€’ Hierarchy mode β€” Sector grouping vs Flat layout
β€’ Sizing mode β€” Equal tiles vs Magnitude-proportional tiles
β€’ Tile click β€” in MTF mode, navigates to Quant Models with that stock selected

πŸ“ Data Structure

heatmap_data.json / mtf_heatmap_data.json follow D3's hierarchical format:

{ "updated_at": "...", "total_stocks": 216, "sectors": [ { "name": "IT & Tech Services", "slug": "it_tech_services", "stocks": [ { "ticker": "INFY", "returns": { "1d": 1.2, "1w": 3.4 } } ] } ] }
πŸ”¬
Quantitative Scatter Models
5 institutional-grade cross-sectional models for stock selection
πŸ“Œ Architecture Overview

The Quant view renders 5 interactive scatter plots computed by build_indices.py using numpy + pandas. Each model takes the full price history of every stock and outputs per-stock (x, y) coordinates plus statistical metadata. The frontend (QuantScatterCanvas.tsx) renders these with a custom Canvas API renderer β€” no charting library β€” with OLS regression overlay, interactive tooltip, and click-to-lock selection.

The same 5 models are computed independently for both watchlists, outputting quant_models.json and mtf_quant_models.json.

Model 1 β€” EMA
EMA Mean Reversion Risk
X: % Distance from 50 EMA  |  Y: % Distance from 20 EMA
Identifies overbought rubber bands and pullback buy zones by plotting short-term vs medium-term EMA extension. Color encodes 200 EMA structural trend (Green = Bull, Red = Bear). Quadrant crosshair at (0,0) divides into 4 regimes. Stocks in Q3 (negative both axes, green) are the highest-quality pullback buys.
Model 2 β€” Momentum
Cross-Sectional Residual Momentum
X: 60-Day Log Return  |  Y: 5-Day Log Return
Identifies true idiosyncratic breakouts by regressing 5-day returns against 60-day returns. The OLS line shows the "expected" recent return given the longer trend. Stocks with studentized residuals above +2.0 (shown as outliers) are exhibiting genuine alpha β€” their recent move is not explained by the broad trend.
Model 3 β€” Kinematic
Velocity Γ— Acceleration State Space
X: Standardized Price Velocity  |  Y: Standardized Price Acceleration
Treats price as a physical system. Velocity = 10-day momentum normalized by 60-day std dev. Acceleration = second derivative (momentum of momentum). Q2 stocks (positive velocity, negative acceleration) are at inflection β€” they were going up but are decelerating, signaling an imminent trend change. Mahalanobis distance identifies statistical extremes.
Model 4 β€” O-U Process
Ornstein-Uhlenbeck Mean Reversion
X: AR(1) Mean Reversion Speed (Ξ²)  |  Y: Current Spread Z-Score
Fits an AR(1) process to each stock's log price spread from its 20-day SMA. The fitted Ξ² coefficient indicates mean-reversion speed; z-score indicates current displacement. Stocks with high Ξ² (elastic) and extreme z-scores are the most tradeable mean-reversion candidates. Half-life = -ln(2)/ln(Ξ²) days.
Model 5 β€” Fractal
Hurst Exponent Regime Classification
X: Hurst Exponent (H)  |  Y: Distance from 50-Day SMA
Computes the Hurst exponent via R/S (Rescaled Range) analysis on 60-day return windows. H < 0.4 = mean-reverting (anti-persistent), H β‰ˆ 0.5 = random walk, H > 0.6 = trending (persistent). Regime color-coding lets you quickly identify which stocks are in breakout-friendly trending regimes vs. chop. "Anti-Fade" stocks (H < 0.4, far from SMA) have the highest statistical probability of snapback.
πŸ–₯️ Frontend Components

β€’ QuantDashboard.tsx β€” top-level orchestrator; loads data, manages model selection state
β€’ QuantModelTabs.tsx β€” 5-tab selector with live signal count badges
β€’ QuantModelHUD.tsx β€” statistical summary panel (regression stats, signal counts, model metadata)
β€’ QuantScatterCanvas.tsx β€” custom Canvas2D renderer; handles OLS line, quadrant crosshairs, point hover/click, sector filter, search filter
β€’ QuantWatchlistTable.tsx β€” sortable table of all stocks in the active model with flags, sector filter, and search; clicking a row syncs the scatter canvas selection

Trading Workflow: For swing trading, the MTF heatmap quickly surfaces which sectors are moving today. Switching to Quant β†’ Residual Momentum shows which specific stocks are breaking out beyond what their sector's trend explains. Combining all 5 models gives a multi-dimensional view of the opportunity set in under 2 minutes.
βš™οΈ
Automation & CI/CD Pipeline
How the data stays fresh without manual intervention
πŸ“… Update Schedule
1:00 PM
IST
Mid-Day Update β€” cron 30 7 * * 1-5 (07:30 UTC)
Captures intraday price movement during active trading hours.
3:40 PM
IST
Post-Market Close Update β€” cron 10 10 * * 1-5 (10:10 UTC)
Runs 10 minutes after NSE closes. Captures final closing prices and computes all quant model outputs.
πŸ” GitHub Actions Workflow β€” 6 Steps
1
Checkout Repository
Uses actions/checkout@v4 to clone the full repo onto the runner.
2
Set Up Python 3.11 with pip cache
Uses actions/setup-python@v5 with cache: 'pip' to accelerate dependency installs on repeat runs.
3
Install Dependencies
Installs yfinance, pandas, numpy, and requests. numpy is required for quant model computation (Hurst exponent, OLS regression, Mahalanobis distance).
4
Execute build_indices.py --watchlist all
Processes both watchlists: loads cache β†’ fetches new OHLCV β†’ computes 14 indices + 5 quant models Γ— 2 watchlists β†’ writes all JSON files to data/.
5
Sync data/ β†’ public/data/ (excluding history.json)
Copies all generated JSONs (except the large history cache) to public/data/ so Vite's build can serve them. history.json is excluded to keep the bundle small.
6
Commit & Push Changes
Stages data/ and public/data/ folders. Uses git diff --staged --quiet to skip commit if nothing changed (market holiday). Commit message includes [skip ci] to prevent infinite CI loops.
Zero Cost Automation: GitHub Actions gives 2,000 free minutes/month. Each run takes ~3–4 minutes (both watchlists). 2 runs/day Γ— 22 market days = ~176 minutes/month β€” under 9% of the free quota.
πŸ–₯️
Frontend Architecture
React 18 + Vite + TypeScript β€” component tree and data flow
πŸ—οΈ Technology Migration

The frontend has been fully migrated from a vanilla HTML/JS single-file to a Vite + React 18 + TypeScript application. This enables:

β€’ Type-safe data contracts β€” all JSON shapes modelled in src/types/index.ts
β€’ React hooks for data fetching (useIndexData, useQuantData)
β€’ Hot Module Replacement during development
β€’ Production build via npm run build β†’ Cloudflare Pages auto-deploys from the dist/ output
β€’ Tailwind CSS utility classes for styling

πŸ“ Component Tree
App.tsx ← root: watchlist + view state β”œβ”€β”€ Navbar.tsx ← brand, watchlist toggle, view switcher, status β”œβ”€β”€ [view === 'chart'] β”‚ β”œβ”€β”€ SectorPills.tsx ← horizontal sector/index selector chips β”‚ β”œβ”€β”€ MetricHUD.tsx ← OHLC stats, 52w range, all-time return β”‚ β”œβ”€β”€ LightweightChart.tsx ← TradingView Lightweight Charts v5 wrapper β”‚ β”œβ”€β”€ ChartToolbar.tsx ← timeframe + candle/line mode toggle β”‚ β”œβ”€β”€ CrosshairHUD.tsx ← live crosshair OHLC overlay β”‚ └── ConstituentsTable.tsx ← sortable/searchable stock table β”œβ”€β”€ [view === 'heatmap'] β”‚ β”œβ”€β”€ HeatmapControls.tsx ← timeframe, sector, search, filter, mode toggles β”‚ └── HeatmapCanvas.tsx ← D3 squarified treemap on Canvas └── [view === 'quant'] └── QuantDashboard.tsx β”œβ”€β”€ QuantModelTabs.tsx ← 5-model tab selector with signal badges β”œβ”€β”€ QuantModelHUD.tsx ← regression stats, signal counts β”œβ”€β”€ QuantScatterCanvas.tsx ← custom Canvas2D scatter + OLS overlay └── QuantWatchlistTable.tsx ← flagged stocks table with sector filter
πŸ”— Data Hooks

useIndexData(watchlist) β€” fetches either:
β€’ Investment: indices_summary.json + heatmap_data.json in parallel
β€’ MTF: mtf_heatmap_data.json only (no summary needed)

useQuantData(watchlist) β€” fetches either:
β€’ Investment: quant_models.json
β€’ MTF: mtf_quant_models.json

Both hooks auto-refresh when the watchlist prop changes via useEffect dependency on the refresh callback.

⚑ LightweightCharts v5 API

TradingView made a breaking change in v5. Old API:

chart.addCandlestickSeries({}) // REMOVED in v5

Correct v5 API used in this project:

chart.addSeries(CandlestickSeries, {}) // v5
πŸ“
Project File Structure
Every file explained
The Ultimate Dashboard/ β”œβ”€β”€ index.html # Vite entry point (references src/main.tsx) β”œβ”€β”€ build_indices.py # Data pipeline β€” ingest, compute, export both watchlists β”œβ”€β”€ Stocks List.json # 132 investment stocks: name, ticker, sector, exchange β”œβ”€β”€ MTF Stocks.json # 216 MTF swing-trading stocks (β‰₯3.5x margin) β”œβ”€β”€ system_docs.html # This document β€” system architecture reference β”œβ”€β”€ package.json # npm deps: react, vite, typescript, tailwindcss, etc. β”œβ”€β”€ tailwind.config.ts # Tailwind CSS configuration β”œβ”€β”€ vite.config.ts # Vite build configuration β”‚ β”œβ”€β”€ src/ # React application source β”‚ β”œβ”€β”€ main.tsx # React root mount point β”‚ β”œβ”€β”€ App.tsx # Root component β€” all state lives here β”‚ β”œβ”€β”€ index.css # Global styles + Tailwind imports β”‚ β”œβ”€β”€ types/ β”‚ β”‚ └── index.ts # All TypeScript types (IndexMeta, HeatmapData, QuantData...) β”‚ β”œβ”€β”€ hooks/ β”‚ β”‚ β”œβ”€β”€ useIndexData.ts # Watchlist-aware data fetching for heatmap + index summary β”‚ β”‚ └── useQuantData.ts # Watchlist-aware data fetching for quant model JSONs β”‚ β”œβ”€β”€ utils/ β”‚ β”‚ └── formatters.ts # Date/number formatting utilities β”‚ └── components/ β”‚ β”œβ”€β”€ Navbar.tsx # Brand + watchlist toggle + centered view switcher + status β”‚ β”œβ”€β”€ SectorPills.tsx # Horizontal index selector (investment mode only) β”‚ β”œβ”€β”€ MetricHUD.tsx # OHLC stats, 52w range, all-time return cards β”‚ β”œβ”€β”€ LightweightChart.tsx # TradingView Lightweight Charts v5 React wrapper β”‚ β”œβ”€β”€ ChartToolbar.tsx # Candle/Line toggle + timeframe selector β”‚ β”œβ”€β”€ CrosshairHUD.tsx # Live OHLC display on chart hover β”‚ β”œβ”€β”€ ConstituentsTable.tsx # Sortable/searchable stock table for index view β”‚ β”œβ”€β”€ HeatmapControls.tsx # All heatmap filter controls β”‚ β”œβ”€β”€ HeatmapCanvas.tsx # D3-hierarchy Canvas treemap renderer β”‚ β”œβ”€β”€ HeatmapTooltip.tsx # Heatmap hover tooltip β”‚ β”œβ”€β”€ Footer.tsx # Last updated timestamp footer β”‚ └── quant/ β”‚ β”œβ”€β”€ QuantDashboard.tsx # Quant view root β€” model selection + data orchestration β”‚ β”œβ”€β”€ QuantModelTabs.tsx # 5-tab model selector with live badge counts β”‚ β”œβ”€β”€ QuantModelHUD.tsx # Stats panel for the active model β”‚ β”œβ”€β”€ QuantScatterCanvas.tsx # Custom Canvas2D scatter plot + OLS overlay β”‚ └── QuantWatchlistTable.tsx # Signal flagged-stocks table β”‚ β”œβ”€β”€ .github/workflows/ β”‚ └── update_indices.yml # GitHub Actions cron: runs build_indices.py twice daily β”‚ β”œβ”€β”€ data/ # Auto-generated by build_indices.py (gitignored for large files) β”‚ β”œβ”€β”€ history.json # Delta cache: raw OHLCV for all 348 stocks (NOT in public/) β”‚ β”œβ”€β”€ indices_summary.json # Investment: 14 indices with metadata + constituents β”‚ β”œβ”€β”€ heatmap_data.json # Investment: D3 hierarchy + multi-timeframe returns β”‚ β”œβ”€β”€ quant_models.json # Investment: 5 quant model outputs β”‚ β”œβ”€β”€ mtf_heatmap_data.json # MTF: D3 hierarchy treemap data β”‚ β”œβ”€β”€ mtf_quant_models.json # MTF: 5 quant model outputs for 216 stocks β”‚ └── sector_*.json Γ— 13 # Per-sector OHLC series for index chart β”‚ └── public/data/ # Synced subset of data/ (consumed by Vite dev + Cloudflare) # Same files as data/ except history.json
πŸ› οΈ
Technology Stack
Every library, service, and tool β€” all free and open-source

The entire system is built on free, open-source, and zero-cost components.

Backend / Data Pipeline
🐍
Python 3.11
Core scripting language for the entire data pipeline
v3.11
πŸ“Š
yfinance
Yahoo Finance API wrapper for downloading OHLCV data
latest
🐼
pandas
DataFrame operations, date alignment, return computation
latest
πŸ”’
numpy
OLS regression, Hurst exponent, Mahalanobis distance for quant models
latest
Frontend / Framework
βš›οΈ
React 18
Component tree, hooks, state management for the dashboard
v18
⚑
Vite
Build tool + dev server. Lightning-fast HMR during development
v5
πŸ”·
TypeScript
Full type safety across components, hooks, and JSON shapes
v5
πŸ’¨
Tailwind CSS
Utility-first CSS framework for all component styling
v3
Visualization Libraries
πŸ“ˆ
TradingView Lightweight Charts
High-performance candlestick & line chart rendering (Index Chart view)
v5.0
πŸ“
D3-hierarchy
Squarified treemap layout algorithm for the Heatmap view
v7.x
🎨
Canvas API
Custom scatter plot renderer in QuantScatterCanvas.tsx β€” no chart lib dependency
Native
πŸ–‹οΈ
Google Fonts
Inter, Space Grotesk, JetBrains Mono typography
CDN
Automation & Deployment
βš™οΈ
GitHub Actions
Cron-based CI/CD. Runs build_indices.py twice daily on Mon–Fri
ubuntu-latest
☁️
Cloudflare Pages
Global CDN hosting. Auto-deploys Vite dist/ on every git push to main
Free tier
πŸ™
GitHub
Version control + static data store. JSON files serve as the "database"
Free tier
πŸ’°
Total Monthly Cost
100% free-tier. No credit card required. $0 forever.
$0.00 / mo
πŸš€
How to Run Locally
Get the full pipeline + dashboard running on your machine
Step 1 β€” Install Python Dependencies
# Create a virtual environment (recommended) python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # Install required packages pip install yfinance pandas numpy requests
Step 2 β€” Run the Data Pipeline
# Process both watchlists (recommended, matches CI behavior) python build_indices.py --watchlist all # Or process only one watchlist: python build_indices.py --watchlist investment python build_indices.py --watchlist mtf # First run fetches ~1 year of history for all stocks # Takes 15-30 mins due to rate limiting (1.5-3s per stock) # Subsequent runs are fast β€” delta only (new days) ~2-4 minutes
Step 3 β€” Sync data/ to public/data/
# The React dev server serves files from public/ # Copy generated JSONs (exclude the large history cache) rsync -av --exclude="history.json" data/ public/data/ # On Windows (PowerShell): # robocopy data public\data /XF history.json /E
Step 4 β€” Start the React Dev Server
# Install Node.js dependencies (first time only) npm install # Start the Vite dev server with HMR npm run dev # Opens at: http://localhost:5173
Step 5 β€” Build for Production (Optional)
# Build the optimized production bundle npm run build # Preview the production build locally npm run preview # The dist/ folder is what Cloudflare Pages deploys automatically # on every git push to main.
Note on data/ vs public/data/: build_indices.py writes to data/ (your local cache). The React app reads from public/data/ in development and /data/ at runtime in production (served from Cloudflare Pages). Always sync after running the pipeline locally.