# QuantTrade Local-first quantitative backtesting on Coolify: Streamlit UI, VectorBT engine, Parquet market data, nightly Yahoo Finance sync, in-app Authentik OIDC, and SQLite strategy persistence. ## Architecture | Layer | Technology | |-------|------------| | Auth | Authentik OIDC inside Streamlit (`OIDC_CLIENT_SECRET` in app env) | | 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 OIDC 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. **Do not** enable Authentik forward auth on the Coolify proxy for this app — login happens inside Streamlit. 4. Set environment variables in Coolify: - `OIDC_CLIENT_SECRET` — **required**; copy from your Authentik provider - `OIDC_CLIENT_ID` — Authentik provider slug (default `quant-web`) - `OIDC_ISSUER` — Authentik issuer URL for the provider - `OIDC_REDIRECT_URI` — optional; defaults to `SERVICE_URL_STREAMLIT_8501` - `BUGSINK_DSN` — DSN from your Bugsink project - `AUTH_REQUIRED` — keep `true` in production - `DEV_USER` — only when `AUTH_REQUIRED=false` for local testing **Coolify note:** if you change any default after the first deploy, update the value manually in Coolify UI > Environment Variables. ### Authentik provider setup In Authentik, create an **OAuth2/OIDC provider** for this app: 1. **Client type:** Confidential 2. **Client ID:** `quant-web` (or match `OIDC_CLIENT_ID`) 3. **Client secret:** paste into Coolify as `OIDC_CLIENT_SECRET` 4. **Redirect URIs:** your public app URL, e.g. `https://quant.example.com` - Must match `OIDC_REDIRECT_URI` or the Coolify-generated `SERVICE_URL_STREAMLIT_8501` 5. **Signing key:** RS256 (Authentik default) Users hit the app → Streamlit redirects to Authentik → callback with auth code → app exchanges code using `OIDC_CLIENT_SECRET` → userinfo drives strategy ownership. ## Services - **`data-seed`** — one-shot 5-year historical download into Parquet (idempotent). - **`harvester`** — cron container; appends daily bars after US cash close. - **`streamlit`** — dashboard, OIDC login, backtests, save/load strategies. ## Local development ```bash cp .env.example .env # Set AUTH_REQUIRED=false and DEV_USER for local testing without Authentik python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt python sync.py --seed streamlit run app.py ``` For full OIDC locally, set `OIDC_CLIENT_SECRET` and register `http://localhost:8501` as a redirect URI in Authentik. ## 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://@bugsink.aexoradao.com/ ``` Both `app.py` and `sync.py` initialize the Sentry-compatible SDK with tracing disabled for Bugsink compatibility.