rex/spec/book/matcher_spec.rb

101 lines
3.7 KiB
Ruby
Raw Normal View History

2023-10-28 22:42:19 +02:00
# frozen_string_literal: true
2023-11-08 20:33:49 +01:00
RSpec.describe Rex::Book::Matcher do
2023-10-28 22:42:19 +02:00
let(:instance) { described_class.new }
describe "#match" do
2023-11-08 20:35:14 +01:00
let(:order_book) { Rex::Book::LimitOrderBook.new }
let(:buy_order) { build(:order, price: 100, is_buy: true, quantity: 100, remaining_quantity: 100) }
let(:cheaper_sell_order) { build(:order, price: 99, is_buy: false, quantity: 50, remaining_quantity: 50) }
let(:pricier_sell_order) { build(:order, price: 100, is_buy: false, quantity: 70, remaining_quantity: 70) }
2023-10-28 22:42:19 +02:00
context "when order book has unmatched orders" do
before do
order_book.add_order(buy_order)
order_book.add_order(cheaper_sell_order)
order_book.add_order(pricier_sell_order)
end
it "returns proper trades" do
trades = instance.match(order_book)
expect(trades.length).to eq(2)
expect(trades[0].id).to eq(1)
expect(trades[0].buy_order).to eq(buy_order)
expect(trades[0].sell_order).to eq(cheaper_sell_order)
expect(trades[0].price).to eq(99)
expect(trades[0].quantity).to eq(50)
2023-10-28 22:42:19 +02:00
expect(trades[1].id).to eq(2)
expect(trades[1].buy_order).to eq(buy_order)
expect(trades[1].sell_order).to eq(pricier_sell_order)
expect(trades[1].price).to eq(100)
expect(trades[1].quantity).to eq(50)
2023-10-28 22:42:19 +02:00
end
it "removes filled orders from the order book" do
instance.match(order_book)
expect(order_book.highest_buy_order).to eq(nil)
expect(order_book.lowest_sell_order).to eq(pricier_sell_order)
expect(order_book.lowest_sell_order.remaining_quantity).to eq(20)
2023-10-28 22:42:19 +02:00
end
end
context "when order book is empty" do
it "returns an empty list" do
2023-11-08 20:35:14 +01:00
expect(instance.match(Rex::Book::LimitOrderBook.new)).to eq([])
2023-10-28 22:42:19 +02:00
end
end
# https://stackoverflow.com/a/18524231/3200224
# Testing a common example with a proven to be correct solution
context "stack overflow scenario" do
let(:order_1) { build(:order, price: 2030, is_buy: false, quantity: 100) }
let(:order_2) { build(:order, price: 2025, is_buy: false, quantity: 100) }
let(:order_3) { build(:order, price: 2030, is_buy: false, quantity: 200) }
let(:order_4) { build(:order, price: 2015, is_buy: true, quantity: 100) }
let(:order_5) { build(:order, price: 2020, is_buy: true, quantity: 200) }
let(:order_6) { build(:order, price: 2015, is_buy: true, quantity: 200) }
let(:crossing_order) { build(:order, is_buy: true, quantity: 250, price: 2035) }
before do
order_book.add_order(order_1)
order_book.add_order(order_2)
order_book.add_order(order_3)
order_book.add_order(order_4)
order_book.add_order(order_5)
order_book.add_order(order_6)
order_book.add_order(crossing_order)
end
it "matches the orders accordingly" do
trades = instance.match(order_book)
expect(trades.length).to eq(3)
expect(trades[0].id).to eq(1)
expect(trades[0].price).to eq(2025)
expect(trades[0].quantity).to eq(100)
expect(trades[0].buy_order).to eq(crossing_order)
expect(trades[0].sell_order).to eq(order_2)
expect(trades[1].id).to eq(2)
expect(trades[1].price).to eq(2030)
expect(trades[1].quantity).to eq(100)
expect(trades[1].buy_order).to eq(crossing_order)
expect(trades[1].sell_order).to eq(order_1)
expect(trades[2].id).to eq(3)
expect(trades[2].price).to eq(2030)
expect(trades[2].quantity).to eq(50)
expect(trades[2].buy_order).to eq(crossing_order)
expect(trades[2].sell_order).to eq(order_3)
end
end
2023-10-28 22:42:19 +02:00
end
end