# -*- 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, ArrayCacheByTimestamp from ccxt.base.types import Any, Int, OrderBook, Trade from ccxt.async_support.base.ws.client import Client from typing import List from ccxt.base.errors import ExchangeError class dydx(ccxt.async_support.dydx): def describe(self) -> Any: return self.deep_extend(super(dydx, self).describe(), { 'has': { 'ws': True, 'watchBalance': False, 'watchTicker': False, 'watchTickers': False, 'watchTrades': True, 'watchOrderBook': True, 'watchOHLCV': True, }, 'urls': { 'test': { 'ws': 'wss://indexer.v4testnet.dydx.exchange/v4/ws', }, 'api': { 'ws': 'wss://indexer.dydx.trade/v4/ws', }, }, 'options': {}, 'streaming': {}, 'exceptions': {}, }) async def watch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]: """ get the list of most recent trades for a particular symbol https://docs.dydx.xyz/indexer-client/websockets#trades :param str symbol: unified symbol of the market to fetch trades for :param int [since]: timestamp in ms of the earliest trade to fetch :param int [limit]: the maximum amount of trades to fetch :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ await self.load_markets() url = self.urls['api']['ws'] market = self.market(symbol) messageHash = 'trade:' + market['symbol'] request: dict = { 'type': 'subscribe', 'channel': 'v4_trades', 'id': market['id'], } trades = await self.watch(url, messageHash, self.extend(request, params), 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: """ unsubscribes from the trades channel https://docs.dydx.xyz/indexer-client/websockets#trades :param str symbol: unified symbol of the market to fetch trades for :param dict [params]: extra parameters specific to the exchange API endpoint :returns dict[]: a list of `trade structures ` """ await self.load_markets() url = self.urls['api']['ws'] market = self.market(symbol) messageHash = 'trade:' + market['symbol'] request: dict = { 'type': 'unsubscribe', 'channel': 'v4_trades', 'id': market['id'], } return await self.watch(url, messageHash, self.extend(request, params), messageHash) def handle_trades(self, client, message): # # { # "type": "subscribed", # "connection_id": "9011edff-d8f7-47fc-bbc6-0c7b5ba7dfae", # "message_id": 3, # "channel": "v4_trades", # "id": "BTC-USD", # "contents": { # "trades": [ # { # "id": "02b6148d0000000200000005", # "side": "BUY", # "size": "0.024", # "price": "114581", # "type": "LIMIT", # "createdAt": "2025-08-04T00:42:07.119Z", # "createdAtHeight": "45487245" # } # ] # } # } # marketId = self.safe_string(message, 'id') market = self.safe_market(marketId) symbol = market['symbol'] content = self.safe_dict(message, 'contents') rawTrades = self.safe_list(content, 'trades', []) stored = self.safe_value(self.trades, symbol) if stored is None: limit = self.safe_integer(self.options, 'tradesLimit', 1000) stored = ArrayCache(limit) self.trades[symbol] = stored parsedTrades = self.parse_trades(rawTrades, market) for i in range(0, len(parsedTrades)): parsed = parsedTrades[i] stored.append(parsed) messageHash = 'trade' + ':' + symbol client.resolve(stored, messageHash) def parse_ws_trade(self, trade, market=None): # # { # "id": "02b6148d0000000200000003", # "side": "BUY", # "size": "0.024", # "price": "114581", # "type": "LIMIT", # "createdAt": "2025-08-04T00:42:07.118Z", # "createdAtHeight": "45487244" # } # timestamp = self.parse8601(self.safe_string(trade, 'createdAt')) return self.safe_trade({ 'id': self.safe_string(trade, 'id'), 'info': trade, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'symbol': self.safe_string(market, 'symbol'), 'order': None, 'type': self.safe_string_lower(trade, 'type'), 'side': self.safe_string_lower(trade, 'side'), 'takerOrMaker': None, 'price': self.safe_string(trade, 'price'), 'amount': self.safe_string(trade, 'size'), 'cost': None, 'fee': None, }, market) 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://docs.dydx.xyz/indexer-client/websockets#orders :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() url = self.urls['api']['ws'] market = self.market(symbol) messageHash = 'orderbook:' + market['symbol'] request: dict = { 'type': 'subscribe', 'channel': 'v4_orderbook', 'id': market['id'], } orderbook = await self.watch(url, messageHash, self.extend(request, params), 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://docs.dydx.xyz/indexer-client/websockets#orders :param str symbol: unified array of symbols :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() url = self.urls['api']['ws'] market = self.market(symbol) messageHash = 'orderbook:' + market['symbol'] request: dict = { 'type': 'unsubscribe', 'channel': 'v4_orderbook', 'id': market['id'], } return await self.watch(url, messageHash, self.extend(request, params), messageHash) def handle_order_book(self, client: Client, message): # # { # "type": "subscribed", # "connection_id": "7af140fb-b33d-4f0e-8f4c-30f16337b360", # "message_id": 1, # "channel": "v4_orderbook", # "id": "BTC-USD", # "contents": { # "bids": [ # { # "price": "114623", # "size": "0.1112" # } # ], # "asks": [ # { # "price": "114624", # "size": "0.0872" # } # ] # } # } # marketId = self.safe_string(message, 'id') market = self.safe_market(marketId) symbol = market['symbol'] content = self.safe_dict(message, 'contents') orderbook = self.safe_value(self.orderbooks, symbol) if orderbook is None: orderbook = self.order_book() orderbook['symbol'] = symbol asks = self.safe_list(content, 'asks', []) bids = self.safe_list(content, 'bids', []) self.handle_deltas(orderbook['asks'], asks) self.handle_deltas(orderbook['bids'], bids) orderbook['nonce'] = self.safe_integer(message, 'message_id') messageHash = 'orderbook:' + symbol self.orderbooks[symbol] = orderbook client.resolve(orderbook, messageHash) def handle_delta(self, bookside, delta): if isinstance(delta, list): price = self.safe_float(delta, 0) amount = self.safe_float(delta, 1) bookside.store(price, amount) else: bidAsk = self.parse_bid_ask(delta, 'price', 'size') bookside.storeArray(bidAsk) async def watch_ohlcv(self, symbol: str, timeframe='1m', since: Int = None, limit: Int = None, params={}) -> List[list]: """ watches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://docs.dydx.xyz/indexer-client/websockets#candles :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() url = self.urls['api']['ws'] market = self.market(symbol) messageHash = 'ohlcv:' + market['symbol'] resolution = self.safe_string(self.timeframes, timeframe, timeframe) request: dict = { 'type': 'subscribe', 'channel': 'v4_candles', 'id': market['id'] + '/' + resolution, } ohlcv = await self.watch(url, messageHash, self.extend(request, params), 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='1m', params={}) -> Any: """ unWatches historical candlestick data containing the open, high, low, and close price, and the volume of a market https://docs.dydx.xyz/indexer-client/websockets#candles :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 :param dict [params.timezone]: if provided, kline intervals are interpreted in that timezone instead of UTC, example '+08:00' :returns int[][]: A list of candles ordered, open, high, low, close, volume """ await self.load_markets() url = self.urls['api']['ws'] market = self.market(symbol) messageHash = 'ohlcv:' + market['symbol'] resolution = self.safe_string(self.timeframes, timeframe, timeframe) request: dict = { 'type': 'unsubscribe', 'channel': 'v4_candles', 'id': market['id'] + '/' + resolution, } return await self.watch(url, messageHash, self.extend(request, params), messageHash) def handle_ohlcv(self, client: Client, message): # # { # "type": "subscribed", # "connection_id": "e00b6e27-590c-4e91-a24d-b0645289434b", # "message_id": 1, # "channel": "v4_candles", # "id": "BTC-USD/1MIN", # "contents": { # "candles": [ # { # "startedAt": "2025-08-05T03:40:00.000Z", # "ticker": "BTC-USD", # "resolution": "1MIN", # "low": "114249", # "high": "114256", # "open": "114256", # "close": "114249", # "baseTokenVolume": "0.4726", # "usdVolume": "53996.1818", # "trades": 7, # "startingOpenInterest": "501.7424", # "orderbookMidPriceOpen": "114255.5", # "orderbookMidPriceClose": "114255.5" # } # ] # } # } # { # "type": "channel_data", # "connection_id": "e00b6e27-590c-4e91-a24d-b0645289434b", # "message_id": 3, # "id": "BTC-USD/1MIN", # "channel": "v4_candles", # "version": "1.0.0", # "contents": { # "startedAt": "2025-08-05T03:40:00.000Z", # "ticker": "BTC-USD", # "resolution": "1MIN", # "low": "114249", # "high": "114262", # "open": "114256", # "close": "114261", # "baseTokenVolume": "0.4753", # "usdVolume": "54304.6873", # "trades": 9, # "startingOpenInterest": "501.7424", # "orderbookMidPriceOpen": "114255.5", # "orderbookMidPriceClose": "114255.5" # } # } # id = self.safe_string(message, 'id') part = id.split('/') interval = self.safe_string(part, 1) timeframe = self.find_timeframe(interval) marketId = self.safe_string(part, 0) market = self.safe_market(marketId) symbol = market['symbol'] content = self.safe_dict(message, 'contents') candles = self.safe_list(content, 'candles') messageHash = 'ohlcv:' + symbol ohlcv = self.safe_dict(candles, 0, content) parsed = self.parse_ohlcv(ohlcv, market) self.ohlcvs[symbol] = self.safe_value(self.ohlcvs, symbol, {}) stored = self.safe_value(self.ohlcvs[symbol], timeframe) if stored is None: limit = self.safe_integer(self.options, 'OHLCVLimit', 1000) stored = ArrayCacheByTimestamp(limit) self.ohlcvs[symbol][timeframe] = stored stored.append(parsed) client.resolve(stored, messageHash) def handle_error_message(self, client: Client, message): # # { # "type": "error", # "message": "....", # "connection_id": "9011edff-d8f7-47fc-bbc6-0c7b5ba7dfae", # "message_id": 4 # } # try: msg = self.safe_string(message, 'message') raise ExchangeError(self.id + ' ' + msg) except Exception as e: client.reject(e) return True def handle_message(self, client: Client, message): type = self.safe_string(message, 'type') if type == 'error': self.handle_error_message(client, message) return if type is not None: topic = self.safe_string(message, 'channel') methods: dict = { 'v4_trades': self.handle_trades, 'v4_orderbook': self.handle_order_book, 'v4_candles': self.handle_ohlcv, } method = self.safe_value(methods, topic) if method is not None: method(client, message)