GitHub - genothomas/dividendcal: Dividend Calendar · GitHub
Skip to content

Repository files navigation

dividendcal

NSE equity dividend calendar — upcoming ex-dates, recent payouts, and per-stock detail with price history, dividend records, and quarterly results.

Flask + SQLite + gunicorn. Data from NSE bhavcopy, NSE corporate-actions API, and Yahoo Finance. No JavaScript framework.

┌─ /                upcoming + recent ex-dates, search, sort
├─ /stocks          all NSE stocks, sort A-Z/gainers/losers/volume, paginated
├─ /stock/<SYM>     per-stock: price chart, facts, dividends, results
├─ /feed.json       JSON feed of upcoming dividends
├─ /sitemap.xml     sitemap
├─ /robots.txt      robots
└─ /api/*           manual refresh + bulk backfill

Quick start (local)

# Clone & install
git clone <repo-url> dividendcal
cd dividendcal
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Initialize DB + load bundled seeds (~2,900 stocks, 150 dividends)
python scripts/init_db.py

# Pull live data from NSE (one-time, ~5s)
python scripts/refresh_now.py

# Serve
gunicorn --bind 127.0.0.1:8000 wsgi:app
# or with hot-reload during dev:
flask --app wsgi run --debug --port 8000

Open http://127.0.0.1:8000.

Deploy (Fly.io)

The repo is configured for Fly.io out of the box.

brew install flyctl
fly auth signup
fly launch --no-deploy     # creates app, configures region
fly volumes create dividendcal_data --size 1 --region blr
fly secrets set SECRET_KEY="$(openssl rand -hex 32)" REFRESH_TOKEN="$(openssl rand -hex 24)"
fly deploy

Persistent SQLite volume at /app/data. The Dockerfile's CMD runs init_db.py then gunicorn on container start; wsgi.py schedules a daily NSE refresh at 16:30 IST.

Data sources

Data Primary Fallback
Price history (1Y) Yahoo Finance via yfinance NSE bhavcopy archives
Today's price (live) Yahoo fast_info NSE bhavcopy close
Dividend history NSE corp-actions API (per-symbol) bundled seed
Quarterly results NSE results-comparison API Yahoo quarterly_income_stmt
Symbol + company name NSE equity list bundled seed
Latest ex-dates (today) NSE corp-actions bulk bundled seed

On-demand fetching

Most data is fetched on first visit to a stock page and cached forever:

  • /stock/RELIANCE (cold) → ~1-2s → fetches 1Y history from Yahoo, 16 dividends from NSE, 5 quarters from NSE
  • /stock/RELIANCE (warm) → ~25ms → served from SQLite
  • Daily refresh (POST /api/refresh or scheduler) → updates prices + recent dividends

Manual triggers:

# Full daily refresh (bhavcopy + corp-actions + equity list)
curl -X POST https://<app>.fly.dev/api/refresh

# Bulk backfill quarterly results (~0.8s/symbol; 50 symbols ~40s)
curl -X POST "https://<app>.fly.dev/api/backfill-results?limit=50&key=$TOKEN"

Architecture

app/
├── __init__.py        # Flask app factory
├── routes.py          # / /stocks /stock/<sym> /feed.json /sitemap /robots /api/*
├── db.py              # SQLite schema + queries (WAL mode)
├── nse.py             # NSE fetcher (bhavcopy, corp-actions, equity list)
├── yahoo.py           # Yahoo Finance wrapper (price history + quote)
├── refresh.py         # daily refresh + on-demand backfill
├── formatting.py      # ₹, lakhs, %, date helpers
├── errors.py          # 404 / 500 handlers
├── templates/         # Jinja: base, index, stocks, stock, 404, 500
└── static/style.css   # newspaper theme (IBM Plex + Spectral)

data/seed/             # bundled CSVs (NSE bhavcopy snapshot for offline demo)
scripts/               # init_db, refresh_now, backfill_results, cleanup
wsgi.py                # gunicorn entry point + APScheduler for daily refresh

Schema (SQLite + WAL)

stocks        (symbol PK, company, isin)
prices       (symbol, date, close, prev_close, volume)   -- daily OHLCV
actions      (id, symbol, ex_date, record_date, purpose, amount, action_type, source)
results      (symbol, period_end, consolidated, income_lakhs, pbt_lakhs, pat_lakhs, eps)
refresh_meta (source, last_run, rows, ok, message)

Indexes: idx_prices_symbol, idx_prices_date, idx_actions_ex_date, idx_actions_symbol.

Testing + linting

pytest tests/ -v          # 24 tests, ~5s
ruff check app/ scripts/   # lint

Environment variables

Var Default Purpose
SECRET_KEY dev-secret-change-me Flask session signing
DATABASE_URL sqlite:///data/dividendcal.db DB path
NSE_USER_AGENT (browser UA) NSE blocks non-browser UAs
NSE_REFRESH_ENABLED true Master switch for refresh
REFRESH_TOKEN empty Required for POST /api/refresh / backfill-results
RUN_SCHEDULER true Start APScheduler thread on boot
REFRESH_HOUR_UTC / REFRESH_MINUTE_UTC 11 / 0 Daily refresh time (16:30 IST)

License

MIT

Disclaimer

Data is provided for information only, not investment advice. Data sourced from NSE public endpoints and Yahoo Finance. The site is not affiliated with NSE or Yahoo.

About

Dividend Calendar

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages