Merge pull request #20 from ochaloup/20211612_top-bids-fix
[orderbook][top-bids] fixing top bids processing
This commit is contained in:
commit
322b02429a
|
@ -202,8 +202,8 @@ class Order(typing.NamedTuple):
|
|||
return order
|
||||
|
||||
@staticmethod
|
||||
def from_ids(id: int, client_id: int, side: Side = Side.BUY) -> "Order":
|
||||
return Order(id=id, client_id=client_id, owner=SYSTEM_PROGRAM_ADDRESS, side=side, price=Decimal(0), quantity=Decimal(0), order_type=OrderType.UNKNOWN)
|
||||
def from_ids(id: int, client_id: int, side: Side = Side.BUY, price: Decimal = Decimal(0), quantity: Decimal = Decimal(0)) -> "Order":
|
||||
return Order(id=id, client_id=client_id, owner=SYSTEM_PROGRAM_ADDRESS, side=side, price=price, quantity=quantity, order_type=OrderType.UNKNOWN)
|
||||
|
||||
def __str__(self) -> str:
|
||||
owner: str = ""
|
||||
|
@ -222,18 +222,34 @@ class OrderBook:
|
|||
def __init__(self, symbol: str, lot_size_converter: LotSizeConverter, bids: typing.Sequence[Order], asks: typing.Sequence[Order]) -> None:
|
||||
self.symbol: str = symbol
|
||||
self.__lot_size_converter: LotSizeConverter = lot_size_converter
|
||||
self.bids: typing.Sequence[Order] = bids
|
||||
self.asks: typing.Sequence[Order] = asks
|
||||
|
||||
# Sort bids high to low, so best bid is at index 0
|
||||
@property
|
||||
def bids(self) -> typing.Sequence[Order]:
|
||||
return self._bids
|
||||
|
||||
@bids.setter
|
||||
def bids(self, bids: typing.Sequence[Order]):
|
||||
""" Sort bids high to low, so best bid is at index 0 """
|
||||
bids_list: typing.List[Order] = list(bids)
|
||||
print(f'list of bids: {bids_list}')
|
||||
bids_list.sort(key=lambda order: order.id, reverse=True)
|
||||
print(f'list of sorted bids: {bids_list}')
|
||||
# bids_list.sort(key=lambda order: order.price, reverse=True)
|
||||
self.bids: typing.Sequence[Order] = bids_list
|
||||
self._bids = bids_list
|
||||
|
||||
# Sort bids low to high, so best bid is at index 0
|
||||
@property
|
||||
def asks(self) -> typing.Sequence[Order]:
|
||||
return self._asks
|
||||
|
||||
@asks.setter
|
||||
def asks(self, asks: typing.Sequence[Order]):
|
||||
""" Sets asks low to high, so best ask is at index 0"""
|
||||
asks_list: typing.List[Order] = list(asks)
|
||||
asks_list.sort(key=lambda order: order.id)
|
||||
# asks_list.sort(key=lambda order: order.price)
|
||||
self.asks: typing.Sequence[Order] = asks_list
|
||||
self._asks = asks_list
|
||||
|
||||
# The top bid is the highest price someone is willing to pay to BUY
|
||||
@property
|
||||
|
|
|
@ -133,6 +133,15 @@ def fake_wallet() -> mango.Wallet:
|
|||
def fake_order(price: Decimal = Decimal(1), quantity: Decimal = Decimal(1), side: mango.Side = mango.Side.BUY, order_type: mango.OrderType = mango.OrderType.LIMIT) -> mango.Order:
|
||||
return mango.Order.from_basic_info(side=side, price=price, quantity=quantity, order_type=order_type)
|
||||
|
||||
# serum ID structure - 16-byte 'int': low 8 bytes is a sequence number, high 8 bytes is price
|
||||
def fake_order_id(index:int, price: int) -> int:
|
||||
# price needs to be max of 64bit/8bytes, considering signed int is not permitted
|
||||
if index > 2**64-1 or price > 2**64-1:
|
||||
raise ValueError(f"Provided index '{index}' or price '{price}' is bigger than 8 bytes int")
|
||||
index_bytes = index.to_bytes(8, byteorder='big', signed=False)
|
||||
price_bytes = price.to_bytes(8, byteorder='big', signed=False)
|
||||
return int.from_bytes((price_bytes+index_bytes), byteorder='big', signed=False)
|
||||
|
||||
|
||||
def fake_price(market: mango.Market = fake_loaded_market(), price: Decimal = Decimal(100), bid: Decimal = Decimal(99), ask: Decimal = Decimal(101)) -> mango.Price:
|
||||
return mango.Price(mango.OracleSource("test", "test", mango.SupportedOracleFeature.TOP_BID_AND_OFFER, market), datetime.datetime.now(), market, bid, price, ask, Decimal(0))
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import random
|
||||
|
||||
from mango.orders import Order
|
||||
|
||||
from .context import mango
|
||||
from .fakes import fake_order_id
|
||||
|
||||
from typing import Sequence
|
||||
from decimal import Decimal
|
||||
|
||||
def test_order_book_sides_sorted_by_price() -> None:
|
||||
bids_side = _construct_order_book_side(mango.Side.BUY, 5)
|
||||
asks_side = _construct_order_book_side(mango.Side.SELL, 5)
|
||||
order_book = _construct_order_book(bids=bids_side, asks=asks_side)
|
||||
assert sorted(bids_side, key = lambda o: o.price, reverse = True) == order_book.bids
|
||||
assert sorted(asks_side, key = lambda o: o.price, reverse = False) == order_book.asks
|
||||
|
||||
def test_orderbook_top_bids_and_asks() -> None:
|
||||
bids = _construct_order_book_side(mango.Side.BUY, 7)
|
||||
asks = _construct_order_book_side(mango.Side.SELL, 7)
|
||||
orderBook: mango.OrderBook = _construct_order_book(bids=bids, asks=asks)
|
||||
|
||||
assert _get_order(bids, -1) == orderBook.top_bid
|
||||
assert _get_order(asks) == orderBook.top_ask
|
||||
# update orderbook
|
||||
update_bids = _construct_order_book_side(mango.Side.BUY, 5)
|
||||
update_asks = _construct_order_book_side(mango.Side.SELL, 5)
|
||||
orderBook.bids = update_bids
|
||||
orderBook.asks = update_asks
|
||||
assert _get_order(update_bids, -1) == orderBook.top_bid
|
||||
assert _get_order(update_asks) == orderBook.top_ask
|
||||
|
||||
|
||||
def test_orderbook_spread() -> None:
|
||||
bids = _construct_order_book_side(mango.Side.BUY, 7)
|
||||
asks = _construct_order_book_side(mango.Side.SELL, 7)
|
||||
# None's in book
|
||||
orderBook: mango.OrderBook = _construct_order_book(bids=bids, asks=[])
|
||||
assert orderBook.spread == Decimal(0)
|
||||
orderBook = _construct_order_book(bids=[], asks=[])
|
||||
assert orderBook.spread == Decimal(0)
|
||||
orderBook = _construct_order_book([], [])
|
||||
assert orderBook.spread == Decimal(0)
|
||||
# ask's spread calculated from generated data
|
||||
orderBook = _construct_order_book(bids=bids, asks=asks)
|
||||
assert orderBook.spread == _get_order(asks).price - _get_order(bids, -1).price
|
||||
|
||||
# ASK is SELL, BID is BUY
|
||||
def _construct_order_book_side(askOrBidSide: mango.Side, size: int) -> Sequence[mango.Order]:
|
||||
random_prices: list[(int,int)] = list(map(
|
||||
lambda x, y: (x, y),
|
||||
range (1, size+1),
|
||||
random.sample(range(1, 1000), size)
|
||||
))
|
||||
result_orders: Sequence[mango.Order] = []
|
||||
for index, price in random_prices:
|
||||
constructed_id = fake_order_id(index, price)
|
||||
order = Order.from_ids(
|
||||
id=constructed_id,
|
||||
client_id=0,
|
||||
side=askOrBidSide,
|
||||
price=price,
|
||||
quantity=random.randint(1, 100),
|
||||
)
|
||||
result_orders.append(order)
|
||||
return result_orders
|
||||
|
||||
def _construct_order_book(
|
||||
bids: Sequence[mango.Order],
|
||||
asks: Sequence[mango.Order]
|
||||
) -> mango.OrderBook:
|
||||
# construct orderbook
|
||||
return mango.OrderBook(
|
||||
symbol='TEST',
|
||||
lot_size_converter=None,
|
||||
bids=bids,
|
||||
asks=asks
|
||||
)
|
||||
|
||||
def _get_order(orders: Sequence[mango.Order], index: int = 0) -> Order:
|
||||
return sorted(orders, key=lambda order: order.price)[index]
|
Loading…
Reference in New Issue