Skip to content

Repository files navigation

BullBearPK

AI-assisted investment analysis for the Pakistan Stock Exchange.

License: MIT CI Python Flask React LangGraph Docker

BullBearPK takes a user's budget, sector preference, risk tolerance, time horizon and investment goal, runs it through a ten-stage agent pipeline against real PSX market data, and returns sized, reasoned buy/sell/hold recommendations — then executes whatever the user decides through a single, transactional portfolio-management path.

This README describes what the system actually does today, not an aspirational roadmap. Known gaps are called out explicitly in Current limitations rather than smoothed over.


Contents


What it does

  1. A user submits an investment profile (budget, sector preference, risk tolerance, time horizon, target profit) from the React frontend.
  2. The backend hands that profile to a LangGraph-orchestrated pipeline of ten agents, which fetches live PSX data (with a database fallback if the feed is down), computes technical indicators from real accumulated history, gathers and scores news sentiment, checks the user's risk profile, past decisions, and current portfolio, then sizes and reasons about a set of recommendations — the reasoning is written by a real LLM call (Groq) grounded in the computed numbers, with a template fallback if no API key is configured.
  3. Whatever the user decides (buy / sell / hold) is executed through one transactional code path (portfolio_manager.py): cash is checked and deducted atomically, sells target specific lots FIFO, and nothing is written on a partial failure.
  4. A separate, offline backtester validates whether the recommendation logic actually correlates with what stocks did afterward, using the same indicator math that runs in production — not a parallel, unverified implementation.

Architecture

BullBearPK system architecture

The frontend never talks to MySQL directly. The Flask API is the only thing that does, and the agentic core is invoked as a single call from the /api/hybrid route — it is not a separate service. Standalone diagram source: Workflow/bullbearpk-architecture.svg.

Agentic pipeline

BullBearPK agentic recommendation pipeline

# Agent Responsibility Source
1 Input Taker Validates budget, risk tolerance, time horizon, sector preference backend/agents/input_taker.py
2 Stock Scraper Live PSX daily-activity data via a direct JSON feed; falls back to the last known DB snapshot, never fabricated prices backend/agents/fin_scraper.py
3 Stock Analyzer RSI, MACD, moving averages, Bollinger Bands computed from real accumulated stock_history backend/agents/advanced_stock_analyzer.py
4 News Scraper Fetches news only for top movers without an already-fresh (<6h) article set backend/agents/news_scraper.py
5 News Analyzer Sentiment scoring with confidence gating and recency decay backend/agents/news_analyzer.py
6 Risk Checker Quantifies the user's risk profile backend/agents/risk_checker.py
7 Past Investments Checker Reviews this user's prior decisions backend/agents/past_investments_checker.py
8 Portfolio Checker Reads current holdings, cash and diversification at live prices backend/agents/portfolio_checker.py
9 Recommendation Agent Sizes positions by risk tier and budget; Groq LLM writes the reasoning (template fallback if unset) backend/agents/recommendation_agent.py
10 Manager Record Agent Validates and delegates the user's decision to the Portfolio Manager backend/agents/manager_record_agent.py

Backtester (backend/agents/backtester.py) sits outside the live request path. It reuses the Stock Analyzer's real indicator math to walk forward through accumulated history with no lookahead bias, and to check past recommendations against what actually happened — reporting sample size honestly rather than presenting a handful of days as a proven track record.

Every stage records why it degraded (stale cache, empty upstream data) instead of silently substituting fabricated numbers behind a success: true response — see the warnings array returned alongside each recommendation.


Tech stack

Layer Choices
Frontend React 18, Vite, TypeScript, Zustand, Tailwind CSS, Recharts, Framer Motion, Axios
Backend Flask, Gunicorn, JWT auth + bcrypt, Flask-Limiter, mysql-connector-python (pooled)
Agentic orchestration LangGraph state machine (see Agentic pipeline)
LLM Groq (chat assistant + recommendation reasoning); optional — every LLM-backed feature has a real, working fallback
Data acquisition Direct JSON feed for PSX data (no browser automation), RSS/feedparser + BeautifulSoup for news, TextBlob for sentiment
Database MySQL 8, schema-file-driven with a real tracked migration runner
Testing pytest (backend), Vitest (frontend)
CI/CD GitHub Actions
Deployment Docker + Docker Compose

Data model

16 tables, defined in backend/database/mysql_schema.sql — the single source of truth for the schema (init_database.py applies this file directly; nothing hand-maintains a parallel copy of it).

Table Holds
users Identity, risk tolerance, cash balance, portfolio totals
stocks / stock_history Latest snapshot per PSX ticker, and one real row per stock per trading day for indicator math
stock_analysis Computed technical indicators per stock
news_records / news_analysis Raw scraped articles and their sentiment scores
portfolios / investments Portfolio summaries and individual holdings
recommendations / user_recommendations_history Generated recommendations and the user-facing history of them
user_form_submissions Each investment-profile submission, for returning-user comparisons
user_feedback Ratings/feedback on recommendations
user_settings Per-user preferences
ai_chat_messages Real chat history for the AI assistant
market_summary Daily index-level summary (KSE-100 etc.)
system_logs Structured application log records
schema_migrations Tracks which migration_*.sql files have been applied (created by database/migrate.py)

Evolving the schema: add a new backend/database/migration_*.sql file, then run python backend/database/migrate.py. It applies anything not yet recorded as run, rolls back cleanly if a migration fails partway through, and --status lists applied/pending without changing anything. mysql_schema.sql already contains every existing migration's effects, so a fresh database gets them all marked as applied via --baseline without re-running them (init_database.py does this automatically).


Getting started

Prerequisites

  • Python 3.11+
  • Node.js 20+
  • MySQL 8.0+

Backend

cd backend
python -m venv .venv
.venv\Scripts\activate        # Windows; use `source .venv/bin/activate` on Linux/macOS
pip install -r requirements.txt

Copy .env.example (repo root) to .env and fill in real values — at minimum DB_PASSWORD and SECRET_KEY. GROQ_API_KEY is optional; without it, the chat assistant returns 503 and recommendations fall back to template-based reasoning.

python init_database.py   # applies mysql_schema.sql and marks it as the baseline
python api_server.py      # dev server on :5000

Frontend

cd frontend
npm install
npm run dev                # dev server on :3001, proxies /api to :5000

Build-time API location is configurable via VITE_API_BASE_URL / VITE_WEBSOCKET_URL (see frontend/src/constants/index.ts) — required for any deployment where the browser and backend aren't on the same machine. Defaults to localhost:5000 for local dev.


Running with Docker

Runs MySQL, Redis, the backend, and the frontend together, with the schema loaded automatically on first start against an empty volume.

cp .env.example .env
# edit .env: set SECRET_KEY and DB_PASSWORD at minimum; GROQ_API_KEY is optional
docker compose up -d --build
  • Frontend: http://localhost:3001
  • Backend API: http://localhost:5000 (health check at /)
  • MySQL: localhost:3306, for connecting a GUI client — not needed by the app itself
  • Redis: backs shared rate-limit counters across the backend's gunicorn workers (see backend/rate_limiter.py) — not exposed on a host port, nothing else needs it

All four services define real Docker healthchecks — frontend won't start until backend reports healthy, and backend won't start until both mysql and redis do. docker compose ps shows each service's health status.

docker compose down stops everything; add -v to also delete the MySQL data volume. Schema changes after the first run must be applied manually via backend/database/migrate.py — the compose file only loads mysql_schema.sql once.


Testing & CI

# Backend
cd backend
pip install -r requirements-dev.txt
python -m pytest -v --capture=no  # --capture=no works around a pytest capture-teardown
                                   # crash triggered by this project's heavy transitive
                                   # deps (see .github/workflows/ci.yml for details)

# Frontend
cd frontend
npx vitest run          # unit tests
npx tsc --noEmit        # type check
npx vite build           # production bundle

GitHub Actions runs backend tests against a real MySQL service container, then type-checks and builds the frontend — both the type check and the bundle build are blocking; tsc --noEmit is clean (0 errors). See .github/workflows/ci.yml.


Current limitations

Reported plainly, not rounded up:

  • Backtesting is honest about small samples, not about being wrong: with limited accumulated history, results are reported but explicitly flagged as not yet statistically meaningful (see MIN_SIGNALS_FOR_MEANINGFUL_RESULT in backtester.py).
  • npm ci needs npm 11+. The committed lockfile isn't reliably consumable by the older npm (~10.x) that ships with node:20-slim/GitHub's Node 20 setup — both the Docker build and CI pin npm install -g npm@11 before npm ci to work around this rather than weakening the build to npm install.

Contributing

See CONTRIBUTING.md for the development workflow, coding conventions, and what to include in a pull request.

License

MIT — see LICENSE.md.

Built for the Pakistan Stock Exchange investment community.

About

agentic-trading-advisor is a full-stack AI investment platform for the Pakistan Stock Exchange (PSX). Using LangGraph, 10 specialized AI agents automate real-time market scraping, NLP sentiment analysis, and technical indicators to deliver personalized portfolio strategies. Built for scale with React, Flask, and MySQL.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages