Automated futures trading stack
- Role
- Design, build, and operations
- Stack
- Python · PostgreSQL · Docker · broker API
- Status
- Running in paper mode, by design
- Year
- 2025–2026
I trade Micro E-mini Nasdaq futures (MNQ). This system automates the mechanical part: it watches a real-time signal feed, parses free-form trade calls into structured intents, checks them against a risk layer, and places the resulting orders through the ProjectX/TopstepX broker API, then manages the position (modifies, trims, break-even stops, cancels) as follow-up signals arrive.
It runs as three containers alongside the rest of my stack, sharing its PostgreSQL instance for state:
The risk layer is the point
Anyone can call a REST endpoint that places an order. The engineering is in everything that decides whether the order should be placed:
- Position limits and dynamic sizing: contract count is computed per-signal from account equity and configured risk, not hardcoded.
- Account selection with fan-out: signals can target one evaluation account or fan out to several, with an allowlist controlling which accounts are eligible.
- Daily-loss-limit snapshots: before acting, the manager snapshots account drawdown against the broker's daily loss limit and refuses trades that would breach it.
- Session awareness: a session clock models the futures trading calendar (nightly resets, weekend closes), so the system doesn't act on stale signals outside market hours.
- Human-in-the-loop mode: signals can require explicit approval from Telegram before execution, and
flatten all/ per-ticker close/trim/break-even commands are always available manually.
Try the risk gate
This is what a signal goes through before it becomes an order. Type a trade call or tap an example; it parses into a structured intent and runs the real checks. The valid oversized order and the two toggles below show how the gate refuses trades that a naive "call the broker endpoint" script would happily place.
The parser turns a free-form call into a structured intent; the gate is where the engineering lives. Flip the session closed or push the account near its daily loss limit and watch the same valid order get refused before it ever reaches the broker.
Incident: the day the stack went down
In June 2026 the stack stopped trading: the broker API key had been invalidated (expired or compromised, treated as compromised either way) and database state had drifted from reality while services retried. Recovery was a proper ops exercise: rotate the credential, audit what the retry loop had written, repair position state against the broker's records, and bring the services back one at a time. Two changes came out of it: credentials got a rotation procedure instead of a "set once" assumption, and startup now reconciles local state against the broker before acting on anything.
The dashboard
Watching the system is a Streamlit dashboard: session equity, open positions, and how much of the broker's daily loss limit the account has used. It lives behind the VPN, so here is a faithful recreation of the layout.
session P&L
+$412.50
open positions
1
session
open · 11:42 ET
equity · today
| symbol | side | size | entry | stop | uP&L |
|---|---|---|---|---|---|
| MNQ | long | 2 | 20,250.00 | 20,180.00 | +$187.00 |
daily loss limit used
31%Status, honestly
The stack runs in paper mode. That's deliberate: the automation is validated against live market data without live capital while I finish the account fan-out work and build a longer performance record. A system that places real orders is exactly the kind of thing that should have to earn its way out of staging.
The repo is private because it contains strategy-specific logic, but I'm glad to walk through the architecture in detail: bretdubois1@gmail.com.