Joel Grunbaum
2022-01-07 4497ee326ceb2dfc3fe9d57b7b9ec9e8adc0cf8b
Added book sorting and protocol decyphering
3 files modified
289 ■■■■ changed files
book.py 151 ●●●●● patch | view | raw | blame | history
protocol.py 104 ●●●●● patch | view | raw | blame | history
test.py 34 ●●●● patch | view | raw | blame | history
book.py
@@ -10,8 +10,8 @@
class OrderSide(Enum):
    """Order side enum."""
    Buy = 0,
    Sell = 1
    Buy = "BUY",
    Sell = "SELL"
class ProductTypeEnum(Enum):
@@ -37,14 +37,18 @@
    side: OrderSide
    remaining_volume: int
    filled_volume: int
    timestamp: float
    id: str
    def __init__(self, price: decimal.Decimal(3), side: OrderSide,
                 volume: int):
                 volume: int, timestamp: float, id: str):
        """Initialise class."""
        self.price = price
        self.side = side
        self.remaining_volume = volume
        self.filled_volume = 0
        self.timestamp = timestamp
        self.id = id
class Level:
@@ -52,44 +56,97 @@
    price: decimal.Decimal
    volume: int
    side: OrderSide
    timestamp: float
    id: str
    def __init__(self, price: decimal.Decimal(3), volume):
    def __init__(self, order: Order):
        """Initialise class."""
        self.priority = int(price)
        self.price = price
        self.volume = volume
        self.price = order.price
        self.volume = order.remaining_volume
        self.side = order.side
        self.timestamp = order.timestamp
        self.id = order.id
    def __lt__(self, other):
        """Less than comparator."""
        if (self.side == OrderSide.Sell):
        if (self.price < other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp < other.timestamp):
                return True
            else:
                return False
        else:
            if (self.price > other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp < other.timestamp):
            return True
        else:
            return False
    def __gt__(self, other):
        """Greater than comparator."""
        if (self.side == OrderSide.Sell):
        if (self.price > other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp < other.timestamp):
                return True
            else:
                return False
        else:
            if (self.price < other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp < other.timestamp):
            return True
        else:
            return False
    def __le__(self, other):
        """Less than or equal compatator."""
        if (self.side == OrderSide.Sell):
        if (self.price <= other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp <= other.timestamp):
                return True
            else:
                return False
        else:
            if (self.price >= other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp <= other.timestamp):
            return True
        else:
            return False
    def __ge__(self, other):
        """Greater than or equal comparator."""
        if (self.side == OrderSide.Sell):
        if (self.price >= other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp <= other.timestamp):
                return True
            else:
                return False
        else:
            if (self.price <= other.price):
                return True
            elif (self.price == other.price and
                  self.timestamp <= other.timestamp):
            return True
        else:
            return False
    def __eq__(self, other):
        """Equalty compatator."""
        if (self.price == other.price):
        if (self.price == other.price and self.timestamp == other.timestamp):
            return True
        else:
            return False
@@ -100,7 +157,8 @@
    def __str__(self):
        """Turn into str for printing."""
        return "Price: " + str(self.price) + ", volume: " + str(self.volume)
        return ("Price: " + str(self.price) + ", volume: " + str(self.volume) +
                ", time: " + str(self.timestamp) + ", id: " + self.id)
class Side:
@@ -111,6 +169,13 @@
    def __init__(self):
        """Initialise class."""
        self.levels = []
    def delete(self, orderId: str):
        """Delete order from side."""
        for i in self.levels:
            if (i.id == orderId):
                self.levels.remove(i)
                heapq.heapify(self.levels)
class Book:
@@ -166,64 +231,58 @@
    def ask(self, order: Order):
        """Add ask to book."""
        while(len(self.bidSide.levels) > 0 and
              self.bidSide.levels[0][1].price >= order.price):
            if (self.bidSide.levels[0][1].volume > order.remaining_volume):
                temp = self.bidSide.levels[0][1].volume
                order.filled_volume += self.bidSide.levels[0][1].volume
                self.bidSide.levels[0][1].volume -= order.remaining_volume
              self.bidSide.levels[0].price >= order.price):
            if (self.bidSide.levels[0].volume > order.remaining_volume):
                temp = self.bidSide.levels[0].volume
                order.filled_volume += self.bidSide.levels[0].volume
                self.bidSide.levels[0].volume -= order.remaining_volume
                order.remaining_volume -= temp
                break
            else:
                order.remaining_volume -= self.bidSide.levels[0][1].volume
                order.filled_volume += self.bidSide.levels[0][1].volume
                order.remaining_volume -= self.bidSide.levels[0].volume
                order.filled_volume += self.bidSide.levels[0].volume
                heapq.heappop(self.bidSide.levels)
        if (order.remaining_volume > 0):
            if (len(self.askSide.levels) > 0 and
                    self.askSide.levels[0][1].price == order.price):
                self.askSide.levels[0][1].addVolume(order.remaining_volume)
            else:
                heapq.heappush(self.askSide.levels,
                               (order.price,
                                Level(order.price, order.remaining_volume)))
            heapq.heappush(self.askSide.levels, Level(order))
    def bid(self, order: Order):
        """Add bid to book."""
        while(len(self.askSide.levels) > 0 and
              self.askSide.levels[0][1].price <= order.price):
            if (self.askSide.levels[0][1].volume > order.remaining_volume):
                temp = self.askSide.levels[0][1].volume
                order.filled_volume += self.askSide.levels[0][1].volume
                self.askSide.levels[0][1].volume -= order.remaining_volume
              self.askSide.levels[0].price <= order.price):
            if (self.askSide.levels[0].volume > order.remaining_volume):
                temp = self.askSide.levels[0].volume
                order.filled_volume += self.askSide.levels[0].volume
                self.askSide.levels[0].volume -= order.remaining_volume
                order.remaining_volume -= temp
                break
            else:
                order.remaining_volume -= self.askSide.levels[0][1].volume
                order.filled_volume -= self.askSide.levels[0][1].volume
                order.remaining_volume -= self.askSide.levels[0].volume
                order.filled_volume -= self.askSide.levels[0].volume
                heapq.heappop(self.askSide.levels)
        if (order.remaining_volume > 0):
            if (len(self.bidSide.levels) > 0 and
                    self.bidSide.levels[0][1].price == order.price):
                self.bidSide.levels[0][1].addVolume(order.remaining_volume)
            else:
                heapq.heappush(self.bidSide.levels,
                               (100 - order.price,
                                Level(order.price, order.remaining_volume)))
            heapq.heappush(self.bidSide.levels, Level(order))
    def printBook(self):
        """Test print book. Book is a heap so not strictly sorted."""
        print("Sell side")
        for i in sorted(self.askSide.levels, reverse=True):
            print(i[1])
        for i in sorted(self.askSide.levels, reverse=True)[-10:]:
            print(i)
        print("Buy side")
        for i in sorted(self.bidSide.levels):
            print(str(i[1]))
        for i in sorted(self.bidSide.levels)[:10]:
            print(str(i))
def testBook():
def testBook(orders: int = 10, printBook=True):
    """Test an example book."""
    b = Book()
    for i in range(1, 10):
        b.addOrder(Order(i, OrderSide.Buy, 10))
    for i in range(11, 20):
        b.addOrder(Order(i, OrderSide.Sell, 10))
    time = 1
    for i in range(1, orders):
        b.addOrder(Order(price=i, side=OrderSide.Buy, volume=10,
                         timestamp=time, id="a"))
        time += 1
    for i in range(orders + 1, 2 * orders):
        b.addOrder(Order(i, OrderSide.Sell, 10, time, "a"))
        time += 1
    if (printBook):
    b.printBook()
    return b
protocol.py
@@ -1,5 +1,19 @@
""".#protocol.py handling protocol."""
import book
from enum import Enum
import httpx
import json
test = True
HOST: str = "sytev070"
if (test):
    PORT = "9005"
else:
    PORT = "9000"
USER: str = "jgrunbau"
PASS: str = "b7d630945a0854581d9f86ba147f34a5"
def handleMessage(bs: {str, book.Book}, message: {str, str}):
@@ -11,7 +25,12 @@
    elif (message["type"] == "ADDED"):
        addOrder(bs, message)
    elif (message["type"] == "DELETED"):
        pass
        deleteOrder(bs, message)
    elif (message["type"] == "TRADE"):
        tradeOrder(bs, message)
    elif (message["type"] in {"BROKER_REQUEST", "BROKER_ACK",
                              "BROKER_CONFIRM"}):
        broker(bs, message)
def announce(bs: {str, book.Book}, message: {str, str}):
@@ -27,14 +46,85 @@
def settle(bs: {str, book.Book}, message: {str, str}):
    """Settle order."""
    bs.pop(message["product"])#.printBook()
    b = bs.pop(message["product"])
    if (False):
        b.printBook()
        print()
        if (len(b.askSide.levels) > 0):
            print(b.askSide.levels[0])
        if (len(b.bidSide.levels)):
            print(b.bidSide.levels[0])
        print()
def addOrder(bs: {str, book.Book}, message: {str, str}):
    """Order added to exchange."""
    price = message["price"]
    volume = message["resting"] + message["filled"]
    if (message["side"] == "BUY"):
        side = book.OrderSide.Buy
        bs[message["product"]].bid(
            book.Order(message["price"], book.OrderSide.Buy,
                       message["resting"],
                       message["timestamp"], message["id"]))
    else:
        side = book.OrderSide.Sell
    bs[message["product"]].addOrder(book.Order(price, side, volume))
        bs[message["product"]].ask(
            book.Order(message["price"], book.OrderSide.Sell,
                       message["resting"],
                       message["timestamp"], message["id"]))
def deleteOrder(bs: {str, book.Book}, message: {str, str}):
    """Delete order from market."""
    if (message["side"] == "BUY"):
        bs[message["product"]].bidSide.delete(message["id"])
    else:
        bs[message["product"]].askSide.delete(message["id"])
def tradeOrder(bs: {str, book.Book}, message: {str, str}):
    """Order traded."""
    if (message["tradeType"] == "BUY_AGGRESSOR"):
        bs[message["product"]].bidSide.delete(message["passiveOrder"])
    elif (message["tradeType"] == "SELL_AGGRESSOR"):
        bs[message["product"]].askSide.delete(message["passiveOrder"])
def broker(bs: {str, book.Book}, message: {str, str}):
    """Broker trades."""
    pass
def send(message):
    """Send message with exchange."""
    return httpx.post(f"http://{HOST}:{PORT}/execution",
                      data={"message": json.dumps(message),
                            "username": USER,
                            "password": PASS}).json()
class OrderEnum(Enum):
    """Enum for ordering from the exchange."""
    ADD = 0,
    DELETE = 1,
    BROKER_REQUEST = 2,
    BROKER_CONFIRM = 3
def order(orderType: OrderEnum, product: str, price: float = 0,
          side: book.OrderSide = book.OrderSide.Buy,
          volume: int = 0, orderId: str = "", counterparty: str = ""):
    """Order from exchange."""
    if (orderType == OrderEnum.ADD):
        resp = send({"type": "ADD",
                     "product": product,
                     "price": price,
                     "side": side,
                     "volume": volume})
    elif (orderType == OrderEnum.DELETE):
        resp = send({"type": "DELETE",
                     "product": product,
                     "id": id})
    elif (orderType == OrderEnum.BROKER_REQUEST):
        pass
    elif (orderType == OrderEnum.BROKER_CONFIRM):
        pass
    return resp
test.py
@@ -1,5 +1,4 @@
""".#test.py main module."""
import json
import time
import httpx
from whereismysock import all
@@ -7,19 +6,16 @@
import book
import protocol
test = False
HOST: str = 'sytev070'
if (test):
    PORT: str = '9005'
else:
PORT: str = '9000'
USER: str = 'test'
PASS: str = 'none'
def send(message):
    """Send message to exchange."""
    return httpx.post(f"http://{HOST}:{PORT}/execution",
                      data={"message": json.dumps(message),
                            "username": USER,
                            "password": PASS}).json()
def logAllIncomingMessages():
@@ -45,8 +41,6 @@
        protocol.handleMessage(bs, message)
message = {"type": "ADD", "product": "F_SOH_APP0104T1700", "price": 99.0,
           "side": "BUY", "volume": 1000000}
# print(json.dumps(message))
# print(send(message))
# printRecoveryLog()
@@ -55,9 +49,15 @@
# wstest()
bs = {}
# recoverBook(bs)
# print(list(bs))
# for i in bs:
    # print(bs[i].product)
    # bs[i].printBook()
book.testBook()
recoverBook(bs)
print(list(bs))
for i in bs:
    print(bs[i].product)
    bs[i].printBook()
    print()
    if (len(bs[i].askSide.levels) > 0):
        print(bs[i].askSide.levels[0])
    if (len(bs[i].bidSide.levels)):
        print(bs[i].bidSide.levels[0])
    print()
# book.testBook()