From df85201c457a855dbb16bf85f7d672202f28518a Mon Sep 17 00:00:00 2001 From: momentum dev Date: Mon, 27 Jul 2026 08:07:32 -0500 Subject: [PATCH] feat: Tradier parallel-stack compose template Self-contained shakeout stack beside the paper stack: own postgres (host 5434, disposable pgdata-tradier volume), own dashboard (host 8768), loop with BROKER=tradier (sandbox). Data plane stays Alpaca; Telegram/deadman disabled so main-stack alerts stay unambiguous; main data dir mounted read-only (inherits learned_params/overrides.env). Filled local copy is git-excluded; bootstrap_live_db.sh playbook reused for DB parity. Co-Authored-By: Claude Fable 5 --- deploy/docker/truenas-compose.tradier.yaml | 111 +++++++++++++++++++++ docs/TRADIER-FALLBACK.md | 20 ++++ 2 files changed, 131 insertions(+) create mode 100644 deploy/docker/truenas-compose.tradier.yaml diff --git a/deploy/docker/truenas-compose.tradier.yaml b/deploy/docker/truenas-compose.tradier.yaml new file mode 100644 index 0000000..b86f3a5 --- /dev/null +++ b/deploy/docker/truenas-compose.tradier.yaml @@ -0,0 +1,111 @@ +# TRADIER PARALLEL STACK — template (fill , save as +# truenas-compose.tradier.local.yaml, never commit the filled copy). +# +# Runs the SAME image/strategy as the paper stack but executes through the +# Tradier mirror (BROKER=tradier, sandbox by default). Fully self-contained: +# own postgres (host 5434), own dashboard (host 8768). Market data stays on +# Alpaca (keys still required). Telegram + deadman are OFF here so the main +# stack's alerts stay unambiguous. +# +# Shares the main stack's data dir READ-ONLY: the loop inherits the nightly +# learned_params.json and overrides.env without being able to clobber them. +# +# Fresh-DB bootstrap (same playbook as live staging), run on cube: +# ./bootstrap_live_db.sh +# or skip it and let the loop start from an empty DB (research baselines absent, +# config knobs at code defaults — fine for a first shakeout). +# +# Requires an image built from >= 1cefb51 (tradier_broker in image). +# +# Sandbox caveat: Tradier sandbox fills are simulated and can be slow/absent — +# an order parked in 'pending_new' is usually the sandbox, not the code. +services: + postgres: + image: postgres:18 + environment: + PGDATA: /var/lib/postgresql/data/pgdata + POSTGRES_DB: momentum + POSTGRES_PASSWORD: + POSTGRES_USER: momentum + TZ: America/New_York + healthcheck: + interval: 10s + retries: 5 + test: + - CMD-SHELL + - pg_isready -U momentum -d momentum + timeout: 5s + ports: + - 5434:5432 + restart: unless-stopped + volumes: + - /mnt/Volume/Storage/Apps/momentum/pgdata-tradier:/var/lib/postgresql/data + + loop: + image: docker.io/fez5587/momentum-app:latest + command: + - python + - -u + - run_live_paper.py + - --no-dashboard + depends_on: + postgres: + condition: service_healthy + environment: &tradier-env + BROKER: tradier + TRADIER_ACCESS_TOKEN: + TRADIER_ACCOUNT_ID: + TRADIER_ENV: sandbox + ALPACA_API_KEY: + ALPACA_SECRET_KEY: + ALPACA_BACKFILL_FEED: sip + ALPACA_BASE_URL: https://paper-api.alpaca.markets + ALPACA_PAPER_SYNC_ENABLED: '1' + DATABASE_URL: postgresql://momentum:@postgres:5432/momentum + # alerts stay with the MAIN stack — no double Telegram / deadman pings + TELEGRAM_BOT_TOKEN: '' + TELEGRAM_CHAT_ID: '' + DEADMAN_DAILY_PING: '0' + DEADMAN_STALE_MIN: '5' + INGEST_STALE_BLOCK: '0' + INGEST_STALE_SECONDS: '180' + ORB_BARS: '5' + PG_SKIP_SCHEMA: '0' + QUOTE_SNAPSHOT_INTERVAL_SECONDS: '8' + STRATEGY_SETUPS: opening_range_break + DEPTH_RECORD_ENABLED: '0' + TZ: America/New_York + healthcheck: + interval: 60s + retries: 5 + start_period: 180s + test: + - CMD + - python + - -c + - import os; os.environ['PG_SKIP_SCHEMA']='1'; import datetime,sys; from research.multi_schema import open_research_db; r=open_research_db('market').execute('SELECT max(timestamp) FROM events').fetchall()[0][0]; sys.exit(0 if r and (datetime.datetime.now()-r).total_seconds()<300 else 1) + timeout: 20s + restart: unless-stopped + volumes: + - /mnt/Volume/Storage/Apps/momentum/data:/app/data:ro + + dashboard: + image: docker.io/fez5587/momentum-app:latest + command: + - python + - -u + - dashboard_api.py + depends_on: + postgres: + condition: service_healthy + environment: + <<: *tradier-env + COMMAND_BUS_ENABLED: '1' + DASHBOARD_HOST: 0.0.0.0 + DASHBOARD_PORT: '8765' + DASHBOARD_TOKEN: + ports: + - 8768:8765 + restart: unless-stopped + volumes: + - /mnt/Volume/Storage/Apps/momentum/data:/app/data:ro diff --git a/docs/TRADIER-FALLBACK.md b/docs/TRADIER-FALLBACK.md index 59de4fd..811a90f 100644 --- a/docs/TRADIER-FALLBACK.md +++ b/docs/TRADIER-FALLBACK.md @@ -44,6 +44,26 @@ Smoke refuses order exercises against the live base URL. filters are client-side; the live loop only queries today + held symbols, so behavior matches. +## Parallel shakeout stack (TrueNAS) + +`deploy/docker/truenas-compose.tradier.yaml` is the committed template; the +filled copy lives (git-excluded) at `truenas-compose.tradier.local.yaml`. +Runs beside the paper stack, fully isolated: + +- own postgres (host **5434**, fresh `pgdata-tradier` volume), own dashboard + (host **8768**), loop with `BROKER=tradier` against the sandbox account +- market data still Alpaca; Telegram + deadman OFF (main stack keeps alerts) +- main data dir mounted **read-only** — inherits nightly + `learned_params.json` / `overrides.env`, cannot clobber them; if a service + ever crashes on a read-only-filesystem error, switch that mount to a + writable copy +- fresh DB starts empty (knobs at code defaults). For research-baseline + + knob parity with the paper stack, reuse the live-staging playbook: + `./bootstrap_live_db.sh ` +- needs an image built from >= 1cefb51 (`tradier_broker/` in image) +- sandbox fills are simulated and can be slow or absent — an order parked in + `pending_new` is usually the sandbox, not the code + ## Before flipping live money to Tradier 1. Rotate the sandbox token (it transited chat) and mint a live token.