File: README.md
QuantDinger can submit real orders when live trading is explicitly enabled.
Start with paper trading, use restricted API keys, and review the risk and
compliance requirements for your jurisdiction. This project does not provide
investment advice.
What QuantDinger is
QuantDinger is an open-source AI Trading OS for independent traders, Python
strategy authors, and small teams. Its local-first, self-hosted design keeps
market data, strategy code, broker credentials, and deployment under the
operator's control.
The project combines:
- multi-provider AI market research and analysis;
- Python indicators and Strategy API V2 development;
- server-side backtesting and experiment workflows;
- paper and live execution across crypto exchanges and traditional brokers;
- web, mobile H5, human API, Agent Gateway, and MCP access;
- PostgreSQL-backed state, durable workers, audit logs, and optional monitoring.
It is not a black-box signal service. Strategy code, risk settings, credentials,
and deployment remain under the operator's control.
What changed in v5
The v5 backend is organized around explicit runtime and operational boundaries:
- the HTTP API no longer owns long-running trading or scheduler loops;
- trading, scheduling, Celery jobs, and migrations run as separate processes;
- Celery handles finite, retryable work while long-lived strategy runtimes stay
in the trading worker; - cache Redis and durable job Redis use separate instances and eviction policies;
- high-risk API contracts are represented in OpenAPI and protected by tests;
- JSON logs, request IDs, Prometheus metrics, dashboards, and alert rules are
available through an optional observability overlay; - the production overlay runs backend processes as a non-root user with a
read-only root filesystem, dropped capabilities, and resource limits; - CI checks syntax, lint, tests, release gates, Compose files, dependencies,
source security, secrets, API compatibility, version drift, and text encoding.
The source version is declared in VERSION. Git release tags use the
same semantic version with a leading v, for example v5.0.1.
Architecture
The diagram above shows the complete product and process architecture. The
runtime topology below focuses on container-to-container ownership and data flow.
flowchart TB
C["Web / Mobile / API / MCP clients"]
FE["Nginx frontend services"]
API["Flask + Gunicorn API"]
PG[("PostgreSQL")]
CACHE[("Redis cache")]
JOBS[("Redis jobs")]
TW["Trading worker"]
SW["Scheduler worker"]
CW["Celery worker"]
BEAT["Celery beat"]
PROM["Prometheus"]
GRAF["Grafana"]
ALERT["Alertmanager"]
C --> FE --> API
API --> PG
API --> CACHE
API -->|"durable commands"| PG
TW -->|"leases, orders, heartbeats"| PG
SW -->|"schedules, monitoring, heartbeats"| PG
API -->|"finite async jobs"| JOBS
BEAT --> JOBS --> CW
CW --> PG
API -. metrics .-> PROM
PG -. exporter .-> PROM
CACHE -. exporter .-> PROM
JOBS -. exporter .-> PROM
PROM --> GRAF
PROM --> ALERTOne backend image is reused by several containers with different commands:
| Process | Responsibility |
|---|---|
migration |
Applies the database schema and exits before application services start. |
backend |
Handles HTTP, authentication, validation, and durable command submission. |
trading-worker |
Owns strategy runtimes, pending orders, broker sessions, and reconciliation. |
scheduler-worker |
Runs portfolio, deployment, payment, and signal schedules. |
celery-worker |
Executes finite AI, backtest, experiment, report, and maintenance jobs. |
celery-beat |
Dispatches periodic Celery tasks. |
See Backend process roles,
architecture, and
concurrency model for the ownership rules.
Quick start
Option A: prebuilt images
Prerequisites: Docker with Compose v2. Node.js and a local Python environment are
not required.
Linux or macOS:
curl -fsSL https://raw.githubusercontent.com/OpenByteInc/QuantDinger/main/install.sh | bashWindows PowerShell:
irm https://raw.githubusercontent.com/OpenByteInc/QuantDinger/main/install.ps1 | iexThe installer asks for the initial administrator credentials, generates the
required secrets, downloads the GHCR Compose stack, and starts it.
Open:
- Web: http://127.0.0.1:8888
- Mobile H5: http://127.0.0.1:8889
- API health: http://127.0.0.1:5000/api/health
Docker administrator and settings notes
Detailed guides: English |
ไธญๆ
On a fresh database, the backend creates the initial administrator fromADMIN_USER, ADMIN_PASSWORD, and optional ADMIN_EMAIL. Passwords are stored
as hashes, never as plaintext. An existing PostgreSQL volume is not overwritten:
the backend only replaces the untouched legacy quantdinger / 123456
administrator when a non-default administrator is explicitly configured. It
never overwrites an account whose password was already changed, and it refuses
to promote an existing account that already uses the requested username.
Manual Docker deployments retain quantdinger / 123456 only for backward
compatibility when the administrator variables are left at their defaults. This
credential is not suitable for an internet-facing deployment; change it before
first start or immediately after the first login. The one-command installer does
not accept 123456 as the chosen password.
The Settings UI writes runtime configuration to /app/.env. In the GHCR stack
this is the host backend.env; in a source deployment it isbackend_api_python/.env. Current backend images automatically give runtime UID10001 ownership and keep mode 600. Do not use chmod 755 or recursive 777:
these files contain passwords and API keys, and 755 still does not grant write
access to UID 10001 when root owns the file.
Verify write access with:
docker compose exec -u 10001:10001 -T backend \
sh -c 'test -w /app/.env && echo writable=yes || echo writable=no'The hardened production override intentionally mounts /app/.env read-only.
When using docker-compose.production.yml, manage configuration on the host and
recreate the services instead of saving it from the Settings UI. See the
English guide or
ไธญๆๆๅ for
legacy-image recovery and rootless/NFS notes.
Option B: source checkout
git clone https://github.com/OpenByteInc/QuantDinger.git
cd QuantDinger
cp backend_api_python/env.example backend_api_python/.env
cp .env.example .envBefore the first start, replace the example values in both environment files:
| File | Required production values |
|---|---|
backend_api_python/.env |
SECRET_KEY, CREDENTIAL_ENCRYPTION_KEY, ADMIN_USER, ADMIN_PASSWORD |
.env |
POSTGRES_PASSWORD, REDIS_PASSWORD, CELERY_REDIS_PASSWORD, GRAFANA_ADMIN_PASSWORD |
Generate independent secrets with:
python -c "import secrets; print(secrets.token_hex(32))"Start the core stack from local backend source:
docker compose up -d --build
docker compose psThe base stack does not start Prometheus, Grafana, or Alertmanager. This keeps
the default open-source installation smaller.
For detailed installation paths, Windows notes, China mirror settings, and
PostgreSQL migration guidance, see
Installation troubleshooting and the
cloud deployment guide.
Production deployment
Validate secrets before starting a production stack:
python backend_api_python/scripts/check_production_config.py \
--env-file .env \
--env-file backend_api_python/.envStart the hardened runtime with optional observability:
docker compose \
-f docker-compose.yml \
-f docker-compose.production.yml \
-f docker-compose.observability.yml \
up -d --buildOmit docker-compose.observability.yml when the host is resource-constrained or
monitoring is provided externally.
Production rules:
- expose only a TLS reverse proxy on ports 80/443;
- keep PostgreSQL, both Redis instances, Prometheus, Grafana, and Alertmanager
off the public internet; - do not deploy with example passwords or empty encryption keys;
- back up PostgreSQL and the durable
redis-jobsvolume; - keep cache Redis disposable and never use it as the Celery broker;
- review worker health and application readiness after every deployment.
The full checklist is in Production hardening.
Local endpoints
All published ports bind to loopback by default.
| Service | Default URL | Purpose |
|---|---|---|
| Web | http://127.0.0.1:8888 | Desktop web client and same-origin API proxy. |
| Mobile H5 | http://127.0.0.1:8889 | Mobile web client and same-origin API proxy. |
| Backend | http://127.0.0.1:5000 | Direct API access and health endpoints. |
| Grafana | http://127.0.0.1:3000 | Dashboards; available only with the observability overlay. |
| Prometheus | http://127.0.0.1:9090 | Metrics storage and queries; optional. |
| Alertmanager | http://127.0.0.1:9093 | Alert grouping, silencing, and delivery; optional. |
Container-only ports such as the job Redis and exporters are not published to
the host.
Observability
The monitoring stack is optional by design:
- Prometheus collects API, worker, PostgreSQL, and Redis metrics.
- Grafana turns those metrics into operator dashboards.
- Alertmanager groups alerts, manages silences, and sends notifications once
a receiver is configured.
Start it for local diagnostics without the production overlay:
docker compose \
-f docker-compose.yml \
-f docker-compose.observability.yml \
up -dMonitoring services stay on 127.0.0.1. Use a VPN, SSH tunnel, or authenticated
reverse proxy for remote administration. See
Observability for dashboards, alerts, retention, and
receiver configuration.
Security model
- Broker credentials and MFA secrets are encrypted with a stable
CREDENTIAL_ENCRYPTION_KEY. - Agent tokens are hashed, scoped, rate-limited, and audit-logged.
- Agent trading is paper-only by default; live access requires both token and
server-side authorization. - Long-running strategy ownership uses leases, heartbeats, and fencing tokens.
- Production containers run without root privileges or Linux capabilities.
- Host port defaults are loopback-only; public access should terminate at a TLS
reverse proxy.
Report vulnerabilities privately according to SECURITY.md. Do
not include credentials, account data, or exploitable details in public issues.
Strategy and integration surfaces
| Area | Current surface |
|---|---|
| Indicators | Python chart overlays, markers, bands, and signals. |
| Strategies | Strategy API V2 intents, sizing, risk, backtests, and live runtime. |
| Crypto | Binance, OKX, Bitget, Bybit, Gate, HTX, and adapter extensions. |
| Traditional brokers | IBKR and Alpaca workflows. |
| AI providers | OpenRouter, OpenAI-compatible APIs, Google, DeepSeek, Grok, MiniMax, and custom endpoints. |
| Automation | Human API, Agent Gateway, MCP server, Celery jobs, schedules, and notifications. |
Start with the Indicator guide,
Strategy guide, and
Extension guide.
AI agents and MCP
The Agent Gateway is exposed under /api/agent/v1. The included MCP server lets
clients such as Cursor, Claude Code, and Codex call approved tools without
receiving broker credentials or administrator JWTs.
Live trading through an agent requires all of the following:
- a token with trading scope;
paper_only=falseon that token;AGENT_LIVE_TRADING_ENABLED=trueon the server;- operator-configured limits and allowlists.
See MCP setup,
Agent quick start, and the
Agent OpenAPI document.
Development
Backend development uses Python 3.12:
cd backend_api_python
python -m venv .venv
python -m pip install -r requirements-dev.txt
python -m pytest -m "not integration and not stress" --ignore=tests/release_gate -q
ruff check app scripts testsUseful repository checks:
python scripts/check_version.py
python scripts/check_mojibake.py
docker compose -f docker-compose.yml config -q
docker compose -f docker-compose.yml -f docker-compose.production.yml -f docker-compose.observability.yml config -qAPI changes should follow API conventions, update the
OpenAPI artifact when required, and pass the compatibility workflow.
Repository layout
This repository contains the backend, worker processes, deployment definitions,
operations configuration, documentation, and MCP server. The desktop and mobile
client source code live in separate repositories; this repository consumes their
published images in the Compose stacks.
/* Detailed source-code truncated for AI context efficiency. */Main execution paths
| Flow | Path through the repository |
|---|---|
| Synchronous API request | app/routes -> app/services -> database, cache, market-data, or trading adapter |
| Durable strategy command | API route -> PostgreSQL command record -> trading-worker -> strategy runtime and broker adapter |
| Finite background job | API or Celery beat -> job Redis -> app/tasks in celery-worker -> PostgreSQL result |
| Scheduled domain work | app/commands/scheduler.py -> scheduling services -> durable state and notifications |
| Monitoring | API and workers -> app/observability metrics -> Prometheus -> Grafana and Alertmanager |
| Agent or MCP call | MCP client -> mcp_server -> /api/agent/v1 -> the same service layer used by human APIs |
Long-lived trading loops belong to the trading worker. Finite, retryable work
belongs to Celery. HTTP routes validate and delegate; they must not own trading
loops, exchange-specific behavior, or large database workflows.
Where changes belong
| Change | Primary location | Usually update as well |
|---|---|---|
| Add or modify an HTTP endpoint | backend_api_python/app/routes/ |
app/openapi/, route/contract tests, API docs |
| Add a business workflow | backend_api_python/app/services/ |
focused service tests |
| Add an exchange or broker integration | app/services/live_trading/ or the broker package |
credential policy, adapter tests, docs |
| Add a market-data source | app/data_sources/ |
provider aggregation, cache keys, tests |
| Add dashboard, news, or macro aggregation | app/data_providers/ |
route facade and cache policy |
| Add a finite asynchronous task | app/tasks/ |
celery_app.py, queue routing, task tests |
| Add long-lived process behavior | app/workers/, app/commands/, or app/runtime/ |
Compose command, health checks, ownership tests |
| Change the database schema | backend_api_python/migrations/ |
migration/release-gate tests and docs |
| Add metrics or alerts | app/observability/ and ops/ |
dashboard, alert rule, observability docs |
| Add an MCP tool | mcp_server/src/quantdinger_mcp/ |
Agent Gateway scope, security tests, agent docs |
The web and mobile repositories publish their own GHCR images. Node.js is only
needed when building those clients from source. For deeper ownership rules, read
Architecture,
Module boundaries, and
Process roles.
Documentation
The maintained documentation index is available at docs/README.md.
| Topic | Document |
|---|---|
| Contributor architecture | Architecture |
| Module ownership | Module boundaries |
| Process and task ownership | Process roles |
| Production runtime | Production hardening |
| Metrics and alerts | Observability |
| Human API contracts | API conventions |
| OpenAPI artifacts | API documentation |
| Strategy development | Strategy guide |
| Indicator development | Indicator guide |
| MCP and agents | Agent documentation |
| Cloud deployment | Cloud deployment |
| Installation problems | Troubleshooting |
Contributing
Read CONTRIBUTING.md and DEVELOPMENT.md
before opening a pull request. Keep routes thin, preserve API compatibility,
place long-running behavior in the correct process, and include focused tests
for high-risk changes.
Exchange partner links
These are referral links. QuantDinger may receive a commission or trading-fee
rebate when a user registers through one of them. This does not add a separate
charge to the user; eligibility and terms are controlled by each venue and may
change. Always verify the destination domain before creating an account.
The same links are available in the application under Profile โ Open account
and Broker Accounts โ Open account.
| Exchange | Signup link |
|---|---|
| Binance | Register |
| Bitget | Register |
| Bybit | Register |
| OKX | Register |
| Gate.io | Register |
| HTX | Register |
License and commercial terms
- Backend source code is licensed under Apache License 2.0.
- QuantDinger is a product of Open Byte Inc. The name, logo, product
identity, and commercial licensing are managed separately from the code license. - Web frontend source is published in
QuantDinger Frontend under
its own source-available license. - Mobile H5 and native client source is published in
QuantDinger Mobile under
its own source-available license. - Trademark, branding, attribution, and watermark use is governed by
TRADEMARKS.md. Apache 2.0 does not grant trademark rights.
For commercial licensing, frontend source access, branding authorization, or
deployment support:
- Website: quantdinger.com
- Telegram: t.me/worldinbroker
- Email: [email protected]
Legal notice and compliance
QuantDinger is intended for lawful research, education, and compliant trading
only. It must not be used for fraud, market manipulation, sanctions evasion,
money laundering, or other illegal activity. Operators are responsible for
following the laws, licensing requirements, tax rules, broker or exchange terms,
and data regulations that apply in every jurisdiction where they deploy or use
the software.
This project does not provide legal, tax, investment, financial, or regulatory
advice. Trading, including automated and leveraged trading, can result in the
loss of some or all capital. Historical data, backtests, simulated results, AI
output, indicators, and strategy examples do not guarantee future performance.
Users must independently review strategies, permissions, order limits, and risk
controls before enabling live execution.
The software is provided under the terms of the applicable license and is used
at the operator's own risk. To the extent permitted by law, project maintainers
and contributors disclaim liability for trading losses, data loss, service
interruption, third-party failures, security incidents, or regulatory consequences
arising from use or misuse of the software.
Community and support
Sponsors
QuantDinger's continued development and open-source community are supported by:
| Atlas Cloud
AI inference sponsor | **Amazon Web Services**
Cloud infrastructure sponsor || --- | --- |
We are grateful to Atlas Cloud for supporting AI
model inference and to Amazon Web Services for
supporting the cloud infrastructure that helps QuantDinger serve its community.
Support the project
If QuantDinger is useful to you, a GitHub star, contribution, or donation helps
fund ongoing development and infrastructure.
Crypto donation address:
0x96fa4962181bea077f8c7240efe46afbe73641a7Crypto transfers are irreversible. Confirm the address and intended network with
the project maintainers before sending funds.
Acknowledgements
QuantDinger stands on top of a strong open-source ecosystem. Special thanks to
the maintainers and contributors of projects including:
- Flask
- Gunicorn
- Celery
- PostgreSQL
- Redis
- Pandas
- NumPy
- CCXT
- yfinance
- AkShare
- Vue.js
- Ant Design Vue
- KLineCharts
- ECharts
- Capacitor
- bip-utils
- Prometheus
- Grafana
P.S. โ A note on the name
QuantDinger is a small tribute to
Erwin Schrรถdinger โ
the โ-dingerโ in our name is the tail of โSchrรถdingerโ. The cat in the box was a
thought experiment; every un-fired strategy is its own little version of it โ
simultaneously winning and losing until the order actually fills. Backtests open
the box. Live trading collapses the wavefunction. Trade carefully.
File: backend_api_python/app/services/alpaca_trading/README.md
Alpaca Trading Module
Trade US stocks, ETFs, and crypto via Alpaca Markets REST API.
Mirrors ibkr_trading/ module structure for consistency.
Why Alpaca?
- Zero commission on stocks/ETFs/crypto (vs IBKR's per-share or tiered fees)
- No TWS/Gateway required โ pure stateless REST authentication
- Paper trading built-in โ separate paper-api.alpaca.markets endpoint
- Modern Python SDK โ
alpaca-pyis well-maintained, async-friendly - Crypto support โ same client handles stocks AND crypto (BTC/USD, ETH/USD, etc.)
- Fractional shares โ buy $5 worth of TSLA without owning a full share
Installation
pip install alpaca-pyConfiguration
Add to backend_api_python/.env:
# Paper trading (recommended for development)
ALPACA_API_KEY=PK********************
ALPACA_SECRET_KEY=********************
ALPACA_PAPER=true
# Live trading (only after thorough paper testing)
# ALPACA_API_KEY=AK******************** # Note: AK prefix = live
# ALPACA_SECRET_KEY=********************
# ALPACA_PAPER=falseKey prefix tells you what mode you're in:
PK*= paper account (paper-api.alpaca.markets, no real money)AK*= live account (api.alpaca.markets, real money)
Get your keys at:
- Paper: https://app.alpaca.markets/paper/dashboard/overview โ "Generate New Key"
- Live: https://app.alpaca.markets/brokerage/dashboard/overview โ "Generate New Key"
Basic usage
from app.services.alpaca_trading import AlpacaClient, AlpacaConfig
config = AlpacaConfig(
api_key="PK********************",
secret_key="********************",
paper=True, # paper trading
)
client = AlpacaClient(config)
client.connect() # Verifies credentials via /account endpoint
# Account snapshot
acct = client.get_account_summary()
print(f"Buying power: ${acct['summary']['BuyingPower']['value']}")
# Place a market BUY of 1 share of SPY
result = client.place_market_order(symbol="SPY", side="buy", quantity=1)
print(f"Order {result.order_id}: filled={result.filled} @ ${result.avg_price}")
# Get current positions
for pos in client.get_positions():
print(f"{pos['symbol']}: {pos['quantity']} @ ${pos['avgCost']}, unrealized=${pos['unrealizedPnL']:+.2f}")
# Cancel any open order
client.cancel_order(order_id="...")
client.disconnect()Crypto trading
Alpaca supports crypto using the same client โ just pass market_type="crypto":
result = client.place_market_order(
symbol="BTC/USD", # or "BTCUSD" โ auto-normalized
side="buy",
quantity=0.01,
market_type="crypto",
)Note: crypto orders use time_in_force=GTC (good-till-cancelled) since the market is 24/7;
stock orders default to time_in_force=DAY.
Extended-hours trading
For limit orders on US equities, you can trade in pre-market (4:00-9:30 ET) or
after-hours (16:00-20:00 ET):
result = client.place_limit_order(
symbol="SPY",
side="buy",
quantity=1,
price=740.00,
extended_hours=True, # Enables pre/post-market trading
)API endpoints (when registered as Flask blueprint)
All endpoints require @login_required:
| Method | Path | Purpose |
|---|---|---|
| GET | /api/alpaca/status |
Connection state |
| POST | /api/alpaca/connect |
Connect with API key/secret |
| POST | /api/alpaca/disconnect |
Clear local state |
| GET | /api/alpaca/account |
Account summary (buying power, equity, etc.) |
| GET | /api/alpaca/positions |
Current positions |
| GET | /api/alpaca/orders |
Open orders |
| POST | /api/alpaca/order |
Place market or limit order |
| DELETE | /api/alpaca/order/ |
Cancel order |
| GET | /api/alpaca/quote/<symbol>?marketType=USStock |
Latest bid/ask |
Differences from IBKR module
| Feature | IBKR | Alpaca |
|---|---|---|
| Auth | TWS/Gateway socket (host/port/clientId) | REST API key+secret |
| Order IDs | Integer | UUID string |
| Connection | Stateful, persistent socket | Stateless REST |
| Paper mode (IBKR TWS/Gateway) | Port 7497/4002 | Different base URL |
| Symbol format | AAPL (stocks), 0700.HK (HK) |
AAPL (US), BRK.B (US class), BTC/USD (crypto) |
| Extended hours | Configurable per-order | Configurable per-order (limit only) |
| Crypto | No (separate IBKR Crypto product) | Yes, same client |
Troubleshooting: WebSocket `400 invalid syntax`
QuantDinger connection test and live orders use the REST Trading API (TradingClient.get_account()), not the market-data WebSocket at wss://stream.data.alpaca.markets/....
If you see Alpaca error code 400 / invalid syntax from the streaming docs, that is almost always from a WebSocket client (custom script, another app, or a chart feed), not from /api/alpaca/connect or /strategies/test-connection.
Common causes:
| Issue | Fix |
|---|---|
| Subscribe before auth | After connected, send {"action":"auth","key":"...","secret":"..."} within 10s |
| Wrong JSON shape | Use action + channel keys, e.g. {"action":"subscribe","trades":["AAPL"]} |
| Invalid symbol | US stocks: AAPL (no BTC/USDT). Crypto: BTC/USD (Alpaca does not use BTC/USDT) |
| Class shares | BRK/B โ BRK.B for REST; do not treat as a crypto pair |
This module normalizes symbols (symbols.py): BTC/USDT โ BTC/USD for crypto, BRK/B โ BRK.B for equities.
Limitations / TODO
- No bracket orders yet (Alpaca supports them; not yet wrapped)
- No stop or stop-limit orders yet (alpaca-py supports
StopOrderRequest,StopLimitOrderRequest) - No historical bar fetching helpers (use
StockHistoricalDataClient.get_stock_bars()directly) - No WebSocket streaming (alpaca-py supports it; not yet wired)
- No options trading (Alpaca recently added; not implemented)
Reference
- alpaca-py docs: https://alpaca.markets/sdks/python
- Alpaca API reference: https://docs.alpaca.markets/reference
File: backend_api_python/app/services/ibkr_trading/README.md
Interactive Brokers Trading Module
Supports US stocks trading via TWS or IB Gateway.
Installation
pip install ib_insyncOr the dependency is already in requirements.txt.
Port Reference
| Client | Live Port | Paper Port |
|---|---|---|
| TWS | 7496 | 7497 |
| IB Gateway | 4001 | 4002 |
TWS / IB Gateway Configuration
- Open TWS or IB Gateway
- Go to Configure -> API -> Settings
- Enable the following options:
- โ Enable ActiveX and Socket Clients
- โ Allow connections from localhost only
- Set Socket port (refer to the table above)
- Click Apply / OK
Client ID (important)
Interactive Brokers allows one API connection per clientId. If two programs use the same id, the newer connection replaces the older โ often within a few seconds it looks like a random disconnect, and live orders then fail with โFailed to connectโ.
POST /api/ibkr/connect(admin UI test) defaults to clientId=1.- Strategy / live-order clients use
ibkr_client_idfrom credentials (default 7 when omitted) or envIBKR_ORDER_CLIENT_ID(default7).
Keep manual testing on 1 and automation on 7 (or any other unused id). If you still see drops on Windows, ensure ib_insync runs with asyncio patched (QuantDinger calls util.patchAsyncio() at app startup).
API Endpoints
Connection Management
GET /api/ibkr/status # Get connection status
POST /api/ibkr/connect # Connect to TWS/Gateway
POST /api/ibkr/disconnect # DisconnectAccount Queries
GET /api/ibkr/account # Account information
GET /api/ibkr/positions # Current positions
GET /api/ibkr/orders # Open ordersTrading
POST /api/ibkr/order # Place order
DELETE /api/ibkr/order/ # Cancel orderMarket Data
GET /api/ibkr/quote?symbol=AAPL&marketType=USStockUsage Examples
Connect
curl -X POST http://localhost:5000/api/ibkr/connect \
-H "Content-Type: application/json" \
-d '{"host": "127.0.0.1", "port": 7497, "clientId": 1}' # TWS Paper defaultPlace Order
# Market order: buy 10 shares of AAPL
curl -X POST http://localhost:5000/api/ibkr/order \
-H "Content-Type: application/json" \
-d '{"symbol": "AAPL", "side": "buy", "quantity": 10, "marketType": "USStock"}'
# Limit order: sell 100 shares of MSFT
curl -X POST http://localhost:5000/api/ibkr/order \
-H "Content-Type: application/json" \
-d '{"symbol": "MSFT", "side": "sell", "quantity": 100, "marketType": "USStock", "orderType": "limit", "price": 400}'Get Positions
curl http://localhost:5000/api/ibkr/positionsSymbol Format
| Market | Format | Examples |
|---|---|---|
| US Stock | Ticker symbol | AAPL, TSLA, GOOGL |
Important Notes
- TWS/Gateway must be running: Ensure TWS or IB Gateway is started and logged in before using the API
- Market data subscription: Real-time quotes may require market data subscription
- Client ID: Use different clientId if multiple programs connect to the same TWS/Gateway
- Readonly mode: Set
readonly: trueto only query without trading - Multi-account: Specify
accountparameter if you have multiple sub-accounts
Troubleshooting
| Error | Cause | Solution |
|---|---|---|
| Connection failed | TWS/Gateway not running | Start and login to TWS/Gateway |
| Connection failed | Wrong port | Check API port setting in TWS/Gateway |
| Connection failed | API not enabled | Enable Socket API in TWS/Gateway settings |
| Client ID conflict | Same clientId already connected | Use a different clientId |
| Invalid contract | Wrong symbol format | Check symbol format |
Removing This Module
To remove this module, delete the following files/directories:
backend_api_python/app/services/ibkr_trading/ # Entire directory
backend_api_python/app/routes/ibkr.py # Route fileThen remove the related import and registration code in app/routes/__init__.py.
Docker Note
When running in Docker, IBKR trading requires TWS/IB Gateway to be accessible from the container.
For local deployment, you can:
- Run TWS/Gateway on host machine
- Use host network mode or configure port mapping
- Set
hostto the host machine's IP address (e.g.,host.docker.internalon Docker Desktop)
Note: IBKR connection parameters are configured per-strategy in the frontend, not via environment variables.
File: backend_api_python/README.md
QuantDinger Python Backend
This directory contains the Flask API, domain services, background workers,
database migrations, OpenAPI integration, and backend tests for QuantDinger.
Start with the project README for product installation. This
document is the backend contributor and operator quick reference.
Runtime model
The production deployment reuses one backend image across independent process
roles:
| Role | Command | Ownership |
|---|---|---|
| API | gunicorn -c gunicorn_config.py run:app |
HTTP, authentication, validation, and durable command submission. |
| Migration | python -m app.commands.migrate |
Fail-fast schema application before services start. |
| Trading | python -m app.commands.trading_worker |
Strategy runtimes, pending orders, broker sessions, and reconciliation. |
| Scheduler | python -m app.commands.scheduler |
Portfolio, deployment, payment, and signal schedules. |
| Celery worker | celery -A app.celery_app:celery_app worker |
Finite AI, backtest, experiment, report, and maintenance jobs. |
| Celery beat | celery -A app.celery_app:celery_app beat |
Periodic task dispatch. |
HTTP processes must not start trading or scheduler threads. Celery must not own
long-lived strategy loops or broker sessions. See
Process roles and durable tasks.
Storage model
- PostgreSQL 18 is the system of record.
redisis an evictable application cache.redis-jobsis the durable Celery broker/result tier with AOF andnoeviction.- Strategy ownership uses PostgreSQL commands, leases, fencing tokens, and
worker heartbeats.
Queue state must never share the cache Redis eviction policy.
Directory map
app/
commands/ Process entry points and operational commands
config/ Environment-backed configuration
data_providers/ Aggregated market and global data providers
data_sources/ Raw market data adapters
observability/ Metrics and request instrumentation
openapi/ Human API schemas, registration, and export metadata
routes/ HTTP facades and compatibility routes
services/ Domain workflows and integration orchestration
tasks/ Celery task definitions
utils/ Small infrastructure helpers
migrations/ PostgreSQL schema and incremental migrations
scripts/ Backend quality, export, and production checks
tests/ Unit, integration, contract, and release-gate testsRead Backend architecture and
Module boundaries before larger changes.
Configuration
Create the runtime environment file:
cp env.example .envAt minimum, replace these values before a shared or production deployment:
SECRET_KEY=
CREDENTIAL_ENCRYPTION_KEY=
ADMIN_USER=
ADMIN_PASSWORD=Generate each secret independently:
python -c "import secrets; print(secrets.token_hex(32))"Docker-level database, Redis, Grafana, port, image, and resource settings belong
in the repository-root .env; application runtime settings belong here.
Docker workflow
Run these commands from the repository root.
Core development stack:
docker compose up -d --build
docker compose psProduction-hardened stack with optional monitoring:
python backend_api_python/scripts/check_production_config.py \
--env-file .env \
--env-file backend_api_python/.env
docker compose \
-f docker-compose.yml \
-f docker-compose.production.yml \
-f docker-compose.observability.yml \
up -d --buildThe production overlay uses UID/GID 10001, a read-only root filesystem,
dropped capabilities, bounded temporary filesystems, and CPU/memory limits.
Remove the observability overlay when monitoring is provided elsewhere.
Local Python workflow
Prerequisites:
- Python 3.12;
- PostgreSQL 18;
- Redis 8 for cache and Celery-backed workflows.
Create an environment and install development dependencies:
python -m venv .venv
python -m pip install --upgrade pip
python -m pip install -r requirements.lock -r requirements-dev.txtApply migrations:
QD_PROCESS_ROLE=migration python -m app.commands.migrateWindows PowerShell equivalent:
$env:QD_PROCESS_ROLE = "migration"
python -m app.commands.migrateStart the API for local debugging:
python run.pyUse the Docker process model when testing trading, scheduler, or Celery ownership
boundaries. A single local API process is not a substitute for production role
separation.
Health and operations
| Endpoint | Purpose |
|---|---|
GET / |
Application identity and resolved version. |
GET /api/health |
Basic liveness. |
GET /api/health/ready |
PostgreSQL and Celery broker readiness. |
GET /api/health/workers |
Trading, scheduler, and Celery heartbeat summary. |
GET /metrics |
Prometheus metrics. Keep this private. |
Container logs default to structured JSON and include process role and request
ID. The optional monitoring stack is documented in
Observability.
API contracts
Human API routes use the existing /api/... surface. AI agents use the scoped/api/agent/v1/... gateway with a separate contract.
- Human API conventions: API_CONVENTIONS.md
- Committed OpenAPI: openapi.yaml
- Agent contract: agent-openapi.json
Regenerate the human API artifact after schema or route metadata changes:
python scripts/export_openapi.pyWith OPENAPI_ENABLED=true, interactive documentation is available at:
- Swagger UI: http://127.0.0.1:5000/api/docs/swagger
- ReDoc: http://127.0.0.1:5000/api/docs/redoc
Quality checks
Run the normal backend suite:
python -m compileall -q app scripts tests
ruff check app scripts tests
python scripts/backend_quality_check.py
python scripts/check_requirements_lock.py
python -m pytest -m "not integration and not stress" --ignore=tests/release_gate -qRun release gates independently:
python -m pytest tests/release_gate/test_cta_backtest_release_gate.py -q
python -m pytest tests/release_gate/test_live_execution_release_gate.py -q
python -m pytest tests/release_gate/test_robot_strategy_unification.py -qSecurity CI additionally runs pip-audit, Bandit, Gitleaks, and CodeQL.
Contributor rules
- Keep routes focused on parsing, authentication, service calls, and response
mapping. - Keep Flask request objects out of domain services.
- Put exchange-specific normalization and errors near the adapter.
- Make state mutations idempotent and define their retry behavior.
- Keep code comments, docstrings, logs, and internal errors in English.
- Preserve existing paths and response fields unless an intentional contract
change is documented and tested. - Add finite retryable work to Celery; keep long-lived ownership in the trading
or scheduler process.
Versioning
The local fallback version is read from VERSION. Release builds can
inject APP_VERSION from a Git tag. Both v5.0.1 and 5.0.1 normalize to the
API display value 5.0.1.
Update checked-in version declarations from the repository root:
python scripts/bump_version.py 5.0.1
python scripts/check_version.pyLicense
Apache License 2.0. See the repository-root LICENSE.
File: docs/api/README.md
QuantDinger Web API (OpenAPI)
Human-facing REST API specification generated from flask-smorest (app/openapi/).
| File | Purpose |
|---|---|
openapi.yaml |
Committed SSOT โ update via backend_api_python/scripts/export_openapi.py |
index.html |
Static Redoc viewer โ must be served over HTTP (see below) |
View docs locally
ReDoc cannot load openapi.yaml when you double-click index.html (file:// is blocked by browser same-origin policy). Use any static server from the repo root or this folder:
# Python (from docs/api/)
python -m http.server 8080
# open http://localhost:8080/index.html
# or from repo root
cd docs/api && npx --yes serve -p 8080If you see process is not defined, refresh after pulling the latest index.html (includes a browser shim + pinned ReDoc 2.4.0).
Conventions
See ../architecture/API_CONVENTIONS.md for envelopes, auth, and Public/Internal tiers.
Agent API
AI agents use a separate contract: ../agent/agent-openapi.json.
Regenerate
cd backend_api_python
pip install -r requirements.txt
python scripts/export_openapi.pyLocal interactive docs
With the backend running in debug mode (or OPENAPI_ENABLED=true):
- Swagger UI: http://localhost:5000/api/docs/swagger
- ReDoc: http://localhost:5000/api/docs/redoc
File: docs/agent/AGENT_ENVIRONMENT_DESIGN.md
QuantDinger โ Multi-Agent Runtime Environments (Design)
| Item | Value |
|---|---|
| Status | Draft (for review and phased rollout) |
| Audience | Maintainers and developers using Cursor, Claude Code, Codex, or similar coding agents |
| Repository | QuantDinger โ self-hosted quant stack: research, strategies, backtests, live trading |
1. Background and goals
1.1 Background
When multiple coding agents and IDE assistants (Cursor, Claude Code, OpenAI Codex, CLI/sandbox bots) work in the same repository without shared conventions, teams often see:
- Agents re-reading a very long README, wasting context and misreading intent;
- Guessed HTTP paths and payloads that do not match the backend;
- Accidental touches to secrets, production databases, or money-adjacent paths;
- Per-tool documentation that drifts from the repo.
1.2 Goals
Without locking into a single vendor, make this repository stable and predictable for many agent environments:
- Code collaboration โ When changing strategies, fixing bugs, or adding APIs, agents can quickly find directories and verification commands.
- Capability discovery โ Humans and machines can learn what is allowed, what is forbidden, and how to validate changes.
- Optional extensions โ MCP, OpenAPI, and similar layers can be added later without replacing this structure.
1.3 Non-goals (explicit for this phase)
- Not every agent product must support MCP.
- No real secrets, internal-only URLs, or production credentials in design docs.
- No promise of fully unattended live trading in v1; live and capital flows stay behind human review and explicit strategy boundaries.
2. Design principles
| Principle | Meaning |
|---|---|
| Single source of truth (SSOT) | โHow to work in this repoโ for agents lives in one primary doc (see ยง8); the root README only indexes it briefly. |
| Layered contracts | Documentation (intent) โ commands (local) โ API / MCP (product capabilities). Upper layers may depend on lower ones; do not mix layers in one blob. |
| Minimal context pack | Token-limited environments get a short, linkable doc (quick path) instead of irrelevant full-tree context. |
| Secure by default | Secrets, .env, production writes, and undocumented order APIs are not encouraged for autonomous agents; red lines must be explicit in Layer 1. |
| Decoupled from implementation | This design states what should exist; filenames (e.g. AGENTS.md) may change at rollout time while semantics stay the same. |
3. Architecture โ three layers
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Layer 1 โ Documentation contract (humans + agents) โ
โ Repo map, red lines, recommended workflow, pointers to 2/3 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Layer 2 โ Command contract (terminal / CI / CLI agents) โ
โ Stable scripts or Make: install, lint, test, docker compose โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Layer 3 โ Machine interfaces (HTTP / MCP, optional) โ
โ OpenAPI or equivalent schema; optional MCP tools (narrow) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ3.1 Layer 1 โ Documentation contract
Role: Any agent that can read repo text knows where to start and how to self-check without calling the network.
Suggested structure (may be split across files; one index is mandatory):
- Top-level directory roles (
backend_api_python/,frontend/,docs/,scripts/). - Indicator and strategy entry points (link to
docs/trading/INDICATOR_DEV_GUIDE.mdanddocs/trading/STRATEGY_DEV_GUIDE.mdโ avoid duplicating long excerpts). - Red lines โ Never commit
.env; never put secrets in docs or examples; money/live changes require human review. - Happy path โ e.g. โPython-only strategy change โ how to bring up Docker โ how to run backend testsโ in 3โ5 steps.
Relationship to the root README โ README keeps user-facing install and product story; add a short โFor coding agentsโ pointer to docs/agent/.
3.2 Layer 2 โ Command contract
Role: Every action that should reproduce locally or in CI is exposed as a small set of stable commands, not ad-hoc shell one-liners.
Implementation choices (Make, npm scripts, invoke, etc.) are up to maintainers:
| Category | Example intent (names may vary) |
|---|---|
| Environment | Copy env template, generate secrets (aligned with env.example, scripts/) |
| Run | Documented equivalent of docker compose up / build |
| Quality | lint, format, typecheck (if adopted) |
| Tests | Backend unit/integration entrypoints matching the repoโs stack |
Constraint: One official entry per goal; if two spellings exist (docker-compose vs docker compose), document the recommended one and compatibility notes.
3.3 Layer 3 โ Machine interfaces (optional evolution)
Role: When an agent or external tool must drive product behavior (health, listings, backtests), it must not rely on guessing HTTP.
- HTTP + OpenAPI (or equivalent) โ Machine-consumable contract for REST; update the contract when routes change.
- MCP (Model Context Protocol) โ Expose narrow, auditable operations as tools (e.g. read-only health, read-only strategy list). Write and trading tools need separate review and allowlists.
Principle: Layer 3 is additive; agents without MCP should still complete most code tasks via Layers 1 + 2.
4. Mapping to agent environments
Product names evolve; map by capability, not brand-only details.
| Environment | Primary layers | Notes |
|---|---|---|
| Cursor | Layer 1 + optional .cursor/rules; optional MCP |
Rules: permanent red lines and repo terminology; MCP aligns with Layer 3. |
| Claude Code | Layer 1 + Layer 2 | Heavy use of terminal and in-repo docs; Bash/PowerShell must be explicit. |
| OpenAI Codex (CLI/IDE) | Layer 1 + Layer 2 | Similar to Claude Code; avoid vendor-only config as the sole source of truth. |
| Lightweight CLI / sandbox bots | Minimal context pack in Layer 1 + Layer 2 | Mount one short file or system prompt instead of the whole tree. |
| Vendor-hosted agents | Layer 1 index + HTTP (Layer 3) | If only APIs are visible, OpenAPI and auth docs are critical. |
Note: For specific products (e.g. OpenClaw, NanoBot), add one appendix row at rollout: product โ config / protocol โ which layer(s). No separate architecture fork.
5. Security and compliance boundaries
5.1 Data and secrets
- Keys, API tokens, and DB passwords come only from environment variables or local
.env(and.envis gitignored). - Design docs, agent rules, and
AGENTS.md-style files must not contain real secrets or production connection strings; use placeholders in examples.
5.2 Capital and live trading
- Order placement, transfers, and exchange credential changes are not in unattended agent flows by default.
- If a subset is ever exposed via MCP, require auth, audit logs, rate limits, human toggles, and document in Layer 1.
5.3 Strategy code and multi-tenancy
- For multi-user deployments, distinguish platform core paths from user strategy / plugin paths so agents do not rewrite shared security logic.
6. Repository anchors (facts)
Keep implementation aligned with the current tree (update this section if layout changes):
| Area | Typical path | Agent focus |
|---|---|---|
| Backend API | backend_api_python/ |
Routes, services, env.example |
| Frontend | frontend/ (includes prebuilt dist) |
Matches README โNode optionalโ story |
| Ops / one-command stack | docker-compose.yml, scripts/ |
Layer 2 commands should mirror this |
| Indicator / strategy guides | docs/trading/INDICATOR_DEV_GUIDE.md, docs/trading/STRATEGY_DEV_GUIDE.md, localized variants |
Layer 1 links; do not duplicate long guides |
7. Phased roadmap (suggested)
| Phase | Deliverable | Value |
|---|---|---|
| P0 | docs/agent/ index + one-line README link; red lines and repo map |
One SSOT for all agents |
| P1 | 3โ5 official commands (scripts or Make) listed in docs | Fewer broken ad-hoc shells |
| P2 | Exported or hand-maintained OpenAPI for the backend | Reliable integrations and codegen |
| P3 | Optional MCP server (read-only first, then expand) | Deeper Cursor / Claude-style tooling |
Phases are independently reviewable; P0 does not depend on P2/P3.
8. Document and file checklist (for implementation)
-
docs/agent/AGENT_ENVIRONMENT_DESIGN.mdโ this design (English SSOT). -
.cursor/skills/quantdinger-agent-workflow/โ project Cursor skill (English) pointing agents here. -
docs/agent/README.mdโ short Layer 1 index fordocs/agent/. - Root
README.mdโ dedicated โFor coding agentsโ subsection pointing to the index above. - (Optional) Root
AGENTS.mdโ industry-style filename; may forward todocs/agent/. - (Optional)
.cursor/rules/โ repo-specific terms and red lines only; avoid duplicating Layer 1 at length. - Layer 2 โ
scripts/agent-*.shor Makefile targets (names TBD at implementation). - Layer 2 โ Windows PowerShell + Bash notes (consistent with root README).
- P2/P3 โ OpenAPI location, MCP package location (separate ADR acceptable).
9. Open questions (for review)
- OpenAPI source โ Generate static JSON from Flask at runtime, or generate in CI and commit? Tradeoff: drift vs build deps.
- MCP packaging โ Monorepo next to the API vs small standalone repo for MCP-only installs?
Resolved: Agent-facing documentation and project .cursor/skills/ are English only; human product docs may remain multilingual elsewhere in docs/.
10. Revision history
| Version | Date | Notes |
|---|---|---|
| 0.1 | 2026-05-02 | First draft: three layers, security, roadmap, checklist |
| 0.2 | 2026-05-02 | Language policy: agent design and project skills are English-only; consolidated from former CN draft |
File: docs/agent/AGENT_QUICKSTART.md
Agent Gateway Quickstart
QuantDinger exposes a tenant-scoped Agent Gateway at /api/agent/v1. Agent tokens are separate from human JWT sessions and enforce capability scopes, market/instrument allowlists, rate limits, expiry, and paper-only restrictions.
The machine-readable contract is agent-openapi.json. MCP setup is documented in MCP_SETUP.md.
Authenticate
Create an Agent Token from the human admin UI, store the full token when it is shown once, and send it as a bearer token:
curl -H "Authorization: Bearer $QUANTDINGER_AGENT_TOKEN" \
http://localhost:8888/api/agent/v1/whoamiScopes are R for reads, W for saved artifacts and deployment configuration, B for backtests, N for notification side effects, and T for runtime or order mutations. C is admin-only. Token permissions never bypass server-side live-trading controls.
Every mutating W/B/N/T request requires a unique Idempotency-Key header. Reuse the same key only when retrying the exact same method, route, query, and body. The gateway atomically reserves the key, returns a stored completed response on replay, and rejects concurrent or mismatched reuse.
Strategy API V2
Executable strategies use Strategy API V2. Code defines initialize(context), declares its universe and subscriptions, and provides handle_data, on_rebalance, or a scheduled callback. Markets, instruments, frequencies, dependencies, warmup, and leverage policy come from the compiled manifest.
The Agent Gateway exposes the complete source lifecycle:
- List starter code with
GET /strategy-sources/templates. - Compile code with
POST /strategy-sources/compile. - Save a private source with
POST /strategy-sources. - Inspect or update it through
/strategy-sources/{source_id}. - Review immutable snapshots through
/strategy-sources/{source_id}/versions. - Create a stopped deployment from the saved source id.
Source restoration requires an explicit confirm=true request and creates another immutable snapshot.
Create a stopped deployment from a saved source:
curl -X POST http://localhost:8888/api/agent/v1/strategies \
-H "Authorization: Bearer $QUANTDINGER_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: deploy-spy-trend-v1" \
-d '{
"name": "spy-trend",
"sourceId": 12,
"initialCapital": 10000,
"executionMode": "signal",
"leverageEnabled": false,
"params": {"lookback": 50}
}'Update the same canonical fields with PATCH /api/agent/v1/strategies/{id}. Starting a deployment is intentionally not part of the W-scope configuration endpoint. A T-scope token can stop a running deployment through /strategies/{id}/stop.
Backtests
Backtests accept Strategy API V2 code and run asynchronously:
curl -X POST http://localhost:8888/api/agent/v1/backtest/run \
-H "Authorization: Bearer $QUANTDINGER_AGENT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: spy-trend-2025" \
-d '{
"code": "def initialize(context):\n g.symbol = \"USStock:SPY\"\n context.set_universe([g.symbol])\n context.subscribe(frequency=\"1d\")\n\ndef handle_data(context, data):\n pass",
"startDate": "2025-01-01",
"endDate": "2025-12-31",
"initialCapital": 10000,
"leverageEnabled": false,
"params": {}
}'Poll /api/agent/v1/jobs/{job_id} or consume /api/agent/v1/jobs/{job_id}/stream. Reuse an idempotency key when retrying the same submission.
Cancel a queued or running job with POST /jobs/{job_id}/cancel using B scope, confirmation at the MCP layer, and a new idempotency key. A running worker may finish its local computation, but it cannot overwrite the durable cancelled state.
Indicators
Indicators are chart-only. Fetch /indicators/authoring-contract, validate with /indicators/validate, and save through /indicators. Indicator code cannot be passed to the backtest endpoint; convert the trading idea to Strategy API V2 first.
Research, broker observations, and notifications
Research tools expose point-in-time universes, factor metadata, and the tenant watchlist under /research/*. Broker observation endpoints under /trading/* return safe credential metadata, account snapshots, account/strategy positions, pending orders, and cursor-paginated trade ledgers. They never return decrypted API keys, secrets, passphrases, or encrypted credential blobs.
N-scope signal-alert endpoints under /notifications/signal-alerts reuse the existing indicator notification service. Immediate evaluation requires explicit MCP confirmation because it may deliver a notification.
Runtime and orders
GET /runtime/overview returns compact tenant runtime state. Quick orders require T scope and an Idempotency-Key. Live execution additionally requires a live-capable token, server live-trading enablement, a credential reference, client-side explicit confirmation, and compliance with the token's max_order_notional and max_daily_notional caps.
The emergency stop at /quick-trade/kill-switch requires confirm=true. It attempts to cancel open agent-originated live orders, cancels open paper orders, revokes all active T-scope tokens for the tenant, and returns any exchange cancellation failures for mandatory human review.
Rate limiting is shared through Redis across API workers and enforces both token and tenant quotas. Responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; 429 responses also include Retry-After.
Never log tokens or credential material. Treat redacted values as terminal and do not attempt to reconstruct them.
File: docs/agent/AI_INTEGRATION_DESIGN.md
QuantDinger โ AI / Agent Integration (Design)
| Item | Value |
|---|---|
| Status | Draft (for review and phased rollout) |
| Audience | Maintainers; integrators wiring external AI agents into QuantDinger |
| Depends on | AGENT_ENVIRONMENT_DESIGN.md โ three-layer contracts |
| Repository | QuantDinger |
Companion to the multi-agent runtime design. That doc explains how coding agents work inside the repo. This doc explains how external and embedded AI agents consume QuantDinger as a product โ research, strategy, backtest, and (carefully) execution.
1. Goals and non-goals
1.1 Goals
- Treat AI agents as first-class API consumers, alongside the existing human web UI and in-product AI chat.
- Provide a stable, documented capability surface so the same agent can do research, backtest, and supervised execution without screen-scraping the UI.
- Enforce least privilege, auditability, and kill-switches before any money-adjacent automation is allowed.
- Allow multiple front doors (HTTP / MCP / event stream) without forking business logic.
1.2 Non-goals (this phase)
- Not a marketplace of third-party plugins.
- No fully unattended live trading by an external LLM out-of-the-box. Live order routing requires explicit per-tenant opt-in and a documented allowlist.
- Not replacing the in-product AI chat (
ai_chat) โ this design is what that chat (and external agents) will call underneath.
2. Personas
| Persona | Example | Primary needs |
|---|---|---|
| P1 Human trader (existing) | QuantDinger user in browser | UI + REST + JWT session |
| P2 In-product AI assistant (existing) | ai_chat route, code-gen helpers |
Same backend services, on behalf of a logged-in user |
| P3 External coding agent | Cursor / Claude Code / Codex working in the repo | Repository contracts (covered by AGENT_ENVIRONMENT_DESIGN.md) |
| P4 External AI agent / app (new) | Custom LLM workflow, MCP client, third-party automation | Authenticated, scoped access to research / backtest / (optional) trading |
| P5 Autonomous strategy AI (gated) | Generator โ Strategy API V2 backtest โ review โ propose | Programmatic strategy deployment and bounded backtest jobs; no autonomous experiment/tuning service |
This document focuses on P4 + P5, keeping consistency with P1/P2.
3. Capability catalog
Capabilities are grouped by risk class. Every endpoint or MCP tool must declare exactly one class.
| Class | Examples | Default for new tokens |
|---|---|---|
| R โ Read | Market data, klines, indicators, strategy listing, backtest results, account read | Allowed |
| W โ Workspace write | Create/update Strategy API V2 deployments and save chart-indicator code | Allowed (workspace-scoped) |
| B โ Backtest / simulation | Run Strategy API V2 backtests | Allowed |
| N โ Notifications & misc side-effects | Send test notification, write user prefs | Allowed (rate-limited) |
| C โ Credentials | Store/rotate exchange or LLM credentials | Denied by default; admin-only |
| T โ Trading / capital | Quick trade, place/cancel order, adjust live strategy live capital | Denied by default; per-tenant explicit opt-in + allowlist |
Rule: A new agent token cannot acquire class C or T without an explicit operator action. Class T further requires a configured paper / sandbox path before it can be flipped to live.
Capability set is sourced from existing route groups: market, kline, indicator, backtest, strategy, portfolio, dashboard, quick_trade, ibkr, polymarket, credentials, settings, community, fast_analysis, ai_chat, health. The legacy experiment/tuning service is retired. New code should not add another way to trade; it should expose existing services with proper class tags.
4. Architecture
4.1 Layered view
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ External AI agents โ P4 / P5
โ (LLM apps, MCP clients, ...) โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ HTTPS + Agent token
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Browser UI (existing) โ โ Agent Gateway (NEW) โ
โ /api/... + JWT session โ โ /api/agent/v1/... โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ โข token auth + scopes โ
โ โ โข rate limit + audit log โ
โ โ โข idempotency-key support โ
โ โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Existing service layer (single source) โ
โ market / strategies / backtest โ
โ portfolio / quick_trade / credentials / ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Postgres โข Redis โข Workers โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Optional, additive:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MCP server (read-mostly subset) โ --> Cursor / Claude-style clients
โ thin wrapper over /api/agent/v1 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโKey decision: the Agent Gateway is a thin layer, not a parallel implementation. It reuses the same Flask services; it adds identity, scopes, rate limits, idempotency, and a stable URL/version.
4.2 URL and versioning convention
/api/agent/v1/...โ agent-facing namespace, stable contract.- The browser UI keeps using
/api/...as today; it may continue to evolve more freely. - Breaking changes to the agent surface bump to
/v2;/v1is supported for a defined window.
4.3 Identity model
- A Tenant is the existing QuantDinger user (single-user or multi-user mode).
- An Agent token belongs to a Tenant and carries:
agent_id(human-readable label, e.g.cursor-mcp,strategy-bot-1)scopes(subset of capability classes from ยง3)marketsallowlist (e.g.crypto,ibkr)instrumentsallowlist (optional, for trading scope)expires_atpaper_onlyflag (default true for any token withT)
- Tokens are prefixed and hashed at rest (e.g.
qd_agent_xxx); only the prefix is shown in audit logs. - Existing JWT user sessions are not valid for
/api/agent/v1and vice versa โ separate auth pipelines, no accidental cross-use.
5. Endpoint shape (illustrative)
These are contract sketches, not committed routes. Final names follow AGENT_ENVIRONMENT_DESIGN.md Layer 3 conventions.
/* Detailed source-code truncated for AI context efficiency. */5.1 Cross-cutting requirements
Idempotency-Keyheader required for mutating classW,B,N, andTcalls. The server atomically reserves method + route + key + request hash, replays completed responses, and rejects in-progress or mismatched reuse.- Async job pattern for backtests to avoid long-lived HTTP and let LLMs poll.
- Pagination is explicit (
?limit=&cursor=); no implicit caps. - Errors follow a single envelope (
code,message,details,retriable). X-RateLimit-*headers always returned.
6. Optional MCP layer
When integrators want tool-style rather than REST:
- Ship an MCP server that wraps a curated subset of
/api/agent/v1(start with classRandB). - The MCP server reads an agent token from its own config; it never asks the model for credentials.
- Tool descriptions explicitly state risk class and scope (e.g.
run_backtest (B, paper)).
MCP is additive: REST stays the source of truth, MCP only re-shapes it for clients that prefer the protocol (Cursor, Claude-style, future tools).
7. Safety, audit, and ops
7.1 Trading safety (class T)
- Tokens default to
paper_only=true. Real-money flip requires:- Operator confirmation in the UI.
- A documented allowlist of instruments and max notional per order / per day.
- A kill switch that revokes all
Ttokens with one click and cancels open agent-originated orders.
- The Agent Gateway tags every order with
source=agent:<agent_id>so portfolio, audit, and kill-switch logic can scope by agent.
7.2 Audit log
- One append-only log per tenant:
(ts, agent_id, route, scope_class, status, idempotency_key, redacted_request_summary). - Stored alongside existing user activity; viewable per agent and per class in admin UI.
- Class
Twrites additionally include(market, instrument, side, qty, est_notional, paper_or_live).
7.3 Rate limits and quotas
- Per-token: requests/min and concurrent backtest-job caps.
- Per-tenant: aggregate cap across all that tenantโs tokens.
- LLM-cost-bearing endpoints (e.g. anything proxying to
LLM_PROVIDER) carry their own quota and are denied for tokens without an explicitai-llmsub-scope.
7.4 Secrets and credentials
- Class
Cis admin-only; the Agent Gateway must never accept exchange API keys in request bodies for non-admin tokens. - Existing encryption-at-rest (
SECRET_KEYโ Fernet forqd_exchange_credentials.encrypted_config) stays unchanged.
7.5 Multi-tenancy
- All queries through the Agent Gateway are forced through tenant-scoped service calls (no cross-tenant data leakage even on internal bugs).
- Test plan: an integration test that issues a token for tenant A and asserts every class-R route returns 404/403 for tenant B objects.
8. Deployment topologies (self-hosted vs SaaS)
QuantDinger ships as a single backend that intentionally supports two operational topologies. The Agent Gateway code is identical in both; the differences are entirely operator-controlled environment variables and where the database lives.
8.1 Topologies
| Dimension | Self-hosted (default) | SaaS / shared / hosted |
|---|---|---|
| Selector env var | QUANTDINGER_DEPLOYMENT_MODE unset (or self/local) |
QUANTDINGER_DEPLOYMENT_MODE=saas (also hosted/shared/multitenant) |
| Tenants per instance | 1 (the operator) | N (one per signup) |
| Token issuance | Operator decides every field | Tenant users may issue T tokens; paper_only=true remains the default and C remains admin-only |
Live trading (AGENT_LIVE_TRADING_ENABLED) |
Operator may flip to true |
Requires risk acknowledgement, a live-capable token, notional caps, and the deployment-wide server flag |
| Exchange credentials | Operator may store + use them | Recommended: do not accept; if you do, encrypt-at-rest and never expose via Agent Gateway (class C is admin-only) |
| Rate limits | Redis-backed per-token + per-tenant quotas | Same gateway quotas plus a per-IP outer proxy cap |
| Audit visibility | Operator | SaaS operator (you) sees everyone; tenant admins see only their own (already enforced by user_id filter in /admin/audit) |
MCP BASE_URL |
http://localhost:8888 (or LAN URL) |
https://ai.quantdinger.com (or your hosted URL) |
8.2 The hosted-mode guard (V3.1.0+)
When QUANTDINGER_DEPLOYMENT_MODE is a hosted spelling, self-service issuance still excludes C scope and defaults every T token to paper-only. A live-capable T token requires ack_live_trading_risk=true, positive per-order and per-day notional caps, and AGENT_LIVE_TRADING_ENABLED=true. The emergency stop revokes all tenant T tokens and reports any live exchange order that could not be cancelled.
This policy is covered by tests/test_agent_v1_saas_guard.py.
8.3 Recommended outer hardening for SaaS
Beyond the gateway controls, a hosted deployment should add at the proxy / infra layer:
- HTTPS-only with HSTS; no plaintext Agent token traffic.
- Per-IP rate limiting in front of the app, in addition to the Redis-backed token and tenant quotas.
- CORS:
/api/agent/v1/*should not be exposed to browser CORS โ agents call from servers / IDE subprocess / native code, not from web pages. - Quota / billing hook: wrap
agent_jobs.submit_jobin billing middleware when future job kinds consume paid services. - Token reveal hygiene: full token shown once in the Vue admin UI, never logged, never echoed back from
/admin/tokensGET. Already enforced.
8.4 Migration between topologies
Switching a running deployment from self-hosted to SaaS is non-destructive:
# Add to the env file used by docker-compose
QUANTDINGER_DEPLOYMENT_MODE=saas
docker compose up -d backendAfter restart:
- Existing T-scope tokens continue to work (the guard runs at issuance, not on each request) โ the operator should
DELETE /admin/tokens/{id}for any token they no longer want active under SaaS rules. A future enhancement may add a one-shot "revoke all T tokens on mode change" startup task. - New issuances follow SaaS rules immediately.
8.5 Why a single binary serves both
We deliberately did not fork SaaS into a separate codebase:
- Less drift: every bug fix and feature ships to both topologies on the same release.
- Self-host parity: enterprise/private users get bit-for-bit identical Agent Gateway behavior to the hosted demo, so trust transfers.
- Test surface: the
_is_saas_mode()branch is a single env-var check, easy to cover (and is, intest_agent_v1_saas_guard.py).
9. Mapping to existing code
| Concern | Existing code | Reuse strategy |
|---|---|---|
| User auth (JWT) | app/routes/auth.py, app/utils/auth.py |
Keep. Agent tokens live in a parallel module (app/utils/agent_auth.py proposed). |
| Trading | quick_trade, ibkr, polymarket |
Wrap in T-class endpoints; reuse service layer; do not fork order logic. |
| Backtest | backtest, app/services/strategy_v2/* |
Keep long-running entrypoints behind the async job table; agent endpoints remain thin submit/poll wrappers. |
| AI chat / code-gen | ai_chat |
Refactor to call internal services; agent endpoints expose the same services without the chat shell. |
| Health | health |
Reuse for /api/agent/v1/health. |
No new Python packages are required for the gateway itself; storage uses existing Postgres (new tables: qd_agent_tokens, qd_agent_jobs, qd_agent_audit).
10. Phased roadmap
| Phase | Deliverable | Risk class enabled | Human action required |
|---|---|---|---|
| A0 | Spec freeze: this doc + endpoint table + scope schema | โ | Review and merge |
| A1 | Agent token issuance + /api/agent/v1/health, markets, symbols, klines, indicators/run |
R | Issue first token in admin UI |
| A2 | Strategies CRUD + backtest async jobs + audit log v1 | R, W, B | Per-tenant opt-in for W |
| A3 | Per-token rate limits and bounded job streaming | R, W, B | โ |
| A4 | Optional MCP server wrapping the current Agent Gateway | R, B, W | Configure MCP client |
| A5 | Trading endpoints in paper-only mode + per-agent kill switch | T (paper) | Explicit per-token opt-in |
| A6 | Live trading promotion path: instrument allowlist, notional caps, dual-control toggle | T (live) | Operator dual confirmation |
A1โA4 are safe to ship without trading exposure. A5/A6 are gated and reversible.
11. Open questions
- Token storage location โ share
qd_userstable family vs new schema namespace? - Job runner โ reuse existing worker toggles (
ENABLE_PENDING_ORDER_WORKER, etc.) or introduce a dedicatedagent-jobsworker? Prefer the latter for blast-radius isolation. - OpenAPI generation โ auto-derive from Flask blueprints or hand-maintain a single
agent-openapi.jsonchecked intodocs/agent/? - MCP transport โ stdio first (simplest for desktop IDEs), HTTP later for cloud agents.
- Cost passthrough โ when class
Btriggers LLM use indirectly (e.g. NLโcode helpers), should the response include token-cost telemetry?
12. Implementation status
| Area | Status | Where it lives |
|---|---|---|
| Schema (tokens / jobs / audit / paper-orders) | Shipped | backend_api_python/migrations/init.sql (section 30) + runtime ensure in app/utils/agent_auth.py |
| Token auth + scopes + distributed token/tenant quotas + audit | Shipped | app/utils/agent_auth.py |
| Atomic idempotency for W/B/N/T mutations | Shipped | qd_agent_idempotency + agent_required |
| Async job runner, progress, and cancellation | Shipped | app/utils/agent_jobs.py |
| Read endpoints (R) | Shipped | app/routes/agent_v1/{health,markets,strategies,jobs,portfolio,research,trading_data}.py |
| Workspace endpoints (W) | Shipped | app/routes/agent_v1/{strategies,strategy_sources,indicators,research}.py |
| Notification automation (N) | Shipped | app/routes/agent_v1/notifications.py |
| Strategy API V2 backtest endpoint (B) | Shipped | app/routes/agent_v1/backtests.py |
| Trading endpoints (T) โ paper and hard-gated live | Shipped | app/routes/agent_v1/quick_trade.py (allowlists, token notional caps, server flag, emergency stop) |
| Admin token CRUD + audit viewer | Shipped | app/routes/agent_v1/admin.py |
| OpenAPI 3.0 spec | Shipped | docs/agent/agent-openapi.json |
| MCP server (Python) | Shipped | mcp_server/ โ stdio (default), sse, and streamable-http transports via QUANTDINGER_MCP_TRANSPORT |
| Operator quickstart | Shipped | docs/agent/AGENT_QUICKSTART.md |
| Job progress streaming (SSE) | Shipped | GET /api/agent/v1/jobs/{id}/stream โ snapshot / progress / ping / result frames; resume via ?since= or Last-Event-ID |
| Token UI (Profile + admin audit) | Shipped | ProfileAgentTokens.vue at Profile โ My Agent Token (/api/agent/v1/me/tokens); admin route /agent-tokens retained |
Hosted-mode hardening (QUANTDINGER_DEPLOYMENT_MODE=saas) |
Shipped | C scope stays admin-only; T defaults paper-only and live eligibility requires explicit risk acknowledgement |
| Published MCP package on PyPI | Shipped | quantdinger-mcp โ install via pipx, uvx, or pip |
| Live quick-order execution (T, self-host only) | Shipped, hard-gated | app/routes/agent_v1/quick_trade.py; requires live-capable token, explicit MCP confirmation, credential, and server flag |
13. Revision history
| Version | Date | Notes |
|---|---|---|
| 0.1 | 2026-05-02 | First draft: personas, capability classes, gateway, MCP, safety, roadmap |
| 0.2 | 2026-05-02 | A0โA5 implemented (schema, auth, R/W/B + paper-only T, admin, MCP, tests, OpenAPI, quickstart) |
| 0.3 | 2026-05-02 | Added: SSE progress streaming for jobs, MCP HTTP/SSE transport, Vue admin UI for token & audit management |
| 0.4 | 2026-05-02 | Added ยง8 Deployment topologies; shipped hosted-mode guard (QUANTDINGER_DEPLOYMENT_MODE=saas โ T-scope rejected, paper_only pinned); MCP package published to PyPI; README EN/CN now documents the SaaS vs self-host paths side-by-side |
| 0.5 | 2026-07-31 | Added full research/observation/notification MCP surface, atomic idempotency, Redis quotas, job cancellation, notional caps, and tenant emergency stop |
File: docs/agent/MCP_SETUP.md
MCP Setup
QuantDinger's MCP server wraps the Agent Gateway and keeps the REST API as the source of truth.
Install
pip install "quantdinger-mcp==0.5.0"For repository development, use pip install -e ./mcp_server instead.
Set these environment variables in the MCP client process:
QUANTDINGER_BASE_URL=http://localhost:8888
QUANTDINGER_AGENT_TOKEN=qd_agent_xxxRun quantdinger-mcp for the default stdio transport. For network use, set QUANTDINGER_MCP_TRANSPORT=sse or streamable-http, plus optional QUANTDINGER_MCP_HOST and QUANTDINGER_MCP_PORT.
Non-loopback network listeners are denied by default unless QUANTDINGER_MCP_AUTH_TOKEN is set to a distinct secret of at least 32 characters. Configure QUANTDINGER_MCP_PUBLIC_URL to the externally reachable HTTPS base URL and send the inbound token as a Bearer token from the MCP client. The inbound token protects the MCP listener; QUANTDINGER_AGENT_TOKEN remains the separate upstream tenant credential.
Client configuration
A typical stdio client entry is:
{
"mcpServers": {
"quantdinger": {
"command": "quantdinger-mcp",
"env": {
"QUANTDINGER_BASE_URL": "http://localhost:8888",
"QUANTDINGER_AGENT_TOKEN": "qd_agent_xxx"
}
}
}
}Keep the configuration private. Prefer an environment-secret facility when the client supports one.
Expected workflow
- Use market tools for discovery and OHLCV reads.
- Use universe, factor, and watchlist tools for research workspace inputs.
- Use indicator tools only for chart artifacts.
- Compile Strategy API V2 code before saving it.
- Save private Strategy API V2 sources and inspect version history.
- Create stopped deployments from saved V2 source IDs.
- Use bounded job polling or SSE streaming for backtest results.
- Use broker and execution observation tools only for reads; credential secrets are never exposed.
- Use N-scope signal-alert tools for notification automation.
- Supply a unique
idempotency_keyto every mutating W/B/N/T tool. - Require explicit confirmation before restoring source versions, cancelling jobs, deleting alerts, delivering notifications, stopping runtime state, emergency-stopping trading, or placing any order.
The strategy manifest owns market, instrument, frequency, warmup, dependency, and leverage scope. Do not pass those as alternate backtest fields.
For the exact tool signatures, see the MCP package README and Agent OpenAPI.
File: docs/agent/README.md
Agent documentation (English)
This folder holds agent-facing material for coding assistants (Cursor, Claude Code, Codex, CLI bots, etc.).
| Document | Purpose |
|---|---|
| MCP_SETUP.md | Wire Cursor / Claude Code / Codex / remote agents to a QuantDinger backend via the quantdinger-mcp MCP server (local stdio + remote HTTP) |
| AGENT_ENVIRONMENT_DESIGN.md | Architecture: layered contracts (docs โ commands โ API/MCP), security boundaries, roadmap, implementation checklist |
| AI_INTEGRATION_DESIGN.md | How external AI agents (P4) and autonomous strategy AIs (P5) consume QuantDinger via a versioned, scoped Agent Gateway |
| AGENT_QUICKSTART.md | Operator + integrator walkthrough: issue a token, call the Gateway, run paper trades |
| agent-openapi.json | Machine-readable contract for /api/agent/v1 (OpenAPI 3.0) |
| ../architecture/API_CONVENTIONS.md | Shared HTTP conventions (envelopes, auth, Public/Internal tiers) |
| ../api/openapi.yaml | Human Web API spec (flask-smorest; migration in progress) |
Language policy: Agent-oriented docs and .cursor/skills/ in this repository are English only so the same text works across tools and locales.
For human product overviews in other languages, see the root README.md and docs/README_CN.md.