Skip to content

Repository files navigation

ZeraPortfolio Advisor

Post-market geopolitical + fundamental intelligence for your Zerodha portfolio.

ZeraPortfolio is an AI-powered portfolio analysis system that runs after Indian market close (3:30 PM IST) every trading day. It fetches your live Zerodha holdings, scrapes fundamentals from Screener.in, pulls geopolitical signals from World Monitor, and runs a swarm of Google Gemini-powered AI agents to produce a comprehensive daily portfolio report — delivered to your dashboard and WhatsApp.


Table of Contents


Features

  • Live Portfolio Sync — Fetches holdings from Zerodha Kite Connect with 1-hour cache
  • Fundamental Analysis — Scrapes P/E, ROE, ROCE, debt ratios, quarterly profits from Screener.in
  • Geopolitical Intelligence — Maps your portfolio sectors to global geopolitical signals
  • AI Agent Swarm — 4 specialist Gemini agents analyze your portfolio in parallel
  • Daily Reports — Auto-generated at market close with executive summary, per-stock briefings, and actionable recommendations
  • Bloomberg-style Dashboard — Dark terminal aesthetic with risk badges, heatmaps, and sparklines
  • PDF Reports — Server-side HTML-to-PDF generation via WeasyPrint
  • WhatsApp Delivery — PDF sent to your phone via Twilio after each report

Architecture

┌──────────────┐     ┌──────────────┐     ┌──────────────────┐
│   Zerodha    │     │ Screener.in  │     │  World Monitor   │
│  Kite API    │     │  (Scraper)   │     │   (Sidecar)      │
└──────┬───────┘     └──────┬───────┘     └────────┬─────────┘
       │                    │                      │
       └────────────────────┼──────────────────────┘
                            │
                   ┌────────▼────────┐
                   │   FastAPI       │
                   │   Backend       │
                   │   (Port 8000)   │
                   └────────┬────────┘
                            │
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
     ┌──────────────┐ ┌──────────┐ ┌───────────┐
     │   Gemini     │ │ Neon.db  │ │  Twilio   │
     │  AI Agents   │ │ Postgres │ │  WhatsApp │
     └──────────────┘ └──────────┘ └───────────┘
                            │
                   ┌────────▼────────┐
                   │  React Frontend │
                   │   (Port 5173)   │
                   └─────────────────┘

Agent Workflow

ZeraPortfolio uses a parallel fan-out + sequential synthesis pattern powered by Google ADK and Gemini:

                        ┌─────────────────────┐
                        │    Orchestrator      │
                        │  (asyncio.TaskGroup) │
                        └──────────┬──────────┘
                                   │
                    ┌──────────────┼──────────────┐
                    ▼              ▼              ▼
          ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
          │  Geopolitical│ │ Fundamental  │ │    Risk      │
          │    Agent     │ │    Agent     │ │    Agent     │
          │ (per stock)  │ │ (per stock)  │ │ (portfolio)  │
          │ gemini-flash │ │ gemini-flash │ │ gemini-flash │
          └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
                 │                │                 │
                 └────────────────┼─────────────────┘
                                  │
                        ┌─────────▼──────────┐
                        │  Synthesis Agent   │
                        │   gemini-2.5-pro   │
                        └────────────────────┘
                                  │
                                  ▼
                          Final Report JSON

How it works step by step:

  1. Trigger — At 3:30 PM IST (or via manual POST /api/reports/trigger), the scheduler kicks off a report.

  2. Data Collection

    • Portfolio Agent fetches live holdings from Zerodha Kite Connect API
    • Screener Scraper fetches fundamentals (P/E, ROE, ROCE, debt, quarterly profits) for each stock with polite 2–5s delays
    • World Monitor Client fetches geopolitical signals, mapping each stock's NSE sector to global risk categories
  3. Parallel Analysis (all run concurrently via asyncio.TaskGroup)

    • Geopolitical Agent (1 per stock) — Assesses how current geopolitical events affect each holding. Uses gemini-2.0-flash for speed. Outputs risk level (HIGH/MEDIUM/LOW), time horizon, key regions, and recommended action.
    • Fundamental Agent (1 per stock) — Analyzes financial ratios and quarterly trends. Uses gemini-2.0-flash. Outputs fundamental strength, key risks/positives, valuation assessment, and analyst note.
    • Risk Agent (1 per portfolio) — Evaluates portfolio-level risks: sector concentration, single-stock overweight (>20%), correlation clusters, missing diversification. Outputs a risk score (1–10).
  4. Synthesis — The Synthesis Agent receives all outputs and produces a unified report using gemini-2.5-pro (deeper reasoning). It generates:

    • Executive Summary (3–5 sentences)
    • Portfolio Health Score (1–10)
    • Per-Holding Briefings with combined geo + fundamental view
    • Portfolio-Level Risk Assessment
    • Actionable Recommendations (max 5, prioritized)
    • Market Context paragraph
  5. Delivery

    • Report JSON stored in Neon.db
    • HTML rendered via Jinja2 (Bloomberg Terminal aesthetic)
    • PDF generated via WeasyPrint
    • PDF sent to WhatsApp via Twilio
    • Dashboard auto-refreshes to show the new report

Agent Error Handling

Each agent is wrapped in try/except. If one stock's analysis fails, the others continue. The synthesis agent handles missing inputs gracefully. One bad stock never crashes the whole pipeline.


Tech Stack

Layer Technology
LLM Google Gemini (gemini-2.0-flash for speed, gemini-2.5-pro for synthesis) via Google AI Studio
Agent Framework Google ADK (google-adk)
Backend FastAPI + Uvicorn
Database Neon.db (PostgreSQL via asyncpg + sqlalchemy[asyncio])
Frontend React 18 + Vite + TailwindCSS + Recharts
PDF WeasyPrint (HTML → PDF)
Messaging Twilio WhatsApp API
Scheduling APScheduler (cron-style)
Broker Zerodha Kite Connect API
Scraping httpx + BeautifulSoup4

Project Structure

portfolio_advisor/
├── README.md
├── CLAUDE.md                          # AI assistant instructions
├── LICENSE
├── docker-compose.yml
├── gstack.yaml
├── .gitignore
│
├── backend/
│   ├── .env.example                   # Environment variables template
│   ├── Dockerfile
│   ├── requirements.txt
│   ├── main.py                        # FastAPI app entry + lifespan
│   ├── config.py                      # Pydantic settings
│   ├── scheduler.py                   # APScheduler cron job
│   │
│   ├── db/
│   │   ├── base.py                    # SQLAlchemy async engine
│   │   ├── models.py                  # ORM models (reports, portfolio_cache, zerodha_tokens)
│   │   └── migrations/               # Alembic migrations
│   │
│   ├── integrations/
│   │   ├── zerodha_mcp.py            # Zerodha Kite Connect client
│   │   ├── screener.py               # Screener.in scraper
│   │   └── world_monitor.py          # World Monitor sidecar client
│   │
│   ├── agents/
│   │   ├── schemas.py                # All Pydantic output schemas
│   │   ├── gemini_client.py          # Shared Gemini structured output
│   │   ├── orchestrator.py           # Swarm orchestrator (fan-out + synthesis)
│   │   ├── portfolio_agent.py        # Portfolio data fetcher
│   │   ├── geopolitical_agent.py     # Geo risk analyst (per stock)
│   │   ├── fundamental_agent.py      # Fundamental analyst (per stock)
│   │   ├── risk_agent.py             # Portfolio risk manager
│   │   └── synthesis_agent.py        # Report assembler (gemini-2.5-pro)
│   │
│   ├── report/
│   │   ├── generator.py              # HTML → PDF via WeasyPrint
│   │   └── templates/
│   │       └── report.html           # Bloomberg-style Jinja2 template
│   │
│   ├── delivery/
│   │   └── twilio_sender.py          # WhatsApp PDF sender
│   │
│   └── api/
│       ├── auth.py                    # Zerodha OAuth flow (automated token)
│       ├── health.py                  # GET /health
│       ├── portfolio.py               # GET /api/portfolio/live
│       └── reports.py                 # All /api/reports/* endpoints
│
├── world_monitor_sidecar/
│   ├── Dockerfile
│   └── server.py                      # FastAPI wrapper for worldmonitor
│
└── frontend/
    ├── Dockerfile
    ├── nginx.conf
    ├── package.json
    ├── tsconfig.json
    ├── vite.config.ts
    ├── tailwind.config.ts
    ├── index.html
    └── src/
        ├── main.tsx
        ├── App.tsx
        ├── index.css
        ├── store.ts                   # Zustand global state
        ├── api/
        │   └── client.ts             # Typed API client
        ├── pages/
        │   ├── Dashboard.tsx          # Main dashboard
        │   └── ReportDetail.tsx       # Full report view
        └── components/
            ├── PortfolioTable.tsx      # Holdings table with badges
            ├── ReportCard.tsx          # Latest report summary card
            ├── GeopoliticalHeatmap.tsx # World map risk visualization
            └── RiskGauge.tsx           # Health score gauge

Getting Started

Prerequisites

  • Python 3.12+
  • Node.js 18+
  • Homebrew (macOS) — for WeasyPrint system dependencies
  • PostgreSQL (or a Neon.db account for serverless Postgres)
  • Google AI Studio API key — free at aistudio.google.com
  • Zerodha Kite Connect API key and secret — from kite.trade
    • Set the redirect URL to http://127.0.0.1:8000/api/auth/zerodha/callback in your Kite Connect app
  • Twilio account (optional, for WhatsApp delivery) — from twilio.com

Installation

# Clone the repository
git clone https://github.com/yourusername/zeraportfolio-advisor.git
cd zeraportfolio-advisor

# System dependencies (macOS — required for PDF generation)
brew install pango

# Backend setup
python3 -m venv .venv
source .venv/bin/activate
pip install -r backend/requirements.txt

# Frontend setup
cd frontend
npm install
cd ..

Configuration

# Copy the example env file
cp backend/.env.example backend/.env

Edit backend/.env and fill in your credentials:

# Required
GOOGLE_API_KEY=your-google-ai-studio-key
DATABASE_URL=postgresql+asyncpg://user:pass@host/dbname
ZERODHA_API_KEY=your-kite-api-key
ZERODHA_API_SECRET=your-kite-secret
APP_SECRET_KEY=any-random-secret-string

# Optional (for WhatsApp delivery)
TWILIO_ACCOUNT_SID=your-twilio-sid
TWILIO_AUTH_TOKEN=your-twilio-token
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886
TWILIO_WHATSAPP_TO=whatsapp:+91XXXXXXXXXX

Note: You do NOT need to set ZERODHA_ACCESS_TOKEN manually. The app has a built-in OAuth flow that handles token generation and storage automatically.

Running Locally

You need 3 terminal tabs:

Tab 1 — World Monitor Sidecar:

cd ~/path/to/portfolio_advisor
source .venv/bin/activate
cd world_monitor_sidecar
uvicorn server:app --port 8001

Tab 2 — Backend (FastAPI):

cd ~/path/to/portfolio_advisor
source .venv/bin/activate
cd backend
uvicorn main:app --reload --port 8000

Tab 3 — Frontend (Vite):

cd ~/path/to/portfolio_advisor/frontend
npm run dev

Step 4 — Connect Zerodha (once per day):

Open http://localhost:8000/api/auth/zerodha/login in your browser. Log in with your Zerodha credentials. The app automatically exchanges the request token for an access token and saves it to the database. You'll see a green "Zerodha Connected!" confirmation page.

The Zerodha token expires daily at ~6 AM IST. Visit the login link again each morning to re-authenticate.

Step 5 — Open Dashboard & Run Analysis:

Go to http://localhost:5173. Click the "Run Analysis" button in the top-right corner to generate your first report on demand. It will ask for your APP_SECRET_KEY (from .env) once and remember it. Reports also auto-generate daily at 3:30 PM IST via the scheduler.

The Vite dev server proxies all /api/* and /health requests to the backend at port 8000.

Running with Docker

# Make sure backend/.env is configured
docker compose up --build

This starts all 3 services:

  • Backend on port 8000
  • Frontend on port 5173
  • World Monitor sidecar on port 8001

API Reference

Method Endpoint Description
GET /health System health check (DB, Zerodha, World Monitor)
GET /api/auth/zerodha/login Redirects to Zerodha login (OAuth flow)
GET /api/auth/zerodha/callback OAuth callback — auto-exchanges token and saves to DB
GET /api/auth/zerodha/status Check if a valid Zerodha token exists
GET /api/portfolio/live Current holdings from Zerodha (1-hour cache)
GET /api/reports List reports (last 30 days)
GET /api/reports/latest Most recent completed report
GET /api/reports/{id} Full report by ID
GET /api/reports/{id}/pdf Download report PDF
POST /api/reports/trigger Manually trigger report generation (requires X-API-Key header)

Manual Trigger (CLI)

curl -X POST http://localhost:8000/api/reports/trigger \
  -H "X-API-Key: your-app-secret-key"

Or use the "Run Analysis" button on the dashboard — it does the same thing with a visual progress indicator.


WhatsApp Setup (Twilio)

  1. Go to Twilio Console → Messaging → Try it out → Send a WhatsApp message
  2. Join the sandbox by sending join <sandbox-word> to +1 415 523 8886 on WhatsApp
  3. Set TWILIO_WHATSAPP_TO in your .env to your WhatsApp number with country code (e.g., whatsapp:+919876543210)

The system will send the daily PDF report to your WhatsApp after each successful generation.


Troubleshooting

WeasyPrint / PDF generation fails

WeasyPrint requires system libraries (pango, glib, gobject). On macOS:

brew install pango

The dashboard and reports work without it — only PDF download will be unavailable until pango is installed.

Zerodha token expired

Visit http://localhost:8000/api/auth/zerodha/login to re-authenticate. Tokens expire daily at ~6 AM IST.

Neon.db connection error

Make sure your DATABASE_URL uses postgresql+asyncpg:// (not postgresql://) and ?ssl=require (not ?sslmode=require&channel_binding=require).


License

This project is licensed under the MIT License — see the LICENSE file for details.


Built by Neelakandan — post-market intelligence, every trading day.

About

ZeraPortfolio is a agent that run analysis of your zerodha portfolio at the end of the day and send the report to your whatsapp

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages