# -*- 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, ArrayCacheBySymbolBySide from ccxt.base.types import Any, Int, OrderBook, Position, Strings, Trade from ccxt.async_support.base.ws.client import Client from typing import List from ccxt.base.errors import ExchangeError from ccxt.base.errors import AuthenticationError class aftermath(ccxt.async_support.aftermath): def describe(self) -> Any: return self.deep_extend(super(aftermath, self).describe(), { 'has': { 'ws': True, 'watchBalance': False, 'watchMyTrades': False, 'watchOHLCV': False, 'watchOrderBook': True, 'watchOrders': False, 'watchTicker': False, 'watchTickers': False, 'watchBidsAsks': False, 'watchTrades': True, 'watchTradesForSymbols': False, 'watchPositions': True, }, 'urls': { 'api': { 'ws': { 'swap': 'wss://aftermath.finance/iperps-api/ccxt/stream', }, }, 'test': { 'ws': { 'swap': 'wss://testnet.aftermath.finance/iperps-api/ccxt/stream', }, }, }, 'options': { 'tradesLimit': 1000, 'ordersLimit': 1000, 'watchPositions': { 'fetchPositionsSnapshot': True, # or False 'awaitPositionsSnapshot': True, # whether to wait for the positions snapshot before providing updates }, }, 'streaming': { 'keepAlive': 59000, }, 'exceptions': { }, }) async def watch_public(self, suffix, messageHash, message): url = self.urls['api']['ws']['swap'] + '/' + suffix return await self.watch(url, messageHash, self.json(message), messageHash, message) async def watch_public_multiple(self, suffix, messageHashes, message): url = self.urls['api']['ws']['swap'] + '/' + suffix return await self.watch_multiple(url, messageHashes, self.json(message), messageHashes, message) 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://testnet.aftermath.finance/docs/#/CCXT/service%3A%3Ahandlers%3A%3Accxt%3A%3Astream%3A%3Atrades :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'] topic = market['id'] + '@trade' request: dict = { 'chId': market['id'], } message = self.extend(request, params) trades = await self.watch_public('trades', topic, message) if self.newUpdates: limit = trades.getLimit(market['symbol'], limit) return self.filter_by_symbol_since_limit(trades, symbol, since, limit, True) def handle_trade(self, client: Client, message): # # { # "amount": 0.1, # "cost": 0.1, # "datetime": "string", # "fee": null, # "id": "string", # "order": "string", # "price": 0.1, # "side": null, # "symbol": "string", # "takerOrMaker": null, # "timestamp": null, # "type": "string" # } # trade = self.parse_trade(message) symbol = trade['symbol'] market = self.market(symbol) if not (symbol in self.trades): limit = self.safe_integer(self.options, 'tradesLimit', 1000) stored = ArrayCache(limit) self.trades[symbol] = stored messageHash = market['id'] + '@trade' trades = self.trades[symbol] trades.append(trade) self.trades[symbol] = trades client.resolve(trades, messageHash) async def watch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook: """ https://testnet.aftermath.finance/docs/#/CCXT/service%3A%3Ahandlers%3A%3Accxt%3A%3Astream%3A%3Aorderbook watches information on open orders with bid(buy) and ask(sell) prices, volumes and other data :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'] topic = market['id'] + '@orderbook' request: dict = { 'chId': market['id'], } message = self.extend(request, params) orderbook = await self.watch_public('orderbook', topic, message) return orderbook.limit() def handle_order_book(self, client: Client, message): # # { # "asks": [ # [] # ], # "bids": [ # [] # ], # "datetime": "string", # "nonce": 9007199254740991, # "symbol": "string", # "timestamp": 9007199254740991 # } # symbol = self.safe_string(message, 'symbol') market = self.market(symbol) topic = market['id'] + '@orderbook' if not (symbol in self.orderbooks): defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000) subscription = client.subscriptions[topic] limit = self.safe_integer(subscription, 'limit', defaultLimit) self.orderbooks[symbol] = self.order_book({}, limit) subscription['limit'] = limit self.spawn(self.fetch_order_book_snapshot, client, message, subscription) else: orderbook = self.orderbooks[symbol] prevNonce = self.safe_integer(orderbook, 'nonce') nonce = self.safe_integer(message, 'nonce') if nonce == (prevNonce + 1): self.handle_order_book_message(client, message, orderbook) client.resolve(orderbook, topic) async def fetch_order_book_snapshot(self, client, message, subscription): symbol = self.safe_string(message, 'symbol') market = self.market(symbol) messageHash = market['id'] + '@orderbook' try: defaultLimit = self.safe_integer(self.options, 'watchOrderBookLimit', 1000) limit = self.safe_integer(subscription, 'limit', defaultLimit) params = self.safe_dict(subscription, 'params') snapshot = await self.fetch_rest_order_book_safe(symbol, limit, params) if self.safe_value(self.orderbooks, symbol) is None: # if the orderbook is dropped before the snapshot is received return orderbook = self.orderbooks[symbol] orderbook.reset(snapshot) self.orderbooks[symbol] = orderbook client.resolve(orderbook, messageHash) except Exception as e: del client.subscriptions[messageHash] client.reject(e, messageHash) def handle_order_book_message(self, client: Client, message, orderbook): self.handle_deltas(orderbook['asks'], self.safe_value(message, 'asks', [])) self.handle_deltas(orderbook['bids'], self.safe_value(message, 'bids', [])) timestamp = self.safe_integer(message, 'timestamp') nonce = self.safe_integer(message, 'nonce') orderbook['timestamp'] = timestamp orderbook['datetime'] = self.iso8601(timestamp) orderbook['nonce'] = nonce return orderbook def handle_delta(self, bookside, delta): price = self.safe_float_2(delta, 'price', 0) amount = self.safe_float_2(delta, 'quantity', 1) bookside.store(price, amount) def handle_deltas(self, bookside, deltas): for i in range(0, len(deltas)): self.handle_delta(bookside, deltas[i]) async def watch_positions(self, symbols: Strings = None, since: Int = None, limit: Int = None, params={}) -> List[Position]: """ https://testnet.aftermath.finance/docs/#/CCXT/service%3A%3Ahandlers%3A%3Accxt%3A%3Astream%3A%3Apositions watch all open positions :param str[]|None symbols: list of unified market symbols :param int [since]: the earliest time in ms to fetch positions for :param int [limit]: the maximum number of position structures to retrieve :param dict params: extra parameters specific to the exchange API endpoint :param int [params.accountNumber]: account number to query orders for, required :returns dict[]: a list of `position structure ` """ await self.load_markets() messageHashes = [] symbols = self.market_symbols(symbols) if not self.is_empty(symbols): for i in range(0, len(symbols)): symbol = symbols[i] messageHashes.append('positions::' + symbol) else: messageHashes.append('positions') accountNumber = self.safe_number(params, 'accountNumber') request = { 'accountNumber': accountNumber, } message = self.extend(request, params) suffix = 'positions' url = self.urls['api']['ws']['swap'] + '/' + suffix client = self.client(url) self.set_positions_cache(client, symbols, params) fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', True) awaitPositionsSnapshot = self.handle_option('watchPositions', 'awaitPositionsSnapshot', True) if fetchPositionsSnapshot and awaitPositionsSnapshot and self.positions is None: snapshot = await client.future('fetchPositionsSnapshot') return self.filter_by_symbols_since_limit(snapshot, symbols, since, limit, True) newPositions = await self.watch_public_multiple(suffix, messageHashes, message) if self.newUpdates: return newPositions return self.filter_by_symbols_since_limit(self.positions, symbols, since, limit, True) def set_positions_cache(self, client: Client, symbols: Strings = None, params: dict = {}): fetchPositionsSnapshot = self.handle_option('watchPositions', 'fetchPositionsSnapshot', False) if fetchPositionsSnapshot: messageHash = 'fetchPositionsSnapshot' if not (messageHash in client.futures): client.future(messageHash) self.spawn(self.load_positions_snapshot, client, messageHash, symbols, params) else: self.positions = ArrayCacheBySymbolBySide() async def load_positions_snapshot(self, client, messageHash, symbols, params): positions = await self.fetch_positions(symbols, params) self.positions = ArrayCacheBySymbolBySide() cache = self.positions for i in range(0, len(positions)): position = positions[i] cache.append(position) # don't remove the future from the .futures cache future = client.futures[messageHash] future.resolve(cache) client.resolve(cache, 'positions') def handle_positions(self, client, message): # # { # "collateral": 0.1, # "contractSize": 0.1, # "contracts": 0.1, # "datetime": "string", # "entryPrice": 0.1, # "id": "0x895037c09dd1025a136b5a5789c4ea2481176adf4c3b0c3521d5d2039bdfc3ba:123", # "initialMargin": 0.1, # "initialMarginPercentage": 0.1, # "leverage": 0.1, # "liquidationPrice": 0.1, # "maintenanceMargin": 0.1, # "maintenanceMarginPercentage": 0.1, # "marginMode": "cross", # "marginRatio": 0.1, # "notional": 0.1, # "side": "long", # "symbol": "BTC/USD:USDC", # "timestamp": 9007199254740991, # "unrealizedPnl": 0.1 # } # if self.positions is None: self.positions = ArrayCacheBySymbolBySide() cache = self.positions symbol = self.safe_string(message, 'symbol') market = self.safe_market(symbol) position = self.parse_position(message, market) cache.append(position) messageHash = 'positions::' + market['symbol'] client.resolve(position, messageHash) client.resolve([position], 'positions') def handle_error_message(self, client: Client, message): # # User error: Expected Message::Text from client, got Ping(b\"\") # if isinstance(message, str): if message.find('error') >= 0: try: feedback = self.id + ' ' + message self.throw_exactly_matched_exception(self.exceptions['exact'], message, feedback) raise ExchangeError(message) except Exception as error: if isinstance(error, AuthenticationError): messageHash = 'authenticated' client.reject(error, messageHash) if messageHash in client.subscriptions: del client.subscriptions[messageHash] else: client.reject(error) return True return False def handle_message(self, client: Client, message): if self.handle_error_message(client, message): return # methods: Dict = { # 'trade': self.handle_trade, # } if 'asks' in message: self.handle_order_book(client, message) elif 'notional' in message: self.handle_positions(client, message) else: self.handle_trade(client, message)