DEV Community

himma
himma

Posted on

Simulate the matching engine of a centralized exchange

【30 Seconds to Understand: High-performance OrderBook Implemented in Go Language 】*

Go # Cryptocurrency # Order Book # High-frequency Trading

Core function: ** Simulate the matching engine of a centralized exchange **

package main

import (
    "fmt"
    "sort"
    "time"
)

// Match represents a completed trade between a bid and an ask.
type Match struct {
    Ask        *Order  // The ask order that was matched
    Bid        *Order  // The bid order that was matched
    SizeFilled float64 // Amount of the order that was filled
    Price      float64 // Execution price of the match
}

// Order represents a single limit order in the book.
type Order struct {
    Size      float64 // Order quantity
    Bid       bool    // true = buy (bid), false = sell (ask)
    Limit     *Limit  // Pointer to the price level this order belongs to
    Timestamp int64   // Nanosecond timestamp for FIFO ordering
}

// Orders is a slice of Order pointers, used for sorting.
type Orders []*Order

// Len, Swap, Less implement sort.Interface for FIFO ordering (oldest first).
func (o Orders) Len() int           { return len(o) }
func (o Orders) Swap(i, j int)      { o[i], o[j] = o[j], o[i] }
func (o Orders) Less(i, j int) bool { return o[i].Timestamp < o[j].Timestamp }

// NewOrder creates a new order with the current timestamp.
func NewOrder(bid bool, size float64) *Order {
    return &Order{
        Size:      size,
        Bid:       bid,
        Timestamp: time.Now().UnixNano(),
    }
}

// String returns a human-readable representation of the order.
func (o *Order) String() string {
    return fmt.Sprintf("[size: %0.2f]", o.Size)
}

// Limit represents a price level in the order book.
type Limit struct {
    Price       float64 // Price of this level
    Orders      Orders  // FIFO queue of orders at this price
    TotalVolume float64 // Sum of all order sizes at this price
}

// Limits is a slice of Limit pointers, used for sorting.
type Limits []*Limit

// ByBestAsk sorts asks from lowest price to highest (best ask first).
type ByBestAsk struct{ Limits }

func (a ByBestAsk) Len() int           { return len(a.Limits) }
func (a ByBestAsk) Swap(i, j int)      { a.Limits[i], a.Limits[j] = a.Limits[j], a.Limits[i] }
func (a ByBestAsk) Less(i, j int) bool { return a.Limits[i].Price < a.Limits[j].Price }

// ByBestBid sorts bids from highest price to lowest (best bid first).
type ByBestBid struct{ Limits }

func (b ByBestBid) Len() int           { return len(b.Limits) }
func (b ByBestBid) Swap(i, j int)      { b.Limits[i], b.Limits[j] = b.Limits[j], b.Limits[i] }
func (b ByBestBid) Less(i, j int) bool { return b.Limits[i].Price > b.Limits[j].Price }

// NewLimit creates a new empty price level.
func NewLimit(price float64) *Limit {
    return &Limit{
        Price:  price,
        Orders: []*Order{},
    }
}

// String returns a human-readable representation of the limit level.
func (l *Limit) String() string {
    return fmt.Sprintf("[price: %0.2f | volume: %.2f]", l.Price, l.TotalVolume)
}

// AddOrder appends an order to this price level and updates total volume.
func (l *Limit) AddOrder(o *Order) {
    o.Limit = l
    l.Orders = append(l.Orders, o)
    l.TotalVolume += o.Size
}

// DeleteOrder removes an order from this price level and updates volume.
// The order queue is kept sorted by timestamp (FIFO).
func (l *Limit) DeleteOrder(o *Order) {
    for i := 0; i < len(l.Orders); i++ {
        if l.Orders[i] == o {
            // Swap with last element and truncate slice
            l.Orders[i] = l.Orders[len(l.Orders)-1]
            l.Orders = l.Orders[:len(l.Orders)-1]
            break
        }
    }
    o.Limit = nil
    l.TotalVolume -= o.Size
    // Re-sort to maintain FIFO order after deletion
    sort.Sort(l.Orders)
}

// OrderBook holds the full limit-order book with fast price-level lookup.
type OrderBook struct {
    Asks []*Limit // All ask (sell) price levels
    Bids []*Limit // All bid (buy) price levels

    AskLimits map[float64]*Limit // price → ask limit (O(1) lookup)
    BidLimits map[float64]*Limit // price → bid limit (O(1) lookup)
}

// NewOrderBook constructs an empty order book.
func NewOrderBook() *OrderBook {
    return &OrderBook{
        Asks:      []*Limit{},
        Bids:      []*Limit{},
        AskLimits: map[float64]*Limit{},
        BidLimits: map[float64]*Limit{},
    }
}

// PlaceOrder attempts to match the incoming order with the opposite side.
// Any remaining size is added to the book as a resting limit order.
// Returns a slice of executed matches.
func (ob *OrderBook) PlaceOrder(price float64, o *Order) []Match {
    // 1. Try to match against the opposite side
    //    (matching logic to be implemented)

    // 2. If any size remains, add it to the book
    if o.Size > 0.0 {
        ob.add(price, o)
    }
    return []Match{} // placeholder – real implementation returns matches
}

// add places a non-matched (or partially matched) order into the book.
// It either extends an existing price level or creates a new one.
func (ob *OrderBook) add(price float64, order *Order) {
    var limit *Limit
    if order.Bid {
        limit = ob.BidLimits[price]
    } else {
        limit = ob.AskLimits[price]
    }

    if limit == nil {
        limit = NewLimit(price)
        limit.AddOrder(order)

        if order.Bid {
            ob.Bids = append(ob.Bids, limit)
            ob.BidLimits[price] = limit
        } else {
            ob.Asks = append(ob.Asks, limit)
            ob.AskLimits[price] = limit
        }
    } else {
        // Existing price level – just add the order
        limit.AddOrder(order)
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)