Software systems that touch money cannot be shipped on trust alone. Before the RBX engineering team enables real capital on Robson, the daemon runs through a formal end-to-end validation session on testnet. This post describes how that process works, what we found during the most recent session, and why several architectural decisions made the validation meaningful rather than ceremonial.
The 5-phase E2E runbook
Testnet validation is not an ad-hoc smoke test. It follows a structured runbook with five sequential phases:
- ARM โ the daemon starts, connects to market data, and confirms all subsystems are operational
- Detector signal โ the detector fires a real signal based on live market data, with a computed stop level
- Fill verification โ the system attempts order placement and verifies fill handling through the audit trail
- Trailing stop monitoring โ the position monitor starts processing ticks and the EventLog captures each cycle
- Exit โ the position closes and all lifecycle events are correctly recorded
Each phase has a pass/fail criterion defined before the session starts. The team does not advance to the next phase until the current one is confirmed in the EventLog. This is deliberate: it makes partial validation visible rather than hiding it behind a green CI badge.
Chart-derived stops, not percentages
One decision made during earlier architecture work (formalized in ADR-0021) directly affected how the validation signal was interpreted.
Robson does not compute stop-loss levels as a percentage of entry price. Instead, stop levels come from the 15-minute chart: the system identifies swing points and uses ATR as a fallback when recent structure is ambiguous. This places the stop at a technically meaningful level rather than an arbitrary distance from entry.
The separation between Opportunity Detection and Technical Stop Analysis is an explicit architectural boundary. The detector identifies that a trade setup exists. A distinct component computes where the stop should be, based on chart geometry. The two concerns are kept separate by design.
This matters for validation because it means the stop level attached to a signal is not a parameter the team chose. It is a result computed from market data. Validating it means verifying that the computation ran correctly, not that someone picked a reasonable percentage.
During this session, the detector fired a real MA crossover signal on a live testnet feed. The signal carried a chart-derived stop computed from recent swing structure. The risk evaluation that followed was operating on real data, not on synthetic inputs crafted for testing.
WebSocket reliability: two bugs found and fixed
Before reaching the signal phase, the testnet session revealed two WebSocket bugs that would have caused periodic, silent degradation in production.
The first was a Pong frame implementation error. Robson's daemon was sending empty Pong frames in response to Binance's Ping messages. The WebSocket protocol requires that a Pong frame echo the payload of the Ping that triggered it. Binance sends a non-empty payload. Sending an empty Pong is technically non-compliant and caused periodic disconnects on the testnet connection.
The second was a URL mismatch. The testnet WebSocket endpoint differs from the production endpoint, and the daemon was using the wrong one for testnet sessions. Connections would appear to establish and then fail silently.
Both bugs were fixed before the signal phase ran. Neither would have been obvious from unit tests alone, because they only manifest against a live WebSocket server with real behavior. This is the argument for running testnet sessions against real infrastructure rather than mocking everything.
Event sourcing as the primary audit trail
Every order that Robson places carries a cycle_id that links it back to the Risk Engine approval that authorized it. This is not a convenience field. It is the audit mechanism.
When validating the fill phase, the team does not inspect logs looking for order IDs. The EventLog is queried directly. An order without a traceable cycle_id would be an anomaly, not just a missing log line.
This design reflects a principle the RBX team has held consistently: logs are operational output, but the EventLog is the record of what the system decided and why. For a financial daemon, that distinction matters because logs can be noisy, filtered, or lost. The EventLog is append-only and is the primary evidence for any post-hoc review.
The risk gate worked as designed
During the validation session, the MA crossover signal fired with a chart-derived stop attached. The Risk Engine evaluated the proposed position size against the configured exposure limit and blocked the trade.
This is the system working correctly.
The signal was real. The stop was real. The Risk Engine did its job: it computed that the proposed size would exceed the configured capital exposure limit and returned a denial. The denial was recorded in the EventLog with the full context of the evaluation.
For teams that have not run this kind of formal validation before, it is worth saying plainly: the risk gate blocking a trade during validation is a passing result, not a failure. The goal is not to confirm that trades go through. The goal is to confirm that the governance layer behaves correctly given the configured constraints.
position_monitor_tick: making short-run validation possible
One gap that became apparent during earlier sessions was that the EventLog only recorded trailing stop updates when the trailing stop actually moved. For a position monitor processing ticks, this meant that a short validation session with limited price movement would produce almost no audit evidence for the monitoring phase.
To fix this, the team added a position_monitor_tick event. This event is appended to the EventLog on every tick processed by the position monitor, regardless of whether the trailing stop level changed. It records the current price, the current stop level, and the evaluated outcome.
The change makes short-run validation possible. The team can verify that the position monitor is running, that ticks are being processed, and that stop evaluation is happening, without waiting for price to move favorably enough to trigger a stop adjustment.
What this session confirmed
The testnet E2E session confirmed several things the architecture assumes but cannot prove through unit tests alone:
- WebSocket connectivity against a live exchange behaves differently from mocked transports
- Chart-derived stop levels are computed and attached to signals correctly
- The risk gate evaluates exposure limits accurately
- The EventLog captures the full lifecycle of a signal, from detection through risk evaluation and denial
- The position monitor processes ticks and records evidence at every cycle
Before real capital is enabled, the team will run the same five phases again, this time with the trailing stop phase allowed to run long enough to confirm a position close through the exit handler. That session will be the final gate before switching from testnet to production keys.
The runbook exists because discipline at this stage is cheaper than a production incident.
