# -*- coding: utf-8 -*- # PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: # https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code import ccxt.async_support from ccxt.async_support.base.ws.cache import ArrayCache, ArrayCacheBySymbolById, ArrayCacheByTimestamp from ccxt.base.types import Any, Bool, Int, Market, Num, Order, OrderBook, OrderRequest, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade from ccxt.async_support.base.ws.client import Client from typing import List class hyperliquid(ccxt.async_support.hyperliquid): def describe(self) -> Any: return self.deep_extend(super(hyperliquid, self).describe(), { 'has': { 'ws': True, 'cancelOrderWs': True, 'cancelOrdersWs': True, 'createOrderWs': True, 'createOrdersWs': True, 'editOrderWs': True, 'watchBalance': False, 'watchMyTrades': True, 'watchOHLCV': True, 'watchOrderBook': True, 'watchOrders': True, 'watchTicker': True, 'watchTickers': True, 'watchTrades': True, 'watchTradesForSymbols': False, 'watchPosition': False, }, 'urls': { 'api': { 'ws': { 'public': 'wss://api.hyperliquid.xyz/ws', }, }, 'test': { 'ws': { 'public': 'wss://api.hyperliquid-testnet.xyz/ws', }, }, }, 'options': { }, 'streaming': { 'ping': self.ping, 'keepAlive': 20000, }, 'exceptions': { 'ws': { 'exact': { }, }, }, }) async def create_orders_ws(self, orders: List[OrderRequest], params={}): """ create a list of trade orders using WebSocket post request https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order :param Array orders: list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: an `order structure ` """ await self.load_markets() url = self.urls['api']['ws']['public'] ordersRequest = self.createOrdersRequest(orders, params) wrapped = self.wrap_as_post_action(ordersRequest) request = self.safe_dict(wrapped, 'request', {}) requestId = self.safe_string(wrapped, 'requestId') response = await self.watch(url, requestId, request, requestId) responseOjb = self.safe_dict(response, 'response', {}) data = self.safe_dict(responseOjb, 'data', {}) statuses = self.safe_list(data, 'statuses', []) return self.parse_orders(statuses, None) async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): """ create a trade order using WebSocket post request https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str side: 'buy' or 'sell' :param float amount: how much of currency you want to trade in units of base currency :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo' :param bool [params.postOnly]: True or False whether the order is post-only :param bool [params.reduceOnly]: True or False whether the order is reduce-only :param float [params.triggerPrice]: The price at which a trigger order is triggered at :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef) :param str [params.slippage]: the slippage for market order :param str [params.vaultAddress]: the vault address for order :returns dict: an `order structure ` """ await self.load_markets() order, globalParams = self.parseCreateEditOrderArgs(None, symbol, type, side, amount, price, params) orders = await self.create_orders_ws([order], globalParams) ordersLength = len(orders) if ordersLength == 0: # not sure why but it is happening sometimes return self.safe_order({}) parsedOrder = orders[0] return parsedOrder async def edit_order_ws(self, id: str, symbol: str, type: str, side: str, amount: Num = None, price: Num = None, params={}): """ edit a trade order https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#modify-multiple-orders :param str id: cancel order id :param str symbol: unified symbol of the market to create an order in :param str type: 'market' or 'limit' :param str side: 'buy' or 'sell' :param float amount: how much of currency you want to trade in units of base currency :param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.timeInForce]: 'Gtc', 'Ioc', 'Alo' :param bool [params.postOnly]: True or False whether the order is post-only :param bool [params.reduceOnly]: True or False whether the order is reduce-only :param float [params.triggerPrice]: The price at which a trigger order is triggered at :param str [params.clientOrderId]: client order id,(optional 128 bit hex string e.g. 0x1234567890abcdef1234567890abcdef) :param str [params.vaultAddress]: the vault address for order :returns dict: an `order structure ` """ await self.load_markets() market = self.market(symbol) url = self.urls['api']['ws']['public'] order, globalParams = self.parseCreateEditOrderArgs(id, symbol, type, side, amount, price, params) postRequest = self.editOrdersRequest([order], globalParams) wrapped = self.wrap_as_post_action(postRequest) request = self.safe_dict(wrapped, 'request', {}) requestId = self.safe_string(wrapped, 'requestId') response = await self.watch(url, requestId, request, requestId) # response is the same self.edit_order responseObject = self.safe_dict(response, 'response', {}) dataObject = self.safe_dict(responseObject, 'data', {}) statuses = self.safe_list(dataObject, 'statuses', []) first = self.safe_dict(statuses, 0, {}) parsedOrder = self.parse_order(first, market) return parsedOrder async def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}): """ cancel multiple orders using WebSocket post request https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/post-requests :param str[] ids: list of order ids to cancel :param str symbol: unified symbol of the market the orders were made in :param dict [params]: extra parameters specific to the exchange API endpoint :param str[] [params.clientOrderId]: list of client order ids to cancel instead of order ids :param str [params.vaultAddress]: the vault address for order cancellation :returns dict[]: a list of `order structures ` """ self.check_required_credentials() await self.load_markets() request = self.cancelOrdersRequest(ids, symbol, params) url = self.urls['api']['ws']['public'] wrapped = self.wrap_as_post_action(request) wsRequest = self.safe_dict(wrapped, 'request', {}) requestId = self.safe_string(wrapped, 'requestId') response = await self.watch(url, requestId, wsRequest, requestId) responseObj = self.safe_dict(response, 'response', {}) data = self.safe_dict(responseObj, 'data', {}) statuses = self.safe_list(data, 'statuses', []) orders = [] for i in range(0, len(statuses)): status = statuses[i] orders.append(self.safe_order({ 'info': status, 'status': status, })) return orders async def cancel_order_ws(self, id: str, symbol: Str = None, params={}): """ cancel a single order using WebSocket post request https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/post-requests :param str id: order id to cancel :param str symbol: unified symbol of the market the order was made in :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.clientOrderId]: client order id to cancel instead of order id :param str [params.vaultAddress]: the vault address for order cancellation :returns dict: an `order structure ` """ orders = await self.cancel_orders_ws([id], symbol, params) return self.safe_dict(orders, 0) async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: """ watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified symbol of the market to fetch the order book for :param int [limit]: the maximum amount of order book entries to return :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: A dictionary of `order book structures ` indexed by market symbols """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] messageHash = 'orderbook:' + symbol url = self.urls['api']['ws']['public'] request: dict = { 'method': 'subscribe', 'subscription': { 'type': 'l2Book', 'coin': market['baseName'] if market['swap'] else market['id'], }, } message = self.extend(request, params) orderbook = await self.watch(url, messageHash, message, messageHash) return orderbook.limit() async def un_watch_order_book(self, symbol: str, params={}) -> Any: """ unWatches information on open orders with bid(buy) and ask(sell) prices, volumes and other data https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified symbol of the market to fetch the order book for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: A dictionary of `order book structures ` indexed by market symbols """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] subMessageHash = 'orderbook:' + symbol messageHash = 'unsubscribe:' + subMessageHash url = self.urls['api']['ws']['public'] id = str(self.nonce()) request: dict = { 'id': id, 'method': 'unsubscribe', 'subscription': { 'type': 'l2Book', 'coin': market['baseName'] if market['swap'] else market['id'], }, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) def handle_order_book(self, client, message): # # { # "channel": "l2Book", # "data": { # "coin": "BTC", # "time": 1710131872708, # "levels": [ # [ # { # "px": "68674.0", # "sz": "0.97139", # "n": 4 # } # ], # [ # { # "px": "68675.0", # "sz": "0.04396", # "n": 1 # } # ] # ] # } # } # entry = self.safe_dict(message, 'data', {}) coin = self.safe_string(entry, 'coin') marketId = self.coinToMarketId(coin) market = self.market(marketId) symbol = market['symbol'] rawData = self.safe_list(entry, 'levels', []) data: dict = { 'bids': self.safe_list(rawData, 0, []), 'asks': self.safe_list(rawData, 1, []), } timestamp = self.safe_integer(entry, 'time') snapshot = self.parse_order_book(data, symbol, timestamp, 'bids', 'asks', 'px', 'sz') if not (symbol in self.orderbooks): ob = self.order_book(snapshot) self.orderbooks[symbol] = ob orderbook = self.orderbooks[symbol] orderbook.reset(snapshot) messageHash = 'orderbook:' + symbol client.resolve(orderbook, messageHash) async def watch_ticker(self, symbol: str, params={}) -> Ticker: """ https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market :param str symbol: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ market = self.market(symbol) symbol = market['symbol'] tickers = await self.watch_tickers([symbol], params) return tickers[symbol] async def watch_tickers(self, symbols: Strings = None, params={}) -> Tickers: """ watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str[] symbols: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, True) messageHash = 'tickers' url = self.urls['api']['ws']['public'] request: dict = { 'method': 'subscribe', 'subscription': { 'type': 'webData2', # allMids 'user': '0x0000000000000000000000000000000000000000', }, } tickers = await self.watch(url, messageHash, self.extend(request, params), messageHash) if self.newUpdates: return self.filter_by_array_tickers(tickers, 'symbol', symbols) return self.tickers async def un_watch_tickers(self, symbols: Strings = None, params={}) -> Any: """ unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str[] symbols: unified symbol of the market to fetch the ticker for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict: a `ticker structure ` """ await self.load_markets() symbols = self.market_symbols(symbols, None, True) subMessageHash = 'tickers' messageHash = 'unsubscribe:' + subMessageHash url = self.urls['api']['ws']['public'] request: dict = { 'method': 'unsubscribe', 'subscription': { 'type': 'webData2', # allMids 'user': '0x0000000000000000000000000000000000000000', }, } return await self.watch(url, messageHash, self.extend(request, params), messageHash) async def watch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ watches information on multiple trades made by the user https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified market symbol of the market orders were made in :param int [since]: the earliest time in ms to fetch orders for :param int [limit]: the maximum number of order structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.user]: user address, will default to self.walletAddress if not provided :returns dict[]: a list of `order structures ` """ userAddress = None userAddress, params = self.handlePublicAddress('watchMyTrades', params) await self.load_markets() messageHash = 'myTrades' if symbol is not None: symbol = self.symbol(symbol) messageHash += ':' + symbol url = self.urls['api']['ws']['public'] request: dict = { 'method': 'subscribe', 'subscription': { 'type': 'userFills', 'user': userAddress, }, } message = self.extend(request, params) trades = await self.watch(url, messageHash, message, messageHash) if self.newUpdates: limit = trades.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True) def handle_ws_tickers(self, client: Client, message): # # { # "channel": "webData2", # "data": { # "meta": { # "universe": [ # { # "szDecimals": 5, # "name": "BTC", # "maxLeverage": 50, # "onlyIsolated": False # }, # ... # ], # }, # "assetCtxs": [ # { # "funding": "0.00003005", # "openInterest": "2311.50778", # "prevDayPx": "63475.0", # "dayNtlVlm": "468043329.64289033", # "premium": "0.00094264", # "oraclePx": "64712.0", # "markPx": "64774.0", # "midPx": "64773.5", # "impactPxs": [ # "64773.0", # "64774.0" # ] # }, # ... # ], # "spotAssetCtxs": [ # { # "prevDayPx": "0.20937", # "dayNtlVlm": "11188888.61984999", # "markPx": "0.19722", # "midPx": "0.197145", # "circulatingSupply": "598760557.12072003", # "coin": "PURR/USDC" # }, # ... # ], # } # } # # spot rawData = self.safe_dict(message, 'data', {}) spotAssets = self.safe_list(rawData, 'spotAssetCtxs', []) parsedTickers = [] for i in range(0, len(spotAssets)): assetObject = spotAssets[i] marketId = self.safe_string(assetObject, 'coin') market = self.safe_market(marketId, None, None, 'spot') symbol = market['symbol'] ticker = self.parse_ws_ticker(assetObject, market) parsedTickers.append(ticker) self.tickers[symbol] = ticker # perpetuals meta = self.safe_dict(rawData, 'meta', {}) universe = self.safe_list(meta, 'universe', []) assetCtxs = self.safe_list(rawData, 'assetCtxs', []) for i in range(0, len(universe)): data = self.extend( self.safe_dict(universe, i, {}), self.safe_dict(assetCtxs, i, {}) ) id = data['name'] + '/USDC:USDC' market = self.safe_market(id, None, None, 'swap') symbol = market['symbol'] ticker = self.parse_ws_ticker(data, market) self.tickers[symbol] = ticker parsedTickers.append(ticker) tickers = self.index_by(parsedTickers, 'symbol') client.resolve(tickers, 'tickers') def parse_ws_ticker(self, rawTicker, market: Market = None) -> Ticker: return self.parse_ticker(rawTicker, market) def handle_my_trades(self, client: Client, message): # # { # "channel": "userFills", # "data": { # "isSnapshot": True, # "user": "0x15f43d1f2dee81424afd891943262aa90f22cc2a", # "fills": [ # { # "coin": "BTC", # "px": "72528.0", # "sz": "0.11693", # "side": "A", # "time": 1710208712815, # "startPosition": "0.11693", # "dir": "Close Long", # "closedPnl": "-0.81851", # "hash": "0xc5adaf35f8402750c218040b0a7bc301130051521273b6f398b3caad3e1f3f5f", # "oid": 7484888874, # "crossed": True, # "fee": "2.968244", # "liquidationMarkPx": null, # "tid": 567547935839686, # "cloid": null # } # ] # } # } # entry = self.safe_dict(message, 'data', {}) if self.myTrades is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) self.myTrades = ArrayCacheBySymbolById(limit) trades = self.myTrades symbols: dict = {} data = self.safe_list(entry, 'fills', []) dataLength = len(data) if dataLength == 0: return for i in range(0, len(data)): rawTrade = data[i] parsed = self.parse_ws_trade(rawTrade) symbol = parsed['symbol'] symbols[symbol] = True trades.append(parsed) keys = list(symbols.keys()) for i in range(0, len(keys)): currentMessageHash = 'myTrades:' + keys[i] client.resolve(trades, currentMessageHash) # non-symbol specific messageHash = 'myTrades' client.resolve(trades, messageHash) async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: # s # @method # @name hyperliquid#watchTrades # @description watches information on multiple trades made in a market # @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions # @param {string} symbol unified market symbol of the market trades were made in # @param {int} [since] the earliest time in ms to fetch trades for # @param {int} [limit] the maximum number of trade structures to retrieve # @param {object} [params] extra parameters specific to the exchange API endpoint # @returns {object[]} a list of `trade structures ` # await self.load_markets() market = self.market(symbol) symbol = market['symbol'] messageHash = 'trade:' + symbol url = self.urls['api']['ws']['public'] request: dict = { 'method': 'subscribe', 'subscription': { 'type': 'trades', 'coin': market['baseName'] if market['swap'] else market['id'], }, } message = self.extend(request, params) trades = await self.watch(url, messageHash, message, messageHash) if self.newUpdates: limit = trades.getLimit(symbol, limit) return self.filter_by_since_limit(trades, since, limit, 'timestamp', True) async def un_watch_trades(self, symbol: str, params={}) -> Any: """ unWatches information on multiple trades made in a market https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified market symbol of the market trades were made in :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] subMessageHash = 'trade:' + symbol messageHash = 'unsubscribe:' + subMessageHash url = self.urls['api']['ws']['public'] request: dict = { 'method': 'unsubscribe', 'subscription': { 'type': 'trades', 'coin': market['baseName'] if market['swap'] else market['id'], }, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) def handle_trades(self, client: Client, message): # # { # "channel": "trades", # "data": [ # { # "coin": "BTC", # "side": "A", # "px": "68517.0", # "sz": "0.005", # "time": 1710125266669, # "hash": "0xc872699f116e012186620407fc08a802015e0097c5cce74710697f7272e6e959", # "tid": 981894269203506 # } # ] # } # entry = self.safe_list(message, 'data', []) first = self.safe_dict(entry, 0, {}) coin = self.safe_string(first, 'coin') marketId = self.coinToMarketId(coin) market = self.market(marketId) symbol = market['symbol'] if not (symbol in self.trades): limit = self.safe_integer(self.options, 'tradesLimit', 1000) stored = ArrayCache(limit) self.trades[symbol] = stored trades = self.trades[symbol] for i in range(0, len(entry)): data = self.safe_dict(entry, i) trade = self.parse_ws_trade(data) trades.append(trade) messageHash = 'trade:' + symbol client.resolve(trades, messageHash) def parse_ws_trade(self, trade: dict, market: Market = None) -> Trade: # # fetchMyTrades # # { # "coin": "BTC", # "px": "72528.0", # "sz": "0.11693", # "side": "A", # "time": 1710208712815, # "startPosition": "0.11693", # "dir": "Close Long", # "closedPnl": "-0.81851", # "hash": "0xc5adaf35f8402750c218040b0a7bc301130051521273b6f398b3caad3e1f3f5f", # "oid": 7484888874, # "crossed": True, # "fee": "2.968244", # "liquidationMarkPx": null, # "tid": 567547935839686, # "cloid": null # } # # fetchTrades # # { # "coin": "BTC", # "side": "A", # "px": "68517.0", # "sz": "0.005", # "time": 1710125266669, # "hash": "0xc872699f116e012186620407fc08a802015e0097c5cce74710697f7272e6e959", # "tid": 981894269203506 # } # timestamp = self.safe_integer(trade, 'time') price = self.safe_string(trade, 'px') amount = self.safe_string(trade, 'sz') coin = self.safe_string(trade, 'coin') marketId = self.coinToMarketId(coin) market = self.safe_market(marketId, None) symbol = market['symbol'] id = self.safe_string(trade, 'tid') side = self.safe_string(trade, 'side') if side is not None: side = 'sell' if (side == 'A') else 'buy' fee = self.safe_string(trade, 'fee') return self.safe_trade({ 'info': trade, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'symbol': symbol, 'id': id, 'order': self.safe_string(trade, 'oid'), 'type': None, 'side': side, 'takerOrMaker': None, 'price': price, 'amount': amount, 'cost': None, 'fee': {'cost': fee, 'currency': 'USDC'}, }, market) async def watch_ohlcv(self, symbol: str, timeframe: str = '1m', since: Int = None, limit: Int = None, params={}) -> List[list]: """ watches historical candlestick data containing the open, high, low, close price, and the volume of a market https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified symbol of the market to fetch OHLCV data for :param str timeframe: the length of time each candle represents :param int [since]: timestamp in ms of the earliest candle to fetch :param int [limit]: the maximum amount of candles to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :returns int[][]: A list of candles ordered, open, high, low, close, volume """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] url = self.urls['api']['ws']['public'] request: dict = { 'method': 'subscribe', 'subscription': { 'type': 'candle', 'coin': market['baseName'] if market['swap'] else market['id'], 'interval': timeframe, }, } messageHash = 'candles:' + timeframe + ':' + symbol message = self.extend(request, params) ohlcv = await self.watch(url, messageHash, message, messageHash) if self.newUpdates: limit = ohlcv.getLimit(symbol, limit) return self.filter_by_since_limit(ohlcv, since, limit, 0, True) async def un_watch_ohlcv(self, symbol: str, timeframe: str = '1m', params={}) -> Any: """ watches historical candlestick data containing the open, high, low, close price, and the volume of a market https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified symbol of the market to fetch OHLCV data for :param str timeframe: the length of time each candle represents :param dict [params]: extra parameters specific to the exchange API endpoint :returns int[][]: A list of candles ordered, open, high, low, close, volume """ await self.load_markets() market = self.market(symbol) symbol = market['symbol'] url = self.urls['api']['ws']['public'] request: dict = { 'method': 'unsubscribe', 'subscription': { 'type': 'candle', 'coin': market['baseName'] if market['swap'] else market['id'], 'interval': timeframe, }, } subMessageHash = 'candles:' + timeframe + ':' + symbol messagehash = 'unsubscribe:' + subMessageHash message = self.extend(request, params) return await self.watch(url, messagehash, message, messagehash) def handle_ohlcv(self, client: Client, message): # # { # channel: 'candle', # data: { # t: 1710146280000, # T: 1710146339999, # s: 'BTC', # i: '1m', # o: '71400.0', # c: '71411.0', # h: '71422.0', # l: '71389.0', # v: '1.20407', # n: 20 # } # } # data = self.safe_dict(message, 'data', {}) base = self.safe_string(data, 's') marketId = self.coinToMarketId(base) symbol = self.safe_symbol(marketId) timeframe = self.safe_string(data, 'i') if not (symbol in self.ohlcvs): self.ohlcvs[symbol] = {} if not (timeframe in self.ohlcvs[symbol]): limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) stored = ArrayCacheByTimestamp(limit) self.ohlcvs[symbol][timeframe] = stored ohlcv = self.ohlcvs[symbol][timeframe] parsed = self.parse_ohlcv(data) ohlcv.append(parsed) messageHash = 'candles:' + timeframe + ':' + symbol client.resolve(ohlcv, messageHash) def handle_ws_post(self, client: Client, message: dict): # { # channel: "post", # data: { # id: , # response: { # type: "info" | "action" | "error", # payload: {...} # } # } data = self.safe_dict(message, 'data') id = self.safe_string(data, 'id') response = self.safe_dict(data, 'response') payload = self.safe_dict(response, 'payload') client.resolve(payload, id) async def watch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]: """ watches information on multiple orders made by the user https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/websocket/subscriptions :param str symbol: unified market symbol of the market orders were made in :param int [since]: the earliest time in ms to fetch orders for :param int [limit]: the maximum number of order structures to retrieve :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.user]: user address, will default to self.walletAddress if not provided :returns dict[]: a list of `order structures ` """ await self.load_markets() userAddress = None userAddress, params = self.handlePublicAddress('watchOrders', params) market = None messageHash = 'order' if symbol is not None: market = self.market(symbol) symbol = market['symbol'] messageHash = messageHash + ':' + symbol url = self.urls['api']['ws']['public'] request: dict = { 'method': 'subscribe', 'subscription': { 'type': 'orderUpdates', 'user': userAddress, }, } message = self.extend(request, params) orders = await self.watch(url, messageHash, message, messageHash) if self.newUpdates: limit = orders.getLimit(symbol, limit) return self.filter_by_symbol_since_limit(orders, symbol, since, limit, True) def handle_order(self, client: Client, message): # # { # channel: 'orderUpdates', # data: [ # { # order: { # coin: 'BTC', # side: 'B', # limitPx: '30000.0', # sz: '0.001', # oid: 7456484275, # timestamp: 1710163596492, # origSz: '0.001' # }, # status: 'open', # statusTimestamp: 1710163596492 # } # ] # } # data = self.safe_list(message, 'data', []) if self.orders is None: limit = self.safe_integer(self.options, 'ordersLimit', 1000) self.orders = ArrayCacheBySymbolById(limit) dataLength = len(data) if dataLength == 0: return stored = self.orders messageHash = 'order' marketSymbols: dict = {} for i in range(0, len(data)): rawOrder = data[i] order = self.parse_order(rawOrder) stored.append(order) symbol = self.safe_string(order, 'symbol') marketSymbols[symbol] = True keys = list(marketSymbols.keys()) for i in range(0, len(keys)): symbol = keys[i] innerMessageHash = messageHash + ':' + symbol client.resolve(stored, innerMessageHash) client.resolve(stored, messageHash) def handle_error_message(self, client: Client, message) -> Bool: # # { # "channel": "post", # "data": { # "id": 1, # "response": { # "type": "action", # "payload": { # "status": "ok", # "response": { # "type": "order", # "data": { # "statuses": [ # { # "error": "Order price cannot be more than 80% away from the reference price" # } # ] # } # } # } # } # } # } # # { # "channel": "error", # "data": "Error parsing JSON into valid websocket request: {\"type\": \"allMids\"}" # } # channel = self.safe_string(message, 'channel', '') if channel == 'error': ret_msg = self.safe_string(message, 'data', '') errorMsg = self.id + ' ' + ret_msg client.reject(errorMsg) return True data = self.safe_dict(message, 'data', {}) id = self.safe_string(message, 'id') if id is None: id = self.safe_string(data, 'id') response = self.safe_dict(data, 'response', {}) payload = self.safe_dict(response, 'payload', {}) status = self.safe_string(payload, 'status') if status is not None and status != 'ok': errorMsg = self.id + ' ' + self.json(payload) client.reject(errorMsg, id) return True type = self.safe_string(payload, 'type') if type == 'error': error = self.id + ' ' + self.json(payload) client.reject(error, id) return True try: self.handle_errors(0, '', '', '', {}, self.json(payload), payload, {}, {}) except Exception as e: client.reject(e, id) return True return False def handle_order_book_unsubscription(self, client: Client, subscription: dict): # # "subscription":{ # "type":"l2Book", # "coin":"BTC", # "nSigFigs":5, # "mantissa":null # } # coin = self.safe_string(subscription, 'coin') marketId = self.coinToMarketId(coin) symbol = self.safe_symbol(marketId) subMessageHash = 'orderbook:' + symbol messageHash = 'unsubscribe:' + subMessageHash self.clean_unsubscription(client, subMessageHash, messageHash) if symbol in self.orderbooks: del self.orderbooks[symbol] def handle_trades_unsubscription(self, client: Client, subscription: dict): # coin = self.safe_string(subscription, 'coin') marketId = self.coinToMarketId(coin) symbol = self.safe_symbol(marketId) subMessageHash = 'trade:' + symbol messageHash = 'unsubscribe:' + subMessageHash self.clean_unsubscription(client, subMessageHash, messageHash) if symbol in self.trades: del self.trades[symbol] def handle_tickers_unsubscription(self, client: Client, subscription: dict): # subMessageHash = 'tickers' messageHash = 'unsubscribe:' + subMessageHash self.clean_unsubscription(client, subMessageHash, messageHash) symbols = list(self.tickers.keys()) for i in range(0, len(symbols)): del self.tickers[symbols[i]] def handle_ohlcv_unsubscription(self, client: Client, subscription: dict): coin = self.safe_string(subscription, 'coin') marketId = self.coinToMarketId(coin) symbol = self.safe_symbol(marketId) interval = self.safe_string(subscription, 'interval') timeframe = self.find_timeframe(interval) subMessageHash = 'candles:' + timeframe + ':' + symbol messageHash = 'unsubscribe:' + subMessageHash self.clean_unsubscription(client, subMessageHash, messageHash) if symbol in self.ohlcvs: if timeframe in self.ohlcvs[symbol]: del self.ohlcvs[symbol][timeframe] def handle_subscription_response(self, client: Client, message): # { # "channel":"subscriptionResponse", # "data":{ # "method":"unsubscribe", # "subscription":{ # "type":"l2Book", # "coin":"BTC", # "nSigFigs":5, # "mantissa":null # } # } # } # # { # "channel":"subscriptionResponse", # "data":{ # "method":"unsubscribe", # "subscription":{ # "type":"trades", # "coin":"PURR/USDC" # } # } # } # data = self.safe_dict(message, 'data', {}) method = self.safe_string(data, 'method') if method == 'unsubscribe': subscription = self.safe_dict(data, 'subscription', {}) type = self.safe_string(subscription, 'type') if type == 'l2Book': self.handle_order_book_unsubscription(client, subscription) elif type == 'trades': self.handle_trades_unsubscription(client, subscription) elif type == 'webData2': self.handle_tickers_unsubscription(client, subscription) elif type == 'candle': self.handle_ohlcv_unsubscription(client, subscription) def handle_message(self, client: Client, message): # # { # "channel":"subscriptionResponse", # "data":{ # "method":"unsubscribe", # "subscription":{ # "type":"l2Book", # "coin":"BTC", # "nSigFigs":5, # "mantissa":null # } # } # } # if self.handle_error_message(client, message): return topic = self.safe_string(message, 'channel', '') methods: dict = { 'pong': self.handle_pong, 'trades': self.handle_trades, 'l2Book': self.handle_order_book, 'candle': self.handle_ohlcv, 'orderUpdates': self.handle_order, 'userFills': self.handle_my_trades, 'webData2': self.handle_ws_tickers, 'post': self.handle_ws_post, 'subscriptionResponse': self.handle_subscription_response, } exacMethod = self.safe_value(methods, topic) if exacMethod is not None: exacMethod(client, message) return keys = list(methods.keys()) for i in range(0, len(keys)): key = keys[i] if topic.find(keys[i]) >= 0: method = methods[key] method(client, message) return def ping(self, client: Client): return { 'method': 'ping', } def handle_pong(self, client: Client, message): # # { # "channel": "pong" # } # client.lastPong = self.safe_integer(message, 'pong') return message def request_id(self) -> float: requestId = self.sum(self.safe_integer(self.options, 'requestId', 0), 1) self.options['requestId'] = requestId return requestId def wrap_as_post_action(self, request: dict) -> dict: requestId = self.request_id() return { 'requestId': requestId, 'request': { 'method': 'post', 'id': requestId, 'request': { 'type': 'action', 'payload': request, }, }, }