class Ubea::OrderBook

Attributes

asks[R]
bids[R]
market[RW]
updated_at[RW]

Public Class Methods

new(hash) click to toggle source
# File lib/ubea/order_book.rb, line 6
def initialize(hash)
  @asks = hash.delete(:asks).sort_by(&:price).freeze # buy
  @bids = hash.delete(:bids).sort_by(&:price).freeze # sell

  raise ArgumentError unless hash.empty?
end

Public Instance Methods

best_offer_price_for_fiat_amount(type, fiat_amount, fiat_currency) click to toggle source
# File lib/ubea/order_book.rb, line 55
def best_offer_price_for_fiat_amount(type, fiat_amount, fiat_currency)
  total_fiat_amount = 0
  good_offers = []

  offers = public_send(type).dup # asks or bids
  offers.reverse! if type == :bids

  offers.each do |offer|
    break if total_fiat_amount >= fiat_amount

    offer = offer.dup # NOTE: dup because we're gonna mess with its volume below
    offer.price = offer.price.exchange_to(fiat_currency)
    good_offers << offer
    total_fiat_amount += offer.price * offer.volume
  end

  if total_fiat_amount > fiat_amount
    substract_fiat = total_fiat_amount - fiat_amount
    substract_volume = substract_fiat / good_offers.last.price
    good_offers.last.volume -= substract_volume
  end

  total_weight_price = good_offers.map { |offer| offer.price * offer.volume }.inject(:+) || 0
  total_volume = good_offers.map(&:volume).inject(:+)
  weighted_price = total_weight_price / total_volume

  Offer.new(
    price: good_offers.last.price,
    volume: total_volume,
    weighted_price: weighted_price,
  )
end
highest_bid() click to toggle source
# File lib/ubea/order_book.rb, line 17
def highest_bid
  bids.last
end
lowest_ask() click to toggle source
# File lib/ubea/order_book.rb, line 21
def lowest_ask
  asks.first
end
max_bid_price_for_volume(min_price, max_volume) click to toggle source
# File lib/ubea/order_book.rb, line 44
def max_bid_price_for_volume(min_price, max_volume)
  filter = if min_price
    ->(price) { price >= min_price }
  else
    ->(_) { true }
  end

  interesting_bids = interesting_offers(bids, filter).reverse # reverse to start from most expensive
  best_offer_price_for_volume(interesting_bids, max_volume)
end
min_ask_price_for_volume(max_price, max_volume) click to toggle source
# File lib/ubea/order_book.rb, line 33
def min_ask_price_for_volume(max_price, max_volume)
  filter = if max_price
    ->(price) { price <= max_price }
  else
    ->(_) { true }
  end

  interesting_asks = interesting_offers(asks, filter)
  best_offer_price_for_volume(interesting_asks, max_volume)
end
to_param() click to toggle source
# File lib/ubea/order_book.rb, line 13
def to_param
  market.to_param
end
weighted_asks_up_to(max_price) click to toggle source
# File lib/ubea/order_book.rb, line 25
def weighted_asks_up_to(max_price)
  weighted_offers(asks, ->(price) { price <= max_price })
end
weighted_bids_down_to(min_price) click to toggle source
# File lib/ubea/order_book.rb, line 29
def weighted_bids_down_to(min_price)
  weighted_offers(bids, ->(price) { price >= min_price })
end

Private Instance Methods

best_offer_price_for_volume(offers, max_volume) click to toggle source
# File lib/ubea/order_book.rb, line 107
def best_offer_price_for_volume(offers, max_volume)
  total_volume = 0
  good_offers = []

  offers.each do |offer|
    if total_volume <= max_volume
      good_offers << offer.dup # NOTE: dup because we're gonna mess with its volume below
      total_volume += offer.volume
    end
  end

  if total_volume > max_volume
    substract_volume = total_volume - max_volume
    good_offers.last.volume -= substract_volume
    total_volume -= substract_volume
  end

  total_weight_price = good_offers.map { |offer| offer.price * offer.volume }.inject(:+) || 0
  weighted_price = total_weight_price / total_volume

  Offer.new(
    price: good_offers.last.price,
    volume: total_volume,
    weighted_price: weighted_price,
  )
end
currency() click to toggle source
# File lib/ubea/order_book.rb, line 134
def currency
  lowest_ask.price.currency
end
interesting_offers(offers, condition) click to toggle source
# File lib/ubea/order_book.rb, line 90
def interesting_offers(offers, condition)
  offers.select { |offer| condition.call(offer.price) }
end
weighted_offers(offers, condition) click to toggle source
# File lib/ubea/order_book.rb, line 94
def weighted_offers(offers, condition)
  interesting_offers = interesting_offers(offers, condition)

  total_volume = interesting_offers.map(&:volume).inject(:+) || 0
  total_weight_price = interesting_offers.map { |offer| offer.price * offer.volume }.inject(:+) || 0
  weighted_price = total_weight_price / total_volume

  Offer.new(
    price: Money.new(weighted_price, currency),
    volume: total_volume
  )
end