Files
jesse-trading/jesse/store/state_orderbook.py

133 lines
4.4 KiB
Python

import numpy as np
import jesse.helpers as jh
from jesse.config import config
from jesse.libs import DynamicNumpyArray
from jesse.models import store_orderbook_into_db
class OrderbookState:
def __init__(self) -> None:
self.storage = {}
self.temp_storage = {}
def init_storage(self) -> None:
for c in config['app']['considering_candles']:
key = jh.key(c[0], c[1])
self.temp_storage[key] = {
'last_updated_timestamp': None,
'asks': [],
'bids': []
}
self.storage[key] = DynamicNumpyArray((60, 2, 50, 2), drop_at=60)
def format_orderbook(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
# trim prices
asks = _trim_orderbook_list(self.temp_storage[key]['asks'], ascending=True)
bids = _trim_orderbook_list(self.temp_storage[key]['bids'], ascending=False)
# fill empty values with NaN
asks = _fix_array_len(np.array(asks), 50)
bids = _fix_array_len(np.array(bids), 50)
return np.array([
asks, bids
])
def add_orderbook(self, exchange: str, symbol: str, asks: list, bids: list) -> None:
key = jh.key(exchange, symbol)
self.temp_storage[key]['asks'] = asks
self.temp_storage[key]['bids'] = bids
# generate new numpy formatted orderbook if it is
# either the first time, or that it has passed
# 1000 milliseconds since the last time
if self.temp_storage[key]['last_updated_timestamp'] is None or jh.now_to_timestamp() - self.temp_storage[key][
'last_updated_timestamp'] >= 1000:
self.temp_storage[key]['last_updated_timestamp'] = jh.now_to_timestamp()
formatted_orderbook = self.format_orderbook(exchange, symbol)
if jh.is_collecting_data():
store_orderbook_into_db(exchange, symbol, formatted_orderbook)
else:
self.storage[key].append(formatted_orderbook)
def get_current_orderbook(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
return self.storage[key][-1]
def get_current_asks(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
return self.storage[key][-1][0]
def get_best_ask(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
return self.storage[key][-1][0][0]
def get_current_bids(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
return self.storage[key][-1][1]
def get_best_bid(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
return self.storage[key][-1][1][0]
def get_orderbooks(self, exchange: str, symbol: str) -> np.ndarray:
key = jh.key(exchange, symbol)
return self.storage[key][:]
def _trim_orderbook_list(arr: list, ascending: bool, limit_len: int = 50) -> list:
"""trims prices up to 4 digits precision"""
first_price = arr[0][0]
if first_price < 0.1:
unit = 1e-5
elif first_price < 1:
unit = 1e-4
elif first_price < 10:
unit = 1e-3
elif first_price < 100:
unit = 1e-2
elif first_price < 1000:
unit = 1e-1
elif first_price < 10000:
unit = 1
else:
unit = 10
trimmed_price = jh.orderbook_trim_price(first_price, ascending, unit)
temp_qty = 0
trimmed_arr = []
for a in arr:
if len(trimmed_arr) == limit_len:
break
if (ascending and a[0] > trimmed_price) or (not ascending and a[0] < trimmed_price):
# add previous record
trimmed_arr.append([
trimmed_price, temp_qty
])
# update temp values
temp_qty = a[1]
trimmed_price = jh.orderbook_trim_price(a[0], ascending, unit)
else:
temp_qty += a[1]
return trimmed_arr
def _fix_array_len(arr: np.ndarray, target_len: int) -> np.ndarray:
"""make sure bids and asks have the same length"""
missing_len = target_len - len(arr)
if missing_len < 0:
raise ValueError(f"len cannot be smaller than array's length. {target_len} sent, while array has {len(arr)} items")
if not missing_len:
return arr
return np.array([*arr, *np.full((missing_len, *arr.shape[1:]), np.nan)])