- 🇺🇸 English (this page)
- 🇪🇸 Español
A global stock market research and watchlist platform built with FastAPI and PostgreSQL.
Track stocks across 14 markets, read live news, and write timestamped research notes with price snapshots — all through a clean REST API.
Live: https://first-order-a3v1.onrender.com/
- Watchlist — add any stock by symbol, auto-created if not in database
- Live prices — fetches real-time data for any Yahoo Finance supported symbol
- Market news — recent headlines per stock, with RSS fallback if primary source rate-limits
- Global overview — 14 major indices fetched in parallel
- Research notes — private notes per stock with price automatically captured at creation time
- Auth — JWT + Argon2 password hashing, full register/login flow
| Layer | Tech |
|---|---|
| Framework | FastAPI |
| Database | PostgreSQL (async via asyncpg) |
| ORM | SQLAlchemy 2.0 (Mapped / mapped_column style) |
| Migrations | Alembic |
| Auth | PyJWT + pwdlib (Argon2) |
| Market Data | yfinance + feedparser (RSS fallback) |
| Config | pydantic-settings |
| Container | Docker + docker-compose |
| Deploy | Render + UptimeRobot |
Yahoo Finance uses exchange suffixes. Here's what to use per market:
| Market | Suffix | Example |
|---|---|---|
| India (NSE) | .NS |
RELIANCE.NS, TCS.NS, HAL.NS |
| India (BSE) | .BO |
RELIANCE.BO |
| USA | (none) | AAPL, NVDA, META |
| Germany (XETRA) | .DE |
VOW3.DE, SAP.DE |
| Japan (TSE) | .T |
7203.T (Toyota), 6758.T (Sony) |
| UK (LSE) | .L |
SHEL.L, HSBA.L |
| Hong Kong | .HK |
0700.HK (Tencent), 9988.HK (Alibaba) |
| China (Shanghai) | .SS |
600519.SS (Moutai) |
| China (Shenzhen) | .SZ |
000001.SZ |
| Taiwan | .TW |
2330.TW (TSMC) |
| South Korea | .KS |
005930.KS (Samsung) |
| France (Euronext) | .PA |
MC.PA (LVMH), AI.PA |
| Netherlands | .AS |
ASML.AS |
| Canada (TSX) | .TO |
RY.TO, SU.TO |
Note: SME stocks, microcaps, and recently listed companies may have incomplete data on Yahoo Finance. Large caps work reliably.
POST /auth/register — create account
POST /auth/login — returns JWT token
GET /auth/me — current user profile (protected)
GET /watchlist/ — get your full watchlist
POST /watchlist/{symbol} — add a stock (e.g. /watchlist/RELIANCE.NS)
DELETE /watchlist/{symbol} — remove a stock
GET /market/price/{symbol} — live price, change %, volume, 52-week range
GET /market/news/{symbol} — recent headlines for a stock
GET /market/overview — snapshot of 14 global indices
GET /market/stocks/search?q=... — search stocks in database by name or symbol
POST /notes/{symbol} — create note (price captured automatically)
GET /notes/ — all your notes across all stocks
GET /notes/stock/{symbol} — all notes for a specific stock
PATCH /notes/{note_id} — update a note
DELETE /notes/{note_id} — delete a note
GET /health — service status
1. Open the docs: https://first-order-a3v1.onrender.com/docs
2. Register an account:
- Go to
POST /auth/register - Click Try it out
- Fill in email, username, password
- Execute
3. Login and get your token:
- Go to
POST /auth/login - Click Try it out
- Fill in email and password
- Execute
- Copy the
access_tokenfrom the response
4. Authorize:
- Click the Authorize button at the top of the page
- In the username field, enter your email (OAuth2 form maps it this way)
- Enter your password
- Click Authorize
You're now logged in. All protected endpoints (watchlist, notes, /me) will work.
5. Try some endpoints:
GET /market/price/RELIANCE.NS — Reliance Industries live price
GET /market/price/AAPL — Apple live price
GET /market/news/TCS.NS — TCS latest news
GET /market/overview — all 14 global indices
POST /watchlist/HAL.NS — add HAL to your watchlist
POST /notes/CDSL.NS — write a research note on CDSL
Note: The free Render tier spins down after inactivity. First request may take ~30 seconds if the service is cold. Subsequent requests are fast.
git clone https://github.com/ah4ddd/first-order
cd first-order
# Copy and fill in your environment variables
cp .env.example .env
# Start everything
docker compose up --build
# In a second terminal — run migrations and seed data
docker compose exec api alembic upgrade head
docker compose exec api python -m app.seed_stocksOpen http://localhost:8000/docs
DATABASE_URL=postgresql+asyncpg://postgres:yourpassword@db/first_order
SECRET_KEY=your-32-byte-hex-key # generate with: openssl rand -hex 32
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
APP_NAME=First-Order
DEBUG=True
NEWS_API_KEY= # optionalfirst-order/
├── app/
│ ├── main.py — FastAPI app, middleware, router registration
│ ├── config.py — pydantic-settings config
│ ├── database.py — async engine, session factory, get_db dependency
│ ├── db_models.py — SQLAlchemy 2.0 table models (Mapped style)
│ ├── models.py — Pydantic request/response schemas
│ ├── dependencies.py — JWT auth dependency, CurrentUser type alias
│ ├── utils.py — get_or_create_stock, country inference
│ ├── seed_stocks.py — initial stock data seeder
│ ├── services/
│ │ └── rss_news.py — RSS feed fetcher (news fallback)
│ └── routers/
│ ├── auth.py — register, login, /me
│ ├── watchlist.py — watchlist CRUD
│ ├── market.py — prices, news, overview, search
│ └── notes.py — research notes CRUD
├── alembic/ — database migrations
├── Dockerfile
├── docker-compose.yml
└── requirements.txt
Async throughout — uses AsyncSession from SQLAlchemy 2.0 and asyncpg driver. Every database call is non-blocking.
yfinance in threadpool — yfinance is synchronous. All calls are wrapped with asyncio.to_thread so the event loop stays free under concurrent requests.
Parallel index fetching — the market overview endpoint fires 14 yfinance requests simultaneously using asyncio.gather. Response time stays flat regardless of how many indices are added.
In-memory caching — prices cache for 60 seconds, news for 5 minutes, overview for 2 minutes. Prevents hammering external APIs on repeated requests.
Get-or-create stocks — when a user adds a symbol to their watchlist or creates a note, if the stock isn't in the database yet it gets created automatically by fetching metadata from yfinance. No manual seeding needed for new symbols.
Price snapshot on notes — when a research note is created, the current market price is captured and stored permanently. Lets you see what price you were looking at when you wrote the thesis.
RSS fallback for news — if yfinance news is rate-limited (common on cloud provider IPs), the news endpoint falls back to RSS feeds from Reuters, BBC Business, Economic Times, and Moneycontrol.
- yfinance rate limiting — Yahoo Finance occasionally blocks requests from cloud provider IPs. Price endpoints may return 503 and retry after a short wait. News has RSS fallback.
- Free tier cold starts — Render free tier spins down after 15 minutes of inactivity. UptimeRobot pings every 5 minutes to mitigate this.
- No real-time streaming — prices are fetched on request, not pushed. Refresh the endpoint to get updated data.
- Indian SME stocks — small/micro caps and recently listed companies may have incomplete metadata on Yahoo Finance.
- [✅] Minimal frontend (dashboard, stock detail page, watchlist view)
- Portfolio positions with P&L tracking
- Geopolitical/macro news feed
- AI-powered note summarization
Built by Ahad (@ah4ddd)
This is my first real FastAPI portfolio project.