quant-web/README.md
epistemophiliac b5db15d6ab Initial QuantTrade stack for Coolify deployment.
Streamlit + VectorBT dashboard, Parquet harvester with nightly cron, Authentik header auth, SQLite strategy persistence, and Bugsink telemetry.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-19 00:46:51 -04:00

90 lines
2.9 KiB
Markdown

# QuantTrade
Local-first quantitative backtesting on Coolify: Streamlit UI, VectorBT engine, Parquet market data, nightly Yahoo Finance sync, Authentik OIDC via reverse proxy, and SQLite strategy persistence.
## Architecture
| Layer | Technology |
|-------|------------|
| Auth | Authentik (`auth.aexoradao.com`) via reverse proxy headers |
| UI | Streamlit (`streamlit` service, port 8501) |
| Engine | VectorBT + NumPy |
| Market data | Parquet volume (`parquet-data`) |
| Ingestion | `harvester` cron @ 17:00 America/New_York (weekdays) |
| Strategies | SQLite on `strategy-data` volume, keyed by proxy username |
| Telemetry | Bugsink via `sentry-sdk` (`bugsink.aexoradao.com`) |
## Coolify deployment
1. Create a **Docker Compose** resource pointing at this repo.
2. Assign your public domain to the **`streamlit`** service on port **8501**.
3. Set environment variables in Coolify (first deploy extracts defaults from compose):
- `BUGSINK_DSN` — DSN from your Bugsink project
- `CORE_TICKERS` — optional comma-separated tickers
- `DEV_USER` — only for unauthenticated local testing
4. Protect the domain in your reverse proxy with Authentik forward auth.
### Authentik / proxy headers
After login, the proxy must forward one of these headers to Streamlit:
- `X-Forwarded-User` (recommended)
- `X-Authentik-Username`
- `Remote-User`
The app reads them via Streamlit websocket headers (`auth.get_current_user()`). Saved strategies are scoped to that username.
Example Traefik middleware (adjust provider labels to your stack):
```yaml
# Forward auth endpoint on Authentik
http:
middlewares:
authentik:
forwardAuth:
address: https://auth.aexoradao.com/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-uid
- X-Forwarded-User
```
Map `X-authentik-username``X-Forwarded-User` in your proxy if Streamlit only sees the latter.
## Services
- **`data-seed`** — one-shot 5-year historical download into Parquet (idempotent).
- **`harvester`** — cron container; appends daily bars after US cash close.
- **`streamlit`** — dashboard, backtests, save/load strategies.
## Local development
```bash
cp .env.example .env
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python sync.py --seed
DEV_USER=you@example.com streamlit run app.py
```
## Manual sync
```bash
python sync.py --seed # full history
python sync.py --daily # append latest bars
```
## Strategy storage
SQLite path: `/data/strategies/strategies.db` (Docker volume `strategy-data`). Each row stores username, name, ticker, and JSON parameters (MA windows, cash, fees).
## Bugsink
Set `BUGSINK_DSN` to your project DSN, e.g.:
```text
http://<public-key>@bugsink.aexoradao.com/<project-id>
```
Both `app.py` and `sync.py` initialize the Sentry-compatible SDK with tracing disabled for Bugsink compatibility.