GMAN:·paisamaker:·gex-advisor:POLLING·fetcher:429rps·npm:v0.1.8 (9rel)
$ cd ..

trading systems

0DTE options · gamma advisory · real-time data warehouse

Three coordinated services on one host. A 104 GB time-series warehouse fed by 5 WebSocket hubs — the REST scaffold has been inactive since the 2026-03-30 cutover. A 73-feature signal evaluator on a 30-second tick. An execution engine with 5 hard gates and a kill switch. Producers write, consumers attach read-only — no message bus, no socket, no internal HTTP in the trading lane. Single-host, single-writer by design: every read is local-disk fast, every write is single source of truth.

Scope: paper trading on IBKR · personal, not a fund · infrastructure validation over strategy backtest.

── pipeline ─ signal to execution
── ingest ──── signal ──── execution ──GEXBot WS (Azure Web PubSub)5 hubs · 39 tickers · sole writer since 2026-03-30 · REST scaffold inactivesvc-market-data-fetcher[WS-ONLY]Python · stdlib + threading5 WS hubs · 13 categories~487k rows / RTH dayp99 write 2 msmarket_data.db (WAL)104 GB · 11.4 M rows · on Hetzner 197G volumesqlite ?mode=roingest_mode='live'svc-gex-advisor[POLLING]Python · pandas + numpy73-feature pipeline / tick30s loop · 240-reading windowCASCADE+CHARM 63% (n=57)alert_log.db (WAL)90 MB · 20,979 alerts · 36 sessionssqlite ?mode=rowatermark dedupsvc-paisamaker[FULLY_LIVE]Python · ib_insync · asyncioDual-source signal queue163 positions · 40 closed20+ safety gatesIBKRpaper · DUP975149port 4002── safety planekill-switch: RUNNING / HALT5 hard gates per order: live_flag · paper_port · kill_level · shadow_check · 0DTE_validateIBKR mode: DATA_ONLY → RECONNECT_WARMUP (60s) → FULLY_LIVE · 2 disconnects survived (NRestarts=0)

operational degraded down — travelling pulses tick at the actual 30s poll cadence; metrics from snapshot 2026-05-06.

── subsystems ─ in execution order
svc-paisamaker[TRADING]uptime 100%

paisamaker · automated 0DTE execution

Python 3.12 · ib_insync · Hetzner systemd · uptime 19h 55m at snapshot, NRestarts=0

Two independent signal sources (a custom GEX evaluator on SPX and an advisor poller on 7 equity tickers) feed a unified dispatcher. The dual-lane design came from one specific observation: IBKR gateway reconnects take ~6 seconds, and a single-loop design would miss the next 30-second cycle. Separate lanes, independent cooldowns, per-(source,ticker) gates. The mode state machine (DATA_ONLY → RECONNECT_WARMUP → FULLY_LIVE, with EXECUTION_DEGRADED on disconnect) blocks new entries during recovery instead of retrying synchronously.

positions (lifetime)163
closed trades40
tickers8
safety gates20+ pre/post-trade
Python 3.12ib_insyncasyncioSQLite WALsystemdDocker (IB Gateway)
svc-gex-advisor[POLLING]uptime 99.8%

gex-advisor · multi-ticker signal evaluator

Python 3.11 · 8 tickers · 30s cycle · 240-reading rolling window

Four alert types on a 73-feature pipeline + five-force alignment model. CASCADE+CHARM combined: 29 wins / 46 resolved (63%) over 27 RTH sessions, n=57 — larger sample than earlier, more honest aggregate. STRUCTURE_BREAK fired 41 times with 0/37 wins → spawned filter #50 (counter-trend suppression); zero fires since 2026-03-31. Every alert lands in alert_log.db with full feature snapshot + outcome fields (MFE, MAE, target_hit, stop_hit) — new signals ship as shadows first (20,670 shadow rows currently logging passively), validate against data, then promote.

alerts (lifetime)20,979
features / tick73
tickers monitored8
CASCADE+CHARM (n=57)63%
Python 3.11SQLite WALpandasDiscord webhookssystemdStreamlit
svc-market-data-fetcher[INGESTING]uptime 99.95%

market-data-fetcher · real-time data warehouse

Python 3.11 · stdlib + asyncio · WAL · 5 WS hubs + 11 REST endpoints / ticker

WebSocket-only in production since the 2026-03-30 cutover — 5 Azure Web PubSub hubs, 39 tickers, 13 categories, server-side downsampled to 30s buckets. Wire is protobuf-over-zstd with a dual-path decoder. Sole writer; the REST poller (39 × 11 = 429 concurrent / 30s) is `inactive` on disk for instant rollback. Today's outage (11:18 → 14:07 ET) exposed lack of REST passive-failover — issue #92. Per-hub circuit breaker (5 fails → 300s pause), per-task exponential backoff (2s → 120s), batched serial SQLite writer with p99 ~2 ms steady. Stdlib only — no frameworks, no ORM.

ingest / RTH day~487k
rows (live)11.4 M
p99 write2 ms
db size104 GB
Python 3.11asyncioaiohttpSQLite WALprotobufzstdsystemd
── safety gates ─ 20 gates, every trade path
/opt/paisamaker-app/safety/20 gates · all tested
gatecategoryfail behavior
kill-switch L0kill-switchsystem running, entries allowed
kill-switch L1kill-switchhalt entries, exits still run
kill-switch L2kill-switchflatten all + no new entries
max positions globalpre-tradereject entry, log reason=max_positions
max positions per-tickerpre-tradereject entry, log reason=ticker_limit
max daily losspre-tradehalt entries for the day
paper-port fail-closedpre-tradereject if IBKR port ≠ 4002
contract validationpre-tradereject if 0DTE expiry or class mismatch
spread gatepre-tradereject if bid-ask spread > 30%
bid > 0 gatepre-tradereject into empty markets
fresh quote re-fetchexecutionre-validate drift + spread at submit
IBKR mode state machinestateDATA_ONLY → DEGRADED → WARMUP → FULLY_LIVE
post-resume warmupstate60s stability before trading resumes
position dedup (UNIQUE)executionDB rejects duplicate contract_id rows
idempotent advisor alertsdataUNIQUE on advisor_alert_id
EOD flattenexitforce close 15:45 ET
EOD panic windowexitMKT escalation from 15:35 ET
max hold timeexit90m SPX / 60m QQQ / 30m singles
exit escalation (3-tier)exitLMT@mid → LMT@bid → MKT
per-source cooldownpre-tradegexwatch doesn't block advisor

Every gate returns a specific reject reason that's logged to discord_signals.db.gex_execution_log. No silent failures. Audit by reading the log.

── planned ─ requires read-only VM API

A live alert feed and live trade feed are planned next. Both require a read-only HTTP API on the Hetzner VM that exposes alert_log.db and gex_execution_log to the public internet. About four hours of FastAPI + systemd config, pending a safer deployment posture (rate limits, read-only contracts enforced at the server layer, not trusted from the client).

  • Live alert feed — tail of the last N alerts with outcome fields
  • Live trade feed (market hours) — current positions + today's fills
  • Week-by-week P&L + fill-rate charts
  • Shadow signal log — signals being validated before promotion
── eof ─