UNPKG

ccxt

Version:

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

1,166 lines (1,164 loc) • 114 kB
// ---------------------------------------------------------------------------- // 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 // EDIT THE CORRESPONDENT .ts FILE INSTEAD // --------------------------------------------------------------------------- import { Precise } from './base/Precise.js'; import Exchange from './abstract/paradex.js'; import { ExchangeError, PermissionDenied, AuthenticationError, BadRequest, ArgumentsRequired, OperationRejected, InvalidOrder } from './base/errors.js'; import { TICK_SIZE } from './base/functions/number.js'; import { ecdsa } from './base/functions/crypto.js'; import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js'; import { secp256k1 } from './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 */ export default class paradex extends Exchange { 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, 'fetchAllGreeks': true, '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, 'fetchGreeks': true, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchLedger': false, 'fetchLeverage': true, 'fetchLeverageTiers': false, 'fetchLiquidations': true, 'fetchMarginMode': true, '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': true, 'setMarginMode': true, '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, 'vaults': 1, 'vaults/balance': 1, 'vaults/config': 1, 'vaults/history': 1, 'vaults/positions': 1, 'vaults/summary': 1, 'vaults/transfers': 1, }, }, 'private': { 'get': { 'account': 1, 'account/info': 1, 'account/history': 1, 'account/margin': 1, 'account/profile': 1, 'account/subaccounts': 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/qr-code': 1, 'referrals/summary': 1, 'transfers': 1, 'algo/orders': 1, 'algo/orders-history': 1, 'algo/orders/{algo_id}': 1, 'vaults/account-summary': 1, }, 'post': { 'account/margin/{market}': 1, 'account/profile/max_slippage': 1, 'account/profile/referral_code': 1, 'account/profile/username': 1, 'auth': 1, 'onboarding': 1, 'orders': 1, 'orders/batch': 1, 'algo/orders': 1, 'vaults': 1, }, 'put': { 'orders/{order_id}': 1, }, 'delete': { 'orders': 1, 'orders/by_client_id/{client_id}': 1, 'orders/{order_id}': 1, 'algo/orders/{algo_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': AuthenticationError, 'BINDING_ERROR': OperationRejected, 'INTERNAL_ERROR': ExchangeError, 'NOT_FOUND': BadRequest, 'SERVICE_UNAVAILABLE': ExchangeError, 'INVALID_REQUEST_PARAMETER': BadRequest, 'ORDER_ID_NOT_FOUND': InvalidOrder, 'ORDER_IS_CLOSED': InvalidOrder, 'ORDER_IS_NOT_OPEN_YET': InvalidOrder, 'CLIENT_ORDER_ID_NOT_FOUND': InvalidOrder, 'DUPLICATED_CLIENT_ID': InvalidOrder, 'INVALID_PRICE_PRECISION': OperationRejected, 'INVALID_SYMBOL': OperationRejected, 'INVALID_TOKEN': OperationRejected, 'INVALID_ETHEREUM_ADDRESS': OperationRejected, 'INVALID_ETHEREUM_SIGNATURE': OperationRejected, 'INVALID_STARKNET_ADDRESS': OperationRejected, 'INVALID_STARKNET_SIGNATURE': OperationRejected, 'STARKNET_SIGNATURE_VERIFICATION_FAILED': AuthenticationError, 'BAD_STARKNET_REQUEST': BadRequest, 'ETHEREUM_SIGNER_MISMATCH': BadRequest, 'ETHEREUM_HASH_MISMATCH': BadRequest, 'NOT_ONBOARDED': BadRequest, 'INVALID_TIMESTAMP': BadRequest, 'INVALID_SIGNATURE_EXPIRATION': AuthenticationError, 'ACCOUNT_NOT_FOUND': AuthenticationError, 'INVALID_ORDER_SIGNATURE': AuthenticationError, 'PUBLIC_KEY_INVALID': BadRequest, 'UNAUTHORIZED_ETHEREUM_ADDRESS': BadRequest, 'ETHEREUM_ADDRESS_ALREADY_ONBOARDED': BadRequest, 'MARKET_NOT_FOUND': BadRequest, 'ALLOWLIST_ENTRY_NOT_FOUND': BadRequest, 'USERNAME_IN_USE': AuthenticationError, 'GEO_IP_BLOCK': PermissionDenied, 'ETHEREUM_ADDRESS_BLOCKED': PermissionDenied, 'PROGRAM_NOT_FOUND': BadRequest, 'INVALID_DASHBOARD': OperationRejected, 'MARKET_NOT_OPEN': BadRequest, 'INVALID_REFERRAL_CODE': OperationRejected, 'PARENT_ADDRESS_ALREADY_ONBOARDED': BadRequest, 'INVALID_PARENT_ACCOUNT': OperationRejected, 'INVALID_VAULT_OPERATOR_CHAIN': OperationRejected, 'VAULT_OPERATOR_ALREADY_ONBOARDED': OperationRejected, 'VAULT_NAME_IN_USE': OperationRejected, 'BATCH_SIZE_OUT_OF_RANGE': OperationRejected, 'ISOLATED_MARKET_ACCOUNT_MISMATCH': OperationRejected, 'POINTS_SUMMARY_NOT_FOUND': OperationRejected, '-32700': BadRequest, '-32600': BadRequest, '-32601': BadRequest, '-32602': BadRequest, '-32603': ExchangeError, '100': BadRequest, '40110': AuthenticationError, '40111': AuthenticationError, '40112': PermissionDenied, // Geo IP blocked }, 'broad': { 'missing or malformed jwt': AuthenticationError, }, }, 'precisionMode': 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" // } // // { // "symbol":"BTC-USD-96000-C", // "base_currency":"BTC", // "quote_currency":"USD", // "settlement_currency":"USDC", // "order_size_increment":"0.001", // "price_tick_size":"0.01", // "min_notional":"100", // "open_at":"1736764200000", // "expiry_at":"0", // "asset_kind":"PERP_OPTION", // "market_kind":"cross", // "position_limit":"10", // "price_bands_width":"0.05", // "iv_bands_width":"0.05", // "max_open_orders":"100", // "max_funding_rate":"0.02", // "option_cross_margin_params":{ // "imf":{ // "long_itm":"0.2", // "short_itm":"0.15", // "short_otm":"0.1", // "short_put_cap":"0.5", // "premium_multiplier":"1" // }, // "mmf":{ // "long_itm":"0.1", // "short_itm":"0.075", // "short_otm":"0.05", // "short_put_cap":"0.5", // "premium_multiplier":"0.5" // } // }, // "price_feed_id":"GVXRSBjFk6e6J3NbVPXohDJetcTjaeeuykUpbQF8UoMU", // "oracle_ewma_factor":"0.20000046249626113", // "max_order_size":"2", // "max_funding_rate_change":"0.02", // "max_tob_spread":"0.2", // "interest_rate":"0.0001", // "clamp_rate":"0.02", // "option_type":"CALL", // "strike_price":"96000", // "funding_period_hours":"24", // "tags":[ // ] // } // const assetKind = this.safeString(market, 'asset_kind'); const isOption = (assetKind === 'PERP_OPTION'); const type = (isOption) ? 'option' : 'swap'; const isSwap = (type === 'swap'); 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); let symbol = base + '/' + quote + ':' + settle; let expiry = this.safeInteger(market, 'expiry_at'); const optionType = this.safeString(market, 'option_type'); const strikePrice = this.safeString(market, 'strike_price'); const takerFee = this.parseNumber('0.0003'); let makerFee = this.parseNumber('-0.00005'); if (isOption) { const optionTypeSuffix = (optionType === 'CALL') ? 'C' : 'P'; symbol = symbol + '-' + strikePrice + '-' + optionTypeSuffix; makerFee = this.parseNumber('0.0003'); } else { expiry = undefined; } return this.safeMarketStructure({ 'id': marketId, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': false, 'margin': undefined, 'swap': isSwap, 'future': false, 'option': isOption, 'active': this.safeBool(market, 'enableTrading'), 'contract': true, 'linear': true, 'inverse': false, 'taker': takerFee, 'maker': makerFee, 'contractSize': this.parseNumber('1'), 'expiry': expiry, 'expiryDatetime': (expiry === 0) ? undefined : this.iso8601(expiry), 'strike': this.parseNumber(strikePrice), 'optionType': this.safeStringLower(market, 'option_type'), '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 = { '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": 1718170156581, // "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.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 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, keccak, 'hex'); } signHash(hash, privateKey) { const signature = ecdsa(hash.slice(-64), privateKey.slice(-64), 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 = awa