mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 16:28:06 +00:00
166 lines
5.7 KiB
Python
166 lines
5.7 KiB
Python
import json
|
|
from websockets.sync.client import connect
|
|
from websockets.client import connect as connect_async
|
|
from lighter.configuration import Configuration
|
|
|
|
class WsClient:
|
|
def __init__(
|
|
self,
|
|
host=None,
|
|
path="/stream",
|
|
order_book_ids=[],
|
|
account_ids=[],
|
|
on_order_book_update=print,
|
|
on_account_update=print,
|
|
):
|
|
if host is None:
|
|
host = Configuration.get_default().host.replace("https://", "")
|
|
|
|
self.base_url = f"wss://{host}{path}"
|
|
|
|
self.subscriptions = {
|
|
"order_books": order_book_ids,
|
|
"accounts": account_ids,
|
|
}
|
|
|
|
if len(order_book_ids) == 0 and len(account_ids) == 0:
|
|
raise Exception("No subscriptions provided.")
|
|
|
|
self.order_book_states = {}
|
|
self.account_states = {}
|
|
|
|
self.on_order_book_update = on_order_book_update
|
|
self.on_account_update = on_account_update
|
|
|
|
self.ws = None
|
|
|
|
def on_message(self, ws, message):
|
|
if isinstance(message, str):
|
|
message = json.loads(message)
|
|
|
|
message_type = message.get("type")
|
|
|
|
if message_type == "connected":
|
|
self.handle_connected(ws)
|
|
elif message_type == "subscribed/order_book":
|
|
self.handle_subscribed_order_book(message)
|
|
elif message_type == "update/order_book":
|
|
self.handle_update_order_book(message)
|
|
elif message_type == "subscribed/account_all":
|
|
self.handle_subscribed_account(message)
|
|
elif message_type == "update/account_all":
|
|
self.handle_update_account(message)
|
|
elif message_type == "ping":
|
|
# Respond to ping with pong
|
|
ws.send(json.dumps({"type": "pong"}))
|
|
else:
|
|
self.handle_unhandled_message(message)
|
|
|
|
async def on_message_async(self, ws, message):
|
|
message = json.loads(message)
|
|
message_type = message.get("type")
|
|
|
|
if message_type == "connected":
|
|
await self.handle_connected_async(ws)
|
|
elif message_type == "ping":
|
|
# Respond to ping with pong
|
|
await ws.send(json.dumps({"type": "pong"}))
|
|
else:
|
|
self.on_message(ws, message)
|
|
|
|
def handle_connected(self, ws):
|
|
for market_id in self.subscriptions["order_books"]:
|
|
ws.send(
|
|
json.dumps({"type": "subscribe", "channel": f"order_book/{market_id}"})
|
|
)
|
|
for account_id in self.subscriptions["accounts"]:
|
|
ws.send(
|
|
json.dumps(
|
|
{"type": "subscribe", "channel": f"account_all/{account_id}"}
|
|
)
|
|
)
|
|
|
|
async def handle_connected_async(self, ws):
|
|
for market_id in self.subscriptions["order_books"]:
|
|
await ws.send(
|
|
json.dumps({"type": "subscribe", "channel": f"order_book/{market_id}"})
|
|
)
|
|
for account_id in self.subscriptions["accounts"]:
|
|
await ws.send(
|
|
json.dumps(
|
|
{"type": "subscribe", "channel": f"account_all/{account_id}"}
|
|
)
|
|
)
|
|
|
|
def handle_subscribed_order_book(self, message):
|
|
market_id = message["channel"].split(":")[1]
|
|
self.order_book_states[market_id] = message["order_book"]
|
|
if self.on_order_book_update:
|
|
self.on_order_book_update(market_id, self.order_book_states[market_id])
|
|
|
|
def handle_update_order_book(self, message):
|
|
market_id = message["channel"].split(":")[1]
|
|
self.update_order_book_state(market_id, message["order_book"])
|
|
if self.on_order_book_update:
|
|
self.on_order_book_update(market_id, self.order_book_states[market_id])
|
|
|
|
def update_order_book_state(self, market_id, order_book):
|
|
self.update_orders(
|
|
order_book["asks"], self.order_book_states[market_id]["asks"]
|
|
)
|
|
self.update_orders(
|
|
order_book["bids"], self.order_book_states[market_id]["bids"]
|
|
)
|
|
|
|
def update_orders(self, new_orders, existing_orders):
|
|
for new_order in new_orders:
|
|
is_new_order = True
|
|
for existing_order in existing_orders:
|
|
if new_order["price"] == existing_order["price"]:
|
|
is_new_order = False
|
|
existing_order["size"] = new_order["size"]
|
|
if float(new_order["size"]) == 0:
|
|
existing_orders.remove(existing_order)
|
|
break
|
|
if is_new_order:
|
|
existing_orders.append(new_order)
|
|
|
|
existing_orders = [
|
|
order for order in existing_orders if float(order["size"]) > 0
|
|
]
|
|
|
|
def handle_subscribed_account(self, message):
|
|
account_id = message["channel"].split(":")[1]
|
|
self.account_states[account_id] = message
|
|
if self.on_account_update:
|
|
self.on_account_update(account_id, self.account_states[account_id])
|
|
|
|
def handle_update_account(self, message):
|
|
account_id = message["channel"].split(":")[1]
|
|
self.account_states[account_id] = message
|
|
if self.on_account_update:
|
|
self.on_account_update(account_id, self.account_states[account_id])
|
|
|
|
def handle_unhandled_message(self, message):
|
|
raise Exception(f"Unhandled message: {message}")
|
|
|
|
def on_error(self, ws, error):
|
|
raise Exception(f"Error: {error}")
|
|
|
|
def on_close(self, ws, close_status_code, close_msg):
|
|
raise Exception(f"Closed: {close_status_code} {close_msg}")
|
|
|
|
def run(self):
|
|
ws = connect(self.base_url)
|
|
self.ws = ws
|
|
|
|
for message in ws:
|
|
self.on_message(ws, message)
|
|
|
|
async def run_async(self):
|
|
ws = await connect_async(self.base_url)
|
|
self.ws = ws
|
|
|
|
async for message in ws:
|
|
await self.on_message_async(ws, message)
|