Last active
April 9, 2025 08:37
-
-
Save thisiscetin/f246f8911aa1a34eebcca2885a5f3f8e to your computer and use it in GitHub Desktop.
Ruby Match Engine- Book Level Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# Struct representing an order (limit or market) | |
Order = Struct.new(:uid, :user_id, :price, :quantity, :type, :side) | |
# Struct representing a successful match between a market and a limit order | |
OrderMatch = Struct.new(:uid, :limit_order_id, :market_order_id, :amount) | |
class Level | |
EPSILON = 1e-8 # Used to compare floating point numbers safely | |
def initialize(base_price, side = :buy, step_size = 0.01) | |
@base_price = base_price | |
@side = side | |
@step_size = step_size | |
@steps = (1.0 / @step_size).to_i | |
@data = Array.new(@steps) { [] } | |
@match_id = 0 | |
end | |
# Places a limit order into its correct price bucket | |
def place_limit(order) | |
validate_order(order, expected_side: @side, expected_type: :limit) | |
raise ArgumentError, 'Invalid price step' unless valid_price?(order.price) | |
@data[price_index(order.price)] << order | |
end | |
# Matches a market order against the level's limit orders | |
# Depending on side, iterates from best to worst price | |
def match_market(order) | |
validate_order(order, expected_side: opposite_side, expected_type: :market) | |
# If this is a buy level, user is selling → start from highest bid | |
# If this is a sell level, user is buying → start from lowest ask | |
iterator = @side == :buy ? (@steps - 1).downto(0) : 0.upto(@steps - 1) | |
match_order(order, iterator) | |
end | |
# Returns a flat snapshot of all active limit orders in this level | |
# in its natural order which removes the need for sorting | |
def snapshot | |
@data.lazy.flat_map(&:itself) | |
end | |
private | |
# Validates order side and type before matching/placing order | |
def validate_order(order, expected_side:, expected_type:) | |
raise ArgumentError, "Invalid order side (#{order.side})" unless order.side == expected_side | |
raise ArgumentError, "Invalid order type (#{order.type})" unless order.type == expected_type | |
end | |
# Checks if a price is aligned with the configured step size | |
def valid_price?(price) | |
steps = (price - @base_price) / @step_size | |
(steps - steps.round).abs < EPSILON | |
end | |
# Calculates the array index for a given price | |
def price_index(price) | |
raise ArgumentError, "Price must be aligned with step size #{@step_size}" unless valid_price?(price) | |
((price - @base_price) / @step_size).round | |
end | |
# Returns the opposite order side (:buy → :sell, or vice versa) | |
def opposite_side | |
@side == :buy ? :sell : :buy | |
end | |
# Core matching logic — iterates over limit orders and creates matches | |
def match_order(order, iterator) | |
matches = [] | |
remaining = order.quantity | |
iterator.each do |i| | |
next if @data[i].empty? | |
new_level_orders = [] | |
@data[i].each do |limit_order| | |
break if remaining <= 0 | |
trade_quantity = [limit_order.quantity, remaining].min | |
matches << OrderMatch.new( | |
@match_id += 1, | |
limit_order.uid, | |
order.uid, | |
trade_quantity | |
) | |
remaining -= trade_quantity | |
limit_order.quantity -= trade_quantity | |
new_level_orders << limit_order if limit_order.quantity > EPSILON | |
end | |
# Replace level with remaining orders (or clear it) | |
@data[i] = new_level_orders if new_level_orders | |
break if remaining <= 0 | |
end | |
matches | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment