Skip to content

Commit a024187

Browse files
committed
v4.0.7
1 parent 29d82f7 commit a024187

2 files changed

Lines changed: 160 additions & 14 deletions

File tree

README.md

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<img src="https://img.shields.io/badge/Docker-Compose%20Ready-2496ED?style=flat-square&logo=docker&logoColor=white" alt="Docker">
4343
<img src="https://img.shields.io/badge/Frontend-Prebuilt-1f8b4c?style=flat-square" alt="Frontend">
4444
<img src="https://img.shields.io/badge/Agent%20Gateway-MCP%20Ready-6f42c1?style=flat-square" alt="Agent Gateway">
45-
<img src="https://img.shields.io/badge/PostgreSQL-16-336791?style=flat-square&logo=postgresql&logoColor=white" alt="PostgreSQL">
45+
<img src="https://img.shields.io/badge/PostgreSQL-18-336791?style=flat-square&logo=postgresql&logoColor=white" alt="PostgreSQL">
4646
<img src="https://img.shields.io/github/stars/brokermr810/QuantDinger?style=flat-square&logo=github" alt="Stars">
4747
<img src="https://img.shields.io/github/forks/brokermr810/QuantDinger?style=flat-square&logo=github&label=Forks" alt="Forks">
4848
</p>
@@ -165,7 +165,7 @@ QuantDinger is a **self-hosted, local-first** quantitative infrastructure layer
165165
| **Agent-native** | First-class **Agent Gateway** (`/api/agent/v1`) + **[`quantdinger-mcp`](https://pypi.org/project/quantdinger-mcp/)** on PyPI — Cursor, Claude Code, and Codex can read markets, run backtests, and trade (paper by default) with full audit logs. |
166166
| **Dual strategy runtimes** | **`IndicatorStrategy`** (four-way dataframe signals + chart overlays) and **`ScriptStrategy`** (event-driven `on_bar`, explicit orders) — research and production in the same codebase. |
167167
| **Multi-venue execution** | Direct adapters for Binance, OKX, Bitget, Bybit, Gate, HTX, Coinbase Exchange, Kraken, **IBKR**, and **Alpaca** — unified Broker Accounts page with isolated multi-tenant sessions. |
168-
| **Production-grade infra** | **PostgreSQL 16** + **Redis 7**, connection pooling, background workers (orders, portfolio monitor, reflection), idempotent schema bootstrap, GHCR multi-arch images (amd64/arm64). |
168+
| **Production-grade infra** | **PostgreSQL 18** + **Redis 7**, connection pooling, background workers (orders, portfolio monitor, reflection), idempotent schema bootstrap, GHCR multi-arch images (amd64/arm64). |
169169
| **Security by default** | Refuses default `SECRET_KEY`, agent tokens hashed at rest, **paper-only trading** unless explicitly unlocked server-side, every agent call audit-logged. |
170170
| **Operator-ready** | OAuth, multi-user roles, credits/membership/USDT billing toggles, an 11-language web UI, and multilingual docs — build a commercial quant product on top, not just a hobby bot. |
171171

@@ -235,13 +235,13 @@ Deeper references: [AI Integration design](docs/agent/AI_INTEGRATION_DESIGN.md)
235235
- **Build** — Professional KLine chart UI; `IndicatorStrategy` (four-way dataframe signals: `open_long`, `close_long`, `open_short`, `close_short`) and `ScriptStrategy` (`on_bar`, `ctx.buy()` / `ctx.sell()`); AI code generation as a starting point, Python as source of truth.
236236
- **Validate** — Server-side backtests with equity curves, drawdown metrics, trade logs, and strategy snapshots — no client-side-only backtest theater.
237237
- **Operate** — Live strategy bots, quick trade, crypto spot/swap execution through direct exchange adapters, **IBKR** / **Alpaca** workflows for traditional markets; unified **Broker Accounts** page; notifications (Telegram, email, SMS, Discord, webhooks).
238-
- **Platform** — Docker Compose + GHCR images, PostgreSQL 16, Redis 7, OAuth, multi-user RBAC, credits / membership / USDT billing toggles, an 11-language web UI, and multilingual documentation.
238+
- **Platform** — Docker Compose + GHCR images, PostgreSQL 18, Redis 7, OAuth, multi-user RBAC, credits / membership / USDT billing toggles, an 11-language web UI, and multilingual documentation.
239239

240240
## Architecture
241241

242242
**Design principle:** separate **market data ingestion**, **strategy/backtest compute**, and **order execution** so research never shares a code path with live capital unless you explicitly promote a strategy.
243243

244-
**Stack:** Nginx serves the prebuilt Vue SPA (`ghcr.io/brokermr810/quantdinger-frontend`); **Flask + Gunicorn** API hosts strategy, AI, billing, and agent services; **PostgreSQL 16** is the system of record; **Redis 7** backs cache and worker coordination. Exchanges, brokers, LLMs, and payment rails plug in through env-driven adapters — swap providers without forking core code.
244+
**Stack:** Nginx serves the prebuilt Vue SPA (`ghcr.io/brokermr810/quantdinger-frontend`); **Flask + Gunicorn** API hosts strategy, AI, billing, and agent services; **PostgreSQL 18** is the system of record; **Redis 7** backs cache and worker coordination. Exchanges, brokers, LLMs, and payment rails plug in through env-driven adapters — swap providers without forking core code.
245245

246246
**Runtime flow:** market feeds → indicator/signal layer → strategy engine → backtest or live runtime → venue-specific execution adapters; pending orders dispatched by background workers with health checks and retry semantics.
247247

@@ -267,7 +267,7 @@ flowchart LR
267267
end
268268
269269
subgraph DATA[State Layer]
270-
PG[(PostgreSQL 16)]
270+
PG[(PostgreSQL 18)]
271271
REDIS[(Redis 7)]
272272
FILES[Logs and Runtime Data]
273273
end
@@ -331,7 +331,14 @@ cd QuantDinger
331331
cp backend_api_python/env.example backend_api_python/.env
332332
```
333333

334-
Almost all runtime behavior is driven by **`backend_api_python/.env`** (database URL, admin user, LLM keys, workers, billing toggles, etc.). The optional **repository root** `.env` only adjusts Compose-level concerns such as **ports** and **image mirrors** (`IMAGE_PREFIX`).
334+
Almost all application behavior is driven by **`backend_api_python/.env`** (admin user, LLM keys, workers, billing toggles, broker settings, etc.).
335+
336+
The optional **repository root** `.env` is for Docker Compose orchestration: exposed ports, image mirrors (`IMAGE_PREFIX`), image tags, Postgres image/version, Postgres data mount, `PGDATA`, and the Compose-generated `DATABASE_URL`. Keep this boundary clear:
337+
338+
| File | Owns |
339+
|------|------|
340+
| `backend_api_python/.env` or `backend.env` | Application runtime settings used by the Flask API. |
341+
| Repository-root `.env` | Docker Compose substitutions such as ports, image tags, Postgres/Redis wiring, and host paths. |
335342

336343
### 3) Set `SECRET_KEY` and admin credentials before the first boot (mandatory)
337344

@@ -370,6 +377,46 @@ docker compose up -d
370377

371378
Services: **`postgres`**, **`redis`**, **`backend`**, **`frontend`**, **`mobile`** (see `docker-compose.yml`).
372379

380+
#### Existing PostgreSQL data or 1Panel migration
381+
382+
For a fresh install, do nothing: Compose creates its own Postgres volume.
383+
384+
For an existing production database, prefer `pg_dump` / `pg_restore` when the dataset is manageable. If the data directory is too large and must be mounted directly, all of these must match:
385+
386+
- The Postgres **major version** of the image and the existing data directory.
387+
- The exact container `PGDATA`.
388+
- The database user/password that already exist inside that data directory.
389+
- Only one Postgres container may use that physical directory at a time.
390+
391+
Check the existing data version before mounting:
392+
393+
```bash
394+
cat /path/to/old/postgres/data/PG_VERSION
395+
# or, for a nested 1Panel layout:
396+
cat /opt/1panel/apps/postgresql/postgresql/data/18/docker/PG_VERSION
397+
```
398+
399+
Example root `.env` for reusing a PostgreSQL 18 data directory managed by 1Panel:
400+
401+
```ini
402+
POSTGRES_IMAGE=postgres:18.3-alpine
403+
POSTGRES_DATA_SOURCE=/opt/1panel/apps/postgresql/postgresql/data/18/docker
404+
POSTGRES_PGDATA=/var/lib/postgresql/18/docker
405+
POSTGRES_DB=quantdinger
406+
POSTGRES_USER=existing_db_user
407+
POSTGRES_PASSWORD=existing_db_password
408+
```
409+
410+
Then stop the old database container, recreate the new one, and verify:
411+
412+
```bash
413+
docker compose up -d postgres
414+
docker compose logs --tail=100 postgres
415+
docker compose up -d backend frontend mobile redis
416+
```
417+
418+
Do **not** mount a PostgreSQL 16 data directory into a PostgreSQL 18 image, or the database will fail with `database files are incompatible with server`.
419+
373420
#### Alternative: zero-repo install from GHCR (lightest)
374421

375422
Prebuilt multi-arch (amd64/arm64) images for **both** backend and frontend — no `git clone`:
@@ -459,6 +506,19 @@ In the normal Docker stack, you usually do **not** need to edit a frontend API U
459506

460507
For LAN testing on a phone, do not use `localhost` inside the phone browser. Use the host machine's LAN IP, such as `http://192.168.1.10:8889`.
461508

509+
### 5.2) Production runtime checks
510+
511+
The Compose backend runs the API with **Gunicorn** and sets `nofile` to `65535`. That is the recommended production path. Avoid running the backend with `python run.py` on a public server; it is meant for development and commonly inherits a low file-descriptor limit such as `1024`.
512+
513+
After deployment, quick-check the backend container:
514+
515+
```bash
516+
docker exec quantdinger-backend sh -lc 'echo "ulimit -n=$(ulimit -n)"; cat /proc/1/cmdline | tr "\0" " "; echo'
517+
curl -fsS http://127.0.0.1:5000/api/health
518+
```
519+
520+
Expected: `ulimit -n` is high enough for your user scale, and the command line contains Gunicorn. If you deploy through 1Panel or another custom runtime, set the equivalent `nofile` limit there and point the reverse proxy at the running backend port.
521+
462522
### 6) Optional: enable AI features
463523

464524
AI analysis, NL→code, and related flows need at least one LLM provider configured. Open `backend_api_python/env.example`, find the **AI / LLM** block, copy the relevant keys into your `.env` (for example `LLM_PROVIDER` + `OPENROUTER_API_KEY`, or another supported provider). Restart the backend after edits.
@@ -499,6 +559,10 @@ If `py` is not on PATH, use `python` or `python3` in the one-liner that generate
499559
| `redis` / `python` / `node` pull fails, `content size of zero` | Docker Hub unreachable from Docker Desktop. Set root `.env` `IMAGE_PREFIX=docker.m.daocloud.io/library/` and/or configure **Docker Desktop → Proxies** (system VPN alone is often not enough). |
500560
| Backend exits immediately | `SECRET_KEY` still default, or invalid `.env` syntax. Read `docker compose logs backend`. |
501561
| Blank page or API errors from browser | `FRONTEND_URL` / origins mismatch; API not reachable from the host you opened. |
562+
| Browser shows `502 Bad Gateway` | The reverse proxy cannot reach the backend. Check `docker compose ps`, `docker compose logs --tail=100 backend`, and make sure Nginx/OpenResty points to `quantdinger-backend:5000` inside Compose or to the exposed host port. |
563+
| `database files are incompatible with server` | The Postgres image major version does not match the mounted data directory. Set `POSTGRES_IMAGE` to the existing data major version, or restore through `pg_dump` / `pg_restore`. |
564+
| `password authentication failed for user ...` | The root `.env` `POSTGRES_USER` / `POSTGRES_PASSWORD` or `DATABASE_URL` does not match the user stored in the existing data directory. Fix the root `.env`, then recreate the backend container. |
565+
| `Too many open files` or `ulimit -n` is `1024` | Use the bundled Compose backend or configure your custom runtime with `nofile` around `65535`, then recreate the container. Restarting without recreating usually keeps the old limit. |
502566
| Mobile or web source dev calls the wrong backend | Use `VITE_DEV_PROXY_TARGET` for `QuantDinger-Vue`, `VITE_DEV_API_TARGET` for `QuantDinger-Mobile`, and restart the dev server after changing env vars. |
503567
| Mobile source dev fails with `crypto.hash is not a function` | Node is too old for Vite 7. Install/switch to Node 22 LTS (or at least Node 20.19+ / 22.12+). |
504568
| Port already in use | Another Postgres, Redis, or local service on `5432` / `6379` / `5000` / `8888` / `8889`. Adjust variables in root `.env` per `docker-compose.yml`. |
@@ -518,13 +582,22 @@ docker compose down
518582

519583
### Optional root `.env` (Compose only)
520584

521-
For **custom ports** or **mirror/prefix** for base images (slow Docker Hub pulls), create a file named `.env` in the **repository root** (same directory as `docker-compose.yml`):
585+
For **custom ports**, **mirror/prefix** for base images, image tags, or Postgres physical data mounts, create a file named `.env` in the **repository root** (same directory as `docker-compose.yml`):
522586

523587
```ini
524588
FRONTEND_PORT=3000
525589
MOBILE_PORT=3001
526590
BACKEND_PORT=127.0.0.1:5001
527591
IMAGE_PREFIX=docker.m.daocloud.io/library/
592+
593+
# Optional: reuse an existing PostgreSQL 18 data directory.
594+
# Stop the old Postgres container before mounting this path.
595+
# POSTGRES_IMAGE=postgres:18.3-alpine
596+
# POSTGRES_DATA_SOURCE=/absolute/path/to/postgres/18/docker
597+
# POSTGRES_PGDATA=/var/lib/postgresql/18/docker
598+
# POSTGRES_DB=quantdinger
599+
# POSTGRES_USER=existing_db_user
600+
# POSTGRES_PASSWORD=existing_db_password
528601
```
529602

530603
When running the desktop or mobile UI containers by themselves, use `BACKEND_URL` on that container instead of the root Compose `.env` port variables:

0 commit comments

Comments
 (0)