Files
ritmex-bot/docs/lighter/lighter-python-main/lighter/ws_client.py
T

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)