Why Is It Designed This Way?
This chapter is the center of the case study. Each production mechanism exists because a simpler-looking alternative breaks a specific invariant.
Why one account executor instead of a task per order?
A task per order maximizes apparent concurrency but creates races between entries, exits, protection updates, manual commands, and reconciliation. One executor serializes mutation of the broker client and account state while the rest of the system remains concurrent.
The preserved invariant is: one authority decides the next account mutation from one ordered view of account state.
Why commands instead of Arc<Mutex<BrokerClient>>?
A shared mutex would prevent simultaneous method calls, but it would not define operation ordering, priority, deduplication, shutdown, or which state must be updated with each call. A command enum makes the ownership transfer and protocol explicit. The receiver can prioritize runtime-control commands ahead of signal traffic.
Why bounded channels instead of unbounded queues?
An unbounded queue converts a slow consumer into memory growth and increasing decision age. A bounded queue suspends producers, propagating overload.
The preserved invariant is not merely bounded memory: the runtime must not act on arbitrarily stale exposure-increasing intent. That is why bounded queues are paired with a stale-bar gate at the executor.
Why a singleton router instead of engines sending directly?
Direct engine-to-executor sends look simpler until routing depends on subscriptions, account availability, duplicate ownership, copy topology, guardrails, and policy allocation. A singleton router gives those decisions one ordered state cache and one place to enforce cross-deployment rules.
Why feed executor state back to signal engines?
Strategies need position context for exits and adjustments, but engines do not own real accounts. Feedback gives them synthetic views without granting broker authority. The executor remains the source of account mutation; engines remain proposal generators.
Why reconcile if local state was just updated?
Networks time out, brokers reject or partially fill, processes restart, and operators act outside the process. Local state records intent and recent knowledge. Broker state records the external fact the runtime must eventually match.
The preserved invariant is: observed account exposure outranks assumptions derived from submitted commands.
Why target exposure instead of imperative “buy one” policy output?
Imperative commands are difficult to retry: repeating “buy one” may double the position. A desired signed quantity is idempotent with respect to observation. After reconciliation, the runtime recomputes the remaining delta.
This turns recovery into repeated comparison:
desired − observed = next safe adjustment
Why generic broker functions instead of trait objects everywhere?
The executor owns one concrete adapter for a long time. Static dispatch retains exclusive mutable ownership, avoids a shared boxed client, and makes test brokers cheap to substitute. Trait objects would be justified where broker types must be selected heterogeneously at runtime inside one collection; they are not required at every helper boundary.
Why standard mutexes inside an async application?
Some critical sections are tiny, non-async memory operations. A standard mutex
is appropriate when the guard is never held across .await; an async mutex
would add scheduling overhead without benefit. Broker operations are different:
they are not placed behind a mutex at all, but owned by one async task.
Why frozen policy packs instead of live warehouse reads?
A live query is mutable input: data corrections, publication changes, and query edits can change decisions without a deployment event. A frozen pack gives simulation, approval, staging, and runtime a versioned object to discuss and replay.
The preserved invariant is: deployment behavior is attributable to an exact reviewed input, not whatever the research warehouse returns now.
Why not fail the whole process when one task fails?
Some surrounding tasks are diagnostic or recoverable, while active account tasks may still need to protect or flatten positions. The supervisor records failure without immediately aborting every peer. This increases availability, but it also demands health reporting and explicit rules for which failures should block new exposure.