From 4497ee326ceb2dfc3fe9d57b7b9ec9e8adc0cf8b Mon Sep 17 00:00:00 2001
From: Joel Grunbaum <joelgrun@gmail.com>
Date: Fri, 07 Jan 2022 05:02:19 +0000
Subject: [PATCH] Added book sorting and protocol decyphering
---
test.py | 36 +++---
book.py | 177 +++++++++++++++++++---------
protocol.py | 104 ++++++++++++++++-
3 files changed, 233 insertions(+), 84 deletions(-)
diff --git a/book.py b/book.py
index a506adb..50543ef 100644
--- a/book.py
+++ b/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.price < other.price):
- return True
+ 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:
- return False
+ 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.price > other.price):
- return True
+ 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:
- return False
+ 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.price <= other.price):
- return True
+ 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:
- return False
+ 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.price >= other.price):
- return True
+ 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:
- return False
+ 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:
@@ -112,6 +170,13 @@
"""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:
"""Book class."""
@@ -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))
- b.printBook()
+ 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
diff --git a/protocol.py b/protocol.py
index 9d662cc..c575102 100644
--- a/protocol.py
+++ 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
diff --git a/test.py b/test.py
index 788ceff..ae18f62 100644
--- a/test.py
+++ b/test.py
@@ -1,5 +1,4 @@
""".#test.py main module."""
-import json
import time
import httpx
from whereismysock import all
@@ -7,21 +6,18 @@
import book
import protocol
+test = False
+
HOST: str = 'sytev070'
-PORT: str = '9000'
+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():
"""Test function to print websocket."""
messages = all(f"ws://{HOST}:{PORT}/information")
@@ -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()
--
Gitblit v1.10.0