Mobile-first NSE candlestick pattern scanner with liquidity box analysis, risk scoring (0-100), and batch index scanning. Built with React 18 + Vite 6, deployed as a PWA to GitHub Pages.
Educational only — not financial advice.
Live: https://utkarsh9891.github.io/candlescan/
- Features
- Tech Stack
- Quick Start
- npm Scripts
- Project Structure
- Data Flow
- Environment Differences
- Deployment
- Local Chart Cache
- Testing
- Versioning
- Debug Mode
- Gate Auth Flow
- Rate Limiting
- Troubleshooting
- License
- Enter any NSE symbol (e.g. RELIANCE, TCS, HDFCBANK)
- 6 timeframes: 1m, 5m, 15m, 30m, 1h, 1d
- Pattern detection: 8 categories, ~46 rules (engulfing, hammer, reversal, pullback, liquidity, momentum, indecision, piercing)
- Liquidity box: Consolidation zone detection with breakout/trap analysis
- Risk score: 0-100 confidence with trade action (STRONG BUY / BUY / WAIT / SHORT / STRONG SHORT / NO TRADE)
- Trade levels: Entry, Stop Loss, Target, Risk:Reward ratio
- Context: Support / Resistance / Breakout / Mid-Range detection
- Custom SVG candlestick renderer (no external charting library)
- Zoom (ctrl+wheel / pinch), pan (scroll/swipe), crosshair
- Drawing tools: horizontal lines (draggable) and boxes (directional +/- display)
- Pattern highlight overlay (toggleable)
- Liquidity box overlay with manipulation zones
- Scan all stocks in an NSE index (NIFTY 50 to NIFTY 500)
- Results sorted by signal strength (STRONG BUY/SHORT first)
- Filter by actionable/all and by direction (Buy/Short)
- Search within results
- Tap any result to drill into single-stock analysis
- Background persistence — scan continues while you browse individual stocks
- Auth-gated: passphrase required (see Worker OPS)
- Install on Android via Chrome "Add to Home Screen"
- Standalone mode (no browser chrome)
- Service worker caches app shell for fast loads
- Auto-update: When a new version is deployed, the service worker detects the change and updates automatically on next visit (Workbox
autoUpdatestrategy)
- Add any NSE index via hamburger menu → "Custom Indices" section
- Validates against live NSE API before adding
- Persists in localStorage, appears in all dropdowns with "(custom)" label
- Remove via minus button in the menu
- Yahoo Finance (default, free) — 1-2 minute delay, no subscription needed
- Zerodha Kite Connect (premium, ₹2000/month) — real-time, requires API subscription
- Dhan HQ (premium, ₹499/month) — real-time, requires Data API subscription + TOTP
All scan modes (Stock, Batch, Simulation, Paper Trading) automatically use the configured data source.
- Historical Simulation — bar-by-bar replay of a trading day with zero lookahead
- Engine-agnostic: runs with any pattern engine variant (scalp/intraday/classic)
- Tracks P&L, win rate, max drawdown, transaction costs
- Best-signal-first: picks top candidates by confidence at each bar
- Live Simulation — real-time price polling with live signal detection
- Scan index → pick signals → track live → get notified → see P&L → repeat
| Layer | Technology |
|---|---|
| UI | React 18 (hooks, no Redux) |
| Build | Vite 6 |
| Chart | Custom SVG (no D3/Chart.js) |
| Data | Yahoo Finance v8 chart API |
| Data (alt) | Zerodha Kite Connect API (optional, premium) |
| Data (alt) | Dhan HQ Data API (optional, premium) |
| Index data | NSE India equity-stockIndices API |
| CORS proxy | Cloudflare Worker (production) |
| PWA | vite-plugin-pwa (Workbox) |
| Deploy | GitHub Actions → GitHub Pages |
| Auth | RSA + SHA-256 gate auth via Cloudflare Worker |
| Rate limit | Cloudflare KV (20 req/day free tier, unlimited premium) |
Runtime dependencies: React + React DOM only. Everything else is custom code.
git clone https://github.com/utkarsh9891/candlescan.git
cd candlescan
npm install
npm startOpen http://127.0.0.1:5173/candlescan/
# Generates simulated candle data for testing
# Add ?simulate=1 to the URL after starting
npm start
# → http://127.0.0.1:5173/candlescan/?simulate=1| Script | Purpose |
|---|---|
npm start / npm run dev |
Vite dev server at 127.0.0.1:5173 with Yahoo + NSE proxy |
npm test |
Run all unit tests (Vitest) |
npm run test:watch |
Run tests in watch mode |
npm run build |
Production build → dist/ (base: /candlescan/) |
npm run preview |
Serve built dist/ locally (no dev proxy, uses CORS fallbacks) |
npm run pages |
Build + copy to sibling ../utkarsh9891.github.io/candlescan/ (legacy) |
npm run test:batch |
Node script: scan NSE index using cache/charts/ |
npm run cache:charts |
Warm local chart cache from Yahoo for all index symbols |
candlescan/
├── index.html # Vite entry + PWA meta tags
├── vite.config.js # Build config, dev proxies, PWA plugin
├── vite-plugin-chart-cache.mjs # Dev-only chart cache middleware
├── package.json # React 18, Vite 6, vite-plugin-pwa
├── public/
│ └── icons/ # PWA icons (192x192, 512x512 SVG)
├── src/
│ ├── main.jsx # React root
│ ├── App.jsx # Root component: all state, scan logic, view routing
│ ├── components/
│ │ ├── Chart.jsx # SVG chart: candles, zoom, pan, crosshair, drawings
│ │ ├── SimpleView.jsx # Compact result card (price, action, score ring)
│ │ ├── AdvancedView.jsx # Extended view (quote, timer, breakdown)
│ │ ├── BatchScanPage.jsx # Index scanner: progress, results, filters
│ │ ├── SimulationPage.jsx # Historical bar-by-bar simulation
│ │ ├── PaperTradingPage.jsx # Live paper trading with real-time polling
│ │ ├── SettingsPage.jsx # Premium gate, data source, Zerodha/Dhan credentials
│ │ ├── GlobalMenu.jsx # Hamburger menu: signal filters + page nav
│ │ ├── Header.jsx # Brand, status badge, mode toggle
│ │ ├── SearchBar.jsx # Symbol input + autocomplete + scan
│ │ ├── TimeframePills.jsx # 1m/5m/15m/25m/30m/1h/1d selector
│ │ ├── DrawingToolbar.jsx # H-Line, Box, Clear
│ │ ├── UpdatePrompt.jsx # SW + GitHub Release update detection
│ │ ├── IndexConstituentsSidebar.jsx # Slide-out stock list
│ │ ├── RiskRing.jsx # Circular confidence gauge
│ │ ├── RiskScoreSignals.jsx # 5-component breakdown display
│ │ └── EmptyState.jsx # Placeholder before first scan
│ ├── engine/
│ │ ├── fetcher.js # Yahoo Finance fetch + fallback chain
│ │ ├── zerodhaFetcher.js # Zerodha Kite Connect API fetcher
│ │ ├── dhanFetcher.js # Dhan HQ Data API fetcher
│ │ ├── dataSourceFetch.js # Data source switch (Yahoo/Zerodha/Dhan)
│ │ ├── patterns.js # Intraday pattern detection (8 categories)
│ │ ├── patterns-scalp.js # Scalp-specific patterns (VWAP, ORB, etc.)
│ │ ├── patterns-classic.js # Classic swing patterns (MA cross, S/R, channels)
│ │ ├── liquidityBox.js # Consolidation box + breakout/trap
│ │ ├── risk.js # Intraday 5-component risk scoring (0-100)
│ │ ├── risk-scalp.js # Scalp risk scoring (maxHoldBars ≤ 15)
│ │ ├── risk-classic.js # Classic swing risk scoring
│ │ ├── simulateDay.js # Bar-by-bar trading simulation engine
│ │ ├── batchScan.js # Throttled multi-stock scanner (progressive results)
│ │ ├── nseIndexFetch.js # NSE index constituent fetcher
│ │ ├── nseIndexParse.js # NSE JSON parser
│ │ └── yahooQuote.js # Yahoo v7 quote (bid/ask)
│ ├── config/
│ │ └── nseIndices.js # Index list + defaults
│ ├── data/
│ │ └── signalCategories.js # Category labels + rule counts
│ └── utils/
│ ├── batchAuth.js # Gate auth — passphrase hashing + localStorage helpers
│ └── credentialVault.js # RSA credential encryption + vault storage
├── worker/
│ ├── index.js # Cloudflare Worker: CORS proxy + auth + rate limiting
│ ├── wrangler.toml # Worker config + KV binding
│ └── OPS.md # Operations guide (passphrase reset, deploy)
├── scripts/
│ ├── start.sh # Dev server wrapper
│ ├── deploy-to-pages.sh # Legacy manual deploy
│ ├── simulate-day.mjs # CLI trading simulation
│ ├── warm-chart-cache.mjs # Pre-warm chart cache
│ ├── rotate-keys.sh # RSA key pair generation + CF Worker deployment
│ └── lib/
│ ├── chart-cache-fs.mjs # Disk-based chart cache read/write
│ └── nse-http.mjs # NSE HTTP helpers for Node
├── docs/
│ └── ZERODHA_SETUP.md # Zerodha integration setup guide
├── cache/
│ └── charts/ # Local chart JSON cache (gitignored)
└── .github/
└── workflows/deploy.yml # Auto-deploy to Pages on push to main
User enters symbol
→ normalizeSymbol() (RELIANCE → RELIANCE.NS, NIFTY50 → ^NSEI)
→ createFetchFn(dataSource) → fetchOHLCV(symbol, timeframe)
→ Yahoo: Vite proxy → CF Worker → Jina Reader → allorigins
→ Zerodha: CF Worker → decrypt vault → Kite API
→ Dhan: CF Worker → decrypt vault → Dhan Charts API
→ detectPatterns(candles) # engine-specific (scalp/intraday/classic)
→ detectLiquidityBox(candles) # consolidation zone detection
→ computeRiskScore({candles, patterns, box})
→ 5 components: signal clarity + noise + R:R + reliability + confluence
→ Action: STRONG BUY/SHORT (≥72), BUY/SHORT (≥58), WAIT (≥50), NO TRADE
→ Render: Chart + SimpleView/AdvancedView
User selects index + timeframe → Scan All
→ fetchNseIndexSymbolList(index) # NSE API → symbol list
→ batchScan({symbols, timeframe, gateToken})
→ 5 concurrent fetches, 200ms delay between batches
→ Per stock: fetchOHLCV → patterns → box → risk
→ Sort by action rank + confidence
→ Display result cards (filterable, searchable, tappable)
| Feature | Dev (npm start) |
Production (Pages) |
|---|---|---|
| Yahoo data | Vite proxy (same-origin) | CF Worker + fallback chain |
| NSE data | Vite proxy | CF Worker + allorigins |
| Chart cache | Local disk (cache/charts/) |
None (network only) |
| Demo data | ?simulate=1 available |
Disabled |
| Service worker | Registered | Auto-update on deploy |
| CORS | No issues | Proxied via CF Worker |
Push to main triggers GitHub Actions:
npm ci+npm run build- Deploy
dist/to GitHub Pages - Live at https://utkarsh9891.github.io/candlescan/
Setup (one-time): Repo Settings → Pages → Source: "GitHub Actions"
See docs/WORKER_OPS.md for full deployment and passphrase management guide.
cd worker
npx wrangler deployDev-only disk cache at cache/charts/. See cache/charts/README.md.
- Auto-populated on first fetch in dev
- 7-day TTL (configurable via
CANDLESCAN_CHART_CACHE_MAX_AGE_MS) - Disable:
CANDLESCAN_CHART_CACHE=0 npm start - Warm all:
npm run cache:charts
npm test # Run all tests once
npm run test:watch # Watch mode (re-runs on file change)47 tests across 6 test files covering:
src/engine/patterns.test.js— pattern detection (bullish/bearish engulfing, hammer, edge cases)src/engine/risk.test.js— risk scoring (confidence range, direction, action labels, context detection)src/engine/liquidityBox.test.js— box detection (consolidation, breakout, empty input)src/engine/fetcher.test.js— utility functions (trimTrailingFlatCandles, timeframe map)src/config/nseIndices.test.js— custom index add/remove/dedup/mergesrc/utils/batchAuth.test.js— token get/set/clear/has
Test fixtures at src/engine/__fixtures__/candles.js — reusable candle data sets.
Tests + build run automatically before every git push. If either fails, the push is blocked.
- Auto-configured via
npm install(uses.git-hooks/pre-push) - Bypass in emergencies:
git push --no-verify
GitHub Actions runs on every push to main:
npm ci— clean installnpm test— all unit tests must passnpm run build— production build- Smoke check — verifies
dist/index.htmlexists and contains "CandleScan" - Deploy to GitHub Pages
If tests or build fail, deployment is blocked.
Pre-1.0 — this project is in active development. Version v0.x.y signals the app is not yet production-ready.
Version is derived from git tags at build time — no hardcoding required. CI auto-increments the patch number on every merge to main.
# After each merge to main, CI auto-tags:
# v0.5.0 → v0.5.1 → v0.5.2 → ...
# Manual minor bump (milestone):
git tag v0.6.0
git push origin v0.6.0- Source:
git describe --tags --always(run at build time invite.config.js) - Display: hamburger menu bottom — e.g.
v0.5.3-2-gabcdef 31 Mar - CI: GitHub Actions fetches full git history (
fetch-depth: 0) so tags are available - Full guide: See
docs/GIT_WORKFLOW.md
In-app API call inspector — toggle via hamburger menu → "Debug mode" checkbox.
When enabled:
- Bottom panel shows all
fetch()calls in real-time - Each entry: timestamp, HTTP status (color-coded), response time, URL
- Key icon indicates requests with gate token
- Shows CF Worker proxy destinations (chart data, NSE index, quotes)
- "Clear" button to reset log
- Last 50 requests retained
Use to verify: gate token is being sent, requests aren't 429/403, response times are reasonable.
- User enters passphrase → SHA-256 hash stored in localStorage (
candlescan_gate_hash) - POST /gate/unlock with hash → CF Worker validates → returns RSA public key
- Zerodha credentials encrypted with RSA public key → stored in localStorage
- API requests send
X-Gate-Tokenheader + encrypted vault blob - CF Worker decrypts vault with RSA private key → proxies to Kite API
Environment variable on the CF Worker: GATE_PASSPHRASE_HASH (see docs/WORKER_OPS.md).
- Free tier: 20 req/day per IP, batch scan disabled
- Premium (valid gate token): unlimited, batch scan + Zerodha enabled
Rate limits are enforced via Cloudflare KV. See docs/WORKER_OPS.md for configuration.
| Issue | Fix |
|---|---|
| Blank page on Pages | Check base: '/candlescan/' in vite.config.js |
| No chart data on Pages | Try different network; CORS proxies may be blocked |
npm start fails |
Run npm install first |
| Gate auth 403 | Wrong passphrase — see docs/WORKER_OPS.md |
| Rate limited | 20/day per IP; unlock premium for unlimited |
| Zerodha 401 | Access token expired — re-enter in Settings |
| Dhan 429 | Rate limit — Dhan allows limited requests per minute; batch scan auto-throttles |
| Dhan timestamps wrong | Worker deployed before timestamp fix — redeploy with cd worker && npx wrangler deploy |
| Vault decrypt error | Keys rotated — re-enter credentials in Settings |
| Demo data not showing | ?simulate=1 only works in dev, not production |
| Browse stocks empty | Redeploy CF Worker with NSE allowlist; or NSE is down |
See LICENSE in this repository.