# -*- 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, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade from ccxt.async_support.base.ws.client import Client from typing import List from ccxt.base.errors import ArgumentsRequired from ccxt.base.errors import NotSupported class pacifica(ccxt.async_support.pacifica): def describe(self) -> Any: return self.deep_extend(super(pacifica, self).describe(), { 'has': { 'ws': True, 'cancelOrderWs': True, 'cancelOrdersWs': True, 'cancelAllOrdersWs': 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, 'unWatchOrderBook': True, 'unWatchTickers': True, 'unWatchTrades': True, 'unWatchOHLCV': True, 'unWatchMyTrades': True, 'unWatchOrders': True, }, 'urls': { 'api': { 'ws': { 'public': 'wss://ws.pacifica.fi/ws', }, }, 'test': { 'ws': { 'public': 'wss://test-ws.pacifica.fi/ws', }, }, }, 'options': { 'ws': { 'options': { 'headers': {}, }, }, }, 'streaming': { 'ping': self.ping, 'keepAlive': 20000, }, 'exceptions': { 'ws': { 'exact': { }, }, }, }) def setup_api_key_headers(self, key: str = None): headers = {} if key is not None: headers['PF-API-KEY'] = key else: if self.handle_option('setupApiKeyHeaders', 'apiKey', None) is not None: headers['PF-API-KEY'] = self.options['apiKey'] self.options['ws']['options']['headers'] = headers async def create_order_ws(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}): """ create a trade order https://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/create-market-order https://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/create-limit-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 fullfilled, in units of the quote currency, ignored in market orders :param dict [params]: extra parameters specific to the exchange API endpoint :param float [params.triggerPrice]: The price a trigger order is triggered at :param float|None [params.stopLossPrice]: the price that a stop loss order is triggered at(optional provide stopLossCloid) :param float|None [params.takeProfitPrice]: the price that a take profit order is triggered at(optional provide takeProfitCloid) :param str|None [params.timeInForce]: "GTC", "IOC", or "PO" or "ALO" or "PO_TOB"(or "TOB" - PO by top of book) :param bool|None [params.reduceOnly]: Ensures that the executed order does not flip the opened position. :param str|None [params.clientOrderId]: client order id,(optional uuid v4 e.g.: f47ac10b-58cc-4372-a567-0e02b2c3d479) :param int|None [params.expiryWindow]: time to live in milliseconds :param str|None [params.agentAddress]: only if agent wallet in use. :param str|None [params.originAddress]: only if agent in use. Agent's owner address( default = credentials walletAddress ) :returns dict: an `order structure ` """ await self.load_markets() request, operationType = self.create_order_request(symbol, type, side, amount, price, params) params = self.omit(params, [ 'reduceOnly', 'clientOrderId', 'stopLimitPrice', 'timeInForce', 'triggerPrice', 'stopLossCloid', 'stopLossPrice', 'stopLossLimitPrice', 'takeProfitCloid', 'takeProfitPrice', 'takeProfitLimitPrice', 'expiryWindow', 'agentAddress', 'originAddress', ]) isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] wsRequest = self.wrap_as_post_action(operationType, request) requestId = self.safe_string(wsRequest, 'id') if operationType == 'create_stop_order': raise NotSupported(self.id + ' createOrderWs() do not support stop order type of order. Check provided arguments correctly!') elif operationType == 'set_position_tpsl': raise NotSupported(self.id + ' createOrderWs() do not support set position tpsl type of order. Check provided arguments correctly!') response = await self.watch(url, requestId, wsRequest, requestId) # # market order # { # "code": 200, # "data": { # "I": "79f948fd-7556-4066-a128-083f3ea49322", # "i": 645953, # "s": "BTC" # }, # "id": "660065de-8f32-46ad-ba1e-83c93d3e3966", # "t": 1749223025962, # "type": "create_market_order" # } # # limit order # { # "code": 200, # "data": { # "I": "79f948fd-7556-4066-a128-083f3ea49322", # "i": 645953, # "s": "BTC" # }, # "id": "660065de-8f32-46ad-ba1e-83c93d3e3966", # "t": 1749223025962, # "type": "create_order" # } # code = self.safe_integer(response, 'code') success = False if code == 200: success = True status = None if not success: status = 'rejected' else: status = 'open' order = self.safe_dict(response, 'data', {}) orderId = self.safe_string(order, 'i') clientOrderId = self.safe_string(order, 'I') return self.safe_order({'id': orderId, 'clientOrderId': clientOrderId, 'status': status, 'info': response, 'symbol': symbol}) 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://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/edit-order :param str id: edit order id :param str symbol: unified symbol of the market to edit 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 :param dict [params]: extra parameters specific to the exchange API endpoint :param str [params.clientOrderId]: client order id,(optional uuid v4 e.g.: f47ac10b-58cc-4372-a567-0e02b2c3d479) :param int|None [params.expiryWindow]: time to live in milliseconds :param str|None [params.agentAddress]: only if agent wallet in use :param str|None [params.originAddress]: only if agent in use. Agent's owner address( default = credentials walletAddress ) :returns dict: an `order structure ` """ batchOperationType = 'edit_order' await self.load_markets() market = self.market(symbol) request = self.edit_order_request(id, symbol, type, side, amount, price, market, params) params = self.omit(params, ['originAddress', 'agentAddress', 'expiryWindow', 'clientOrderId']) isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] wsRequest = self.wrap_as_post_action(batchOperationType, request) requestId = self.safe_string(wsRequest, 'id') response = await self.watch(url, requestId, wsRequest, requestId) # { # "code": 200, # "data": { # "I": "f47ac10b-58cc-4372-a567-0e02b2c3d479", # "i": 645954, # "s": "BTC" # }, # "id": "660065de-8f32-46ad-ba1e-83c93d3e3966", # "t": 1749223026150, # "type": "edit_order" # } code = self.safe_integer(response, 'code') success = False if code == 200: success = True status = None if not success: status = 'rejected' else: status = 'open' order = self.safe_dict(response, 'data', {}) orderId = self.safe_string(order, 'i') clientOrderId = self.safe_string(order, 'I') return self.safe_order({'id': orderId, 'clientOrderId': clientOrderId, 'status': status, 'info': response, 'symbol': symbol}) async def cancel_orders_ws(self, ids: List[str], symbol: Str = None, params={}): """ cancel multiple orders https://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/batch-order https://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/cancel-order :param str[] ids: order ids :param str [symbol]: unified market symbol :param dict [params]: extra parameters specific to the exchange API endpoint :param string|str[] [params.clientOrderId]: client order ids,(optional uuid v4 e.g.: f47ac10b-58cc-4372-a567-0e02b2c3d479) :param int|None [params.expiryWindow]: time to live in milliseconds :param str|None [params.agentAddress]: only if agent wallet in use :param str|None [params.originAddress]: only if agent in use. Agent's owner address( default = credentials walletAddress ) :returns dict: an list of `order structures ` """ batchOperationType = 'batch_orders' await self.load_markets() if symbol is None: raise ArgumentsRequired(self.id + 'cancelOrders() requires a "symbol" argument!') request = self.cancelOrdersRequest(ids, symbol, params) params = self.omit(params, ['originAddress', 'agentAddress', 'expiryWindow', 'clientOrderIds']) isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] wsRequest = self.wrap_as_post_action(batchOperationType, request) requestId = self.safe_string(wsRequest, 'id') response = await self.watch(url, requestId, wsRequest, requestId) # # { # "code": 200, # "data": { # "results": [ # { # "success": True, # "order_id": 645953, # "client_order_id": "57a5efb1-bb96-49a5-8bfd-f25d5f22bc7e", # "symbol": "BTC" # }, # { # "success": True, # "order_id": 645954, # "symbol": "ETH" # } # ] # }, # "id": "660065de-8f32-46ad-ba1e-83c93d3e3966", # "t": 1749223025962, # "type": "batch_orders" # } # data = self.safe_dict(response, 'data', {}) results = self.safe_list(data, 'results', []) ordersToReturn = [] for i in range(0, len(results)): order = results[i] error = self.safe_string(order, 'error', None) success = self.safe_bool(order, 'success', False) marketId = self.safe_string(order, 'symbol') market = self.safe_market(marketId) orderId = self.safe_string(order, 'i') clientOrderId = self.safe_string(order, 'I') status = None if (error is not None) or (not success): status = 'closed' else: status = 'canceled' ordersToReturn.append(self.safe_order({'id': orderId, 'clientOrderId': clientOrderId, 'status': status, 'info': response, 'symbol': market['symbol']})) return ordersToReturn async def cancel_order_ws(self, id: str, symbol: Str = None, params={}): """ cancels an open order https://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/cancel-order :param str id: order id :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 bool|None [params.stop]: necessary if self is to cancel a stop order. :param str|None [params.clientOrderId]: client order id,(optional uuid v4 e.g.: f47ac10b-58cc-4372-a567-0e02b2c3d479) :param int|None [params.expiryWindow]: time to live in milliseconds :param str|None [params.agentAddress]: only if agent wallet in use :param str|None [params.originAddress]: only if agent in use. Agent's owner address( default = credentials walletAddress ) :returns dict: An `order structure ` """ operationType = 'cancel_order' await self.load_markets() if symbol is None: raise ArgumentsRequired(self.id + ' cancelOrderWs() requires a symbol argument') request = self.cancel_order_request(id, symbol, params) params = self.omit(params, ['originAddress', 'agentAddress', 'expiryWindow', 'trigger', 'stop', 'clientOrderId']) isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] wsRequest = self.wrap_as_post_action(operationType, request) requestId = self.safe_string(wsRequest, 'id') response = await self.watch(url, requestId, wsRequest, requestId) # # { # "code": 200, # "data": { # "I": "79f948fd-7556-4066-a128-083f3ea49322", # "i": null, # "s": "BTC" # }, # "id": "1bb2b72f-f545-4938-8a38-c5cda8823675", # "t": 1749223343610, # "type": "cancel_order" # } # code = self.safe_integer(response, 'code') success = False if code == 200: success = True status = None if not success: status = 'rejected' else: status = 'open' order = self.safe_dict(response, 'data', {}) orderId = self.safe_string(order, 'i') clientOrderId = self.safe_string(order, 'I') return self.safe_order({'id': orderId, 'clientOrderId': clientOrderId, 'status': status, 'info': response, 'symbol': symbol}) async def cancel_all_orders_ws(self, symbol: Str = None, params={}): """ cancel all open orders in a market https://docs.pacifica.fi/api-documentation/api/websocket/trading-operations/cancel-all-orders :param str symbol:(optional) unified market symbol of the market to cancel orders in. :param dict [params]: extra parameters specific to the exchange API endpoint :param boolean|None [params.excludeReduceOnly]: whether to exclude reduce-only orders :param int|None [params.expiryWindow]: time to live in milliseconds :param str|None [params.agentAddress]: only if agent wallet in use :param str|None [params.originAddress]: only if agent in use. Agent's owner address( default = credentials walletAddress ) :returns dict[]: a list of `order structures ` """ await self.load_markets() operationType = 'cancel_all_orders' request = self.cancelAllOrdersRequest(symbol, params) params = self.omit(params, ['excludeReduceOnly', 'agentAddress', 'originAddress', 'expiryWindow']) isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] wsRequest = self.wrap_as_post_action(operationType, request) requestId = self.safe_string(wsRequest, 'id') response = await self.watch(url, requestId, wsRequest, requestId) # { # "code": 200, # "data": { # "cancelled_count": 10 # }, # "id": "b86b4f45-49da-4191-84e2-93e141acdeab", # "t": 1749221787291, # "type": "cancel_all_orders" # } # return [ self.safe_order({ 'info': response, }), ] 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.pacifica.fi/api-documentation/api/websocket/subscriptions/orderbook :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 :param int|None [params.aggLevel]: aggregation level for price grouping. Defaults to 1. Can be 1, 10, 100, 1000, 10000 :returns dict: A dictionary of `order book structures ` indexed by market symbols """ self.setup_api_key_headers() await self.load_markets() market = self.market(symbol) aggLevel = None aggLevel, params = self.handle_option_and_params(params, 'fetchOrderBook', 'aggLevel', 1) messageHash = 'orderbook:' + symbol isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'subscribe', 'params': { 'source': 'book', 'symbol': market['id'], 'agg_level': aggLevel, }, } 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/orderbook :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 :param int|None [params.aggLevel]: aggregation level for price grouping. Defaults to 1. Can be 1, 10, 100, 1000, 10000 :returns dict: A dictionary of `order book structures ` indexed by market symbols """ await self.load_markets() market = self.market(symbol) aggLevel = None aggLevel, params = self.handle_option_and_params(params, 'fetchOrderBook', 'aggLevel', 1) subMessageHash = 'orderbook:' + symbol messageHash = 'unsubscribe:' + subMessageHash isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'unsubscribe', 'params': { 'source': 'book', 'symbol': market['id'], 'agg_level': aggLevel, }, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) def handle_order_book(self, client, message): # # { # "channel": "book", # "data": { # "l": [ # [ # { # "a": "37.86", # "n": 4, # "p": "157.47" # }, # # ... other aggegated bid levels # ], # [ # { # "a": "12.7", # "n": 2, # "p": "157.49" # }, # { # "a": "44.45", # "n": 3, # "p": "157.5" # }, # # ... other aggregated ask levels # ] # ], # "s": "SOL", # "t": 1749051881187, # "li": 1559885104 # sequence id - last order id # } # } # entry = self.safe_dict(message, 'data', {}) marketId = self.safe_string(entry, 's') market = self.safe_market(marketId) symbol = market['symbol'] levels = self.safe_list(entry, 'l', []) result: dict = { 'bids': self.safe_list(levels, 0, []), 'asks': self.safe_list(levels, 1, []), } timestamp = self.safe_integer(entry, 't') snapshot = self.parse_order_book(result, symbol, timestamp, 'bids', 'asks', 'p', 'a') nonce = self.safe_integer(entry, 'li') if nonce: snapshot['nonce'] = nonce 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/prices 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 ` """ 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/prices :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 ` """ self.setup_api_key_headers() await self.load_markets() symbols = self.market_symbols(symbols, None, True) messageHash = 'tickers' isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'subscribe', 'params': { 'source': 'prices', }, } 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/prices :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 isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'unsubscribe', 'params': { 'source': 'prices', }, } 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/account-trades :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|None [params.account]: will default to options' walletAddress if not provided :returns dict[]: a list of `order structures ` """ userAddress = None userAddress, params = self.handleOriginAndSingleAddress('watchMyTrades', params) await self.load_markets() messageHash = 'myTrades' if symbol is not None: symbol = self.symbol(symbol) messageHash += ':' + symbol isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'subscribe', 'params': { 'source': 'account_trades', 'account': 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) async def un_watch_my_trades(self, symbol: Str = None, params={}) -> Any: """ unWatches information on multiple trades made by the user https://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/account-trades :param str symbol: unified market symbol of the market orders were made in :param dict [params]: extra parameters specific to the exchange API endpoint :param str|None [params.account]: will default to options' walletAddress if not provided :returns dict[]: a list of `order structures ` """ await self.load_markets() if symbol is not None: raise NotSupported(self.id + ' unWatchMyTrades does not support a symbol argument, unWatch from all markets only') userAddress = None userAddress, params = self.handleOriginAndSingleAddress('unWatchMyTrades', params) messageHash = 'unsubscribe:myTrades' isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'unsubscribe', 'params': { 'source': 'account_trades', 'account': userAddress, }, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) def handle_ws_tickers(self, client: Client, message): # # { # "channel": "prices", # "data": [ # { # "funding": "0.0000125", # "mark": "105473", # "mid": "105476", # "next_funding": "0.0000125", # "open_interest": "0.00524", # "oracle": "105473", # "symbol": "BTC", # "timestamp": 1749051612681, # "volume_24h": "63265.87522", # "yesterday_price": "955476" # } # # ... other symbol prices # ], # } # parsedTickers = [] data = self.safe_list(message, 'data', []) for i in range(0, len(data)): info = data[i] marketId = self.safe_string(info, 'symbol') market = self.safe_market(marketId) symbol = market['symbol'] ticker = self.parse_ws_ticker(info, market) self.tickers[symbol] = ticker parsedTickers.append(ticker) tickers = self.index_by(parsedTickers, 'symbol') client.resolve(tickers, 'tickers') return True 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": "account_trades", # "data": [ # { # "h": 80063441, # history id # "i": 1559912767, # oid # "I": null, # cloid # "u": "BrZp5bidJ3WUvceSq7X78bhjTfZXeezzGvGEV4hAYKTa", # account address # "s": "BTC", # symbol # "p": "89477", # price # "o": "89505", # entry price # "a": "0.00036", # amount # "te": "fulfill_taker", # "ts": "close_long", # "tc": "normal", # trade type # "f": "0.012885", # fee # "n": "-0.022965", # pnl # "t": 1765018588190, # "li": 1559912767 # } # ] # } # 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(message, 'data', []) 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]: """ watches information on multiple trades made in a market https://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/trades :param str 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 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'] messageHash = 'trade:' + symbol isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'subscribe', 'params': { 'source': 'trades', 'symbol': 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/trades :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 isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'unsubscribe', 'params': { 'source': 'trades', 'symbol': 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": [ # { # "h": 80062522, # "s": "BTC", # "a": "0.00001", # "p": "89471", # "d": "close_short", # "tc": "normal", # "t": 1765018379085, # "li": 1559885104 # } # ] # } # entry = self.safe_list(message, 'data', []) first = self.safe_dict(entry, 0, {}) marketId = self.safe_string(first, 's') market = self.safe_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 # # { # "h": 80063441, # history id # "i": 1559912767, # oid # "I": null, # cloid # "u": "BrZp5bidJ3WUvceSq7X78bhjTfZXeezzGvGEV4hAYKTa", # account address # "s": "BTC", # symbol # "p": "89477", # price # "o": "89505", # entry price # "a": "0.00036", # amount # "te": "fulfill_taker", # "ts": "close_long", # "tc": "normal", # trade type # "f": "0.012885", # fee # "n": "-0.022965", # pnl # "t": 1765018588190, # "li": 1559912767 # } # # fetchTrades # # { # "h": 80062522, # "s": "BTC", # "a": "0.00001", # "p": "89471", # "d": "close_short", # "tc": "normal", # "t": 1765018379085, # "li": 1559885104 # } # timestamp = self.safe_integer(trade, 't') price = self.safe_string(trade, 'p') amount = self.safe_string(trade, 'a') marketId = self.safe_string(trade, 's') market = self.safe_market(marketId, market) symbol = market['symbol'] id = self.safe_string(trade, 'h') fee = self.safe_string(trade, 'f') side = self.safe_string_2(trade, 'ts', 'd') if side == 'open_long': side = 'buy' elif side == 'close_long': side = 'sell' elif side == 'open_short': side = 'sell' elif side == 'close_short': side = 'buy' eventType = self.safe_string(trade, 'te') takerOrMaker = None if eventType is not None: takerOrMaker = 'maker' if (eventType == 'fulfill_maker') else 'taker' orderId = self.safe_string(trade, 'i') # public trades have no orderId if orderId is None: takerOrMaker = None return self.safe_trade({ 'info': trade, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'symbol': symbol, 'id': id, 'order': self.safe_string(trade, 'i'), 'type': None, 'side': side, 'takerOrMaker': takerOrMaker, '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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/candle :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'] isTestnet = self.isSandboxModeEnabled parsedTf = self.safe_string(self.timeframes, timeframe, timeframe) urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'subscribe', 'params': { 'source': 'candle', 'symbol': market['id'], 'interval': parsedTf, }, } messageHash = 'candles:' + parsedTf + ':' + 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/candle :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'] isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'unsubscribe', 'params': { 'source': 'candle', 'symbol': 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": 1749052260000, # "T": 1749052320000, # "s": "SOL", # "i": "1m", # "o": "157.3", # "c": "157.32", # "h": "157.32", # "l": "157.3", # "v": "1.22", # "n": 8 # } # } # data = self.safe_dict(message, 'data', {}) marketId = self.safe_string(data, 's') market = self.safe_market(marketId) symbol = market['symbol'] 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) 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://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/account-order-updates :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|None [params.account]: will default to options' walletAddress if not provided :returns dict[]: a list of `order structures ` """ await self.load_markets() userAddress = None userAddress, params = self.handleOriginAndSingleAddress('watchOrders', params) market = None messageHash = 'order' if symbol is not None: market = self.market(symbol) symbol = market['symbol'] messageHash = messageHash + ':' + symbol isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] request: dict = { 'method': 'subscribe', 'params': { 'source': 'account_order_updates', 'account': 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) async def un_watch_orders(self, symbol: Str = None, params={}) -> Any: """ unWatches information on multiple orders made by the user https://docs.pacifica.fi/api-documentation/api/websocket/subscriptions/account-order-updates :param str symbol: unified market symbol of the market orders were made in :param dict [params]: extra parameters specific to the exchange API endpoint :param str|None [params.account]: will default to options' walletAddress if not provided :returns dict[]: a list of `order structures ` """ await self.load_markets() if symbol is not None: raise NotSupported(self.id + ' unWatchOrders() does not support a symbol argument, unWatch from all markets only') messageHash = 'unsubscribe:order' isTestnet = self.isSandboxModeEnabled urlKey = 'test' if (isTestnet) else 'api' url = self.urls[urlKey]['ws']['public'] userAddress = None userAddress, params = self.handleOriginAndSingleAddress('unWatchOrders', params) request: dict = { 'method': 'unsubscribe', 'params': { 'source': 'account_order_updates', 'account': userAddress, }, } message = self.extend(request, params) return await self.watch(url, messageHash, message, messageHash) def handle_order(self, client: Client, message): # not snapshot, only updates # { # "channel": "account_order_updates", # "data": [ # { # "i": 1559665358, # "I": null, # "u": "BrZp5bidJ3WUvceSq7X78bhjTfZXeezzGvGEV4hAYKTa", # "s": "BTC", # "d": "bid", # "p": "89501", # "ip": "89501", # "lp": "89501", # "a": "0.00012", # "f": "0.00012", # "oe": "fulfill_limit", # "os": "filled", # "ot": "limit", # "sp": null, # "si": null, # "r": False, # "ct": 1765017049008, # "ut": 1765017219639, # "li": 1559696133 # } # ] # } 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: # # 'rl' key is present only when a rate-limited API key is used # {"id":"64107e37-a999-4b90-a3cf-b4322ae110d9","type":"cancel_order","code":420,"err":"Failed to cancel order","t":1769474703073,"rl":{"r":1245,"q":1250,"t":56}} # error = self.safe_string(message, 'err', '') postType = self.safe_string(message, 'type', '') data = self.safe_dict(message, 'data', {}) id = self.safe_string(message, 'id') if id is None: id = self.safe_string(data, 'id') try: self.handle_errors(0, error, '', postType, self.options['ws']['options']['headers'], self.json(data), message, {}, {}) except Exception as e: client.reject(e, id) return True return False def handle_order_book_unsubscription(self, client: Client, subscription: dict): marketId = self.safe_string_2(subscription, 'symbol', 's') market = self.safe_market(marketId) symbol = market['symbol'] 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): marketId = self.safe_string_2(subscription, 'symbol', 's') market = self.safe_market(marketId) symbol = market['symbol'] 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): marketId = self.safe_string_2(subscription, 'symbol', 's') market = self.safe_market(marketId) symbol = market['symbol'] 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_order_unsubscription(self, client: Client, subscription: dict): subHash = 'order' unSubHash = 'unsubscribe:' + subHash self.clean_unsubscription(client, subHash, unSubHash, True) topicStructure = { 'topic': 'orders', } self.clean_cache(topicStructure) def handle_my_trades_unsubscription(self, client: Client, subscription: dict): subHash = 'myTrades' unSubHash = 'unsubscribe:' + subHash self.clean_unsubscription(client, subHash, unSubHash, True) topicStructure = { 'topic': 'myTrades', } self.clean_cache(topicStructure) def handle_subscription_response(self, client: Client, message): # { # "channel": "subscribe", # "data": { # "source": "book", # "symbol": "SOL", # "agg_level": 1 # } # } # # { # "channel": "unsubscribe", # "data": { # "source": "book", # "symbol": "SOL", # "agg_level": 1 # } # } # data = self.safe_dict(message, 'data', {}) method = self.safe_string(message, 'channel') if method == 'unsubscribe': subscription = self.safe_dict(data, 'data', {}) type = self.safe_string(subscription, 'source') if type == 'book': self.handle_order_book_unsubscription(client, subscription) elif type == 'trades': self.handle_trades_unsubscription(client, subscription) elif type == 'prices': self.handle_tickers_unsubscription(client, subscription) elif type == 'candle': self.handle_ohlcv_unsubscription(client, subscription) elif type == 'account_order_updates': self.handle_order_unsubscription(client, subscription) elif type == 'account_trades': self.handle_my_trades_unsubscription(client, subscription) def handle_message(self, client: Client, message): # # { # "channel":"subscribe", # "data":{ # "method":"unsubscribe", # "subscription":{ # "type":"l2Book", # "coin":"BTC", # "nSigFigs":5, # "mantissa":null # } # } # } # if self.handle_error_message(client, message): return postType = self.safe_string(message, 'type', None) topic = self.safe_string(message, 'channel', '') methods: dict = { 'pong': self.handle_pong, 'trades': self.handle_trades, 'book': self.handle_order_book, 'candle': self.handle_ohlcv, 'account_order_updates': self.handle_order, 'account_trades': self.handle_my_trades, 'prices': self.handle_ws_tickers, 'subscribe': self.handle_subscription_response, 'unsubscribe': self.handle_subscription_response, } exacMethod = self.safe_value(methods, topic) if exacMethod is not None: exacMethod(client, message) return if postType is not None: self.handle_ws_post(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', self.milliseconds()) return message def request_id(self) -> str: return self.uuid() # uuid v4 def wrap_as_post_action(self, operationType: str, request: dict) -> dict: if operationType is None: raise ArgumentsRequired(self.id + 'postAction() requires a "operationType" argument!') requestId = self.request_id() payload = { 'id': requestId, 'params': {}, } payload['params'][operationType] = request return payload def handle_ws_post(self, client: Client, message: dict): # # market order # { # "code": 200, # "data": { # "I": "79f948fd-7556-4066-a128-083f3ea49322", # "i": 645953, # "s": "BTC" # }, # "id": "660065de-8f32-46ad-ba1e-83c93d3e3966", # "t": 1749223025962, # "type": "create_market_order" # } # # limit order # { # "code": 200, # "data": { # "I": "79f948fd-7556-4066-a128-083f3ea49322", # "i": 645953, # "s": "BTC" # }, # "id": "660065de-8f32-46ad-ba1e-83c93d3e3966", # "t": 1749223025962, # "type": "create_order" # } # id = self.safe_string(message, 'id') client.resolve(message, id)