- RiskManager halts + flattens on max daily loss / drawdown, caps position size and trades per day; adds a manual kill switch (POST /api/bot/kill). - Telegram and webhook notifications on trades, errors and halts. - Optional API bearer token on /api, injected server-side by the frontend nginx so the browser never sees it. - Kraken and Coinbase market data (public); Coinbase live broker. Live on an unsupported exchange now errors instead of silently falling back to paper. - Strategy presets backed by the StrategyConfig table (CRUD API + UI). - Idempotent additive-column migration so existing SQLite databases upgrade in place (fixes the new strategy_configs.name column).
113 lines
4.7 KiB
YAML
113 lines
4.7 KiB
YAML
name: crypto_trading
|
|
|
|
services:
|
|
backend:
|
|
build: ./backend
|
|
image: crypto_trading-backend
|
|
container_name: crypto-trading-backend
|
|
restart: unless-stopped
|
|
mem_limit: ${CRYPTO_MEM_LIMIT:-1g}
|
|
cpus: ${CRYPTO_CPUS:-2}
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "5"
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
# Reachable only on the internal network; the frontend nginx proxies to it.
|
|
expose:
|
|
- "8000"
|
|
environment:
|
|
- MODE=${CRYPTO_MODE:-paper}
|
|
- EXCHANGE=${CRYPTO_EXCHANGE:-binance}
|
|
- SYMBOL=${CRYPTO_SYMBOL:-BTC/USDT}
|
|
- TIMEFRAME=${CRYPTO_TIMEFRAME:-1m}
|
|
- POLL_INTERVAL=${CRYPTO_POLL_INTERVAL:-5}
|
|
- STARTING_BALANCE=${CRYPTO_STARTING_BALANCE:-10000}
|
|
- FEE_BPS=${CRYPTO_FEE_BPS:-10}
|
|
- SLIPPAGE_BPS=${CRYPTO_SLIPPAGE_BPS:-5}
|
|
- DATABASE_URL=sqlite:////data/trader.db
|
|
- CORS_ORIGINS=${CRYPTO_CORS_ORIGINS:-*}
|
|
- EXCHANGE_API_KEY=${CRYPTO_EXCHANGE_API_KEY:-}
|
|
- EXCHANGE_API_SECRET=${CRYPTO_EXCHANGE_API_SECRET:-}
|
|
- EXCHANGE_SANDBOX=${CRYPTO_EXCHANGE_SANDBOX:-false}
|
|
- MAX_DAILY_LOSS=${CRYPTO_MAX_DAILY_LOSS:-0}
|
|
- MAX_DRAWDOWN_PCT=${CRYPTO_MAX_DRAWDOWN_PCT:-0}
|
|
- MAX_POSITION_PCT=${CRYPTO_MAX_POSITION_PCT:-1.0}
|
|
- MAX_TRADES_PER_DAY=${CRYPTO_MAX_TRADES_PER_DAY:-0}
|
|
- TELEGRAM_BOT_TOKEN=${CRYPTO_TELEGRAM_BOT_TOKEN:-}
|
|
- TELEGRAM_CHAT_ID=${CRYPTO_TELEGRAM_CHAT_ID:-}
|
|
- NOTIFY_WEBHOOK_URL=${CRYPTO_NOTIFY_WEBHOOK_URL:-}
|
|
- API_TOKEN=${CRYPTO_API_TOKEN:-}
|
|
- API_TOKEN_HEADER=${CRYPTO_API_TOKEN_HEADER:-X-API-Token}
|
|
volumes:
|
|
# Persist the SQLite database across releases. Prefer an absolute host
|
|
# path (CRYPTO_DATA_DIR / PERSISTENT_MOUNT); the repo-relative default
|
|
# can be cleaned by runner maintenance.
|
|
- ${CRYPTO_DATA_DIR:-./data}:/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "python -c \"import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=5).status==200 else 1)\""]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 20s
|
|
networks:
|
|
- internal
|
|
|
|
frontend:
|
|
build: ./frontend
|
|
image: crypto_trading-frontend
|
|
container_name: crypto-trading-frontend
|
|
restart: unless-stopped
|
|
mem_limit: ${CRYPTO_MEM_LIMIT:-1g}
|
|
cpus: ${CRYPTO_CPUS:-2}
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "5"
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
depends_on:
|
|
backend:
|
|
condition: service_healthy
|
|
environment:
|
|
- API_TOKEN=${CRYPTO_API_TOKEN:-}
|
|
- API_TOKEN_HEADER=${CRYPTO_API_TOKEN_HEADER:-X-API-Token}
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:80/ || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|
|
networks:
|
|
- internal
|
|
- docker_default
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=docker_default"
|
|
- "traefik.http.routers.crypto-trading.rule=Host(`crypto.${BASE_DOMAIN:-matitos.org}`)"
|
|
- "traefik.http.routers.crypto-trading.entrypoints=websecure"
|
|
- "traefik.http.routers.crypto-trading.tls.certresolver=myresolvercd"
|
|
- "traefik.http.services.crypto-trading.loadbalancer.server.port=80"
|
|
# Basic auth on the WebUI (htpasswd), required: the stack refuses to
|
|
# start when CRYPTO_FRONTEND_AUTH_USERS is unset (no admin/admin
|
|
# fallback). A per-source rate limit reduces credential brute-forcing.
|
|
- "traefik.http.routers.crypto-trading.middlewares=crypto-trading-auth@docker,crypto-trading-sec@docker,crypto-trading-authlimit@docker"
|
|
- "traefik.http.middlewares.crypto-trading-auth.basicauth.users=${CRYPTO_FRONTEND_AUTH_USERS:?CRYPTO_FRONTEND_AUTH_USERS is required (htpasswd string, e.g. htpasswd -nbB user pass)}"
|
|
- "traefik.http.middlewares.crypto-trading-authlimit.ratelimit.average=${CRYPTO_AUTH_RATE_LIMIT_AVERAGE:-10}"
|
|
- "traefik.http.middlewares.crypto-trading-authlimit.ratelimit.burst=${CRYPTO_AUTH_RATE_LIMIT_BURST:-5}"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.stsSeconds=31536000"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.stsIncludeSubdomains=true"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.forceSTSHeader=true"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.contentTypeNosniff=true"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.browserXssFilter=true"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.frameDeny=true"
|
|
- "traefik.http.middlewares.crypto-trading-sec.headers.referrerPolicy=strict-origin-when-cross-origin"
|
|
|
|
networks:
|
|
internal:
|
|
docker_default:
|
|
external: true
|