Actor Model¶
Arbiter-Bot uses an actor-based architecture with message passing for concurrent execution.
Overview¶
The system consists of three main actors communicating via tokio MPSC channels:
graph LR
PM[PolymarketMonitor] -->|MarketUpdate| A[ArbiterActor]
KM[KalshiMonitor] -->|MarketUpdate| A
A -->|StartArbitrage| E[ExecutionActor]
E -->|Leg1Result| E
E -->|Leg2Result| E
E -->|HedgeResult| E
Actors¶
ExecutionActor¶
Location: arbiter-engine/src/actors/executor.rs
Manages saga state machines for arbitrage executions:
- Receives opportunities from ArbiterActor
- Initiates Leg 1 orders (buy on first exchange)
- Initiates Leg 2 orders (sell on second exchange)
- Handles failures with hedge logic
- Maintains concurrent saga state for multiple opportunities
Messages:
pub enum ExecutionMsg {
StartArbitrage(Opportunity),
Leg1Result(Uuid, Result<FillDetails, ExecutionError>),
Leg2Result(Uuid, Result<FillDetails, ExecutionError>),
HedgeResult(Uuid, Result<FillDetails, ExecutionError>),
}
ArbiterActor¶
Location: arbiter-engine/src/actors/arbiter.rs
Monitors markets and detects arbitrage opportunities:
- Receives orderbook updates from monitors
- Maintains current market state with staleness tracking
- Runs arbitrage detection algorithm
- Only uses verified market mappings (safety gate)
- Sends detected opportunities to ExecutionActor
Messages:
Market Monitors¶
Locations:
arbiter-engine/src/market/polymarket.rs- PolymarketMonitorarbiter-engine/src/market/kalshi.rs- KalshiMonitor
Connect to exchange WebSockets and publish orderbook updates:
- Maintain persistent WebSocket connections
- Parse market data messages
- Convert to normalized
OrderBookstructs - Send updates to ArbiterActor
Actor Trait¶
All actors implement the base Actor trait:
#[async_trait]
pub trait Actor: Send + 'static {
type Message: Send + 'static;
async fn handle(&mut self, message: Self::Message) -> Result<(), ActorError>;
async fn run(mut self, mut rx: mpsc::Receiver<Self::Message>) -> Result<(), ActorError>;
}
Benefits¶
- Isolation - Component failures don't cascade
- Testability - Easy to inject test messages
- Scalability - Add more actors for parallelism
- Clarity - Clear boundaries and interfaces