How Mercurio Is Built: A Tour of the System That Places the Trades
Four gates between a chart and a live order, a reconciliation loop that treats the broker as the only source of truth, and a regime filter that keeps the bot in cash most of the time. This is the architecture, drawn out.
Most trading bots are a single script: fetch data, compute a signal, send an order. That design is fine until the day the database is empty, or the broker disagrees with your local records, or a bull-market backtest quietly convinces you to trade through a bear market. Mercurio is built defensively, because every one of those failures has already happened to us once -- and each one is now a named lesson in the codebase.
This article is the map. It is built around three interactive diagrams of the system's real control flow. You can drag to pan and use the controls to zoom. Each diagram is generated from the same specification the engine documents, so it cannot drift out of date with hand-drawn boxes.
The architectural thesis
The edge is the technical strategy. Everything else -- the AI layer, the risk manager, the regime gate, the reconciler -- exists to protect that edge from itself and from the real world. Layers can only ever reduce risk, never amplify it.
The shape of the system
Mercurio is a hybrid: a Python trading engine and FastAPI gateway on one side, a Next.js dashboard on the other, with PostgreSQL for persistence and Alpaca as the broker. The pieces are deliberately decoupled so the engine can keep trading even if the dashboard, the database, or the AI service is down.
4
Independent gates between a signal and an order
1
Source of truth (Alpaca) for every position
15
Max open positions, enforced in the risk manager
1.5%
Hard risk cap per trade
100%
Paper capital -- nothing here is live money
10
Green index days required before any long
1. The decision pipeline
A trade does not happen because one indicator lit up. It happens because a signal survived a gauntlet. The trend strategy proposes; four independent gates dispose. Any single gate can route the candidate to cash, and the gates are ordered cheapest-to-most-expensive so the system does the least work to reject a bad idea.
The decision pipeline: from market data to a live order
Every signal passes four independent gates. Any one of them can send it to cash. Only a signal that clears all four becomes a bracket order at Alpaca.
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
01Signal generation. Three stacked EMA pairs (12/48, 24/72, 48/144) must agree on direction, and ADX must confirm a real trend exists. No stack, no signal.
02Regime gate. Even a perfect signal is ignored unless the broad market is in a confirmed uptrend. This is the single biggest filter -- it keeps the bot in cash for long stretches. More on it below.
03AI veto. The AI layer can only remove a trade (earnings tomorrow, a sentiment shock), never create one. If the AI is down, the system trades on technicals alone -- graceful degradation, not failure.
04Risk manager. The final gate sizes the position to 1.5% risk, attaches a stop-loss, and checks exposure caps and buying power. It can shrink a trade or reject it, but it can never enlarge one.
Why the order matters
Putting the regime gate before the expensive AI call is not cosmetic. In a bear or choppy market the gate rejects nearly everything, so the system almost never spends an AI budget call on a trade it was never going to take. Cheap filters first is both safer and cheaper.
2. The reconciliation loop
The most dangerous bug we ever shipped was not in a strategy. It was a deploy where the database came up empty, the engine saw zero local records for its live positions, concluded they were all orphans, and queued market-sell orders for the entire portfolio. The orders were cancelled in time. The lesson was permanent: the broker, not our database, is the source of truth.
So reconciliation is not a nightly batch job -- it is a permanent loop that runs every cycle. It reads Alpaca's actual positions, compares them against the local trade log, repairs any drift in our metadata, and mirrors any fills the engine missed back into Alpaca's record. The dashboard only ever shows reconciled numbers.
The reconciliation loop: Alpaca is the source of truth
On every cycle the reconciler treats the broker as ground truth, repairs local metadata, mirrors any missing fills back to Alpaca, and feeds the dashboard reconciled numbers. State can never silently drift.
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
This is why the live number is honest
Because Alpaca is ground truth, the dashboard cannot flatter us. Right now the live paper account shows a realized loss of about -$722 across 536 round-trips at a 51% win rate. That is the reconciled number, straight from the broker. We would rather show a real loss than a comfortable fiction -- and we never present a backtest figure as a live one.
3. The regime gate
Trend following on a tech-heavy universe makes money in sustained bull markets and loses it almost everywhere else. We proved that the hard way: over a full five-year cycle including the 2022 bear, the raw strategy is net negative. The fix is not a cleverer entry -- it is the discipline to not trade at all when the weather is wrong.
The regime gate reads the S&P 500's own daily trend. Longs are unlocked only after the index closes above both its 50-day and 200-day moving averages for ten consecutive sessions. Until that streak is met, the engine sits in cash. It is a slow, deliberately conservative switch.
The regime gate: ten green days before a single long
The gate reads the S&P 500's own daily trend. Longs are only unlocked after the index closes above both its 50- and 200-day averages for ten consecutive sessions. Anything less means cash.
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
The ten-day confirmation is not an arbitrary number -- it survived walk-forward testing where dozens of flashier ideas did not. It improved results on both the in-sample and out-of-sample windows, which is the only kind of improvement we trust.
Regime configuration
In-sample Sharpe
Out-of-sample Sharpe
Directional (long + short)
+0.99 IS
-0.13 OOS (fails)
Bull-only (no confirmation)
0.51
0.50
Bull-only + 10-day confirm
1.13
0.58
The mechanism is visible in one statistic: in the 2022 bear market, the bull-only filter without confirmation still took 200 longs and lost roughly $6,900. With the ten-day confirmation, the streak never reached ten, so it took zero longs and sat the whole bear out. The gate did its job by doing nothing.
Fault tolerance is the feature
Each subsystem degrades independently instead of taking the others down with it. This is what lets a retail-scale bot run unattended:
AI service down -> trade on technicals alone; the veto simply abstains.
Database down -> keep trading, log to a local file, sync when it returns; never auto-liquidate on an empty DB.
Exit order doesn't fill -> retry every cycle until it does, instead of abandoning the position naked.
Drawdown breaker trips -> flatten and enter a cooldown; persisted to state so a restart cannot silently re-enable trading.
State that affects money is never in-memory only
Kill switches, loss-streak counters, the drawdown breaker, and the daily-reset flag all persist to a state store. An early version kept them in memory; a mid-day restart wiped them and re-enabled strategies that had been killed for losing. Now any state that can move money survives a restart by design.
Read the code
None of this is a marketing diagram drawn after the fact. The pipeline lives in orchestrator.py, the gates in risk_manager.py and intermarket.py, and the validation in portfolio_backtest.py. If a diagram here and the code ever disagree, the code wins -- and that is a bug for us to fix.
Disclaimer. Mercurio runs on paper capital. Live figures cited here are reconciled paper-trading results, not real-money performance; backtest figures are historical simulations. Nothing here is financial advice. Past performance does not guarantee future results.