Skip to content
View Pmeet's full-sized avatar
🏠
Working from home
🏠
Working from home

Block or report Pmeet

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
Pmeet/README.md

₹ Paisa Radar

A free India investing research web app for stocks and mutual funds — discover, filter, compare, and track without a paywalled screener.

Built on open, official data sources:

Source What it provides How it's used
AMFI NAVAll.txt Daily NAVs, scheme names, AMCs, SEBI categories for every MF scheme Parsed on each refresh into the fund universe
NSE index API (equity-stockIndices) Index constituents with price, change, volume, 52-week range Fetched per configured index (NIFTY 50 / NEXT 50 / MIDCAP 50 by default)
mfapi.in (optional) Free per-scheme NAV history Computes 1Y/3Y/5Y returns during refresh
SEBI category rules Riskometer-style classification Fallback risk inference by scheme category

No paid screener or subscription endpoint is used anywhere. Every source URL is an environment variable, so a moved endpoint is a config change, not a code change.

Features (MVP)

  • Stock discovery — filter by sector, market-cap bucket, and index; sort by gainers, losers, volume, price, or P/E; paginated.
  • Mutual fund discovery — filter by category, AMC, risk label, AUM floor, and minimum 1Y return; sort by returns or AUM.
  • Unified search — one box searches symbols, company names, sectors, scheme names, AMCs, and sub-categories, ranked by match quality.
  • Compare — 2 to 5 assets side by side (stocks and funds can be mixed; each group gets its own metric table). The best numeric value per row is highlighted. Comparisons are shareable URLs.
  • Watchlist — save any asset with one tap (stored in localStorage, shaped for a future account-backed API). Alerts are a visible phase-2 placeholder.
  • Plain-language metrics — hover/long-press any metric label for a simple explanation.

Architecture

Browser ── pages (Next.js App Router, Tailwind)
   │
   ▼
API routes (/api/stocks /funds /search /compare /meta /audits /refresh)
   │
   ▼
store.ts — 3-tier data access
   1. in-memory cache (5 min TTL, per instance)
   2. Postgres (Supabase/Neon) when DATABASE_URL is set
   3. bundled seed JSON (data/seed/) — zero-config fallback
   ▲
refresh.ts — scheduled ingestion (Vercel Cron → /api/refresh)
   ├─ sources/amfi.ts → transforms/amfi.ts   (NAV dump → funds)
   ├─ sources/nse.ts  → transforms/nse.ts    (index payloads → stocks)
   └─ sources/amfi.ts → computeReturns       (NAV history → 1Y/3Y/5Y)

Design decisions:

  • Transforms are pure functions (src/lib/transforms/) with no I/O — they carry the test coverage and make the pipeline transparent and replayable.
  • Merging never destroys data. AMFI's dump has no AUM/returns; NSE payloads can be partial. Refreshes merge field-by-field, so a missing value never erases a known one.
  • Every refresh is audited (refresh_audit table, /api/audits) with source, timing, row counts, and error messages. Raw payloads are cached in raw_snapshots.
  • The API layer is the only data path the UI uses, so a future mobile app gets the same contract for free.

Getting started

npm install
npm run dev        # http://localhost:3000 — works immediately on seed data
npm test           # vitest: data-transform and query tests

No environment variables are required for local development: without DATABASE_URL the app serves the bundled example data in data/seed/ (illustrative sample values, not live quotes — regenerate with node scripts/generate-seed.mjs).

Environment variables

Copy .env.example to .env.local and fill in what you need:

Variable Required Purpose
DATABASE_URL no Postgres (Supabase/Neon). Enables durable storage + audit log.
CRON_SECRET in prod Bearer token protecting /api/refresh. Vercel Cron sends it automatically.
AMFI_NAV_URL no AMFI NAV dump (defaults to the official URL).
NSE_INDEX_URL, NSE_HOME_URL no NSE API endpoint template + cookie-handshake origin.
NSE_INDICES no Comma-separated indices to ingest.
MF_HISTORY_URL no NAV-history API template for returns enrichment ({CODE} placeholder).

Database setup (optional but recommended for production)

  1. Create a Postgres database (Supabase or Neon free tier is fine).
  2. Set DATABASE_URL (use the pooled connection string on serverless).
  3. Apply the schema: DATABASE_URL=... npm run db:schema
  4. Trigger a first refresh: curl -H "Authorization: Bearer $CRON_SECRET" https://<app>/api/refresh?source=all

Refreshing data

GET/POST /api/refresh?source=all|amfi|nse|returns (Bearer-token protected):

  • amfi — pulls the full NAV dump (~15k schemes) and merges NAVs/categories.
  • nse — pulls each configured index; partial failures are recorded, not fatal.
  • returns — enriches up to 50 growth-plan schemes per run with 1Y/3Y/5Y returns from the NAV-history API (rate-limit friendly; coverage accumulates across runs).
  • Every run writes an audit row; inspect via GET /api/audits.

vercel.json schedules a weekday refresh at 12:30 UTC (6 pm IST, after AMFI publishes daily NAVs).

Live stock quotes during market hours

Stock prices update near-live (default: at most 3 minutes old) while NSE is open (09:15–15:30 IST, Mon–Fri), without needing high-frequency crons:

  • Server: any stock request during market hours checks data age; if stale, a background refresh (Next.js after()) pulls fresh NSE quotes once — with per-instance locking and a 90-second attempt backoff so traffic spikes can't stampede NSE.
  • Client: stock lists and detail pages re-poll every 60 seconds while the market is open and the tab is visible, and show a pulsing Live badge with the quote time (IST). When the market is closed, polling stops and the badge shows "Market closed".
  • A 15-minute grace window after the close captures closing prices.
  • Tune with LIVE_REFRESH_SECONDS; list NSE holidays in NSE_HOLIDAYS to skip polling.

This is polling of NSE's public API, not exchange tick data — quotes can lag by a couple of minutes and NSE may throttle data-center IPs. When a refresh fails, the app keeps serving the last good data, the failure lands in the audit log, and the badge's timestamp makes the data age visible.

Known constraint: NSE's API sits behind a cookie handshake and rate-limits aggressively from data-center IPs. The fetcher does the handshake and degrades gracefully (partial data + audit entry); if it is blocked entirely, the app keeps serving the last good data (DB or seed). For production-grade stock coverage, point NSE_INDEX_URL at your own mirror/proxy of the official data.

Deploying to Vercel

  1. Push this repo to GitHub and import it in Vercel (framework auto-detected: Next.js).
  2. Add environment variables: DATABASE_URL, CRON_SECRET (and any source overrides).
  3. Deploy. The cron in vercel.json is picked up automatically (Hobby plan allows daily crons; the default schedule works on all plans).
  4. Apply the DB schema once from your machine: DATABASE_URL=... npm run db:schema.
  5. Hit /api/refresh?source=all once with the bearer token to populate real data.

API reference

Endpoint Query params Notes
GET /api/stocks q, sector, cap, index, sort, page, pageSize sort prefix - = descending
GET /api/funds q, category, subCategory, amc, risk, minAum, maxAum, minReturn1y, sort, page, pageSize
GET /api/search q, limit Unified, ranked across both types
GET /api/compare ids=stock:RELIANCE,fund:120465 2–5 ids; unknown ids reported in missing
GET /api/meta Distinct sectors/categories/AMCs for filter UIs
GET /api/audits Recent refresh audit log
GET /api/refresh source Bearer-token protected

Roadmap (built for, not yet built)

  • Alerts — the watchlist already reserves a per-item alert slot; a cron route can evaluate rules and notify.
  • Account-backed watchlists — the localStorage shape (kind + id) imports directly into a watchlists table keyed by user.
  • Portfolio tracking — buy/sell transactions on top of the same normalized tables.
  • More sources — additional indices via NSE_INDICES; AMFI AUM downloads; SEBI statistics for validation.

Disclaimer

Data may be delayed, incomplete, or (in seed mode) illustrative sample data. This is a research tool, not investment advice. Verify with official sources — AMFI, NSE, SEBI, and AMC pages — before making investment decisions.

Pinned Loading

  1. dash dash Public

    Forked from plotly/dash

    Analytical Web Apps for Python, R, Julia, and Jupyter. No JavaScript Required.

    Python

  2. client-python client-python Public

    Forked from civo/client-python

    Python package to interact with Civo's API

    Python

  3. chalice chalice Public

    Forked from aws/chalice

    Python Serverless Microframework for AWS

    Python

  4. cookiecutter cookiecutter Public

    Forked from cookiecutter/cookiecutter

    A cross-platform command-line utility that creates projects from cookiecutters (project templates), e.g. Python package projects, C projects.

    Python

  5. Gooey Gooey Public

    Forked from chriskiehl/Gooey

    Turn (almost) any Python command line program into a full GUI application with one line

    Python

  6. openverse-catalog openverse-catalog Public

    Forked from WordPress/openverse-catalog

    Identifies and collects data on cc-licensed content across web crawl data and public apis.

    Python