Files
Dena/app/trading/matching/order_book.rb
2026-08-13 19:50:53 +03:30

138 lines
3.3 KiB
Ruby

# encoding: UTF-8
# frozen_string_literal: true
require_relative 'constants'
module Matching
class OrderBook
attr :side
def initialize(market, side, options={})
@market = market
@side = side.to_sym
@limit_orders = RBTree.new
@market_orders = RBTree.new
@on_change = options[:on_change]
singleton = class<<self;self;end
singleton.send :define_method, :limit_top, self.class.instance_method("#{@side}_limit_top")
end
def notify_change(market, side, price, amount=nil)
return unless @on_change
amount ||= yield
@on_change.call(market, side, price, amount)
rescue StandardError => e
report_exception(e)
end
def best_limit_price
limit_top.try(:price)
end
def top
unless @market_orders.empty?
order = @market_orders.first[1]
raise MarketOrderbookError.new(order, 'market order in orderbook detected')
end
limit_top
end
def fill_top(trade_price, trade_volume, trade_funds)
order = top
raise "No top order in empty book." unless order
order.fill trade_price, trade_volume, trade_funds
if order.filled?
remove order
else
notify_change(@market, @side, trade_price) { @limit_orders[trade_price]&.total }
end
end
def find(order)
case order
when LimitOrder
@limit_orders[order.price].find(order.id)
when MarketOrder
@market_orders[order.id]
end
end
def add(order)
raise OrderError.new(order, 'volume is less than or equal to zero') if order.volume <= ZERO
case order
when LimitOrder
@limit_orders[order.price] ||= PriceLevel.new(order.price)
@limit_orders[order.price].add order
notify_change(@market, @side, order.price) { @limit_orders[order.price]&.total }
when MarketOrder
raise MarketOrderbookError.new(order, 'market order adding to orderbook detected')
else
raise ArgumentError, "Unknown order type"
end
end
def remove(order)
case order
when LimitOrder
updated_order = remove_limit_order(order)
notify_change(@market, @side, order.price) { @limit_orders[order.price]&.total }
return updated_order
when MarketOrder
remove_market_order(order)
else
raise ArgumentError, "Unknown order type"
end
end
def limit_orders
orders = {}
@limit_orders.keys.each {|k| orders[k] = @limit_orders[k].orders }
orders
end
def market_orders
@market_orders.values
end
private
def remove_limit_order(order)
price_level = @limit_orders[order.price]
return unless price_level
order = price_level.find order.id # so we can return fresh order
return unless order
price_level.remove order
@limit_orders.delete(order.price) if price_level.empty?
order
end
def remove_market_order(order)
if order = @market_orders[order.id]
@market_orders.delete order.id
order
end
end
def ask_limit_top # lowest price wins
return if @limit_orders.empty?
price, level = @limit_orders.first
level.top
end
def bid_limit_top # highest price wins
return if @limit_orders.empty?
price, level = @limit_orders.last
level.top
end
end
end