UNPKG

ccxt

Version:

A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go

1,081 lines • 55.7 kB
// --------------------------------------------------------------------------- import Precise from '../base/Precise.js'; import { ArrayCache } from '../base/ws/Cache.js'; import lighterRest from '../lighter.js'; // --------------------------------------------------------------------------- export default class lighter extends lighterRest { describe() { return this.deepExtend(super.describe(), { 'has': { 'ws': true, 'watchTicker': true, 'watchMarkPrice': true, 'watchMarkPrices': true, 'watchTickers': true, 'watchBidsAsks': false, 'watchOrderBook': true, 'watchTrades': true, 'watchTradesForSymbols': false, 'watchOrderBookForSymbols': false, 'watchBalance': true, 'watchLiquidations': true, 'watchLiquidationsForSymbols': false, 'watchMyLiquidations': false, 'watchMyLiquidationsForSymbols': false, 'watchOHLCV': false, 'watchOHLCVForSymbols': false, 'watchOrders': true, 'watchMyTrades': true, 'watchPositions': false, 'watchFundingRate': false, 'watchFundingRates': false, 'unWatchOrderBook': true, 'unWatchTicker': true, 'unWatchTickers': true, 'unWatchTrades': true, 'unWatchMyTrades': true, 'unWatchMarkPrice': true, 'unWatchMarkPrices': true, 'unWatchOrders': true, }, 'urls': { 'api': { 'ws': 'wss://mainnet.zklighter.elliot.ai/stream', }, 'test': { 'ws': 'wss://testnet.zklighter.elliot.ai/stream', }, }, 'options': {}, }); } getMessageHash(unifiedChannel, symbol = undefined, extra = undefined) { let hash = unifiedChannel; if (symbol !== undefined) { hash += '::' + symbol; } else { hash += 's'; // tickers, orderbooks, ohlcvs ... } if (extra !== undefined) { hash += '::' + extra; } return hash; } async subscribePublic(messageHash, params = {}) { const url = this.urls['api']['ws']; const request = { 'type': 'subscribe', }; const subscription = { 'messageHash': messageHash, 'params': params, }; return await this.watch(url, messageHash, this.extend(request, params), messageHash, subscription); } async subscribePublicMultiple(messageHashes, params = {}) { const url = this.urls['api']['ws']; const request = { 'type': 'subscribe', }; const subscription = { 'messageHashes': messageHashes, 'params': params, }; return await this.watchMultiple(url, messageHashes, this.extend(request, params), messageHashes, subscription); } async unsubscribe(messageHash, params = {}) { const url = this.urls['api']['ws']; const request = { 'type': 'unsubscribe', }; const subscription = { 'messageHash': messageHash, 'params': params, }; return await this.watch(url, messageHash, this.extend(request, params), messageHash, subscription); } async subscribePrivate(messageHash, params = {}) { await this.preLoadLighterLibrary(); params['auth'] = this.createAuth(params); return await this.subscribePublic(messageHash, params); } handleDelta(bookside, delta) { const price = this.safeFloat(delta, 'price'); const amount = this.safeFloat(delta, 'size'); bookside.store(price, amount); } handleDeltas(bookside, deltas) { for (let i = 0; i < deltas.length; i++) { this.handleDelta(bookside, deltas[i]); } } handleOrderBookMessage(client, message, orderbook) { const data = this.safeDict(message, 'order_book', {}); this.handleDeltas(orderbook['asks'], this.safeList(data, 'asks', [])); this.handleDeltas(orderbook['bids'], this.safeList(data, 'bids', [])); orderbook['nonce'] = this.safeInteger(data, 'offset'); const timestamp = this.safeInteger(message, 'timestamp'); orderbook['timestamp'] = timestamp; orderbook['datetime'] = this.iso8601(timestamp); return orderbook; } handleOrderBook(client, message) { // // { // "channel": "order_book:0", // "offset": 11413309, // "order_book": { // "code": 0, // "asks": [ // { // "price": "2979.64", // "size": "61.9487" // } // ], // "bids": [ // { // "price": "2979.36", // "size": "0.0000" // } // ], // "offset": 11413309, // "nonce": 3107818665 // }, // "timestamp": 1763448665923, // "type": "update/order_book" // } // const data = this.safeDict(message, 'order_book', {}); const channel = this.safeString(message, 'channel', ''); const parts = channel.split(':'); const marketId = parts[1]; const market = this.safeMarket(marketId); const symbol = market['symbol']; const timestamp = this.safeInteger(message, 'timestamp'); if (!(symbol in this.orderbooks)) { this.orderbooks[symbol] = this.orderBook(); } const orderbook = this.orderbooks[symbol]; const type = this.safeString(message, 'type', ''); if (type === 'subscribed/order_book') { const parsed = this.parseOrderBook(data, symbol, timestamp, 'bids', 'asks', 'price', 'size'); parsed['nonce'] = this.safeInteger(data, 'offset'); orderbook.reset(parsed); } else if (type === 'update/order_book') { this.handleOrderBookMessage(client, message, orderbook); } const messageHash = this.getMessageHash('orderbook', symbol); client.resolve(orderbook, messageHash); } /** * @method * @name lighter#watchOrderBook * @description watches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://apidocs.lighter.xyz/docs/websocket-reference#order-book * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure} indexed by market symbols */ async watchOrderBook(symbol, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'order_book/' + market['id'], }; const messageHash = this.getMessageHash('orderbook', symbol); const orderbook = await this.subscribePublic(messageHash, this.extend(request, params)); return orderbook.limit(); } /** * @method * @name lighter#unWatchOrderBook * @description unWatches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://apidocs.lighter.xyz/docs/websocket-reference#order-book * @param {string} symbol unified symbol of the market * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure} indexed by market symbols */ async unWatchOrderBook(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'order_book/' + market['id'], }; const messageHash = this.getMessageHash('unsubscribe', symbol); return await this.unsubscribe(messageHash, this.extend(request, params)); } handleTicker(client, message) { // // watchTicker // { // "channel": "market_stats:0", // "market_stats": { // "market_id": 0, // "index_price": "3015.56", // "mark_price": "3013.91", // "open_interest": "122736286.659423", // "open_interest_limit": "72057594037927936.000000", // "funding_clamp_small": "0.0500", // "funding_clamp_big": "4.0000", // "last_trade_price": "3013.13", // "current_funding_rate": "0.0012", // "funding_rate": "0.0012", // "funding_timestamp": 1763532000004, // "daily_base_token_volume": 643235.2763, // "daily_quote_token_volume": 1983505435.673896, // "daily_price_low": 2977.42, // "daily_price_high": 3170.81, // "daily_price_change": -0.3061987051035322 // }, // "type": "update/market_stats" // } // // watchTickers // { // "channel": "market_stats:all", // "market_stats": { // "96": { // "market_id": 96, // "index_price": "1.15901", // "mark_price": "1.15954", // "open_interest": "19392952.260530", // "open_interest_limit": "50000000000000.000000", // "funding_clamp_small": "0.0500", // "funding_clamp_big": "4.0000", // "last_trade_price": "1.15955", // "current_funding_rate": "0.0000", // "funding_rate": "0.0000", // "funding_timestamp": 1763532000004, // "daily_base_token_volume": 117634224.1, // "daily_quote_token_volume": 136339744.383989, // "daily_price_low": 1.15774, // "daily_price_high": 1.16105, // "daily_price_change": -0.004311757299805109 // } // }, // "type": "update/market_stats" // } // const data = this.safeDict(message, 'market_stats', {}); const channel = this.safeString(message, 'channel'); if (channel === 'market_stats:all') { const marketIds = Object.keys(data); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const market = this.safeMarket(marketId); const symbol = market['symbol']; const ticker = this.parseTicker(data[marketId], market); this.tickers[symbol] = ticker; client.resolve(ticker, this.getMessageHash('ticker', symbol)); client.resolve(ticker, this.getMessageHash('ticker')); } } else { const marketId = this.safeString(data, 'market_id'); const market = this.safeMarket(marketId); const symbol = market['symbol']; const ticker = this.parseTicker(data, market); this.tickers[symbol] = ticker; client.resolve(ticker, this.getMessageHash('ticker', symbol)); } } /** * @method * @name lighter#watchTicker * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async watchTicker(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'market_stats/' + market['id'], }; const messageHash = this.getMessageHash('ticker', symbol); return await this.subscribePublic(messageHash, this.extend(request, params)); } /** * @method * @name lighter#unWatchTicker * @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async unWatchTicker(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'market_stats/' + market['id'], }; const messageHash = this.getMessageHash('unsubscribe', symbol); return await this.unsubscribe(messageHash, this.extend(request, params)); } /** * @method * @name lighter#watchTickers * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @description watches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list * @param {string[]} [symbols] unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.channel] the channel to subscribe to, tickers by default. Can be tickers, sprd-tickers, index-tickers, block-tickers * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async watchTickers(symbols = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols); const request = { 'channel': 'market_stats/all', }; const messageHashes = []; let symbolsLength = 0; if (symbols !== undefined) { symbolsLength = symbols.length; } if (symbolsLength === 0) { messageHashes.push(this.getMessageHash('ticker')); } else { for (let i = 0; i < symbols.length; i++) { const symbol = symbols[i]; messageHashes.push(this.getMessageHash('ticker', symbol)); } } const newTicker = await this.subscribePublicMultiple(messageHashes, this.extend(request, params)); if (this.newUpdates) { const result = {}; result[newTicker['symbol']] = newTicker; return result; } return this.filterByArray(this.tickers, 'symbol', symbols); } /** * @method * @name lighter#unWatchTickers * @description unWatches a price ticker, a statistical calculation with the information calculated over the past 24 hours for all markets of a specific list * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @param {string[]} [symbols] unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async unWatchTickers(symbols = undefined, params = {}) { await this.loadMarkets(); const request = { 'channel': 'market_stats/all', }; const messageHash = this.getMessageHash('unsubscribe'); return await this.unsubscribe(messageHash, this.extend(request, params)); } /** * @method * @name lighter#watchMarkPrice * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @description watches a mark price * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async watchMarkPrice(symbol, params = {}) { return await this.watchTicker(symbol, params); } /** * @method * @name lighter#watchMarkPrices * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @description watches mark prices * @param {string[]} [symbols] unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async watchMarkPrices(symbols = undefined, params = {}) { return await this.watchTickers(symbols, params); } /** * @method * @name lighter#unWatchMarkPrice * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @description unWatches a mark price * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async unWatchMarkPrice(symbol, params = {}) { return await this.unWatchTicker(symbol, params); } /** * @method * @name lighter#unWatchMarkPrices * @see https://apidocs.lighter.xyz/docs/websocket-reference#market-stats * @description unWatches mark prices * @param {string[]} [symbols] unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/?id=ticker-structure} */ async unWatchMarkPrices(symbols = undefined, params = {}) { return await this.unWatchTickers(symbols, params); } parseWsTrade(trade, market = undefined) { // // { // "trade_id": 526801155, // "tx_hash": "1998d9df580acb7540aa141cc369d6ef926d003b3062196d2007bca15f978ab208e0caae4ac5872b", // "type": "trade", // "market_id": 0, // "size": "0.0346", // "price": "3028.85", // "usd_amount": "104.798210", // "ask_id": 281475673670566, // "bid_id": 562949291740362, // "ask_client_id": 76303170, // "bid_client_id": 27601, // "ask_account_id": 99349, // "bid_account_id": 243008, // "is_maker_ask": false, // "block_height": 102322769, // "timestamp": 1763623734215, // "taker_position_size_before": "0.0346", // "taker_entry_quote_before": "104.359926", // "taker_initial_margin_fraction_before": 500, // "taker_position_sign_changed": true, // "maker_fee": 20, // "maker_position_size_before": "2.1277", // "maker_entry_quote_before": "6444.179555", // "maker_initial_margin_fraction_before": 200 // } // const timestamp = this.safeInteger(trade, 'timestamp'); const tradeId = this.safeString(trade, 'trade_id'); const priceString = this.safeString(trade, 'price'); const amountString = this.safeString(trade, 'size'); const isMakerAsk = this.safeBool(trade, 'is_maker_ask'); const side = isMakerAsk ? 'buy' : 'sell'; return this.safeTrade({ 'info': trade, 'id': tradeId, 'order': undefined, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': this.safeSymbol(undefined, market), 'type': undefined, 'side': side, 'takerOrMaker': 'taker', 'price': priceString, 'amount': amountString, 'cost': this.safeString(trade, 'usd_amount'), 'fee': undefined, }, market); } handleTrades(client, message) { // // { // "channel": "trade:0", // "liquidation_trades": [], // "nonce": 3159738569, // "trades": [ // { // "trade_id": 526801155, // "tx_hash": "1998d9df580acb7540aa141cc369d6ef926d003b3062196d2007bca15f978ab208e0caae4ac5872b", // "type": "trade", // "market_id": 0, // "size": "0.0346", // "price": "3028.85", // "usd_amount": "104.798210", // "ask_id": 281475673670566, // "bid_id": 562949291740362, // "ask_client_id": 76303170, // "bid_client_id": 27601, // "ask_account_id": 99349, // "bid_account_id": 243008, // "is_maker_ask": false, // "block_height": 102322769, // "timestamp": 1763623734215, // "taker_position_size_before": "0.0346", // "taker_entry_quote_before": "104.359926", // "taker_initial_margin_fraction_before": 500, // "taker_position_sign_changed": true, // "maker_fee": 20, // "maker_position_size_before": "2.1277", // "maker_entry_quote_before": "6444.179555", // "maker_initial_margin_fraction_before": 200 // } // ], // "type": "subscribed/trade" // } // const liquidationData = this.safeList(message, 'liquidation_trades', []); const liquidationDataLength = liquidationData.length; if (liquidationDataLength > 0) { this.handleLiquidation(client, message); } const data = this.safeList(message, 'trades', []); const channel = this.safeString(message, 'channel', ''); const parts = channel.split(':'); const marketId = parts[1]; const market = this.safeMarket(marketId); const symbol = market['symbol']; let stored = this.safeValue(this.trades, symbol); if (stored === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); stored = new ArrayCache(limit); this.trades[symbol] = stored; } const dataLength = data.length; for (let i = 0; i < dataLength; i++) { const iReversed = dataLength - 1 - i; const trade = this.parseWsTrade(data[iReversed], market); stored.append(trade); } const messageHash = this.getMessageHash('trade', symbol); client.resolve(stored, messageHash); } /** * @method * @name lighter#watchTrades * @description get the list of most recent trades for a particular symbol * @see https://apidocs.lighter.xyz/docs/websocket-reference#trade * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades} */ async watchTrades(symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'trade/' + market['id'], }; const messageHash = this.getMessageHash('trade', market['symbol']); const trades = await this.subscribePublic(messageHash, this.extend(request, params)); return this.filterBySinceLimit(trades, since, limit, 'timestamp', true); } /** * @method * @name lighter#unWatchTrades * @description unsubscribe from the trades channel * @see https://apidocs.lighter.xyz/docs/websocket-reference#trade * @param {string} symbol unified symbol of the market to fetch trades for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades} */ async unWatchTrades(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'trade/' + market['id'], }; const messageHash = this.getMessageHash('unsubscribe', symbol); return await this.unsubscribe(messageHash, this.extend(request, params)); } parseWsOrderTrade(trade, market = undefined) { // // { // "trade_id": 526801155, // "tx_hash": "1998d9df580acb7540aa141cc369d6ef926d003b3062196d2007bca15f978ab208e0caae4ac5872b", // "type": "trade", // "market_id": 0, // "size": "0.0346", // "price": "3028.85", // "usd_amount": "104.798210", // "ask_id": 281475673670566, // "bid_id": 562949291740362, // "ask_client_id": 76303170, // "bid_client_id": 27601, // "ask_account_id": 99349, // "bid_account_id": 243008, // "is_maker_ask": false, // "block_height": 102322769, // "timestamp": 1763623734215, // "taker_position_size_before": "0.0346", // "taker_entry_quote_before": "104.359926", // "taker_initial_margin_fraction_before": 500, // "taker_position_sign_changed": true, // "maker_fee": 20, // "maker_position_size_before": "2.1277", // "maker_entry_quote_before": "6444.179555", // "maker_initial_margin_fraction_before": 200 // } // const timestamp = this.safeInteger(trade, 'timestamp'); const tradeId = this.safeString(trade, 'trade_id'); const priceString = this.safeString(trade, 'price'); const amountString = this.safeString(trade, 'size'); const costString = this.safeString(trade, 'usd_amount'); const isMakerAsk = this.safeBool(trade, 'is_maker_ask'); const side = isMakerAsk ? 'buy' : 'sell'; const accountIndex = this.safeInteger(trade, 'accountIndex'); let order = undefined; let takerOrMaker = undefined; if (accountIndex !== undefined) { if (this.safeInteger(trade, 'bid_account_id') === accountIndex) { order = this.safeString(trade, 'bid_id'); takerOrMaker = isMakerAsk ? 'taker' : 'maker'; } else if (this.safeInteger(trade, 'ask_account_id') === accountIndex) { order = this.safeString(trade, 'ask_id'); takerOrMaker = isMakerAsk ? 'maker' : 'taker'; } } let fee = undefined; if (takerOrMaker !== undefined) { const feeRateRaw = (takerOrMaker === 'maker') ? this.safeString(trade, 'maker_fee') : this.safeString(trade, 'taker_fee'); const feeRate = (feeRateRaw !== undefined) ? Precise.stringDiv(feeRateRaw, '1000000') : '0'; const feeAmount = Precise.stringMul(costString, feeRate); fee = { 'cost': feeAmount, 'currency': 'USDC', 'rate': feeRate, }; } return this.safeTrade({ 'info': trade, 'id': tradeId, 'order': order, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': this.safeSymbol(undefined, market), 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': priceString, 'amount': amountString, 'cost': costString, 'fee': fee, }, market); } handleMyTrades(client, message) { // // { // "channel": "account_all_trades:723310", // "trades": { // 13: [{ // "trade_id": 526801155, // "tx_hash": "1998d9df580acb7540aa141cc369d6ef926d003b3062196d2007bca15f978ab208e0caae4ac5872b", // "type": "trade", // "market_id": 0, // "size": "0.0346", // "price": "3028.85", // "usd_amount": "104.798210", // "ask_id": 281475673670566, // "bid_id": 562949291740362, // "ask_client_id": 76303170, // "bid_client_id": 27601, // "ask_account_id": 99349, // "bid_account_id": 243008, // "is_maker_ask": false, // "block_height": 102322769, // "timestamp": 1763623734215, // "taker_position_size_before": "0.0346", // "taker_entry_quote_before": "104.359926", // "taker_initial_margin_fraction_before": 500, // "taker_position_sign_changed": true, // "maker_fee": 20, // "maker_position_size_before": "2.1277", // "maker_entry_quote_before": "6444.179555", // "maker_initial_margin_fraction_before": 200 // }] // }, // "type": "update/account_all_trades" // } // const channel = this.safeString(message, 'channel', ''); const parts = channel.split(':'); const accountIndex = parts[1]; const data = this.safeDict(message, 'trades', {}); const marketIds = Object.keys(data); const idsLength = marketIds.length; if (idsLength === 0) { return false; // nothing to process } if (this.myTrades === undefined) { const limit = this.safeInteger(this.options, 'tradesLimit', 1000); this.myTrades = new ArrayCache(limit); } const stored = this.myTrades; const messageHash = this.getMessageHash('myTrades'); for (let i = 0; i < marketIds.length; i++) { const marketId = marketIds[i]; const market = this.safeMarket(marketId); const trades = this.safeList(data, marketId, []); const tradesLength = trades.length; for (let j = 0; j < tradesLength; j++) { const jReversed = tradesLength - 1 - j; const tradeRaw = trades[jReversed]; tradeRaw['accountIndex'] = accountIndex; const trade = this.parseWsOrderTrade(tradeRaw, market); stored.append(trade); const symbol = trade['symbol']; if (symbol !== undefined) { const symbolSpecificMessageHash = this.getMessageHash('myTrades', symbol); client.resolve(stored, symbolSpecificMessageHash); } } } client.resolve(stored, messageHash); return true; } /** * @method * @name lighter#watchMyTrades * @description subscribe to recent trades of an account. * @see https://apidocs.lighter.xyz/docs/websocket-reference#account-all-trades * @param {string} [symbol] unified market symbol * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades} */ async watchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let accountIndex = undefined; [accountIndex, params] = await this.handleAccountIndex(params, 'watchMyTrades', 'accountIndex', 'account_index'); let messageHash = this.getMessageHash('myTrades'); if (symbol !== undefined) { const market = this.market(symbol); symbol = market['symbol']; messageHash = this.getMessageHash('myTrades', symbol); } const request = { 'channel': 'account_all_trades/' + this.numberToString(accountIndex), }; const trades = await this.subscribePublic(messageHash, this.extend(request, params)); if (this.newUpdates) { limit = trades.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(trades, symbol, since, limit, true); } /** * @method * @name lighter#unWatchMyTrades * @description unsubscribe from the account trades channel * @see https://apidocs.lighter.xyz/docs/websocket-reference#account-all-trades * @param {string} [symbol] unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades} */ async unWatchMyTrades(symbol = undefined, params = {}) { let accountIndex = undefined; [accountIndex, params] = await this.handleAccountIndex(params, 'unWatchMyTrades', 'accountIndex', 'account_index'); let messageHash = this.getMessageHash('unsubscribe', 'myTrades'); if (symbol !== undefined) { await this.loadMarkets(); const market = this.market(symbol); symbol = market['symbol']; messageHash = this.getMessageHash('unsubscribe', symbol); } const request = { 'channel': 'account_all_trades/' + accountIndex, }; return await this.unsubscribe(messageHash, this.extend(request, params)); } parseWsLiquidation(liquidation, market = undefined) { // // { // "trade_id": 526801155, // "tx_hash": "1998d9df580acb7540aa141cc369d6ef926d003b3062196d2007bca15f978ab208e0caae4ac5872b", // "type": "liquidation", // "market_id": 0, // "size": "0.0346", // "price": "3028.85", // "usd_amount": "104.798210", // "ask_id": 281475673670566, // "bid_id": 562949291740362, // "ask_client_id": 76303170, // "bid_client_id": 27601, // "ask_account_id": 99349, // "bid_account_id": 243008, // "is_maker_ask": false, // "block_height": 102322769, // "timestamp": 1763623734215, // "taker_position_size_before": "0.0346", // "taker_entry_quote_before": "104.359926", // "taker_initial_margin_fraction_before": 500, // "taker_position_sign_changed": true, // "maker_fee": 20, // "maker_position_size_before": "2.1277", // "maker_entry_quote_before": "6444.179555", // "maker_initial_margin_fraction_before": 200 // } // const timestamp = this.safeInteger(liquidation, 'timestamp'); const isMakerAsk = this.safeBool(liquidation, 'is_maker_ask'); const side = isMakerAsk ? 'buy' : 'sell'; const contracts = this.safeString(liquidation, 'size'); const contractSize = this.safeString(market, 'contractSize'); const price = this.safeString(liquidation, 'price'); const baseValue = Precise.stringMul(contracts, contractSize); const quoteValue = Precise.stringMul(baseValue, price); return this.safeLiquidation({ 'info': liquidation, 'symbol': market['symbol'], 'contracts': contracts, 'contractSize': contractSize, 'price': price, 'side': side, 'baseValue': baseValue, 'quoteValue': quoteValue, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), }); } handleLiquidation(client, message) { // // { // "channel": "trade:0", // "liquidation_trades": [], // "nonce": 3159738569, // "trades": [ // { // "trade_id": 526801155, // "tx_hash": "1998d9df580acb7540aa141cc369d6ef926d003b3062196d2007bca15f978ab208e0caae4ac5872b", // "type": "trade", // "market_id": 0, // "size": "0.0346", // "price": "3028.85", // "usd_amount": "104.798210", // "ask_id": 281475673670566, // "bid_id": 562949291740362, // "ask_client_id": 76303170, // "bid_client_id": 27601, // "ask_account_id": 99349, // "bid_account_id": 243008, // "is_maker_ask": false, // "block_height": 102322769, // "timestamp": 1763623734215, // "taker_position_size_before": "0.0346", // "taker_entry_quote_before": "104.359926", // "taker_initial_margin_fraction_before": 500, // "taker_position_sign_changed": true, // "maker_fee": 20, // "maker_position_size_before": "2.1277", // "maker_entry_quote_before": "6444.179555", // "maker_initial_margin_fraction_before": 200 // } // ], // "type": "subscribed/trade" // } // const data = this.safeList(message, 'liquidation_trades', []); const channel = this.safeString(message, 'channel', ''); const parts = channel.split(':'); const marketId = parts[1]; const market = this.safeMarket(marketId); const symbol = market['symbol']; let stored = this.safeValue(this.liquidations, symbol); if (stored === undefined) { const limit = this.safeInteger(this.options, 'liquidationsLimit', 1000); this.liquidations = new ArrayCache(limit); stored = this.liquidations; } const dataLength = data.length; for (let i = 0; i < dataLength; i++) { const iReversed = dataLength - 1 - i; const liquidation = this.parseWsLiquidation(data[iReversed], market); stored.append(liquidation); } const messageHash = this.getMessageHash('liquidations', symbol); client.resolve(stored, messageHash); } /** * @method * @name lighter#watchLiquidations * @description watch the public liquidations of a trading pair * @see https://apidocs.lighter.xyz/docs/websocket-reference#trade * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades} */ async watchLiquidations(symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'channel': 'trade/' + market['id'], }; const messageHash = this.getMessageHash('liquidations', symbol); return await this.subscribePublic(messageHash, this.extend(request, params)); } /** * @method * @name lighter#watchBalance * @description watch balance and get the amount of funds available for trading or funds locked in orders * @see https://apidocs.lighter.xyz/docs/websocket-reference#account-all-assets * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.type] 'spot' or 'swap', default is 'swap' * @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure} */ async watchBalance(params = {}) { await this.loadMarkets(); const defaultType = this.safeString2(this.options, 'watchBalance', 'defaultType', 'spot'); let type = undefined; [type, params] = this.handleParamString(params, 'type', defaultType); let accountIndex = undefined; [accountIndex, params] = await this.handleAccountIndex(params, 'watchBalance', 'accountIndex', 'account_index'); const messageHash = this.getMessageHash('balances', undefined, type); const request = {}; if (type === 'spot') { request['channel'] = 'account_all_assets/' + this.numberToString(accountIndex); return await this.subscribePrivate(messageHash, this.extend(request, params)); } else { request['channel'] = 'user_stats/' + this.numberToString(accountIndex); return await this.subscribePublic(messageHash, this.extend(request, params)); } } handleBalance(client, message) { // // spot balance // { // "assets": { // "1": { // "symbol": "ETH", // "asset_id": 1, // "balance": "7.1072", // "locked_balance": "0.0000" // }, // "3": { // "symbol": "USDC", // "asset_id": 3, // "balance": "6343.581906", // "locked_balance": "297.000000" // } // }, // "channel": "account_all_assets:1234", // "timestamp": 1773158679717, // "type": "update/account_all_assets" // } // // swap balance // { // "channel": "user_stats:10", // "stats": { // "collateral": "5000.00", // "portfolio_value": "15000.00", // "leverage": "3.0", // "available_balance": "2000.00", // "margin_usage": "0.80", // "buying_power": "4000.00", // "account_trading_mode": 1, // "cross_stats":{ // "collateral":"0.000000", // "portfolio_value":"0.000000", // "leverage":"0.00", // "available_balance":"0.000000", // "margin_usage":"0.00", // "buying_power":"0" // }, // "total_stats":{ // "collateral":"0.000000", // "portfolio_value":"0.000000", // "leverage":"0.00", // "available_balance":"0.000000", // "margin_usage":"0.00", // "buying_power":"0" // } // }, // "timestamp": 1773158679717, // "type": "update/user_stats" // } // const channel = this.safeString(message, 'channel', ''); let type = 'spot'; if (channel.indexOf('user_stats:') >= 0) { type = 'swap'; } const balance = this.safeDict(this.balance, type, {}); if (type === 'spot') { const assets = this.safeDict(message, 'assets', {}); const assetIds = Object.keys(assets); for (let i = 0; i < assetIds.length; i++) { const assetId = assetIds[i]; const asset = assets[assetId]; const codeId = this.safeString(asset, 'symbol'); const code = this.safeCurrencyCode(codeId); const account = this.account(); account['used'] = this.safeString(asset, 'locked_balance'); account['total'] = this.safeString(asset, 'balance'); balance[code] = account; } } else { const stats = this.safeDict(message, 'stats', {}); const account = this.account(); account['free'] = this.safeString(stats, 'available_balance'); account['total'] = this.safeString(stats, 'collateral'); account['info'] = stats; balance['USDC'] = account; } const timestamp = this.safeInteger(message, 'timestamp'); balance['timestamp'] = timestamp; balance['datetime'] = this.iso8601(timestamp); this.balance[type] = this.safeBalance(balance); const messageHash = this.getMessageHash('balances', undefined, type); client.resolve(this.balance[type], messageHash); return true; } /** * @name lighter#watchOrders * @description watches information on multiple orders made by the user * @see https://apidocs.lighter.xyz/docs/websocket-reference#account-all-orders * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure} */ async watchOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let accountIndex = undefined; [accountIndex, params] = await this.handleAccountIndex(params, 'watchOrders', 'accountIndex', 'account_index'); let messageHash = undefined; const request = {}; if (symbol !== undefined) { const market = this.market(symbol); messageHash = this.getMessageHash('orders', market['symbol']); request['channel'] = 'account_orders/' + market['id'] + '/' + this.numberToString(accountIndex); } else { messageHash = this.getMessageHash('orders'); request['channel'] = 'account_all_orders/' + this.numberToString(accountIndex); } const orders = await this.subscribePrivate(messageHash, this.extend(request, params)); if (this.newUpdates) { limit = orders.getLimit(symbol, limit); } return this.filterBySymbolSinceLimit(orders, symbol, since, limit, true); } /** * @method * @name lighter#unWatchOrders * @description unWatches information on multiple orders made by the user * @see https://apidocs.lighter.xyz/docs/websocket-reference#account-all-orders * @param {string} symbol unified market symbol of the market orders were made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} a list of [order structures]{@link https://docs.ccxt.com/?id=order-structure} */ async unWatchOrders(symbol = undefined, params = {}) { await this.loadMarkets(); let accountIndex = undefined; [accountIndex, params] = await this.handleAccountIndex(params, 'watchOrders', 'accountIndex', 'account_index'); let messageHash = undefined; const request = {}; if (symbol !== undefined) { const market = this.market(symbol); messageHash = this.getMessageHash('orders', market['symbol']); request['channel'] = 'account_orders/' + market['id'] + '/' + this.numberToString(accountIndex); } else { messageHash = this.getMessageHash('orders'); request['channel'] = 'account_all_orders/' + this.numberToString(accountIndex); } return await this.unsubscribe(messageHash, this.extend(request, params)); } handleOrders(clie