Automated tracker for all NSE-listed stocks. Scrapes live prices every 10 minutes during trading hours, updates a formatted Excel workbook, and emails you a portfolio summary every Friday β all running free on GitHub Actions with no server required.
- Live market data β scrapes all NSE-listed stocks from a live public NSE data source every 10 minutes during trading hours, no API key needed
- Formatted Excel workbook β two sheets: a live Market sheet and a personal Portfolio sheet with auto-calculated gain/loss
- Weekly email report β HTML email every Friday at 5:00 PM EAT showing your full portfolio performance
- Zero infrastructure β runs entirely on GitHub Actions free tier; no server, no database, no cloud bill
- Configurable β trading hours, email schedule, and data source all live in
config.json, no code changes needed - CLI flags β
--force,--start,--end,--send-emailfor local testing and overrides
GitHub Actions (cron, every 10 min)
β
βΌ
stock_poller.py
β
βββΊ GET live NSE data source
β
βββΊ Write Market sheet βββΊ stock_prices.xlsx
β Ticker β Company β Sector β Price β Change % β Volume β Updated
β
βββΊ Update Portfolio sheet (VLOOKUP formulas against Market sheet)
β Ticker β Shares β Buy Price β Current Price β Value β Gain/Loss
β
βββΊ Friday 5 PM EAT β send HTML email via Gmail SMTP
| Ticker | Company | Sector | Price (KES) | Change (%) | Volume | Last Updated |
|---|---|---|---|---|---|---|
| SCOM | Safaricom PLC | Telecommunication | 30 | +0.61% | 4,821,300 | 2026-02-27 14:00 EAT |
| EQTY | Equity Group Holdings | Banking | 75 | +2.68% | 1,203,400 | 2026-02-27 14:00 EAT |
| KCB | KCB Group PLC | Banking | 50 | -0.97% | 983,100 | 2026-02-27 14:00 EAT |
| ... |
Columns AβE are yours to fill in. Columns FβI are auto-calculated via VLOOKUP from the Market sheet.
| # | Ticker | Company | Shares Owned | Buy Price (KES) | Current Price | Current Value | Gain/Loss | Gain/Loss % |
|---|---|---|---|---|---|---|---|---|
| 1 | SCOM | (auto) | 1,000 | 30.00 | (auto) | (auto) | (auto) | (auto) |
A styled HTML email delivered every Friday at 5:00 PM EAT showing total invested, current value, total gain/loss, and a per-stock breakdown.
- A GitHub account (free)
- A Gmail account with 2-Step Verification enabled
git clone https://github.com/IdahK/biashara-watch-public.git
cd biashara-watch-publicOr click Fork at the top right of this page to create your own copy.
cp config.example.json config.jsonEdit config.json if you want to change trading hours or the email schedule. The defaults match NSE hours (9 AMβ5 PM EAT, MonβFri).
In your repo β Settings β Actions β General β Workflow permissions β select Read and write permissions β Save.
Go to Settings β Secrets and variables β Actions β New repository secret and add:
| Secret | Value |
|---|---|
GMAIL_ADDRESS |
Your Gmail address |
GMAIL_APP_PASS |
A Gmail App Password (16 chars, not your login password) |
NOTIFY_EMAIL |
Where to send the weekly report (can be same as above) |
Go to Actions β biasharaWatch β NSE Stock Tracker β Run workflow. After ~60 seconds, stock_prices.xlsx will appear in your repo. Download it and open in Excel or Google Sheets.
- Download
stock_prices.xlsx - Open the My Portfolio sheet
- Fill in: Column B (Ticker), Column D (Shares Owned), Column E (Buy Price in KES)
- Upload the file back to your repo
The script will read your entries and fill in live prices and gain/loss on every run.
# Install dependencies
pip install -r requirements.txt
# Copy and edit config
cp config.example.json config.json
# Run anytime β bypasses the trading hours check
python stock_poller.py --force
# Override trading hours for this run
python stock_poller.py --start 8 --end 18
# Force-send the weekly email right now
python stock_poller.py --force --send-email| Flag | Description |
|---|---|
--force / -f |
Run regardless of trading hours |
--start HOUR |
Override start hour for this run (e.g. --start 8) |
--end HOUR |
Override end hour for this run (e.g. --end 18) |
--send-email |
Send the weekly portfolio email immediately |
All settings live in config.json β edit this file, never the script:
{
"trading_hours": {
"start": 9,
"end": 17,
"days": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"timezone": "EAT (UTC+3)"
},
"email": {
"send_on": "Friday",
"send_at_hour": 17
},
"output_file": "stock_prices.xlsx",
"source_url": "https://example.com/nse-data"
}If you change start/end, also update the cron lines in .github/workflows/stock_poller.yml to match (cron runs in UTC; EAT = UTC+3, so subtract 3 hours).
biashara-watch-public/
βββ stock_poller.py # Main script
βββ config.example.json # Config template (copy to config.json)
βββ requirements.txt # Python dependencies
βββ .gitignore # Blocks config.json and stock_prices.xlsx
βββ README.md
βββ SETUP.md # Detailed step-by-step setup guide
βββ .github/
βββ workflows/
βββ stock_poller.yml # GitHub Actions schedule & job
config.jsonandstock_prices.xlsxare in.gitignoreβ they stay local/private and are never committed to the public repo.
| Problem | Fix |
|---|---|
| Workflow not visible in Actions tab | Ensure the workflow file is at exactly .github/workflows/stock_poller.yml |
| Push permission error | Re-check Step 3 β workflow needs read/write permissions |
[ERROR] Data source structure changed |
The upstream data source updated its page β open an issue |
| Email not arriving | Double-check secrets; check spam folder; verify App Password has 2FA enabled |
| Script exits immediately locally | Add --force flag to bypass the trading hours check |
| Empty columns in Excel | Delete stock_prices.xlsx from the repo and re-run β the old file has a stale column layout |
| Tool | Role |
|---|---|
| Python 3.11 | Core scripting |
requests + BeautifulSoup4 |
HTTP fetch + HTML parsing |
openpyxl |
Excel workbook generation and formatting |
| GitHub Actions | Free cron scheduling and execution |
| Gmail SMTP | Weekly email delivery |
NSE market data is fetched from a live public data source.