Tim Kächele
0efb6b0e88
It is not super relevant for the current matching engine, we are going to solve this in the server implementation
101 lines
3.6 KiB
Ruby
101 lines
3.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe Rex::Matcher do
|
|
let(:instance) { described_class.new }
|
|
|
|
describe "#match" do
|
|
let(:order_book) { Rex::OrderBook.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) }
|
|
|
|
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)
|
|
|
|
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)
|
|
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)
|
|
end
|
|
end
|
|
|
|
context "when order book is empty" do
|
|
it "returns an empty list" do
|
|
expect(instance.match(Rex::OrderBook.new)).to eq([])
|
|
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
|
|
end
|
|
end
|