Move order book to Rex::Book namespace
This commit is contained in:
parent
0efb6b0e88
commit
509b00bb14
10
lib/rex.rb
10
lib/rex.rb
@ -1,11 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "rex/version"
|
require_relative "rex/version"
|
||||||
require_relative "rex/limit"
|
require_relative "rex/book/limit"
|
||||||
require_relative "rex/order"
|
require_relative "rex/book/order"
|
||||||
require_relative "rex/trade"
|
require_relative "rex/book/trade"
|
||||||
require_relative "rex/order_book"
|
require_relative "rex/book/order_book"
|
||||||
require_relative "rex/matcher"
|
require_relative "rex/book/matcher"
|
||||||
|
|
||||||
module Rex
|
module Rex
|
||||||
class Error < StandardError; end
|
class Error < StandardError; end
|
||||||
|
81
lib/rex/book/limit.rb
Normal file
81
lib/rex/book/limit.rb
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
module Rex
|
||||||
|
module Book
|
||||||
|
class Limit
|
||||||
|
attr_reader(
|
||||||
|
:first_order,
|
||||||
|
:last_order,
|
||||||
|
:price
|
||||||
|
)
|
||||||
|
def initialize(price)
|
||||||
|
@price = price
|
||||||
|
@first_order = nil
|
||||||
|
@last_order = nil
|
||||||
|
@order_count = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def peek_first_order
|
||||||
|
@first_order
|
||||||
|
end
|
||||||
|
|
||||||
|
def count
|
||||||
|
@order_count
|
||||||
|
end
|
||||||
|
|
||||||
|
def pop_first_order
|
||||||
|
return nil if empty?
|
||||||
|
|
||||||
|
order = @first_order
|
||||||
|
@first_order = @first_order.next_order
|
||||||
|
@first_order&.previous_order = nil
|
||||||
|
|
||||||
|
if @first_order.nil?
|
||||||
|
@last_order = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
order.next_order = nil
|
||||||
|
order.previous_order = nil
|
||||||
|
|
||||||
|
@order_count -= 1
|
||||||
|
order
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_order(order)
|
||||||
|
if empty?
|
||||||
|
@first_order = order
|
||||||
|
else
|
||||||
|
@last_order.next_order = order
|
||||||
|
order.previous_order = @last_order
|
||||||
|
end
|
||||||
|
@last_order = order
|
||||||
|
@order_count += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Assumption when calling: the order is part of the limit
|
||||||
|
def remove_order(order)
|
||||||
|
if @first_order == order
|
||||||
|
@first_order = order.next_order
|
||||||
|
end
|
||||||
|
|
||||||
|
if @last_order == order
|
||||||
|
@last_order = order.previous_order
|
||||||
|
end
|
||||||
|
|
||||||
|
previous_order = order.previous_order
|
||||||
|
next_order = order.next_order
|
||||||
|
|
||||||
|
previous_order&.next_order = next_order
|
||||||
|
next_order&.previous_order = previous_order
|
||||||
|
|
||||||
|
order.previous_order = nil
|
||||||
|
order.next_order = nil
|
||||||
|
@order_count -= 1
|
||||||
|
|
||||||
|
order
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
@order_count == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
lib/rex/book/matcher.rb
Normal file
41
lib/rex/book/matcher.rb
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
module Rex
|
||||||
|
module Book
|
||||||
|
class Matcher
|
||||||
|
def match(order_book)
|
||||||
|
trades = []
|
||||||
|
highest_buy_order = order_book.highest_buy_order
|
||||||
|
lowest_sell_order = order_book.lowest_sell_order
|
||||||
|
return trades if highest_buy_order.nil? || lowest_sell_order.nil?
|
||||||
|
|
||||||
|
while highest_buy_order.price >= lowest_sell_order.price
|
||||||
|
max_quantity = min(highest_buy_order.remaining_quantity, lowest_sell_order.remaining_quantity)
|
||||||
|
trade = Trade.new(
|
||||||
|
id: order_book.next_trade_id,
|
||||||
|
buy_order: highest_buy_order,
|
||||||
|
sell_order: lowest_sell_order,
|
||||||
|
quantity: max_quantity,
|
||||||
|
price: lowest_sell_order.price
|
||||||
|
)
|
||||||
|
|
||||||
|
order_book.process_trade(trade)
|
||||||
|
trades.push(trade)
|
||||||
|
|
||||||
|
# Go for the next run
|
||||||
|
highest_buy_order = order_book.highest_buy_order
|
||||||
|
lowest_sell_order = order_book.lowest_sell_order
|
||||||
|
|
||||||
|
return trades if highest_buy_order.nil? || lowest_sell_order.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
trades
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def min(a, b)
|
||||||
|
return a if a < b
|
||||||
|
b
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
33
lib/rex/book/order.rb
Normal file
33
lib/rex/book/order.rb
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
module Rex
|
||||||
|
module Book
|
||||||
|
class Order
|
||||||
|
attr_accessor(
|
||||||
|
:id,
|
||||||
|
:is_buy,
|
||||||
|
:price,
|
||||||
|
:user_id,
|
||||||
|
:quantity,
|
||||||
|
:previous_order,
|
||||||
|
:next_order,
|
||||||
|
:remaining_quantity
|
||||||
|
)
|
||||||
|
|
||||||
|
def initialize(attrs = {})
|
||||||
|
@id = attrs[:id]
|
||||||
|
|
||||||
|
@user_id = attrs[:user_id]
|
||||||
|
@price = attrs[:price]
|
||||||
|
@is_buy = attrs[:is_buy]
|
||||||
|
@quantity = attrs[:quantity]
|
||||||
|
@remaining_quantity = attrs[:quantity]
|
||||||
|
|
||||||
|
@next_order = nil
|
||||||
|
@previous_order = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def filled?
|
||||||
|
remaining_quantity == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
96
lib/rex/book/order_book.rb
Normal file
96
lib/rex/book/order_book.rb
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
require "rbtree"
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Book
|
||||||
|
class OrderBook
|
||||||
|
def initialize(matcher: Matcher.new)
|
||||||
|
@matcher = matcher
|
||||||
|
@sell_side = RBTree.new
|
||||||
|
@buy_side = RBTree.new
|
||||||
|
@order_ids = {} # order_id => order
|
||||||
|
@current_trade_id = 0
|
||||||
|
@current_order_id = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_order(order)
|
||||||
|
side = side_for_order(order)
|
||||||
|
order.id = next_order_id
|
||||||
|
|
||||||
|
side[order.price] ||= Limit.new(order.price)
|
||||||
|
side[order.price].add_order(order)
|
||||||
|
|
||||||
|
order_ids[order.id] = order
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_and_match_order(order)
|
||||||
|
add_order(order)
|
||||||
|
@matcher.match(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_order(order_id)
|
||||||
|
order = order_ids[order_id]
|
||||||
|
return nil if order.nil?
|
||||||
|
|
||||||
|
side = side_for_order(order)
|
||||||
|
limit = side[order.price]
|
||||||
|
limit.remove_order(order)
|
||||||
|
|
||||||
|
if limit.empty?
|
||||||
|
side.delete(limit.price)
|
||||||
|
end
|
||||||
|
|
||||||
|
order_ids.delete(order.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :cancel_order, :remove_order
|
||||||
|
|
||||||
|
def process_trade(trade)
|
||||||
|
trade.buy_order.remaining_quantity -= trade.quantity
|
||||||
|
trade.sell_order.remaining_quantity -= trade.quantity
|
||||||
|
|
||||||
|
remove_order(trade.buy_order.id) if trade.buy_order.filled?
|
||||||
|
remove_order(trade.sell_order.id) if trade.sell_order.filled?
|
||||||
|
end
|
||||||
|
|
||||||
|
def highest_buy_order
|
||||||
|
buy_side.last&.[](1)&.peek_first_order
|
||||||
|
end
|
||||||
|
|
||||||
|
def lowest_sell_order
|
||||||
|
sell_side.first&.[](1)&.peek_first_order
|
||||||
|
end
|
||||||
|
|
||||||
|
def best_buy_price
|
||||||
|
buy_side.last&.[](0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def best_sell_price
|
||||||
|
sell_side.first&.[](0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_trade_id
|
||||||
|
@current_trade_id += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader(
|
||||||
|
:order_ids,
|
||||||
|
:buy_side,
|
||||||
|
:sell_side
|
||||||
|
)
|
||||||
|
|
||||||
|
def next_order_id
|
||||||
|
@current_order_id += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def side_for_order(order)
|
||||||
|
if order.is_buy
|
||||||
|
buy_side
|
||||||
|
else
|
||||||
|
sell_side
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
lib/rex/book/trade.rb
Normal file
21
lib/rex/book/trade.rb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
module Rex
|
||||||
|
module Book
|
||||||
|
class Trade
|
||||||
|
attr_accessor(
|
||||||
|
:id,
|
||||||
|
:buy_order,
|
||||||
|
:sell_order,
|
||||||
|
:quantity,
|
||||||
|
:price
|
||||||
|
)
|
||||||
|
|
||||||
|
def initialize(attributes = {})
|
||||||
|
@id = attributes[:id]
|
||||||
|
@buy_order = attributes[:buy_order]
|
||||||
|
@sell_order = attributes[:sell_order]
|
||||||
|
@quantity = attributes[:quantity]
|
||||||
|
@price = attributes[:price]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,79 +0,0 @@
|
|||||||
module Rex
|
|
||||||
class Limit
|
|
||||||
attr_reader(
|
|
||||||
:first_order,
|
|
||||||
:last_order,
|
|
||||||
:price
|
|
||||||
)
|
|
||||||
def initialize(price)
|
|
||||||
@price = price
|
|
||||||
@first_order = nil
|
|
||||||
@last_order = nil
|
|
||||||
@order_count = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def peek_first_order
|
|
||||||
@first_order
|
|
||||||
end
|
|
||||||
|
|
||||||
def count
|
|
||||||
@order_count
|
|
||||||
end
|
|
||||||
|
|
||||||
def pop_first_order
|
|
||||||
return nil if empty?
|
|
||||||
|
|
||||||
order = @first_order
|
|
||||||
@first_order = @first_order.next_order
|
|
||||||
@first_order&.previous_order = nil
|
|
||||||
|
|
||||||
if @first_order.nil?
|
|
||||||
@last_order = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
order.next_order = nil
|
|
||||||
order.previous_order = nil
|
|
||||||
|
|
||||||
@order_count -= 1
|
|
||||||
order
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_order(order)
|
|
||||||
if empty?
|
|
||||||
@first_order = order
|
|
||||||
else
|
|
||||||
@last_order.next_order = order
|
|
||||||
order.previous_order = @last_order
|
|
||||||
end
|
|
||||||
@last_order = order
|
|
||||||
@order_count += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# Assumption when calling: the order is part of the limit
|
|
||||||
def remove_order(order)
|
|
||||||
if @first_order == order
|
|
||||||
@first_order = order.next_order
|
|
||||||
end
|
|
||||||
|
|
||||||
if @last_order == order
|
|
||||||
@last_order = order.previous_order
|
|
||||||
end
|
|
||||||
|
|
||||||
previous_order = order.previous_order
|
|
||||||
next_order = order.next_order
|
|
||||||
|
|
||||||
previous_order&.next_order = next_order
|
|
||||||
next_order&.previous_order = previous_order
|
|
||||||
|
|
||||||
order.previous_order = nil
|
|
||||||
order.next_order = nil
|
|
||||||
@order_count -= 1
|
|
||||||
|
|
||||||
order
|
|
||||||
end
|
|
||||||
|
|
||||||
def empty?
|
|
||||||
@order_count == 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,39 +0,0 @@
|
|||||||
module Rex
|
|
||||||
class Matcher
|
|
||||||
def match(order_book)
|
|
||||||
trades = []
|
|
||||||
highest_buy_order = order_book.highest_buy_order
|
|
||||||
lowest_sell_order = order_book.lowest_sell_order
|
|
||||||
return trades if highest_buy_order.nil? || lowest_sell_order.nil?
|
|
||||||
|
|
||||||
while highest_buy_order.price >= lowest_sell_order.price
|
|
||||||
max_quantity = min(highest_buy_order.remaining_quantity, lowest_sell_order.remaining_quantity)
|
|
||||||
trade = Trade.new(
|
|
||||||
id: order_book.next_trade_id,
|
|
||||||
buy_order: highest_buy_order,
|
|
||||||
sell_order: lowest_sell_order,
|
|
||||||
quantity: max_quantity,
|
|
||||||
price: lowest_sell_order.price
|
|
||||||
)
|
|
||||||
|
|
||||||
order_book.process_trade(trade)
|
|
||||||
trades.push(trade)
|
|
||||||
|
|
||||||
# Go for the next run
|
|
||||||
highest_buy_order = order_book.highest_buy_order
|
|
||||||
lowest_sell_order = order_book.lowest_sell_order
|
|
||||||
|
|
||||||
return trades if highest_buy_order.nil? || lowest_sell_order.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
trades
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def min(a, b)
|
|
||||||
return a if a < b
|
|
||||||
b
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,31 +0,0 @@
|
|||||||
module Rex
|
|
||||||
class Order
|
|
||||||
attr_accessor(
|
|
||||||
:id,
|
|
||||||
:is_buy,
|
|
||||||
:price,
|
|
||||||
:user_id,
|
|
||||||
:quantity,
|
|
||||||
:previous_order,
|
|
||||||
:next_order,
|
|
||||||
:remaining_quantity
|
|
||||||
)
|
|
||||||
|
|
||||||
def initialize(attrs = {})
|
|
||||||
@id = attrs[:id]
|
|
||||||
|
|
||||||
@user_id = attrs[:user_id]
|
|
||||||
@price = attrs[:price]
|
|
||||||
@is_buy = attrs[:is_buy]
|
|
||||||
@quantity = attrs[:quantity]
|
|
||||||
@remaining_quantity = attrs[:quantity]
|
|
||||||
|
|
||||||
@next_order = nil
|
|
||||||
@previous_order = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def filled?
|
|
||||||
remaining_quantity == 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,94 +0,0 @@
|
|||||||
require "rbtree"
|
|
||||||
|
|
||||||
module Rex
|
|
||||||
class OrderBook
|
|
||||||
def initialize(matcher: Matcher.new)
|
|
||||||
@matcher = matcher
|
|
||||||
@sell_side = RBTree.new
|
|
||||||
@buy_side = RBTree.new
|
|
||||||
@order_ids = {} # order_id => order
|
|
||||||
@current_trade_id = 0
|
|
||||||
@current_order_id = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_order(order)
|
|
||||||
side = side_for_order(order)
|
|
||||||
order.id = next_order_id
|
|
||||||
|
|
||||||
side[order.price] ||= Limit.new(order.price)
|
|
||||||
side[order.price].add_order(order)
|
|
||||||
|
|
||||||
order_ids[order.id] = order
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_and_match_order(order)
|
|
||||||
add_order(order)
|
|
||||||
@matcher.match(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def remove_order(order_id)
|
|
||||||
order = order_ids[order_id]
|
|
||||||
return nil if order.nil?
|
|
||||||
|
|
||||||
side = side_for_order(order)
|
|
||||||
limit = side[order.price]
|
|
||||||
limit.remove_order(order)
|
|
||||||
|
|
||||||
if limit.empty?
|
|
||||||
side.delete(limit.price)
|
|
||||||
end
|
|
||||||
|
|
||||||
order_ids.delete(order.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :cancel_order, :remove_order
|
|
||||||
|
|
||||||
def process_trade(trade)
|
|
||||||
trade.buy_order.remaining_quantity -= trade.quantity
|
|
||||||
trade.sell_order.remaining_quantity -= trade.quantity
|
|
||||||
|
|
||||||
remove_order(trade.buy_order.id) if trade.buy_order.filled?
|
|
||||||
remove_order(trade.sell_order.id) if trade.sell_order.filled?
|
|
||||||
end
|
|
||||||
|
|
||||||
def highest_buy_order
|
|
||||||
buy_side.last&.[](1)&.peek_first_order
|
|
||||||
end
|
|
||||||
|
|
||||||
def lowest_sell_order
|
|
||||||
sell_side.first&.[](1)&.peek_first_order
|
|
||||||
end
|
|
||||||
|
|
||||||
def best_buy_price
|
|
||||||
buy_side.last&.[](0)
|
|
||||||
end
|
|
||||||
|
|
||||||
def best_sell_price
|
|
||||||
sell_side.first&.[](0)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_trade_id
|
|
||||||
@current_trade_id += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_reader(
|
|
||||||
:order_ids,
|
|
||||||
:buy_side,
|
|
||||||
:sell_side
|
|
||||||
)
|
|
||||||
|
|
||||||
def next_order_id
|
|
||||||
@current_order_id += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
def side_for_order(order)
|
|
||||||
if order.is_buy
|
|
||||||
buy_side
|
|
||||||
else
|
|
||||||
sell_side
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,19 +0,0 @@
|
|||||||
module Rex
|
|
||||||
class Trade
|
|
||||||
attr_accessor(
|
|
||||||
:id,
|
|
||||||
:buy_order,
|
|
||||||
:sell_order,
|
|
||||||
:quantity,
|
|
||||||
:price
|
|
||||||
)
|
|
||||||
|
|
||||||
def initialize(attributes = {})
|
|
||||||
@id = attributes[:id]
|
|
||||||
@buy_order = attributes[:buy_order]
|
|
||||||
@sell_order = attributes[:sell_order]
|
|
||||||
@quantity = attributes[:quantity]
|
|
||||||
@price = attributes[:price]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Rex::Limit do
|
RSpec.describe Rex::Book::Limit do
|
||||||
let(:instance) { described_class.new(100) }
|
let(:instance) { described_class.new(100) }
|
||||||
let(:order_a) { build(:order) }
|
let(:order_a) { build(:order) }
|
||||||
let(:order_b) { build(:order) }
|
let(:order_b) { build(:order) }
|
@ -1,10 +1,10 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Rex::Matcher do
|
RSpec.describe Rex::Book::Matcher do
|
||||||
let(:instance) { described_class.new }
|
let(:instance) { described_class.new }
|
||||||
|
|
||||||
describe "#match" do
|
describe "#match" do
|
||||||
let(:order_book) { Rex::OrderBook.new }
|
let(:order_book) { Rex::Book::OrderBook.new }
|
||||||
let(:buy_order) { build(:order, price: 100, is_buy: true, quantity: 100, remaining_quantity: 100) }
|
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(: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) }
|
let(:pricier_sell_order) { build(:order, price: 100, is_buy: false, quantity: 70, remaining_quantity: 70) }
|
||||||
@ -45,7 +45,7 @@ RSpec.describe Rex::Matcher do
|
|||||||
|
|
||||||
context "when order book is empty" do
|
context "when order book is empty" do
|
||||||
it "returns an empty list" do
|
it "returns an empty list" do
|
||||||
expect(instance.match(Rex::OrderBook.new)).to eq([])
|
expect(instance.match(Rex::Book::OrderBook.new)).to eq([])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Rex::OrderBook do
|
RSpec.describe Rex::Book::OrderBook do
|
||||||
let(:instance) { described_class.new }
|
let(:instance) { described_class.new }
|
||||||
|
|
||||||
describe "#add_order" do
|
describe "#add_order" do
|
||||||
@ -57,7 +57,7 @@ RSpec.describe Rex::OrderBook do
|
|||||||
|
|
||||||
describe "add_and_match_order" do
|
describe "add_and_match_order" do
|
||||||
context "when matcher is given" do
|
context "when matcher is given" do
|
||||||
let(:matcher) { instance_double(Rex::Matcher) }
|
let(:matcher) { instance_double(Rex::Book::Matcher) }
|
||||||
let(:instance) { described_class.new(matcher: matcher) }
|
let(:instance) { described_class.new(matcher: matcher) }
|
||||||
let(:order) { build(:order, is_buy: true, price: 100) }
|
let(:order) { build(:order, is_buy: true, price: 100) }
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ RSpec.describe Rex::OrderBook do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#lowest_sell_Order" do
|
describe "#lowest_sell_order" do
|
||||||
context "when there is nothing in the book" do
|
context "when there is nothing in the book" do
|
||||||
it "returns nil " do
|
it "returns nil " do
|
||||||
expect(instance.highest_buy_order).to eq(nil)
|
expect(instance.highest_buy_order).to eq(nil)
|
@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
RSpec.describe Rex::Order do
|
RSpec.describe Rex::Book::Order do
|
||||||
describe "#filled?" do
|
describe "#filled?" do
|
||||||
let(:order) do
|
let(:order) do
|
||||||
instance = build(:order)
|
instance = build(:order)
|
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :limit, class: Rex::Limit do
|
factory :limit, class: Rex::Book::Limit do
|
||||||
transient do
|
transient do
|
||||||
price { 100 }
|
price { 100 }
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :order, class: Rex::Order do
|
factory :order, class: Rex::Book::Order do
|
||||||
sequence(:id) { |i| i }
|
sequence(:id) { |i| i }
|
||||||
sequence(:user_id) { |i| i }
|
sequence(:user_id) { |i| i }
|
||||||
sequence(:is_buy) { |i| (i % 2).zero? }
|
sequence(:is_buy) { |i| (i % 2).zero? }
|
||||||
|
Loading…
Reference in New Issue
Block a user