UNPKG

ccxt

Version:

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

1,176 lines (1,174 loc) • 168 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 Exchange from './abstract/hyperliquid.js'; import { ExchangeError, ArgumentsRequired, NotSupported, InvalidOrder, OrderNotFound, BadRequest, InsufficientFunds } from './base/errors.js'; import { Precise } from './base/Precise.js'; import { ROUND, SIGNIFICANT_DIGITS, DECIMAL_PLACES, TICK_SIZE } from './base/functions/number.js'; import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js'; import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js'; import { ecdsa } from './base/functions/crypto.js'; // --------------------------------------------------------------------------- /** * @class hyperliquid * @augments Exchange */ export default class hyperliquid extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'hyperliquid', 'name': 'Hyperliquid', 'countries': [], 'version': 'v1', 'rateLimit': 50, 'certified': true, 'pro': true, 'dex': true, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': true, 'future': true, 'option': false, 'addMargin': true, 'borrowCrossMargin': false, 'borrowIsolatedMargin': false, 'cancelAllOrders': false, 'cancelAllOrdersAfter': true, 'cancelOrder': true, 'cancelOrders': true, 'cancelOrdersForSymbols': true, 'closeAllPositions': false, 'closePosition': false, 'createMarketBuyOrderWithCost': false, 'createMarketOrderWithCost': false, 'createMarketSellOrderWithCost': false, 'createOrder': true, 'createOrders': true, 'createOrderWithTakeProfitAndStopLoss': true, 'createReduceOnlyOrder': true, 'createStopOrder': true, 'createTriggerOrder': true, 'editOrder': true, 'editOrders': true, 'fetchAccounts': false, 'fetchBalance': true, 'fetchBorrowInterest': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchCanceledAndClosedOrders': true, 'fetchCanceledOrders': true, 'fetchClosedOrders': true, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchCurrencies': true, 'fetchDepositAddress': false, 'fetchDepositAddresses': false, 'fetchDeposits': true, 'fetchDepositWithdrawFee': 'emulated', 'fetchDepositWithdrawFees': false, 'fetchFundingHistory': true, 'fetchFundingRate': false, 'fetchFundingRateHistory': true, 'fetchFundingRates': true, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchLedger': true, 'fetchLeverage': false, 'fetchLeverageTiers': false, 'fetchLiquidations': false, 'fetchMarginMode': undefined, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMyLiquidations': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterest': true, 'fetchOpenInterestHistory': false, 'fetchOpenInterests': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchOrderTrades': false, 'fetchPosition': true, 'fetchPositionMode': false, 'fetchPositions': true, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': 'emulated', 'fetchTickers': true, 'fetchTime': false, 'fetchTrades': true, 'fetchTradingFee': true, 'fetchTradingFees': false, 'fetchTransfer': false, 'fetchTransfers': false, 'fetchWithdrawal': false, 'fetchWithdrawals': true, 'reduceMargin': true, 'repayCrossMargin': false, 'repayIsolatedMargin': false, 'sandbox': true, 'setLeverage': true, 'setMarginMode': true, 'setPositionMode': false, 'transfer': true, 'withdraw': true, }, 'timeframes': { '1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '8h': '8h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '1M': '1M', }, 'hostname': 'hyperliquid.xyz', 'urls': { 'logo': 'https://github.com/ccxt/ccxt/assets/43336371/b371bc6c-4a8c-489f-87f4-20a913dd8d4b', 'api': { 'public': 'https://api.{hostname}', 'private': 'https://api.{hostname}', }, 'test': { 'public': 'https://api.hyperliquid-testnet.xyz', 'private': 'https://api.hyperliquid-testnet.xyz', }, 'www': 'https://hyperliquid.xyz', 'doc': 'https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api', 'fees': 'https://hyperliquid.gitbook.io/hyperliquid-docs/trading/fees', 'referral': 'https://app.hyperliquid.xyz/', }, 'api': { 'public': { 'post': { 'info': { 'cost': 20, 'byType': { 'l2Book': 2, 'allMids': 2, 'clearinghouseState': 2, 'orderStatus': 2, 'spotClearinghouseState': 2, 'exchangeStatus': 2, }, }, }, }, 'private': { 'post': { 'exchange': 1, }, }, }, 'fees': { 'swap': { 'taker': this.parseNumber('0.00045'), 'maker': this.parseNumber('0.00015'), }, 'spot': { 'taker': this.parseNumber('0.0007'), 'maker': this.parseNumber('0.0004'), }, }, 'requiredCredentials': { 'apiKey': false, 'secret': false, 'walletAddress': true, 'privateKey': true, }, 'exceptions': { 'exact': {}, 'broad': { 'Price must be divisible by tick size.': InvalidOrder, 'Order must have minimum value of $10': InvalidOrder, 'Insufficient margin to place order.': InsufficientFunds, 'Reduce only order would increase position.': InvalidOrder, 'Post only order would have immediately matched,': InvalidOrder, 'Order could not immediately match against any resting orders.': InvalidOrder, 'Invalid TP/SL price.': InvalidOrder, 'No liquidity available for market order.': InvalidOrder, 'Order was never placed, already canceled, or filled.': OrderNotFound, 'User or API Wallet ': InvalidOrder, 'Order has invalid size': InvalidOrder, 'Order price cannot be more than 80% away from the reference price': InvalidOrder, 'Order has zero size.': InvalidOrder, 'Insufficient spot balance asset': InsufficientFunds, 'Insufficient balance for withdrawal': InsufficientFunds, 'Insufficient balance for token transfer': InsufficientFunds, }, }, 'precisionMode': TICK_SIZE, 'commonCurrencies': {}, 'options': { 'defaultType': 'swap', 'sandboxMode': false, 'defaultSlippage': 0.05, 'zeroAddress': '0x0000000000000000000000000000000000000000', }, 'features': { 'default': { 'sandbox': true, 'createOrder': { 'marginMode': false, 'triggerPrice': false, 'triggerPriceType': undefined, 'triggerDirection': false, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': { 'triggerPriceType': { 'last': false, 'mark': false, 'index': false, }, 'triggerPrice': true, 'type': true, 'price': true, }, 'timeInForce': { 'IOC': true, 'FOK': false, 'PO': true, 'GTD': false, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': false, 'marketBuyRequiresPrice': false, 'selfTradePrevention': false, 'iceberg': false, }, 'createOrders': { 'max': 1000, }, 'fetchMyTrades': { 'marginMode': false, 'limit': 2000, 'daysBack': undefined, 'untilDays': undefined, 'symbolRequired': true, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': 2000, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOrders': { 'marginMode': false, 'limit': 2000, 'daysBack': undefined, 'untilDays': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchClosedOrders': { 'marginMode': false, 'limit': 2000, 'daysBack': undefined, 'daysBackCanceled': undefined, 'untilDays': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOHLCV': { 'limit': 5000, }, }, 'spot': { 'extends': 'default', }, 'forPerps': { 'extends': 'default', 'createOrder': { 'stopLossPrice': true, 'takeProfitPrice': true, 'attachedStopLossTakeProfit': undefined, // todo, in two orders }, }, 'swap': { 'linear': { 'extends': 'forPerps', }, 'inverse': { 'extends': 'forPerps', }, }, 'future': { 'linear': { 'extends': 'forPerps', }, 'inverse': { 'extends': 'forPerps', }, }, }, }); } setSandboxMode(enabled) { super.setSandboxMode(enabled); this.options['sandboxMode'] = enabled; } /** * @method * @name hyperliquid#fetchCurrencies * @description fetches all available currencies on an exchange * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-metadata * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an associative dictionary of currencies */ async fetchCurrencies(params = {}) { if (this.checkRequiredCredentials(false)) { await this.handleBuilderFeeApproval(); } const request = { 'type': 'meta', }; const response = await this.publicPostInfo(this.extend(request, params)); // // [ // { // "universe": [ // { // "maxLeverage": 50, // "name": "SOL", // "onlyIsolated": false, // "szDecimals": 2 // } // ] // } // ] // const meta = this.safeList(response, 'universe', []); const result = {}; for (let i = 0; i < meta.length; i++) { const data = this.safeDict(meta, i, {}); const id = i; const name = this.safeString(data, 'name'); const code = this.safeCurrencyCode(name); result[code] = this.safeCurrencyStructure({ 'id': id, 'name': name, 'code': code, 'precision': undefined, 'info': data, 'active': undefined, 'deposit': undefined, 'withdraw': undefined, 'networks': undefined, 'fee': undefined, 'type': 'crypto', 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, }); } return result; } /** * @method * @name hyperliquid#fetchMarkets * @description retrieves data on all markets for hyperliquid * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchMarkets(params = {}) { const rawPromises = [ this.fetchSwapMarkets(params), this.fetchSpotMarkets(params), ]; const promises = await Promise.all(rawPromises); const swapMarkets = promises[0]; const spotMarkets = promises[1]; return this.arrayConcat(swapMarkets, spotMarkets); } /** * @method * @name hyperliquid#fetchSwapMarkets * @description retrieves data on all swap markets for hyperliquid * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchSwapMarkets(params = {}) { const request = { 'type': 'metaAndAssetCtxs', }; const response = await this.publicPostInfo(this.extend(request, params)); // // [ // { // "universe": [ // { // "maxLeverage": 50, // "name": "SOL", // "onlyIsolated": false, // "szDecimals": 2 // } // ] // }, // [ // { // "dayNtlVlm": "9450588.2273", // "funding": "0.0000198", // "impactPxs": [ // "108.04", // "108.06" // ], // "markPx": "108.04", // "midPx": "108.05", // "openInterest": "10764.48", // "oraclePx": "107.99", // "premium": "0.00055561", // "prevDayPx": "111.81" // } // ] // ] // // const meta = this.safeDict(response, 0, {}); const universe = this.safeList(meta, 'universe', []); const assetCtxs = this.safeList(response, 1, []); const result = []; for (let i = 0; i < universe.length; i++) { const data = this.extend(this.safeDict(universe, i, {}), this.safeDict(assetCtxs, i, {})); data['baseId'] = i; result.push(data); } return this.parseMarkets(result); } /** * @method * @name hyperliquid#calculatePricePrecision * @description Helper function to calculate the Hyperliquid DECIMAL_PLACES price precision * @param {float} price the price to use in the calculation * @param {int} amountPrecision the amountPrecision to use in the calculation * @param {int} maxDecimals the maxDecimals to use in the calculation * @returns {int} The calculated price precision */ calculatePricePrecision(price, amountPrecision, maxDecimals) { let pricePrecision = 0; const priceStr = this.numberToString(price); if (priceStr === undefined) { return 0; } const priceSplitted = priceStr.split('.'); if (Precise.stringEq(priceStr, '0')) { // Significant digits is always 5 in this case const significantDigits = 5; // Integer digits is always 0 in this case (0 doesn't count) const integerDigits = 0; // Calculate the price precision pricePrecision = Math.min(maxDecimals - amountPrecision, significantDigits - integerDigits); } else if (Precise.stringGt(priceStr, '0') && Precise.stringLt(priceStr, '1')) { // Significant digits, always 5 in this case const significantDigits = 5; // Get the part after the decimal separator const decimalPart = this.safeString(priceSplitted, 1, ''); // Count the number of leading zeros in the decimal part let leadingZeros = 0; while ((leadingZeros <= decimalPart.length) && (decimalPart[leadingZeros] === '0')) { leadingZeros = leadingZeros + 1; } // Calculate price precision based on leading zeros and significant digits pricePrecision = leadingZeros + significantDigits; // Calculate the price precision based on maxDecimals - szDecimals and the calculated price precision from the previous step pricePrecision = Math.min(maxDecimals - amountPrecision, pricePrecision); } else { // Count the numbers before the decimal separator const integerPart = this.safeString(priceSplitted, 0, ''); // Get significant digits, take the max() of 5 and the integer digits count const significantDigits = Math.max(5, integerPart.length); // Calculate price precision based on maxDecimals - szDecimals and significantDigits - integerPart.length pricePrecision = Math.min(maxDecimals - amountPrecision, significantDigits - integerPart.length); } return this.parseToInt(pricePrecision); } /** * @method * @name hyperliquid#fetchSpotMarkets * @description retrieves data on all spot markets for hyperliquid * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchSpotMarkets(params = {}) { const request = { 'type': 'spotMetaAndAssetCtxs', }; const response = await this.publicPostInfo(this.extend(request, params)); // // [ // { // "tokens": [ // { // "name": "USDC", // "szDecimals": 8, // "weiDecimals" 8, // "index": 0, // "tokenId": "0x6d1e7cde53ba9467b783cb7c530ce054", // "isCanonical": true, // "evmContract":null, // "fullName":null // }, // { // "name": "PURR", // "szDecimals": 0, // "weiDecimals": 5, // "index": 1, // "tokenId": "0xc1fb593aeffbeb02f85e0308e9956a90", // "isCanonical": true, // "evmContract":null, // "fullName":null // } // ], // "universe": [ // { // "name": "PURR/USDC", // "tokens": [1, 0], // "index": 0, // "isCanonical": true // } // ] // }, // [ // { // "dayNtlVlm":"8906.0", // "markPx":"0.14", // "midPx":"0.209265", // "prevDayPx":"0.20432" // } // ] // ] // const first = this.safeDict(response, 0, {}); const second = this.safeList(response, 1, []); const meta = this.safeList(first, 'universe', []); const tokens = this.safeList(first, 'tokens', []); const markets = []; for (let i = 0; i < meta.length; i++) { const market = this.safeDict(meta, i, {}); const index = this.safeInteger(market, 'index'); const extraData = this.safeDict(second, index, {}); const marketName = this.safeString(market, 'name'); // if (marketName.indexOf ('/') < 0) { // // there are some weird spot markets in testnet, eg @2 // continue; // } // const marketParts = marketName.split ('/'); // const baseName = this.safeString (marketParts, 0); // const quoteId = this.safeString (marketParts, 1); const fees = this.safeDict(this.fees, 'spot', {}); const taker = this.safeNumber(fees, 'taker'); const maker = this.safeNumber(fees, 'maker'); const tokensPos = this.safeList(market, 'tokens', []); const baseTokenPos = this.safeInteger(tokensPos, 0); const quoteTokenPos = this.safeInteger(tokensPos, 1); const baseTokenInfo = this.safeDict(tokens, baseTokenPos, {}); const quoteTokenInfo = this.safeDict(tokens, quoteTokenPos, {}); const baseName = this.safeString(baseTokenInfo, 'name'); const quoteId = this.safeString(quoteTokenInfo, 'name'); const base = this.safeCurrencyCode(baseName); const quote = this.safeCurrencyCode(quoteId); const symbol = base + '/' + quote; const innerBaseTokenInfo = this.safeDict(baseTokenInfo, 'spec', baseTokenInfo); // const innerQuoteTokenInfo = this.safeDict (quoteTokenInfo, 'spec', quoteTokenInfo); const amountPrecisionStr = this.safeString(innerBaseTokenInfo, 'szDecimals'); const amountPrecision = parseInt(amountPrecisionStr); const price = this.safeNumber(extraData, 'midPx'); let pricePrecision = 0; if (price !== undefined) { pricePrecision = this.calculatePricePrecision(price, amountPrecision, 8); } const pricePrecisionStr = this.numberToString(pricePrecision); // const quotePrecision = this.parseNumber (this.parsePrecision (this.safeString (innerQuoteTokenInfo, 'szDecimals'))); const baseId = this.numberToString(index + 10000); markets.push(this.safeMarketStructure({ 'id': marketName, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': undefined, 'baseId': baseId, 'baseName': baseName, 'quoteId': quoteId, 'settleId': undefined, 'type': 'spot', 'spot': true, 'subType': undefined, 'margin': undefined, 'swap': false, 'future': false, 'option': false, 'active': true, 'contract': false, 'linear': undefined, 'inverse': undefined, 'taker': taker, 'maker': maker, 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber(this.parsePrecision(amountPrecisionStr)), 'price': this.parseNumber(this.parsePrecision(pricePrecisionStr)), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.parseNumber('10'), 'max': undefined, }, }, 'created': undefined, 'info': this.extend(extraData, market), })); } return markets; } parseMarket(market) { // // { // "maxLeverage": "50", // "name": "ETH", // "onlyIsolated": false, // "szDecimals": "4", // "dayNtlVlm": "1709813.11535", // "funding": "0.00004807", // "impactPxs": [ // "2369.3", // "2369.6" // ], // "markPx": "2369.6", // "midPx": "2369.45", // "openInterest": "1815.4712", // "oraclePx": "2367.3", // "premium": "0.00090821", // "prevDayPx": "2381.5" // } // const quoteId = 'USDC'; const baseName = this.safeString(market, 'name'); const base = this.safeCurrencyCode(baseName); const quote = this.safeCurrencyCode(quoteId); const baseId = this.safeString(market, 'baseId'); const settleId = 'USDC'; const settle = this.safeCurrencyCode(settleId); let symbol = base + '/' + quote; const contract = true; const swap = true; if (contract) { if (swap) { symbol = symbol + ':' + settle; } } const fees = this.safeDict(this.fees, 'swap', {}); const taker = this.safeNumber(fees, 'taker'); const maker = this.safeNumber(fees, 'maker'); const amountPrecisionStr = this.safeString(market, 'szDecimals'); const amountPrecision = parseInt(amountPrecisionStr); const price = this.safeNumber(market, 'markPx', 0); let pricePrecision = 0; if (price !== undefined) { pricePrecision = this.calculatePricePrecision(price, amountPrecision, 6); } const pricePrecisionStr = this.numberToString(pricePrecision); const isDelisted = this.safeBool(market, 'isDelisted'); let active = true; if (isDelisted !== undefined) { active = !isDelisted; } return this.safeMarketStructure({ 'id': baseId, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'baseName': baseName, 'quoteId': quoteId, 'settleId': settleId, 'type': 'swap', 'spot': false, 'margin': undefined, 'swap': swap, 'future': false, 'option': false, 'active': active, 'contract': contract, 'linear': true, 'inverse': false, 'taker': taker, 'maker': maker, 'contractSize': this.parseNumber('1'), 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber(this.parsePrecision(amountPrecisionStr)), 'price': this.parseNumber(this.parsePrecision(pricePrecisionStr)), }, 'limits': { 'leverage': { 'min': undefined, 'max': this.safeInteger(market, 'maxLeverage'), }, 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.parseNumber('10'), 'max': undefined, }, }, 'created': undefined, 'info': market, }); } /** * @method * @name hyperliquid#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-a-users-token-balances * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-users-perpetuals-account-summary * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {string} [params.user] user address, will default to this.walletAddress if not provided * @param {string} [params.type] wallet type, ['spot', 'swap'], defaults to swap * @param {string} [params.marginMode] 'cross' or 'isolated', for margin trading, uses this.options.defaultMarginMode if not passed, defaults to undefined/None/null * @param {string} [params.subAccountAddress] sub account user address * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async fetchBalance(params = {}) { let userAddress = undefined; [userAddress, params] = this.handlePublicAddress('fetchBalance', params); let type = undefined; [type, params] = this.handleMarketTypeAndParams('fetchBalance', undefined, params); let marginMode = undefined; [marginMode, params] = this.handleMarginModeAndParams('fetchBalance', params); const isSpot = (type === 'spot'); const request = { 'type': (isSpot) ? 'spotClearinghouseState' : 'clearinghouseState', 'user': userAddress, }; const response = await this.publicPostInfo(this.extend(request, params)); // // { // "assetPositions": [], // "crossMaintenanceMarginUsed": "0.0", // "crossMarginSummary": { // "accountValue": "100.0", // "totalMarginUsed": "0.0", // "totalNtlPos": "0.0", // "totalRawUsd": "100.0" // }, // "marginSummary": { // "accountValue": "100.0", // "totalMarginUsed": "0.0", // "totalNtlPos": "0.0", // "totalRawUsd": "100.0" // }, // "time": "1704261007014", // "withdrawable": "100.0" // } // spot // // { // "balances":[ // { // "coin":"USDC", // "hold":"0.0", // "total":"1481.844" // }, // { // "coin":"PURR", // "hold":"0.0", // "total":"999.65004" // } // } // const balances = this.safeList(response, 'balances'); if (balances !== undefined) { const spotBalances = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const code = this.safeCurrencyCode(this.safeString(balance, 'coin')); const account = this.account(); const total = this.safeString(balance, 'total'); const used = this.safeString(balance, 'hold'); account['total'] = total; account['used'] = used; spotBalances[code] = account; } return this.safeBalance(spotBalances); } const data = this.safeDict(response, 'marginSummary', {}); const usdcBalance = { 'total': this.safeNumber(data, 'accountValue'), }; if ((marginMode !== undefined) && (marginMode === 'isolated')) { usdcBalance['free'] = this.safeNumber(response, 'withdrawable'); } else { usdcBalance['used'] = this.safeNumber(data, 'totalMarginUsed'); } const result = { 'info': response, 'USDC': usdcBalance, }; const timestamp = this.safeInteger(response, 'time'); result['timestamp'] = timestamp; result['datetime'] = this.iso8601(timestamp); return this.safeBalance(result); } /** * @method * @name hyperliquid#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#l2-book-snapshot * @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 = { 'type': 'l2Book', 'coin': market['swap'] ? market['baseName'] : market['id'], }; const response = await this.publicPostInfo(this.extend(request, params)); // // { // "coin": "ETH", // "levels": [ // [ // { // "n": "2", // "px": "2216.2", // "sz": "74.0637" // } // ], // [ // { // "n": "2", // "px": "2216.5", // "sz": "70.5893" // } // ] // ], // "time": "1704290104840" // } // const data = this.safeList(response, 'levels', []); const result = { 'bids': this.safeList(data, 0, []), 'asks': this.safeList(data, 1, []), }; const timestamp = this.safeInteger(response, 'time'); return this.parseOrderBook(result, market['symbol'], timestamp, 'bids', 'asks', 'px', 'sz'); } /** * @method * @name hyperliquid#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/spot#retrieve-spot-asset-contexts * @param {string[]} [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 * @param {string} [params.type] 'spot' or 'swap', by default fetches both * @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); // at this stage, to get tickers data, we use fetchMarkets endpoints let response = []; const type = this.safeString(params, 'type'); params = this.omit(params, 'type'); if (type === 'spot') { response = await this.fetchSpotMarkets(params); } else if (type === 'swap') { response = await this.fetchSwapMarkets(params); } else { response = await this.fetchMarkets(params); } // same response as under "fetchMarkets" const result = {}; for (let i = 0; i < response.length; i++) { const market = response[i]; const info = market['info']; const ticker = this.parseTicker(info, market); const symbol = this.safeString(ticker, 'symbol'); result[symbol] = ticker; } return this.filterByArrayTickers(result, 'symbol', symbols); } /** * @method * @name hyperliquid#fetchFundingRates * @description retrieves data on all swap markets for hyperliquid * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint/perpetuals#retrieve-perpetuals-asset-contexts-includes-mark-price-current-funding-open-interest-etc * @param {string[]} [symbols] list of unified market symbols * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchFundingRates(symbols = undefined, params = {}) { const request = { 'type': 'metaAndAssetCtxs', }; const response = await this.publicPostInfo(this.extend(request, params)); // // [ // { // "universe": [ // { // "maxLeverage": 50, // "name": "SOL", // "onlyIsolated": false, // "szDecimals": 2 // } // ] // }, // [ // { // "dayNtlVlm": "9450588.2273", // "funding": "0.0000198", // "impactPxs": [ // "108.04", // "108.06" // ], // "markPx": "108.04", // "midPx": "108.05", // "openInterest": "10764.48", // "oraclePx": "107.99", // "premium": "0.00055561", // "prevDayPx": "111.81" // } // ] // ] // // const meta = this.safeDict(response, 0, {}); const universe = this.safeList(meta, 'universe', []); const assetCtxs = this.safeList(response, 1, []); const result = []; for (let i = 0; i < universe.length; i++) { const data = this.extend(this.safeDict(universe, i, {}), this.safeDict(assetCtxs, i, {})); result.push(data); } return this.parseFundingRates(result, symbols); } parseFundingRate(info, market = undefined) { // // { // "maxLeverage": "50", // "name": "ETH", // "onlyIsolated": false, // "szDecimals": "4", // "dayNtlVlm": "1709813.11535", // "funding": "0.00004807", // "impactPxs": [ // "2369.3", // "2369.6" // ], // "markPx": "2369.6", // "midPx": "2369.45", // "openInterest": "1815.4712", // "oraclePx": "2367.3", // "premium": "0.00090821", // "prevDayPx": "2381.5" // } // const base = this.safeString(info, 'name'); const marketId = this.coinToMarketId(base); const symbol = this.safeSymbol(marketId, market); const funding = this.safeNumber(info, 'funding'); const markPx = this.safeNumber(info, 'markPx'); const oraclePx = this.safeNumber(info, 'oraclePx'); const fundingTimestamp = (Math.floor(this.milliseconds() / 60 / 60 / 1000) + 1) * 60 * 60 * 1000; return { 'info': info, 'symbol': symbol, 'markPrice': markPx, 'indexPrice': oraclePx, 'interestRate': undefined, 'estimatedSettlePrice': undefined, 'timestamp': undefined, 'datetime': undefined, 'fundingRate': funding, 'fundingTimestamp': fundingTimestamp, 'fundingDatetime': this.iso8601(fundingTimestamp), 'nextFundingRate': undefined, 'nextFundingTimestamp': undefined, 'nextFundingDatetime': undefined, 'previousFundingRate': undefined, 'previousFundingTimestamp': undefined, 'previousFundingDatetime': undefined, 'interval': '1h', }; } parseTicker(ticker, market = undefined) { // // { // "prevDayPx": "3400.5", // "dayNtlVlm": "511297257.47936022", // "markPx": "3464.7", // "midPx": "3465.05", // "oraclePx": "3460.1", // only in swap // "openInterest": "64638.1108", // only in swap // "premium": "0.00141614", // only in swap // "funding": "0.00008727", // only in swap // "impactPxs": [ "3465.0", "3465.1" ], // only in swap // "coin": "PURR", // only in spot // "circulatingSupply": "998949190.03400207", // only in spot // }, // const bidAsk = this.safeList(ticker, 'impactPxs'); return this.safeTicker({ 'symbol': market['symbol'], 'timestamp': undefined, 'datetime': undefined, 'previousClose': this.safeNumber(ticker, 'prevDayPx'), 'close': this.safeNumber(ticker, 'midPx'), 'bid': this.safeNumber(bidAsk, 0), 'ask': this.safeNumber(bidAsk, 1), 'quoteVolume': this.safeNumber(ticker, 'dayNtlVlm'), 'info': ticker, }, market); } /** * @method * @name hyperliquid#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/info-endpoint#candle-snapshot * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents, support '1m', '15m', '1h', '1d' * @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 until = this.safeInteger(params, 'until', this.milliseconds()); let useTail = since === undefined; const originalSince = since; if (since === undefined) { if (limit !== undefined) { // optimization if limit is provided const timeframeInMilliseconds = this.parseTimeframe(timeframe) * 1000; since = this.sum(until, timeframeInMilliseconds * limit * -1); useTail = false; } else { since = 0; } } params = this.omit(params, ['until']); const request = { 'type': 'candleSnapshot', 'req': { 'coin': market['swap'] ? market['baseName'] : market['id'], 'interval': this.safeString(this.timeframes, timeframe, timeframe), 'startTime': since, 'endTime': until, }, }; const response = await this.publicPostInfo(this.extend(request, params)); // // [ // { // "T": 1704287699999, // "c": "2226.4", // "h": "2247.9", // "i": "15m", // "l": "2224.6", // "n": 46, // "o": "2247.9", // "s": "ETH", // "t": 1704286800000, // "v": "591.6427" // } // ] // return this.parseOHLCVs(response, market, timeframe, originalSince, limit, useTail); } parseOHLCV(ohlcv, market = undefined) { // // { // "T": 1704287699999, // "c": "2226.4", // "h": "2247.9", // "i": "15m", // "l": "2224.6", // "n": 46, // "o": "2247.9", // "s": "ETH", // "t": 1704286800000, // "v": "591.6427" // } // return [ this.safeInteger(ohlcv, 't'), this.safeNumber(ohlcv, 'o'), this.safe