-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from olned/develop
Deribit order book
- Loading branch information
Showing
18 changed files
with
279 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#!/usr/bin/env python | ||
import asyncio | ||
import json | ||
import logging | ||
from ssc2ce import Deribit | ||
from ssc2ce.deribit.l2_book import L2Book | ||
|
||
conn = Deribit() | ||
|
||
pending = {} | ||
books = {} | ||
|
||
logging.basicConfig(format='%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s', level=logging.INFO) | ||
logger = logging.getLogger("deribit-book") | ||
|
||
|
||
async def handle_instruments(data: dict): | ||
request_id = data["id"] | ||
del pending[request_id] | ||
print(json.dumps(data)) | ||
if not pending: | ||
await subscribe_books(list(books.keys())) | ||
|
||
|
||
async def handle_currencies(data: dict): | ||
for currency in data["result"]: | ||
symbol = currency["currency"] | ||
instrument = symbol+"-PERPETUAL" | ||
book = L2Book(instrument) | ||
book.top_bid = [0., 0.] | ||
book.top_ask = [0., 0.] | ||
books[instrument] = book | ||
request_id = await conn.get_instruments(symbol, kind="future", callback=handle_instruments) | ||
pending[request_id] = symbol | ||
|
||
|
||
async def get_currencies(): | ||
await conn.get_currencies(handle_currencies) | ||
|
||
|
||
async def subscribe_books(instruments: list): | ||
await conn.send_public(request={ | ||
"method": "public/subscribe", | ||
"params": { | ||
"channels": [f"book.{i}.raw" for i in instruments] | ||
} | ||
}) | ||
|
||
|
||
async def handle_subscription(data): | ||
method = data.get("method") | ||
if method and method == "subscription": | ||
params = data["params"] | ||
channel = params["channel"] | ||
if channel.startswith("book."): | ||
params_data = params["data"] | ||
instrument = params_data["instrument_name"] | ||
book: L2Book = books[instrument] | ||
if "prev_change_id" in params_data: | ||
book.handle_update(params_data) | ||
else: | ||
book.handle_snapshot(params_data) | ||
|
||
if book.top_ask[0] != book.asks[0][0] or book.top_bid[0] != book.bids[0][0]: | ||
book.top_ask = book.asks[0].copy() | ||
book.top_bid = book.bids[0].copy() | ||
print(f"{instrument} bid:{book.top_bid[0]} ask:{book.top_ask[0]}") | ||
else: | ||
print("Unknown channel", json.dumps(data)) | ||
|
||
|
||
conn.on_connect_ws = get_currencies | ||
conn.method_routes += [("subscription", handle_subscription)] | ||
|
||
loop = asyncio.get_event_loop() | ||
|
||
try: | ||
loop.run_until_complete(conn.run_receiver()) | ||
except KeyboardInterrupt: | ||
print("Application closed by KeyboardInterrupt.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ aiohttp | |
python-dotenv | ||
twine | ||
wheel | ||
sortedcontainers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .bitfinex import Bitfinex |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .auth_type import AuthType |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from sortedcontainers import SortedKeyList | ||
from abc import abstractmethod, ABC | ||
from .l2_book_side import L2BookSide, SortedKeyList | ||
|
||
|
||
class AbstractL2Book(ABC): | ||
""" | ||
""" | ||
|
||
def __init__(self, instrument: str): | ||
""" | ||
:param instrument: | ||
""" | ||
self.instrument = instrument | ||
self._bids = L2BookSide(is_bids=True) | ||
self._asks = L2BookSide(is_bids=False) | ||
|
||
@abstractmethod | ||
def handle_snapshot(self, message: dict) -> None: | ||
""" | ||
:param message: | ||
:return: | ||
""" | ||
pass | ||
|
||
@abstractmethod | ||
def handle_update(self, message: dict) -> None: | ||
""" | ||
:param message: | ||
:return: | ||
""" | ||
pass | ||
|
||
@property | ||
def bids(self): | ||
return self._bids.data | ||
|
||
@property | ||
def asks(self): | ||
return self._asks.data |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
class Ssc2ceError(Exception): | ||
"""Base class for errors.""" | ||
|
||
|
||
class BrokenOrderBook(Exception): | ||
def __init__(self, instrument, prev_change_id, change_id): | ||
self.instrument = instrument | ||
self.message = f"instrument:{self.instrument} expected:{change_id} != received:{prev_change_id}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from sortedcontainers import SortedKeyList | ||
|
||
VERY_SMALL_NUMBER = 1e-11 | ||
|
||
|
||
class L2BookSide: | ||
|
||
def __init__(self, is_bids: bool): | ||
if is_bids: | ||
self.data = SortedKeyList(key=lambda val: -val[0]) | ||
else: | ||
self.data = SortedKeyList(key=lambda val: val[0]) | ||
self.is_bids = is_bids | ||
self.time = None | ||
self.changes = list() | ||
|
||
def fill(self, source): | ||
self.data.clear() | ||
for item in source: | ||
self.add(item) | ||
|
||
def add(self, item): | ||
price = float(item[0]) | ||
size = float(item[1]) | ||
self.changes.append([price, size, size]) | ||
self.data.add([price, size]) | ||
|
||
def update(self, price: float, size: float): | ||
key = -price if self.is_bids else price | ||
i = self.data.bisect_key_left(key) | ||
|
||
if 0 <= i < len(self.data): | ||
value = self.data[i] | ||
else: | ||
if size <= VERY_SMALL_NUMBER: | ||
self.changes.append([price, size, 0.0]) | ||
return False | ||
|
||
self.data.add([price, size]) | ||
self.changes.append([price, size, size]) | ||
return True | ||
|
||
if size <= VERY_SMALL_NUMBER: | ||
if value[0] == price: | ||
old_size = self.data[i][1] | ||
self.data.discard(value) | ||
self.changes.append([price, size, -old_size]) | ||
return True | ||
else: | ||
self.changes.append([price, size, 0.0]) | ||
return False | ||
|
||
if value[0] == price: | ||
old_size = self.data[i][1] | ||
self.data[i][1] = size | ||
self.changes.append([price, size, size - old_size]) | ||
else: | ||
self.data.add([price, size]) | ||
self.changes.append([price, size, size]) | ||
return True | ||
|
||
def delete(self, price: float): | ||
return self.update(price, 0.0) |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .deribit import Deribit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import logging | ||
from ssc2ce.common.abstract_l2_book import AbstractL2Book | ||
from collections import deque | ||
|
||
from ssc2ce.common.exceptions import BrokenOrderBook | ||
|
||
|
||
class L2Book(AbstractL2Book): | ||
change_id = None | ||
timestamp = None | ||
logger = logging.getLogger(__name__) | ||
|
||
def __init__(self, instrument: str): | ||
""" | ||
:param instrument: | ||
""" | ||
AbstractL2Book.__init__(self, instrument) | ||
|
||
def handle_snapshot(self, message: dict) -> None: | ||
""" | ||
:param message: | ||
:return: | ||
""" | ||
self.asks.clear() | ||
self.bids.clear() | ||
|
||
self.change_id = message["change_id"] | ||
self.timestamp = message["timestamp"] | ||
for i in message['bids']: | ||
self.bids.add([i[1], i[2]]) | ||
|
||
for i in message['asks']: | ||
self.asks.add([i[1], i[2]]) | ||
|
||
def handle_update(self, message: dict) -> None: | ||
""" | ||
:param message: | ||
:return: | ||
""" | ||
prev_change_id = message["prev_change_id"] | ||
if prev_change_id != self.change_id: | ||
raise BrokenOrderBook(self.instrument, prev_change_id, self.change_id) | ||
|
||
self.change_id = message["change_id"] | ||
self.timestamp = message["timestamp"] | ||
for change in message['bids']: | ||
if change[0] == 'new': | ||
self._bids.add(change[1:]) | ||
elif change[0] == 'delete': | ||
self._bids.delete(change[1]) | ||
else: | ||
self._bids.update(price=change[1], size=change[2]) | ||
|
||
for change in message['asks']: | ||
if change[0] == 'new': | ||
self._asks.add(change[1:]) | ||
elif change[0] == 'delete': | ||
self._asks.delete(change[1]) | ||
else: | ||
self._asks.update(price=change[1], size=change[2]) | ||
|
||
|
||
def create_l2_order_book(instrument: str) -> AbstractL2Book: | ||
return L2Book(instrument) |
This file was deleted.
Oops, something went wrong.