Build a Smaller Aikido Runtime
The reconstruction preserves the control loop, not trading sophistication.
Rebuild one proposal source, one router, one single-writer account executor, one broker simulator, desired-versus-observed reconciliation, and safe shutdown.
1. Start with concrete synchronous state
Represent one instrument and one account. Feed predetermined bars into one
strategy function and produce Action::Enter, Action::Exit, or Action::Hold.
Apply actions to an in-memory account with no traits or tasks.
2. Separate proposal from authority
The strategy returns proposals. A router decides whether a proposal is allowed and which account receives it. The account is the only component allowed to change observed exposure.
3. Introduce a command protocol
enum AccountCommand {
ApplyProposal(Proposal),
Converge { target: i32 },
Reconcile,
Shutdown,
}
Run one account loop that owns the account and receives commands synchronously. Test ordering and idempotency before adding Tokio.
4. Add bounded Tokio channels
Create one producer task, one router task, and one account task. Give each edge a small bounded channel so overload appears in tests quickly. Close senders and verify receivers finish rather than waiting forever.
5. Add a generic broker
Extract only the capabilities the account loop already uses:
trait Broker: Send {
async fn adjust(&mut self, delta: i32) -> Result<()>;
async fn observed_position(&mut self) -> Result<i32>;
}
Implement SimBroker first. Then add FlakyBroker that can accept an order but
lose the response, delay observation, and report external position changes.
6. Make target state idempotent
Store desired and observed signed quantities. Submit only their difference. Never assume a successful return changed observed state; poll the broker and recompute.
7. Add stale and duplicate defenses
Give proposals timestamps and intent IDs. Reject stale exposure increases while still allowing exits. Remember recently applied intent IDs in a bounded window.
8. Add lifecycle and supervision
Use a shutdown signal, explicit Shutdown commands, sender-drop behavior, and
a JoinSet. Persist a tiny journal before acknowledging mutations. Restart the
account task from the journal, then reconcile against the broker.
9. Compare with production
Map the reconstruction back to:
SignalEngineas proposal generation;SignalRouteras centralized policy and routing;ExecutorCommandas the ownership-transfer protocol;AccountExecutoras the single writer;BrokerClientas the generic external boundary;ConvergeToTargetas idempotent desired state; and- reconciliation as recovery of external truth.
The smaller runtime intentionally omits real market data, multiple instruments, bracket orders, account lifecycle rules, policy packs, persistence databases, copy routing, operational UIs, and broker authentication. Its architectural center should still survive process restart, duplicate intent, delayed broker observation, channel pressure, and orderly shutdown.