Article · Next.js
Sometimes the best trade is no trade: a fail-closed risk governor for AI agents
ThesisCircuit lets three strategy agents argue, a critic object, and a deterministic risk governor decide. It trades on paper only, and NO TRADE is an audited result rather than an error.
Paper trading only. Everything below describes a system that talks exclusively to Alpaca's paper endpoint. No real money was involved, and nothing here is investment advice.
Most trading-agent demos are built to produce a trade. We built ThesisCircuit, as team teqprotech for the Alpaca AI Trading Agents Hackathon on lablab.ai, to do something harder: defend a decision, including the decision not to trade. Its line is "AI strategies compete. Risk decides. Sometimes the best trade is no trade."
Separate the voices from the vote
Each cycle starts from current Alpaca market, account and options data. Three deterministic strategy agents read the same snapshot:
- Trend looks for directional continuation.
- Range looks for mean reversion.
- Defensive prefers to preserve capital when the evidence is weak.
Each one emits a typed thesis: its rationale, confidence, data timestamps, invalidation conditions and a bounded-loss estimate. A decision council compares the theses, and a critic goes looking for stale evidence, disagreement, liquidity problems and hidden assumptions.
Then comes the part that matters. Agent consensus can create a proposal, but it can't authorise an order. Authority belongs to one component.
The governor
The risk governor is deterministic. It checks paper mode, endpoint safety, data freshness, liquidity, buying power, bounded premium loss, conflicts, idempotency and session budgets. It fails closed: if something can't be confirmed, the answer is no.
A rejection isn't an exception to swallow. It becomes an audited NO TRADE, stored with the reasons like any other outcome. A system that can't record why it refused will, sooner or later, hide a refusal.
The same philosophy runs down to configuration. The only broker host the system accepts is Alpaca's paper endpoint, and live trading was never created. A live configuration doesn't degrade gracefully. It fails closed.
Surviving the boring failures
Trading systems rarely fail on the clever part. They fail on a timeout halfway through a submission, a restart, or two workers picking up the same job. ThesisCircuit treats those as first-class problems:
- Durable order intents. An approval becomes an order intent in Supabase that exactly one worker can claim.
- Reconcile before retry. If a submission times out, the system reconciles by
client_order_idbefore it tries again, so a lost response can't turn into a duplicate order. - Bounded sessions. Execution sessions expire on their own and carry separate budgets for opening, closing and total orders.
- Conservative accounting. An order in an UNKNOWN state still consumes budget, and terminal outcomes force the execution flags off.
Shadow records, clearly labelled
Rejected but well-formed proposals are kept as shadow records. They're never shown as broker activity. A documented rule marks each one against later quotes without letting duplicate cycles inflate the sample.
We're explicit about the limits. Historical options paths and exact-horizon quote retrieval aren't implemented, so the shadow analysis is not a backtest, and we don't present it as one.
One paper-account proof, then stop
On 2 September 2026 the full pipeline placed a single, deliberately small order in the dedicated paper account: one SPY call contract, a DAY limit order, filled at the limit or better. No second or closing order was submitted. Production now rests with execution and autonomous trading disabled. Bounded autonomous activation was verified synthetically with zero broker calls.
The final validation suite had 256 backend tests passing, plus Ruff, a frontend typecheck, a production build and a paper-safety audit. The backend is FastAPI on the official alpaca-py SDK, hosted on Railway, and the dashboard is a Next.js app on Vercel.
What generalises
You can swap "order" for "quote", "stock allocation" or "dispatch" and the design still holds:
- Let agents argue, and make them argue in typed, timestamped structures.
- Give one deterministic component the authority, and make it fail closed.
- Treat "no" as a result with reasons, not an error.
- Assume the network will lie to you about whether something happened, and reconcile before you retry.
It's the same pattern as the human approval step in QuotePilot AI and the protected reserve in NeighborOps AI. In those two the final authority is a person. Here it's a set of rules people wrote in advance. I wrote more about that pattern in Human approval is an architecture decision. The AI agents and Python skill pages list the related builds, and Teqprotech's AI agents with human approval service is the client-facing side of the same approach. If you're designing an agent of your own, the free human-approval architecture checklist turns these rules into checks you can run.
Work like this runs through Teqprotech · Python, Scraping & Data Automation , AI Agents with Human Approval , API & CRM Integrations