Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AI Investment Research Agent

A FastAPI-based backend that accepts a company name, gathers financial data and news, and returns an Invest or Pass decision with detailed reasoning powered by an LLM (OpenAI or Groq).

Architecture

Client → POST /auth/signup ──→ JWT token
Client → POST /auth/login  ──→ JWT token

Client → POST /research (JWT Bearer)
           │
           ├── WebSearchTool (DuckDuckGo) → news & web data
           ├── FinancialDataTool (yfinance) → price, P/E, market cap, etc.
           │
           └── LLM (OpenAI / Groq) → structured JSON decision
                │
                └── Response: {decision, reasoning, supporting_data}

The system uses a linear pipeline:

  1. Auth — user signs up or logs in to receive a JWT token.
  2. Data gathering — two tools run in parallel (web search + yfinance).
  3. LLM analysis — collected data is injected into a prompt that instructs a chat model (OpenAI GPT-3.5-turbo or Groq Llama 3 70B) to act as an investment analyst and produce a structured JSON output.
  4. Validation — the JSON is parsed and validated against a Pydantic model before being returned.

Requirements

  • Python 3.10+
  • An API key for OpenAI (gpt-3.5-turbo) or Groq (llama3-70b-8192 — free tier available)
  • Internet access for DuckDuckGo search and yfinance

Setup

# 1. Clone / copy the project
cd ai-investment-agent

# 2. Create virtual environment
python -m venv venv
source venv/bin/activate

# 3. Install dependencies
pip install -r requirements.txt

# 4. Configure environment
cp .env.example .env
# Edit .env with your API keys and change JWT_SECRET

# 5. Run the server
uvicorn main:app --host 0.0.0.0 --port 8000

Environment Variables

Variable Default Description
LLM_PROVIDER openai "openai" or "groq"
OPENAI_API_KEY Your OpenAI API key
GROQ_API_KEY Your Groq API key
LLM_MODEL_NAME gpt-3.5-turbo Model name (see provider docs)
MAX_SEARCH_RESULTS 5 Max DuckDuckGo search results
LOG_LEVEL INFO INFO or DEBUG
JWT_SECRET Secret key for signing JWT tokens (change to a random string)
JWT_ALGORITHM HS256 JWT signing algorithm
JWT_EXPIRE_MINUTES 1440 Token expiry in minutes (24h)

API Reference

Health check

GET /health

No auth required.

curl http://localhost:8000/health
{"status": "ok"}

Signup

POST /auth/signup

No auth required. Creates a new user and returns a JWT token.

curl -X POST http://localhost:8000/auth/signup \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "your-password"}'

201 Created

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer"
}

409 Conflict — email already registered

{"detail": "Email already registered"}

422 Unprocessable — validation error (e.g., invalid email)

{
  "detail": [
    {
      "type": "value_error",
      "loc": ["body", "email"],
      "msg": "value is not a valid email address"
    }
  ]
}

Login

POST /auth/login

No auth required. Authenticates and returns a JWT token.

curl -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "your-password"}'

200 OK

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer"
}

401 Unauthorized — wrong email or password

{"detail": "Invalid email or password"}

Research

POST /research

Requires Authorization: Bearer <token> header.

curl -X POST http://localhost:8000/research \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -d '{"company_name": "Apple"}'

200 OK

{
  "decision": "Pass",
  "reasoning": "Apple remains a fundamentally strong company...",
  "supporting_data": {
    "financial_metrics": {
      "current_price": 283.78,
      "market_cap": 4167977926656,
      "pe_ratio": 34.36,
      "fifty_two_week_high": 317.4,
      "fifty_two_week_low": 199.26,
      "revenue_growth": 0.166,
      "short_ratio": 2.88,
      "dividend_yield": 0.38
    },
    "search_summary": "Title: Apple Inc. (AAPL) Stock Price, News..."
  },
  "error": null
}

401 Unauthorized — missing or invalid token

{"detail": "Not authenticated"}

Authentication Flow

  1. SignupPOST /auth/signup with email + password → returns JWT token.
  2. LoginPOST /auth/login with email + password → returns JWT token (same format).
  3. Use — pass the token in the Authorization: Bearer <token> header for /research.
  4. Expiry — tokens expire after JWT_EXPIRE_MINUTES (default 24h). Login again to get a new one.
  5. Storage — user credentials are stored hashed in users.db (SQLite).

Project Structure

├── .env.example         # Environment variable template
├── requirements.txt     # Python dependencies
├── README.md            # This file
├── main.py              # FastAPI app, CORS, auth + research endpoints
├── core/
│   ├── __init__.py
│   ├── config.py        # Pydantic Settings from env vars
│   ├── database.py      # Async SQLite — user table, CRUD helpers
│   ├── auth.py          # JWT create/verify, password hashing, auth dependency
│   ├── agent.py         # LangChain chain: tools → LLM → JSON
│   ├── tools.py         # @tool: web_search_tool, financial_data_tool
│   ├── models.py        # Pydantic models (request/responses + auth schemas)
│   └── utils.py         # Logging setup
└── tests/
    ├── __init__.py
    └── test_dummy.py    # Placeholder

Trade-offs

Decision Rationale
DuckDuckGo (free) instead of Google/Bing API No API key needed, though results are less comprehensive.
yfinance (free) instead of Bloomberg/Alpha Vantage Simple, zero-cost stock data. May be rate-limited for heavy use.
SQLite instead of PostgreSQL Zero setup, file-based — ideal for a VPS. Swap to Postgres for scale.
Simple linear pipeline instead of a ReAct agent loop Easier to debug and maintain; avoids extra LLM calls.
Partial data mode If a tool fails, the LLM still produces a decision with available data rather than returning a 500 error.

Potential Improvements

  • Add caching (e.g., Redis) for repeated company lookups.
  • Integrate SEC filings (Edgar API) for fundamental analysis.
  • Use a ReAct agent for multi-step reasoning (e.g., look up ticker, then fetch data).
  • Add sentiment analysis on news headlines using a local model.
  • Add rate limiting for production deployment.
  • Deploy with Docker and a reverse proxy (nginx / Caddy).

About

Backend of Assignment

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages