113 lines
4.2 KiB
Markdown
113 lines
4.2 KiB
Markdown
# chart
|
|
|
|
FastAPI backend + Vue 3 (from CDN, no build step) served at
|
|
<https://chart.amow.com>.
|
|
|
|
The app charts Yahoo's `ES=F` feed, builds CME-session-aware timeframes and daily moving
|
|
averages, and alerts on confluence zones. The full spec lives in
|
|
[`docs/IMPLEMENTATION_PLAN.md`](docs/IMPLEMENTATION_PLAN.md) — read it before writing
|
|
code; it records decisions and verified API facts that are expensive to rediscover.
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
docker compose up --build
|
|
```
|
|
|
|
Then open <http://localhost:8010>. Override the host port with, for example,
|
|
`PORT=8020 docker compose up`. The port binds to all host interfaces, so another
|
|
machine can connect at `http://HOST_IP:8010`. The source tree is bind-mounted and uvicorn
|
|
runs with `--reload`, so edits to `main.py` or `static/` take effect without a
|
|
rebuild. Rebuild only when `requirements.txt` changes.
|
|
|
|
The Compose stack also includes Playwright for browser screenshots. It reaches
|
|
the app over the internal Compose network and writes ignored artifacts locally:
|
|
|
|
```bash
|
|
docker compose exec playwright playwright screenshot \
|
|
--lang en-US --wait-for-timeout 5000 http://api:8000 /artifacts/chart.png
|
|
```
|
|
|
|
Without Docker:
|
|
|
|
```bash
|
|
python3 -m venv .venv && . .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload
|
|
```
|
|
|
|
Copy settings from `.env.example` as needed. To recalibrate the alert threshold against
|
|
Yahoo's current eight-day minute tape:
|
|
|
|
```bash
|
|
python3 -m scripts.calibrate_alerts
|
|
```
|
|
|
|
The M4 calibration on 2026-08-09 replayed 8,065 minute bars across seven sessions.
|
|
Threshold `12` generated 210 alerts from lone daily MAs; `24` and the selected `28`
|
|
generated none. The selected threshold deliberately requires at least three clustered
|
|
daily MAs (score `36`) and should be revisited as more varied tapes are recorded.
|
|
|
|
## Layout
|
|
|
|
| Path | Purpose |
|
|
|---|---|
|
|
| `main.py` | FastAPI lifespan and app wiring; JSON under `/api`, SPA at `/` |
|
|
| `app/` | Market sources, aggregation, analysis, alerts, and API |
|
|
| `static/` | `index.html`, `app.js`, `style.css` — Vue 3 loaded from unpkg |
|
|
| `requirements.txt` | Python deps |
|
|
| `Procfile` | Start command; **nixpacks needs this** or the deploy has nothing to run |
|
|
| `Dockerfile.dev`, `docker-compose.yml` | Local dev only — production does not use them |
|
|
|
|
## Deployment
|
|
|
|
Push to `main` → Forgejo webhook → Coolify rebuilds with nixpacks → live in
|
|
roughly 25 seconds.
|
|
|
|
```bash
|
|
git push
|
|
```
|
|
|
|
Two things to know before changing the deploy config:
|
|
|
|
- The Coolify domain is registered as `https://chart.amow.com:8000`. The port
|
|
suffix is how Coolify decides the Traefik target port; drop it and every
|
|
request 502s while the container looks perfectly healthy. It does not appear
|
|
in the public URL.
|
|
- Ignore a 502 in the first minute after a deploy — that's the rolling container
|
|
swap, and it clears on its own.
|
|
|
|
Manual redeploy:
|
|
|
|
```bash
|
|
curl -X POST -H "Authorization: Bearer $(cat ~/.coolify-token)" \
|
|
"http://127.0.0.1:8000/api/v1/deploy?uuid=dgvch0xqv8uvjfor7dl8bwl9&force=true"
|
|
```
|
|
|
|
|
|
## Access token
|
|
|
|
`CHART_AUTH_TOKEN` guards everything under `/api` plus the `/ws` stream. Leave
|
|
it blank and the app is wide open, which is what you want locally — nothing
|
|
prompts. Set it and every request needs the token, as the `X-Chart-Token`
|
|
header or a `?token=` query parameter (WebSocket handshakes can't carry
|
|
headers, hence the second form).
|
|
|
|
In production the token lives in **Coolify's environment variables**, not in
|
|
this repo and not in `.env` — that file is gitignored and never exists in the
|
|
built container. Coolify re-injects its env vars into every container it
|
|
builds, so the token survives redeploys and reboots.
|
|
|
|
The browser asks for it once on the first 401 and keeps it in `localStorage`.
|
|
To clear it: `localStorage.removeItem('chart-token')`.
|
|
|
|
`/api/health` and `/api/version` deliberately stay open — `bin/wait-deploy`
|
|
polls the latter from whatever machine you pushed from, and neither reveals
|
|
anything about the market data or the configuration.
|
|
|
|
## Saved trendlines
|
|
|
|
Manual trendlines are written to `MANUAL_LINES_PATH` (`./data/manual_lines.json`).
|
|
In production `/app/data` is a **Coolify persistent volume** — without it the
|
|
container filesystem is ephemeral and every deploy would silently wipe every
|
|
line you've drawn.
|