Skip to content

Saga Pattern

The execution module implements a saga pattern for handling multi-leg arbitrage transactions with compensation logic.

Why Saga?

True atomicity is impossible between:

  • Polymarket - Blockchain-based (Polygon)
  • Kalshi - Web2 exchange (REST API)

The saga pattern provides:

  • Explicit state management for distributed transactions
  • Compensation logic for partial failures
  • Audit trail of execution steps

State Machine

Location: arbiter-engine/src/execution/state_machine.rs

stateDiagram-v2
    [*] --> Pending
    Pending --> Leg1Initiated: Leg1Submitted
    Leg1Initiated --> Leg1Filled: Leg1Confirmed
    Leg1Initiated --> Failed: Leg1Failed
    Leg1Filled --> Leg2Initiated: Leg2Submitted
    Leg2Initiated --> Completed: Leg2Confirmed
    Leg2Initiated --> Failed: Leg2Failed
    Failed --> Compensating: CompensationStarted
    Compensating --> Compensated: CompensationFinished

States

State Description
Pending Initial state, no orders placed
Leg1Initiated(Uuid) First leg order submitted
Leg1Filled(FillDetails) First leg order filled
Leg2Initiated(Uuid, FillDetails) Second leg order submitted, preserves Leg1 fill
Completed(FillDetails, FillDetails) Both legs filled successfully
Failed(FailureReason) Execution failed
Compensating(HedgeStrategy, FillDetails) Hedge in progress
Compensated(FillDetails, FillDetails) Hedge completed

Events

pub enum ArbEvent {
    Leg1Submitted(Uuid),
    Leg1Confirmed(FillDetails),
    Leg1Failed(String),
    Leg2Submitted(Uuid),
    Leg2Confirmed(FillDetails),
    Leg2Failed(String),
    CompensationStarted(HedgeStrategy),
    CompensationFinished(FillDetails),
}

Failure Handling

Leg 1 Failure

If the first leg fails before filling:

  • No capital is at risk
  • Transition directly to Failed state
  • No compensation needed

Leg 2 Failure

If the second leg fails after Leg 1 fills:

  1. Position exists on first exchange
  2. FillDetails preserved in failure state
  3. Hedge strategy triggered
  4. Compensation order placed to exit position

Hedge Strategies

Location: arbiter-engine/src/execution/hedge.rs

Strategy Description
DumpLeg1 Market order to exit Leg 1 position immediately
RetryLeg2 Retry the failed leg (planned)
LimitChaseLeg2 Aggressive limit order chase (planned)
Hold Hold position for manual intervention (planned)

Key Safety Feature

The Leg2RejectedWithPosition failure reason preserves the Leg 1 FillDetails:

pub enum FailureReason {
    Leg1Rejected(String),
    Leg2RejectedWithPosition(String, FillDetails), // Preserves fill!
}

This ensures the hedge logic has all information needed for compensation.