On April 22, 2026, Robson opened its first trade with real capital: a BTCUSDT perpetual futures long position, entered at $77,932.40, with a chart-derived stop at $76,158.25 and a maximum risk of $4.
That number is intentional. The first trade was sized conservatively on purpose. The goal was not to generate meaningful returns. It was to confirm, end-to-end, that the full system works under production conditions: real credentials, real exchange, real order flow, real position monitor ticking every twenty seconds.
This post covers how the system was built, what the validation process confirmed, and what the architecture looks like now that Robson is operational.
The migration that made this possible
The path to the first real trade involved a significant architectural shift in the exchange layer.
Robson originally operated through Binance Isolated Margin, placing orders against spot holdings with leverage applied per-symbol. That model has narrow limits: isolated margin accounts are subject to position restrictions, liquidation behavior differs from futures, and the order API does not match the USD-M Futures API that governs perpetual contracts.
The migration moved Robson to Binance USD-M Perpetual Futures. This is a different account type, a different API surface, and a different set of constraints. The position mode must be One-way (not Hedge mode). Leverage must be set explicitly per symbol before any order is placed. Quantity precision requirements are stricter. The funding rate model is different from margin interest.
This was not a configuration change. It required rewriting the exchange adapter from scratch, updating every path that touched order placement, fill handling, and position reconciliation, and updating the validation runbooks to cover futures-specific failure modes.
Architecture overview
Robson follows a hexagonal architecture organized across six Rust crates.
The domain crate has no external dependencies. It defines the core types: positions, events, risk configuration, technical stop distances, and quantity. Every financial value uses rust_decimal::Decimal, never floating-point.
The engine crate contains the decision logic. It receives a DetectorSignal carrying an entry price and a chart-derived stop level. It computes position size using the formula:
position_size = max_risk / stop_distance
where max_risk is 1% of the configured capital and stop_distance is the absolute price distance from the proposed entry to the chart-derived stop. This produces a quantity in BTC. The engine then applies the 10x futures leverage to compute margin required.
The daemon crate exposes an HTTP API. Mutating routes (ARM, approve, panic, cancel) require a bearer token. Read-only routes (health, status, events, metrics) are unauthenticated. The Prometheus metrics endpoint allows external monitoring without credentials.
The exchange adapter validates futures settings before every entry: it calls the Binance API to confirm the account is in One-way position mode, then calls set_leverage for the symbol. Both operations are idempotent. If the account is in Hedge mode, the adapter returns a safety violation error and the trade does not proceed.
Event sourcing as the audit layer
Every decision Robson makes is recorded in an append-only event log.
When the ARM request arrives, a position_armed event is written. When the detector fires a signal, a detector_signal_emitted event is written with the proposed entry price and the chart-derived stop level. When the risk engine approves the trade, a risk_engine_approved event is written with the full risk evaluation context. When the entry order is placed, an entry_order_placed event is written with the exchange order ID and a cycle_id that links this order back to the risk approval that authorized it.
The cycle_id is not a convenience field. It is the audit mechanism. Every order on the exchange can be traced back to a specific risk approval in the event log. A position without a traceable entry is, by definition, untracked. Untracked positions are a P0 condition: the system closes them immediately and halts.
The position monitor appends a position_monitor_tick event on every cycle, regardless of whether the trailing stop moved. This makes it possible to verify that the monitor is running and processing ticks during short validation sessions, without waiting for price movement to trigger a stop adjustment.
The approval gate
Robson does not enter positions automatically after a signal. Every position that exceeds a configured notional threshold requires operator approval before the entry order is placed.
When the approval gate triggers, the system records the proposed trade in a pending approval queue. The operator reviews the entry price, stop level, computed quantity, and notional value, then explicitly approves or rejects. An unanswered approval expires after a configurable timeout. The daemon re-arms automatically if the position is still viable.
For the first real trade, the signal appeared twice. The first approval expired before the operator responded. The daemon re-armed, detected a new signal, and presented a second approval. The operator reviewed the parameters and approved. The entry order was placed immediately.
The validation sequence
Before enabling real capital, the team completed two formal validation phases.
VAL-001 ran on testnet with synthetic Binance credentials. It confirmed the full position lifecycle across five sequential checkpoints: ARM, detector signal, fill verification, trailing stop monitoring, and exit. Each checkpoint has a pass/fail criterion verified against the event log. The session ran on April 22 and confirmed a clean cycle with zero untracked positions.
VAL-002 activated production. It followed a defined sequence: store real Binance credentials, refresh the cluster secret, restart the daemon, verify the production endpoint in logs, run safety checks against the event log and exchange account, and enable the position monitor via GitOps. The monitor change was committed to the infrastructure repository and applied through the continuous delivery pipeline. The daemon restarted with the monitor enabled and confirmed connectivity to the real Binance endpoint.
Ten minutes of post-activation monitoring produced no untracked positions, no safety net exits, and no unexpected events. The safety checks confirmed zero open rows in the positions ledger and zero open exchange positions before the monitor was enabled.
What the first trade confirmed
The MA crossover detector fired a signal on a live BTCUSDT feed. The risk engine computed a position size of approximately 0.0022 BTC, a notional value of $178, and a margin requirement of roughly $18 against the 10x leverage. The approval gate presented these values to the operator. The operator approved.
The entry filled at $77,932.40. The chart-derived stop was set at $76,158.25, a distance of $1,774.15 from entry. The trailing stop is now active, adjusting upward as price moves in the position's favor.
The system ran exactly as designed: the exchange adapter set the leverage, confirmed One-way mode, placed a market order, received the fill, recorded the entry_order_placed event with the exchange order ID, and started the position monitor. The daemon is running with zero restarts.
The position is open. The trailing stop is tracking. The event log is recording every tick.
What comes next
The immediate priority is supervising this position through its full lifecycle: monitor ticks, trailing stop adjustments, and the eventual exit event. Once the position closes and the exit is confirmed in the event log, the team will review the full audit trail and assess whether the sizing and approval parameters need adjustment for subsequent trades.
The monthly state persistence layer (EP-006) is active in production. It tracks capital base and realized losses across the trading month, feeding the risk gate's daily loss limit and dynamic slot allocation. These mechanisms were built before the first trade. They are now running against real data.
