From 592350f4a5544ea7e975cabdd0a37e77589559aa Mon Sep 17 00:00:00 2001 From: Petter Reinholdtsen Date: Fri, 5 Mar 2021 18:43:56 +0100 Subject: [PATCH] Add client demonstrating how Kraken websocket give negative spread. After a fairly short time, the code will produce an error, for example looking like this: error: Improbable negative spread lastupdate=1614966172.542893 ask=(Decimal('47973.30000'), Decimal('0.10000000')) bid=(Decimal('47975.10000'), Decimal('1.00000000')) --- client.py | 98 ++++++++++++++++++++++++ kraken_wsclient_py/kraken_wsclient_py.py | 3 +- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 client.py diff --git a/client.py b/client.py new file mode 100644 index 0000000..bf80785 --- /dev/null +++ b/client.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +import simplejson + +from decimal import Decimal +from operator import neg +from sortedcontainers.sorteddict import SortedDict + +from kraken_wsclient_py import kraken_wsclient_py as client + +class Orderbook(object): + SIDE_ASK = "ask" + SIDE_BID = "bid" + def __init__(self): + self.ask = SortedDict() + self.bid = SortedDict(neg) + self.lastupdate = None + def copy(self): + o = Orderbook() + o.ask = self.ask.copy() + o.bid = self.bid.copy() + o.lastupdate = self.lastupdate + return o + def update(self, side, price, volume, timestamp = None): + table = { + self.SIDE_ASK : self.ask, + self.SIDE_BID : self.bid, + }[side] + table[price] = volume + if timestamp and (self.lastupdate is None or timestamp > self.lastupdate): + self.lastupdate = timestamp + def remove(self, side, price): + table = { + self.SIDE_ASK : self.ask, + self.SIDE_BID : self.bid, + }[side] + del table[price] + + +book = Orderbook() + +def my_handler(m): + global book + # Here you can do stuff with the messages + #print(m) + if list == type(m): + updates = list(m[1].keys()) + pair = ('XBT', 'USD') + #print("channel update:", updates, pair) + for update in updates: + if update in ('as', 'bs'): + o = Orderbook() + for side in ('as', 'bs'): + oside = { + 'as' : o.SIDE_ASK, + 'bs' : o.SIDE_BID, + }[side] + for e in m[1][side]: + o.update(oside, Decimal(e[0]), Decimal(e[1]), float(e[2])) + book = o + elif update in ('a', 'b'): + o = book + for side in ('a', 'b'): + oside = { + 'a' : book.SIDE_ASK, + 'b' : book.SIDE_BID, + }[side] + if side in m[1]: + for e in m[1][side]: + price = Decimal(e[0]) + if '0.00000000' == e[1]: + try: + book.remove(oside, price) + except KeyError as e: + raise ValueError('asked to remove non-existing %s order %s' % (oside, price)) + else: + volume = Decimal(e[1]) + book.update(oside, price, volume, float(e[2])) + askprice = book.ask.peekitem(0) + bidprice = book.bid.peekitem(0) + if askprice < bidprice: + print("error: Improbable negative spread lastupdate=%s ask=%s bid=%s" %(book.lastupdate, askprice, bidprice)) + exit(1) + return + + +my_client = client.WssClient() +my_client.start() + +# Sample public-data subscription: + +my_client.subscribe_public( + subscription = { + 'name': 'book' + }, + pair = ['XBT/USD'], + callback = my_handler +) diff --git a/kraken_wsclient_py/kraken_wsclient_py.py b/kraken_wsclient_py/kraken_wsclient_py.py index aca1f53..ecb71a2 100644 --- a/kraken_wsclient_py/kraken_wsclient_py.py +++ b/kraken_wsclient_py/kraken_wsclient_py.py @@ -3,6 +3,7 @@ import json import hmac import hashlib +from decimal import Decimal from autobahn.twisted.websocket import WebSocketClientFactory, \ WebSocketClientProtocol, \ connectWS @@ -30,7 +31,7 @@ def onConnect(self, response): def onMessage(self, payload, isBinary): if not isBinary: try: - payload_obj = json.loads(payload.decode('utf8')) + payload_obj = json.loads(payload.decode('utf8'), parse_float=Decimal) except ValueError: pass else: