Document issuer, outpost, and header settings for Coolify, fail closed when AUTH_REQUIRED is true, and add harvester healthcheck per Coolify conventions. |
||
|---|---|---|
| .streamlit | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| app.py | ||
| auth.py | ||
| backtest.py | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Dockerfile.harvester | ||
| README.md | ||
| requirements.txt | ||
| strategy_db.py | ||
| sync.py | ||
| telemetry.py | ||
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
- Create a Docker Compose resource pointing at this repo.
- Assign your public domain to the
streamlitservice on port 8501. - Enable Authentik forward auth on that domain in Coolify (OIDC happens at the proxy; Streamlit never holds client secrets).
- Set environment variables in Coolify (first deploy extracts defaults from compose):
BUGSINK_DSN— DSN from your Bugsink projectAUTHENTIK_ISSUER,OIDC_ISSUER,AUTHENTIK_OUTPOST_URL,OIDC_CLIENT_ID— match your Authentik application/outpostAUTH_USERNAME_HEADER— header Coolify/Traefik forwards after login (defaultX-Forwarded-User)AUTH_REQUIRED— keeptruein productionCORE_TICKERS— optional comma-separated tickersDEV_USER— only whenAUTH_REQUIRED=falsefor local testing
Coolify note: if you change any default above after the first deploy, update the value manually in Coolify UI > Environment Variables. Coolify stores first-seen defaults and will not auto-refresh them from compose.
Authentik / proxy headers
OIDC login is handled by Authentik + Coolify reverse proxy. The container does not run an OAuth code flow; it trusts identity headers injected after forward auth.
After login, the proxy must forward headers such as:
X-Forwarded-User(defaultAUTH_USERNAME_HEADER)X-Authentik-Uid(AUTH_UID_HEADER)X-Forwarded-Email(AUTH_EMAIL_HEADER)
The app reads them via Streamlit websocket headers (auth.get_current_user()). With AUTH_REQUIRED=true, missing headers block the UI instead of falling back to anonymous.
Example Traefik middleware (adjust provider labels to your stack):
# 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
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
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.:
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.