← back to blog

Adapter Pattern in Multi-Exchange Trading Systems

2026-03-01·8 min read·
designarchitecture

The Problem

When you connect to one exchange, you write an API client. When you connect to ten, you need an architecture.

Every venue speaks a different dialect. REST vs WebSocket vs FIX protocol. Different order types, different rate limits, different error codes. Some return fills synchronously, others stream them. Some have sub-accounts, others don't.

The naive approach — writing custom code for each venue — creates a maintenance nightmare. Every new feature requires changes across all connectors. Every bug fix is multiplied by the number of venues.

The Adapter Pattern

The solution is straightforward: define a common interface and make each venue conform to it.

trait VenueAdapter {
    async fn get_quote(&self, request: QuoteRequest) -> Result<Quote>;
    async fn execute(&self, order: Order) -> Result<Execution>;
    async fn cancel(&self, order_id: OrderId) -> Result<()>;
    fn stream_prices(&self) -> impl Stream<Item = PriceUpdate>;
}

Each venue implements this trait. The core system only knows about the trait, never about specific venues. Adding a new venue means writing one adapter — the rest of the system is unchanged.

Where It Gets Interesting

The happy path is easy. The real engineering is in the unhappy path:

  • Timeout handling: What happens when a venue doesn't respond? You can't just retry — you might create a duplicate order.
  • Partial fills: Some venues split large orders. Your adapter needs to track fill state.
  • Rate limiting: Each venue has different limits. The adapter must queue and throttle transparently.
  • Reconnection: WebSocket connections drop. The adapter must reconnect, resubscribe, and reconcile state.

Lessons Learned

Building these adapters taught me that interfaces are contracts, not just type signatures. The behavioral guarantees matter more than the method signatures. An adapter that implements the right methods but doesn't handle reconnection correctly is worse than useless — it's dangerous.

The adapter pattern isn't just about code organization. In a system that handles real money, it's about creating a boundary where you can reason about correctness.