UNPKG

ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 100+ exchanges

1,166 lines (1,164 loc) • 94.1 kB
'use strict'; var Precise = require('./base/Precise.js'); var paradex$1 = require('./abstract/paradex.js'); var errors = require('./base/errors.js'); var number = require('./base/functions/number.js'); var crypto = require('./base/functions/crypto.js'); var sha3 = require('./static_dependencies/noble-hashes/sha3.js'); var secp256k1 = require('./static_dependencies/noble-curves/secp256k1.js'); // ---------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** * @class paradex * @description Paradex is a decentralized exchange built on the StarkWare layer 2 scaling solution. To access private methods you can either use the ETH public key and private key by setting (exchange.privateKey and exchange.walletAddress) * or alternatively you can provide the startknet private key and public key by setting exchange.options['paradexAccount'] with add {"privateKey": A, "publicKey": B, "address": C} * @augments Exchange */ class paradex extends paradex$1 { describe() { return this.deepExtend(super.describe(), { 'id': 'paradex', 'name': 'Paradex', 'countries': [], 'version': 'v1', 'rateLimit': 50, 'certified': false, 'pro': true, 'dex': true, 'has': { 'CORS': undefined, 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'addMargin': false, 'borrowCrossMargin': false, 'borrowIsolatedMargin': false, 'cancelAllOrders': true, 'cancelAllOrdersAfter': false, 'cancelOrder': false, 'cancelOrders': false, 'cancelOrdersForSymbols': false, 'closeAllPositions': false, 'closePosition': false, 'createMarketBuyOrderWithCost': false, 'createMarketOrderWithCost': false, 'createMarketSellOrderWithCost': false, 'createOrder': true, 'createOrders': false, 'createReduceOnlyOrder': false, 'createStopOrder': true, 'createTriggerOrder': true, 'editOrder': false, 'fetchAccounts': false, 'fetchBalance': true, 'fetchBorrowInterest': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchCanceledOrders': false, 'fetchClosedOrders': false, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchCurrencies': false, 'fetchDepositAddress': false, 'fetchDepositAddresses': false, 'fetchDeposits': true, 'fetchDepositWithdrawFee': false, 'fetchDepositWithdrawFees': false, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchLedger': false, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchLiquidations': true, 'fetchMarginMode': undefined, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyLiquidations': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterest': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchOrderTrades': false, 'fetchPosition': true, 'fetchPositionMode': false, 'fetchPositions': true, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': true, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTransfer': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': true, 'reduceMargin': false, 'repayCrossMargin': false, 'repayIsolatedMargin': false, 'sandbox': true, 'setLeverage': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, 'withdraw': false, }, 'timeframes': { '1m': 1, '3m': 3, '5m': 5, '15m': 15, '30m': 30, '1h': 60, }, 'hostname': 'paradex.trade', 'urls': { 'logo': 'https://github.com/user-attachments/assets/84628770-784e-4ec4-a759-ec2fbb2244ea', 'api': { 'v1': 'https://api.prod.{hostname}/v1', }, 'test': { 'v1': 'https://api.testnet.{hostname}/v1', }, 'www': 'https://www.paradex.trade/', 'doc': 'https://docs.api.testnet.paradex.trade/', 'fees': 'https://docs.paradex.trade/getting-started/trading-fees', 'referral': 'https://app.paradex.trade/r/ccxt24', }, 'api': { 'public': { 'get': { 'bbo/{market}': 1, 'funding/data': 1, 'markets': 1, 'markets/klines': 1, 'markets/summary': 1, 'orderbook/{market}': 1, 'insurance': 1, 'referrals/config': 1, 'system/config': 1, 'system/state': 1, 'system/time': 1, 'trades': 1, }, }, 'private': { 'get': { 'account': 1, 'account/profile': 1, 'balance': 1, 'fills': 1, 'funding/payments': 1, 'positions': 1, 'tradebusts': 1, 'transactions': 1, 'liquidations': 1, 'orders': 1, 'orders-history': 1, 'orders/by_client_id/{client_id}': 1, 'orders/{order_id}': 1, 'points_data/{market}/{program}': 1, 'referrals/summary': 1, 'transfers': 1, }, 'post': { 'account/profile/referral_code': 1, 'account/profile/username': 1, 'auth': 1, 'onboarding': 1, 'orders': 1, }, 'delete': { 'orders': 1, 'orders/by_client_id/{client_id}': 1, 'orders/{order_id}': 1, }, }, }, 'fees': { 'swap': { 'taker': this.parseNumber('0.0002'), 'maker': this.parseNumber('0.0002'), }, 'spot': { 'taker': this.parseNumber('0.0002'), 'maker': this.parseNumber('0.0002'), }, }, 'requiredCredentials': { 'apiKey': false, 'secret': false, 'walletAddress': true, 'privateKey': true, }, 'exceptions': { 'exact': { 'VALIDATION_ERROR': errors.AuthenticationError, 'BINDING_ERROR': errors.OperationRejected, 'INTERNAL_ERROR': errors.ExchangeError, 'NOT_FOUND': errors.BadRequest, 'SERVICE_UNAVAILABLE': errors.ExchangeError, 'INVALID_REQUEST_PARAMETER': errors.BadRequest, 'ORDER_ID_NOT_FOUND': errors.InvalidOrder, 'ORDER_IS_CLOSED': errors.InvalidOrder, 'ORDER_IS_NOT_OPEN_YET': errors.InvalidOrder, 'CLIENT_ORDER_ID_NOT_FOUND': errors.InvalidOrder, 'DUPLICATED_CLIENT_ID': errors.InvalidOrder, 'INVALID_PRICE_PRECISION': errors.OperationRejected, 'INVALID_SYMBOL': errors.OperationRejected, 'INVALID_TOKEN': errors.OperationRejected, 'INVALID_ETHEREUM_ADDRESS': errors.OperationRejected, 'INVALID_ETHEREUM_SIGNATURE': errors.OperationRejected, 'INVALID_STARKNET_ADDRESS': errors.OperationRejected, 'INVALID_STARKNET_SIGNATURE': errors.OperationRejected, 'STARKNET_SIGNATURE_VERIFICATION_FAILED': errors.AuthenticationError, 'BAD_STARKNET_REQUEST': errors.BadRequest, 'ETHEREUM_SIGNER_MISMATCH': errors.BadRequest, 'ETHEREUM_HASH_MISMATCH': errors.BadRequest, 'NOT_ONBOARDED': errors.BadRequest, 'INVALID_TIMESTAMP': errors.BadRequest, 'INVALID_SIGNATURE_EXPIRATION': errors.AuthenticationError, 'ACCOUNT_NOT_FOUND': errors.AuthenticationError, 'INVALID_ORDER_SIGNATURE': errors.AuthenticationError, 'PUBLIC_KEY_INVALID': errors.BadRequest, 'UNAUTHORIZED_ETHEREUM_ADDRESS': errors.BadRequest, 'ETHEREUM_ADDRESS_ALREADY_ONBOARDED': errors.BadRequest, 'MARKET_NOT_FOUND': errors.BadRequest, 'ALLOWLIST_ENTRY_NOT_FOUND': errors.BadRequest, 'USERNAME_IN_USE': errors.AuthenticationError, 'GEO_IP_BLOCK': errors.PermissionDenied, 'ETHEREUM_ADDRESS_BLOCKED': errors.PermissionDenied, 'PROGRAM_NOT_FOUND': errors.BadRequest, 'INVALID_DASHBOARD': errors.OperationRejected, 'MARKET_NOT_OPEN': errors.BadRequest, 'INVALID_REFERRAL_CODE': errors.OperationRejected, 'PARENT_ADDRESS_ALREADY_ONBOARDED': errors.BadRequest, 'INVALID_PARENT_ACCOUNT': errors.OperationRejected, 'INVALID_VAULT_OPERATOR_CHAIN': errors.OperationRejected, 'VAULT_OPERATOR_ALREADY_ONBOARDED': errors.OperationRejected, 'VAULT_NAME_IN_USE': errors.OperationRejected, 'BATCH_SIZE_OUT_OF_RANGE': errors.OperationRejected, 'ISOLATED_MARKET_ACCOUNT_MISMATCH': errors.OperationRejected, 'POINTS_SUMMARY_NOT_FOUND': errors.OperationRejected, '-32700': errors.BadRequest, '-32600': errors.BadRequest, '-32601': errors.BadRequest, '-32602': errors.BadRequest, '-32603': errors.ExchangeError, '100': errors.BadRequest, '40110': errors.AuthenticationError, '40111': errors.AuthenticationError, '40112': errors.PermissionDenied, // Geo IP blocked }, 'broad': { 'missing or malformed jwt': errors.AuthenticationError, }, }, 'precisionMode': number.TICK_SIZE, 'commonCurrencies': {}, 'options': { 'paradexAccount': undefined, 'broker': 'CCXT', }, 'features': { 'spot': undefined, 'forSwap': { 'sandbox': true, 'createOrder': { 'marginMode': false, 'triggerPrice': true, 'triggerDirection': true, 'triggerPriceType': undefined, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'IOC': true, 'FOK': false, 'PO': true, 'GTD': false, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': false, 'marketBuyRequiresPrice': false, 'selfTradePrevention': true, 'iceberg': false, }, 'createOrders': undefined, 'fetchMyTrades': { 'marginMode': false, 'limit': 100, 'daysBack': 100000, 'untilDays': 100000, 'symbolRequired': false, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': 100, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': { 'marginMode': false, 'limit': 100, 'daysBack': 100000, 'untilDays': 100000, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchClosedOrders': undefined, 'fetchOHLCV': { 'limit': undefined, // todo by from/to }, }, 'swap': { 'linear': { 'extends': 'forSwap', }, 'inverse': undefined, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, }); } /** * @method * @name paradex#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @see https://docs.api.testnet.paradex.trade/#get-system-time-unix-milliseconds * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {int} the current integer timestamp in milliseconds from the exchange server */ async fetchTime(params = {}) { const response = await this.publicGetSystemTime(params); // // { // "server_time": "1681493415023" // } // return this.safeInteger(response, 'server_time'); } /** * @method * @name paradex#fetchStatus * @description the latest known information on the availability of the exchange API * @see https://docs.api.testnet.paradex.trade/#get-system-state * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [status structure]{@link https://docs.ccxt.com/#/?id=exchange-status-structure} */ async fetchStatus(params = {}) { const response = await this.publicGetSystemState(params); // // { // "status": "ok" // } // const status = this.safeString(response, 'status'); return { 'status': (status === 'ok') ? 'ok' : 'maintenance', 'updated': undefined, 'eta': undefined, 'url': undefined, 'info': response, }; } /** * @method * @name paradex#fetchMarkets * @description retrieves data on all markets for bitget * @see https://docs.api.testnet.paradex.trade/#list-available-markets * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchMarkets(params = {}) { const response = await this.publicGetMarkets(params); // // { // "results": [ // { // "symbol": "BODEN-USD-PERP", // "base_currency": "BODEN", // "quote_currency": "USD", // "settlement_currency": "USDC", // "order_size_increment": "1", // "price_tick_size": "0.00001", // "min_notional": "200", // "open_at": 1717065600000, // "expiry_at": 0, // "asset_kind": "PERP", // "position_limit": "2000000", // "price_bands_width": "0.2", // "max_open_orders": 50, // "max_funding_rate": "0.05", // "delta1_cross_margin_params": { // "imf_base": "0.2", // "imf_shift": "180000", // "imf_factor": "0.00071", // "mmf_factor": "0.5" // }, // "price_feed_id": "9LScEHse1ioZt2rUuhwiN6bmYnqpMqvZkQJDNUpxVHN5", // "oracle_ewma_factor": "0.14999987905913592", // "max_order_size": "520000", // "max_funding_rate_change": "0.0005", // "max_tob_spread": "0.2" // } // ] // } // const data = this.safeList(response, 'results'); return this.parseMarkets(data); } parseMarket(market) { // // { // "symbol": "BODEN-USD-PERP", // "base_currency": "BODEN", // "quote_currency": "USD", // "settlement_currency": "USDC", // "order_size_increment": "1", // "price_tick_size": "0.00001", // "min_notional": "200", // "open_at": 1717065600000, // "expiry_at": 0, // "asset_kind": "PERP", // "position_limit": "2000000", // "price_bands_width": "0.2", // "max_open_orders": 50, // "max_funding_rate": "0.05", // "delta1_cross_margin_params": { // "imf_base": "0.2", // "imf_shift": "180000", // "imf_factor": "0.00071", // "mmf_factor": "0.5" // }, // "price_feed_id": "9LScEHse1ioZt2rUuhwiN6bmYnqpMqvZkQJDNUpxVHN5", // "oracle_ewma_factor": "0.14999987905913592", // "max_order_size": "520000", // "max_funding_rate_change": "0.0005", // "max_tob_spread": "0.2" // } // const marketId = this.safeString(market, 'symbol'); const quoteId = this.safeString(market, 'quote_currency'); const baseId = this.safeString(market, 'base_currency'); const quote = this.safeCurrencyCode(quoteId); const base = this.safeCurrencyCode(baseId); const settleId = this.safeString(market, 'settlement_currency'); const settle = this.safeCurrencyCode(settleId); const symbol = base + '/' + quote + ':' + settle; const expiry = this.safeInteger(market, 'expiry_at'); const takerFee = this.parseNumber('0.0003'); const makerFee = this.parseNumber('-0.00005'); return this.safeMarketStructure({ 'id': marketId, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': 'swap', 'spot': false, 'margin': undefined, 'swap': true, 'future': false, 'option': false, 'active': this.safeBool(market, 'enableTrading'), 'contract': true, 'linear': true, 'inverse': undefined, 'taker': takerFee, 'maker': makerFee, 'contractSize': this.parseNumber('1'), 'expiry': (expiry === 0) ? undefined : expiry, 'expiryDatetime': (expiry === 0) ? undefined : this.iso8601(expiry), 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.safeNumber(market, 'order_size_increment'), 'price': this.safeNumber(market, 'price_tick_size'), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': undefined, 'max': this.safeNumber(market, 'max_order_size'), }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber(market, 'min_notional'), 'max': undefined, }, }, 'created': undefined, 'info': market, }); } /** * @method * @name paradex#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://docs.api.testnet.paradex.trade/#ohlcv-for-a-symbol * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} 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 {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] timestamp in ms of the latest candle to fetch * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume */ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'resolution': this.safeString(this.timeframes, timeframe, timeframe), 'symbol': market['id'], }; const now = this.milliseconds(); const duration = this.parseTimeframe(timeframe); const until = this.safeInteger2(params, 'until', 'till', now); params = this.omit(params, ['until', 'till']); if (since !== undefined) { request['start_at'] = since; if (limit !== undefined) { request['end_at'] = this.sum(since, duration * (limit + 1) * 1000) - 1; } else { request['end_at'] = until; } } else { request['end_at'] = until; if (limit !== undefined) { request['start_at'] = until - duration * (limit + 1) * 1000 + 1; } else { request['start_at'] = until - duration * 101 * 1000 + 1; } } const response = await this.publicGetMarketsKlines(this.extend(request, params)); // // { // "results": [ // [ // 1720071900000, // 58961.3, // 58961.3, // 58961.3, // 58961.3, // 1591 // ] // ] // } // const data = this.safeList(response, 'results', []); return this.parseOHLCVs(data, market, timeframe, since, limit); } parseOHLCV(ohlcv, market = undefined) { // // [ // 1720071900000, // 58961.3, // 58961.3, // 58961.3, // 58961.3, // 1591 // ] // return [ this.safeInteger(ohlcv, 0), this.safeNumber(ohlcv, 1), this.safeNumber(ohlcv, 2), this.safeNumber(ohlcv, 3), this.safeNumber(ohlcv, 4), this.safeNumber(ohlcv, 5), ]; } /** * @method * @name paradex#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://docs.api.testnet.paradex.trade/#list-available-markets-summary * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async fetchTickers(symbols = undefined, params = {}) { await this.loadMarkets(); symbols = this.marketSymbols(symbols); const request = {}; if (symbols !== undefined) { if (Array.isArray(symbols)) { request['market'] = this.marketId(symbols[0]); } else { request['market'] = this.marketId(symbols); } } else { request['market'] = 'ALL'; } const response = await this.publicGetMarketsSummary(this.extend(request, params)); // // { // "results": [ // { // "symbol": "BTC-USD-PERP", // "oracle_price": "68465.17449906", // "mark_price": "68465.17449906", // "last_traded_price": "68495.1", // "bid": "68477.6", // "ask": "69578.2", // "volume_24h": "5815541.397939004", // "total_volume": "584031465.525259686", // "created_at": 1718170156580, // "underlying_price": "67367.37268422", // "open_interest": "162.272", // "funding_rate": "0.01629574927887", // "price_change_rate_24h": "0.009032" // } // ] // } // const data = this.safeList(response, 'results', []); return this.parseTickers(data, symbols); } /** * @method * @name paradex#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://docs.api.testnet.paradex.trade/#list-available-markets-summary * @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 fetchTicker(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'market': market['id'], }; const response = await this.publicGetMarketsSummary(this.extend(request, params)); // // { // "results": [ // { // "symbol": "BTC-USD-PERP", // "oracle_price": "68465.17449906", // "mark_price": "68465.17449906", // "last_traded_price": "68495.1", // "bid": "68477.6", // "ask": "69578.2", // "volume_24h": "5815541.397939004", // "total_volume": "584031465.525259686", // "created_at": 1718170156580, // "underlying_price": "67367.37268422", // "open_interest": "162.272", // "funding_rate": "0.01629574927887", // "price_change_rate_24h": "0.009032" // } // ] // } // const data = this.safeList(response, 'results', []); const ticker = this.safeDict(data, 0, {}); return this.parseTicker(ticker, market); } parseTicker(ticker, market = undefined) { // // { // "symbol": "BTC-USD-PERP", // "oracle_price": "68465.17449906", // "mark_price": "68465.17449906", // "last_traded_price": "68495.1", // "bid": "68477.6", // "ask": "69578.2", // "volume_24h": "5815541.397939004", // "total_volume": "584031465.525259686", // "created_at": 1718170156580, // "underlying_price": "67367.37268422", // "open_interest": "162.272", // "funding_rate": "0.01629574927887", // "price_change_rate_24h": "0.009032" // } // let percentage = this.safeString(ticker, 'price_change_rate_24h'); if (percentage !== undefined) { percentage = Precise["default"].stringMul(percentage, '100'); } const last = this.safeString(ticker, 'last_traded_price'); const marketId = this.safeString(ticker, 'symbol'); market = this.safeMarket(marketId, market); const symbol = market['symbol']; const timestamp = this.safeInteger(ticker, 'created_at'); return this.safeTicker({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeString(ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeString(ticker, 'ask'), 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': percentage, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': this.safeString(ticker, 'volume_24h'), 'markPrice': this.safeString(ticker, 'mark_price'), 'info': ticker, }, market); } /** * @method * @name paradex#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://docs.api.testnet.paradex.trade/#get-market-orderbook * @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 fetchOrderBook(symbol, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'market': market['id'] }; const response = await this.publicGetOrderbookMarket(this.extend(request, params)); // // { // "market": "BTC-USD-PERP", // "seq_no": 14115975, // "last_updated_at": 1718172538340, // "asks": [ // [ // "69578.2", // "3.019" // ] // ], // "bids": [ // [ // "68477.6", // "0.1" // ] // ] // } // if (limit !== undefined) { request['depth'] = limit; } const timestamp = this.safeInteger(response, 'last_updated_at'); const orderbook = this.parseOrderBook(response, market['symbol'], timestamp); orderbook['nonce'] = this.safeInteger(response, 'seq_no'); return orderbook; } /** * @method * @name paradex#fetchTrades * @description get the list of most recent trades for a particular symbol * @see https://docs.api.testnet.paradex.trade/#trade-tape * @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 * @param {int} [params.until] the latest time in ms to fetch trades for * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades} */ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets(); let paginate = false; [paginate, params] = this.handleOptionAndParams(params, 'fetchTrades', 'paginate'); if (paginate) { return await this.fetchPaginatedCallCursor('fetchTrades', symbol, since, limit, params, 'next', 'cursor', undefined, 100); } const market = this.market(symbol); let request = { 'market': market['id'], }; if (limit !== undefined) { request['page_size'] = limit; } if (since !== undefined) { request['start_at'] = since; } [request, params] = this.handleUntilOption('end_at', request, params); const response = await this.publicGetTrades(this.extend(request, params)); // // { // "next": "...", // "prev": "...", // "results": [ // { // "id": "1718154353750201703989430001", // "market": "BTC-USD-PERP", // "side": "BUY", // "size": "0.026", // "price": "69578.2", // "created_at": 1718154353750, // "trade_type": "FILL" // } // ] // } // const trades = this.safeList(response, 'results', []); for (let i = 0; i < trades.length; i++) { trades[i]['next'] = this.safeString(response, 'next'); } return this.parseTrades(trades, market, since, limit); } parseTrade(trade, market = undefined) { // // fetchTrades (public) // // { // "id": "1718154353750201703989430001", // "market": "BTC-USD-PERP", // "side": "BUY", // "size": "0.026", // "price": "69578.2", // "created_at": 1718154353750, // "trade_type": "FILL" // } // // fetchMyTrades (private) // // { // "id": "1718947571560201703986670001", // "side": "BUY", // "liquidity": "TAKER", // "market": "BTC-USD-PERP", // "order_id": "1718947571540201703992340000", // "price": "64852.9", // "size": "0.01", // "fee": "0.1945587", // "fee_currency": "USDC", // "created_at": 1718947571569, // "remaining_size": "0", // "client_id": "", // "fill_type": "FILL" // } // const marketId = this.safeString(trade, 'market'); market = this.safeMarket(marketId, market); const id = this.safeString(trade, 'id'); const timestamp = this.safeInteger(trade, 'created_at'); const priceString = this.safeString(trade, 'price'); const amountString = this.safeString(trade, 'size'); const side = this.safeStringLower(trade, 'side'); const liability = this.safeStringLower(trade, 'liquidity', 'taker'); const isTaker = liability === 'taker'; const takerOrMaker = (isTaker) ? 'taker' : 'maker'; const currencyId = this.safeString(trade, 'fee_currency'); const code = this.safeCurrencyCode(currencyId); return this.safeTrade({ 'info': trade, 'id': id, 'order': this.safeString(trade, 'order_id'), 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': market['symbol'], 'type': undefined, 'takerOrMaker': takerOrMaker, 'side': side, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': { 'cost': this.safeString(trade, 'fee'), 'currency': code, 'rate': undefined, }, }, market); } /** * @method * @name paradex#fetchOpenInterest * @description retrieves the open interest of a contract trading pair * @see https://docs.api.testnet.paradex.trade/#list-available-markets-summary * @param {string} symbol unified CCXT market symbol * @param {object} [params] exchange specific parameters * @returns {object} an open interest structure{@link https://docs.ccxt.com/#/?id=open-interest-structure} */ async fetchOpenInterest(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); if (!market['contract']) { throw new errors.BadRequest(this.id + ' fetchOpenInterest() supports contract markets only'); } const request = { 'market': market['id'], }; const response = await this.publicGetMarketsSummary(this.extend(request, params)); // // { // "results": [ // { // "symbol": "BTC-USD-PERP", // "oracle_price": "68465.17449906", // "mark_price": "68465.17449906", // "last_traded_price": "68495.1", // "bid": "68477.6", // "ask": "69578.2", // "volume_24h": "5815541.397939004", // "total_volume": "584031465.525259686", // "created_at": 1718170156580, // "underlying_price": "67367.37268422", // "open_interest": "162.272", // "funding_rate": "0.01629574927887", // "price_change_rate_24h": "0.009032" // } // ] // } // const data = this.safeList(response, 'results', []); const interest = this.safeDict(data, 0, {}); return this.parseOpenInterest(interest, market); } parseOpenInterest(interest, market = undefined) { // // { // "symbol": "BTC-USD-PERP", // "oracle_price": "68465.17449904", // "mark_price": "68465.17449906", // "last_traded_price": "68495.1", // "bid": "68477.6", // "ask": "69578.2", // "volume_24h": "5815541.397939004", // "total_volume": "584031465.525259686", // "created_at": 1718170156580, // "underlying_price": "67367.37268422", // "open_interest": "162.272", // "funding_rate": "0.01629574927887", // "price_change_rate_24h": "0.009032" // } // const timestamp = this.safeInteger(interest, 'created_at'); const marketId = this.safeString(interest, 'symbol'); market = this.safeMarket(marketId, market); const symbol = market['symbol']; return this.safeOpenInterest({ 'symbol': symbol, 'openInterestAmount': this.safeString(interest, 'open_interest'), 'openInterestValue': undefined, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'info': interest, }, market); } hashMessage(message) { return '0x' + this.hash(message, sha3.keccak_256, 'hex'); } signHash(hash, privateKey) { const signature = crypto.ecdsa(hash.slice(-64), privateKey.slice(-64), secp256k1.secp256k1, undefined); const r = signature['r']; const s = signature['s']; const v = this.intToBase16(this.sum(27, signature['v'])); return '0x' + r.padStart(64, '0') + s.padStart(64, '0') + v; } signMessage(message, privateKey) { return this.signHash(this.hashMessage(message), privateKey.slice(-64)); } async getSystemConfig() { const cachedConfig = this.safeDict(this.options, 'systemConfig'); if (cachedConfig !== undefined) { return cachedConfig; } const response = await this.publicGetSystemConfig(); // // { // "starknet_gateway_url": "https://potc-testnet-sepolia.starknet.io", // "starknet_fullnode_rpc_url": "https://pathfinder.api.testnet.paradex.trade/rpc/v0_7", // "starknet_chain_id": "PRIVATE_SN_POTC_SEPOLIA", // "block_explorer_url": "https://voyager.testnet.paradex.trade/", // "paraclear_address": "0x286003f7c7bfc3f94e8f0af48b48302e7aee2fb13c23b141479ba00832ef2c6", // "paraclear_decimals": 8, // "paraclear_account_proxy_hash": "0x3530cc4759d78042f1b543bf797f5f3d647cde0388c33734cf91b7f7b9314a9", // "paraclear_account_hash": "0x41cb0280ebadaa75f996d8d92c6f265f6d040bb3ba442e5f86a554f1765244e", // "oracle_address": "0x2c6a867917ef858d6b193a0ff9e62b46d0dc760366920d631715d58baeaca1f", // "bridged_tokens": [ // { // "name": "TEST USDC", // "symbol": "USDC", // "decimals": 6, // "l1_token_address": "0x29A873159D5e14AcBd63913D4A7E2df04570c666", // "l1_bridge_address": "0x8586e05adc0C35aa11609023d4Ae6075Cb813b4C", // "l2_token_address": "0x6f373b346561036d98ea10fb3e60d2f459c872b1933b50b21fe6ef4fda3b75e", // "l2_bridge_address": "0x46e9237f5408b5f899e72125dd69bd55485a287aaf24663d3ebe00d237fc7ef" // } // ], // "l1_core_contract_address": "0x582CC5d9b509391232cd544cDF9da036e55833Af", // "l1_operator_address": "0x11bACdFbBcd3Febe5e8CEAa75E0Ef6444d9B45FB", // "l1_chain_id": "11155111", // "liquidation_fee": "0.2" // } // this.options['systemConfig'] = response; return response; } async prepareParadexDomain(l1 = false) { const systemConfig = await this.getSystemConfig(); if (l1 === true) { const l1D = { 'name': 'Paradex', 'chainId': systemConfig['l1_chain_id'], 'version': '1', }; return l1D; } const domain = { 'name': 'Paradex', 'chainId': systemConfig['starknet_chain_id'], 'version': 1, }; return domain; } async retrieveAccount() { const cachedAccount = this.safeDict(this.options, 'paradexAccount'); if (cachedAccount !== undefined) { return cachedAccount; } this.checkRequiredCredentials(); const systemConfig = await this.getSystemConfig(); const domain = await this.prepareParadexDomain(true); const messageTypes = { 'Constant': [ { 'name': 'action', 'type': 'string' }, ], }; const message = { 'action': 'STARK Key', }; const msg = this.ethEncodeStructuredData(domain, messageTypes, message); const signature = this.signMessage(msg, this.privateKey); const account = this.retrieveStarkAccount(signature, systemConfig['paraclear_account_hash'], systemConfig['paraclear_account_proxy_hash']); this.options['paradexAccount'] = account; return account; } async onboarding(params = {}) { const account = await this.retrieveAccount(); const req = { 'action': 'Onboarding', }; const domain = await this.prepareParadexDomain(); const messageTypes = { 'Constant': [ { 'name': 'action', 'type': 'felt' }, ], }; const msg = this.starknetEncodeStructuredData(domain, messageTypes, req, account['address']); const signature = this.starknetSign(msg, account['privateKey']); params['signature'] = signature; params['account'] = account['address']; params['public_key'] = account['publicKey']; const response = await this.privatePostOnboarding(params); return response; } async authenticateRest(params = {}) { const cachedToken = this.safeString(this.options, 'authToken'); const now = this.nonce(); if (cachedToken !== undefined) { const cachedExpires = this.safeInteger(this.options, 'expires'); if (now < cachedExpires) { return cachedToken; } } const account = await this.retrieveAccount(); // https://docs.paradex.trade/api-reference/general-information/authentication const expires = now + 180; const req = { 'method': 'POST', 'path': '/v1/auth', 'body': '', 'timestamp': now, 'expiration': expires, }; const domain = await this.prepareParadexDomain(); const messageTypes = { 'Request': [ { 'name': 'method', 'type': 'felt' }, { 'name': 'path', 'type': 'felt' }, { 'name': 'body', 'type': 'felt' }, { 'name': 'timestamp', 'type': 'felt' }, { 'name': 'expiration', 'type': 'felt' }, ], }; const msg = this.starknetEncodeStructuredData(domain, messageTypes, req, account['address']); const signature = this.starknetSign(msg, account['privateKey']); params['signature'] = signature; params['account'] = account['address']; params['timestamp'] = req['timestamp']; params['expiration'] = req['expiration']; const response = await this.privatePostAuth(params); // // { // jwt_token: "ooooccxtooootoooootheoooomoonooooo" // } // const token = this.safeString(response, 'jwt_token'); this.options['authToken'] = token; this.options['expires'] = expires; return token; } parseOrder(order, market = undefined) { // // { // "account": "0x4638e3041366aa71720be63e32e53e1223316c7f0d56f7aa617542ed1e7512x", // "avg_fill_price": "26000", // "client_id": "x1234", // "cancel_reason": "NOT_ENOUGH_MARGIN", // "created_at": 1681493746016, // "flags": [ // "REDUCE_ONLY" // ], // "id": "123456", // "instruction": "GTC", // "last_updated_at": 1681493746016, // "market": "BTC-USD-PERP", // "price": "26000", // "published_at": 1681493746016, // "received_at": 1681493746016, // "remaining_size": "0", // "seq_no": 1681471234972000000, // "side": "BUY", // "size": "0.05", // "status"