UNPKG

ccxt

Version:

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

1,011 lines (1,008 loc) • 159 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var sha3_js = require('@noble/hashes/sha3.js'); var secp256k1_js = require('@noble/curves/secp256k1.js'); var grvt$1 = require('./abstract/grvt.js'); var errors = require('./base/errors.js'); var Precise = require('./base/Precise.js'); var crypto = require('./base/functions/crypto.js'); var number = require('./base/functions/number.js'); // ---------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** * @class grvt * @augments Exchange */ class grvt extends grvt$1["default"] { describe() { const rlOthers = 40; const rlOrders = 20; return this.deepExtend(super.describe(), { 'id': 'grvt', 'name': 'GRVT', 'countries': ['SG'], // Singapore 'rateLimit': 10, 'certified': false, 'version': 'v1', 'dex': true, 'pro': true, 'has': { 'CORS': undefined, 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'cancelAllOrders': true, 'cancelOrder': true, 'createOrder': true, 'fetchBalance': true, 'fetchCurrencies': true, 'fetchDeposits': true, 'fetchFundingHistory': true, 'fetchFundingRateHistory': true, 'fetchLeverages': true, 'fetchMarginModes': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchPositions': true, 'fetchTicker': true, 'fetchTrades': true, 'fetchTransfers': true, 'fetchWithdrawals': true, 'setLeverage': true, 'signIn': true, 'transfer': true, 'withdraw': true, }, 'timeframes': { '1m': 'CI_1_M', '3m': 'CI_3_M', '5m': 'CI_5_M', '15m': 'CI_15_M', '30m': 'CI_30_M', '1h': 'CI_1_H', '2h': 'CI_2_H', '4h': 'CI_4_H', '6h': 'CI_6_H', '8h': 'CI_8_H', '12h': 'CI_12_H', '1d': 'CI_1_D', '3d': 'CI_3_D', '5d': 'CI_5_D', '1w': 'CI_1_W', '2w': 'CI_2_W', '3w': 'CI_3_W', '4w': 'CI_4_W', }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/cff0d37c-e594-40cb-88b3-90650ddadc18', 'api': { 'privateEdge': 'https://edge.grvt.io/', 'privateTrading': 'https://trades.grvt.io/', 'publicMarket': 'https://market-data.grvt.io/', }, 'test': { 'privateEdge': 'https://edge.testnet.grvt.io/', 'privateTrading': 'https://trades.testnet.grvt.io/', 'publicMarket': 'https://market-data.testnet.grvt.io/', }, 'www': 'https://grvt.io', 'referral': 'https://grvt.io/?ref=WBLS9D1', 'doc': [ 'https://api-docs.grvt.io/', ], 'fees': 'https://help.grvt.io/en/articles/9614699-how-does-grvt-s-fee-model-work', }, 'api': { // RL : https://help.grvt.io/en/articles/9636566-what-are-the-rate-limitations-on-grvt 'privateEdge': { 'post': { 'auth/api_key/login': 100, 'auth/wallet/login': 100, }, }, 'publicMarket': { 'post': { 'full/v1/instrument': 4, 'full/v1/all_instruments': 4, 'full/v1/instruments': 4, 'full/v1/currency': 12, 'full/v1/margin_rules': 12, 'full/v1/mini': 4, 'full/v1/ticker': 4, 'full/v1/book': 12, 'full/v1/trade': 12, 'full/v1/trade_history': 12, 'full/v1/kline': 12, 'full/v1/funding': 12, }, }, 'privateTrading': { 'post': { 'full/v1/create_order': 5, 'full/v1/cancel_order': 5, 'full/v1/cancel_on_disconnect': 100, 'full/v1/cancel_all_orders': 50, 'full/v1/order': rlOrders, 'full/v1/order_history': rlOrders, 'full/v1/open_orders': rlOrders, 'full/v1/fill_history': rlOrders, 'full/v1/positions': rlOrders, 'full/v1/funding_payment_history': rlOthers, 'full/v1/get_sub_accounts': rlOthers, 'full/v1/account_summary': rlOthers, 'full/v1/account_history': rlOthers, 'full/v1/aggregated_account_summary': rlOthers, 'full/v1/funding_account_summary': rlOthers, 'full/v1/transfer': 100, 'full/v1/deposit_history': 100, 'full/v1/transfer_history': 100, 'full/v1/withdrawal': 100, 'full/v1/withdrawal_history': 100, 'full/v1/add_position_margin': rlOthers, // addMargin 'full/v1/get_position_margin_limits': rlOthers, 'full/v1/set_position_config': rlOthers, // setPositionMode/setMarginMode 'full/v1/set_initial_leverage': rlOthers, 'full/v1/get_all_initial_leverage': rlOthers, 'full/v1/set_derisk_mm_ratio': rlOthers, 'full/v1/vault_burn_tokens': rlOthers, 'full/v1/vault_invest': rlOthers, 'full/v1/vault_investor_summary': rlOthers, 'full/v1/vault_redeem': rlOthers, 'full/v1/vault_redeem_cancel': rlOthers, 'full/v1/vault_view_redemption_queue': rlOthers, 'full/v1/vault_manager_investor_history': rlOthers, 'full/v1/authorize_builder': rlOthers, // https://pastebin(dot)com/0Mb8cFhN 'full/v1/get_authorized_builders': rlOthers, 'full/v1/builder_fill_history': rlOthers, }, }, }, // exchange-specific options 'options': { 'accountId': undefined, // needs to be set manually by user // https://api.rhino.fi/bridge/configs 'networks': { 'ARBONE': '42161', 'AVAXC': '43114', 'BASE': '8453', 'BSC': '56', 'ETH': '1', 'ERC20': '1', 'OP': '10', 'SOL': '900', 'TRX': '728126428', 'ZKSYNCERA': '324', 'KAIA': '8217', }, 'networksById': { '1': 'ERC20', }, 'builderFee': true, 'builder': '0x21d2a053495994b1132a38cd1171acec40c6741e', 'builderRate': 0.01, }, 'precisionMode': number.TICK_SIZE, 'features': { 'default': { 'sandbox': true, 'createOrder': { 'marginMode': false, 'triggerPrice': true, 'triggerPriceType': { 'last': true, 'mark': true, 'index': true, 'median': true, // mid }, 'triggerDirection': true, 'stopLossPrice': true, 'takeProfitPrice': true, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'IOC': true, 'FOK': true, 'PO': true, 'GTD': false, }, 'hedged': false, 'leverage': false, 'marketBuyRequiresPrice': false, 'marketBuyByCost': false, 'selfTradePrevention': false, 'trailing': false, 'iceberg': false, }, 'createOrders': undefined, 'fetchMyTrades': { 'marginMode': false, 'limit': 1000, 'daysBack': 1000, 'untilDays': 1000, 'symbolRequired': false, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': { 'marginMode': false, 'limit': 1000, 'daysBack': undefined, 'untilDays': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchClosedOrders': undefined, 'fetchOHLCV': { 'limit': 1000, }, }, 'spot': undefined, 'swap': { 'linear': { 'extends': 'default', }, 'inverse': undefined, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, 'requiredCredentials': { 'privateKey': true, 'apiKey': false, 'secret': false, }, 'quoteJsonNumbers': false, // needed for some endpoints (todo: specify in implementations) 'exceptions': { 'exact': { '1000': errors.AuthenticationError, // "You need to authenticate prior to using this functionality" '1001': errors.PermissionDenied, // "You are not authorized to access this functionality" '1002': errors.OperationFailed, // "Internal Server Error" '1003': errors.BadRequest, // "Request could not be processed due to malformed syntax" '1004': errors.OperationRejected, // "Data Not Found" '1005': errors.OperationFailed, // "Unknown Error" '1006': errors.RateLimitExceeded, // "You have surpassed the allocated rate limit for your tier" '1008': errors.PermissionDenied, // "Your IP has not been whitelisted for access" '1009': errors.OperationRejected, // "We are temporarily deactivating this API endpoint, please try again later" '1012': errors.BadRequest, // "Invalid signature chain ID" '1400': errors.PermissionDenied, // "Signer does not have trade permission" '2000': errors.PermissionDenied, // "Signature is from an unauthorized signer" '2001': errors.InvalidNonce, // "Signature has expired" '2002': errors.BadRequest, // "Signature does not match payload" '2003': errors.PermissionDenied, // "Order sub account does not match logged in user" '2004': errors.InvalidNonce, // "Signature is from an expired session key" '2005': errors.BadRequest, // "Signature V must be 27/28" '2006': errors.BadRequest, // "Signature R/S must have exactly 64 characters long without 0x prefix" '2007': errors.BadRequest, // "Signature S must be in the lower half of the curve" '2008': errors.BadRequest, // "Signature exceeds maximum allowed duration." '2010': errors.InvalidOrder, // "Order ID should be empty when creating an order" '2011': errors.InvalidOrder, // "Client Order ID should be supplied when creating an order" '2012': errors.InvalidOrder, // "Client Order ID overlaps with existing active order" '2020': errors.InvalidOrder, // "Market Order must always be supplied without a limit price" '2021': errors.InvalidOrder, // "Limit Order must always be supplied with a limit price" '2030': errors.InvalidOrder, // "Orderbook Orders must have a TimeInForce of GTT/IOC/FOK" '2031': errors.InvalidOrder, // "RFQ Orders must have a TimeInForce of GTT/AON/IOC/FOK" '2032': errors.InvalidOrder, // "Post Only can only be set to true for GTT/AON orders" '2040': errors.InvalidOrder, // "Order must contain at least one leg" '2041': errors.InvalidOrder, // "Order Legs must be sorted by Derivative.Instrument/Underlying/BaseCurrency/Expiration/StrikePrice" '2042': errors.InvalidOrder, // "Orderbook Orders must contain only one leg" '2050': errors.InvalidOrder, // "Order state must be empty upon creation" '2051': errors.InvalidOrder, // "Order execution metadata must be empty upon creation" '2060': errors.BadSymbol, // "Order Legs contain one or more inactive derivative" '2061': errors.BadSymbol, // "Unsupported Instrument Requested" '2062': errors.InvalidOrder, // "Order size smaller than min size" '2063': errors.InvalidOrder, // "Order size smaller than min block size in block trade venue" '2064': errors.InvalidOrder, // "Invalid limit price tick" '2065': errors.InvalidOrder, // "Order size too granular" '2070': errors.InvalidOrder, // "Liquidation Order is not supported" '2080': errors.InsufficientFunds, // "Insufficient margin to create order" '2081': errors.OperationRejected, // "Order Fill would result in exceeding maximum position size" '2082': errors.InvalidOrder, // "Pre-order check failed" '2083': errors.OperationRejected, // "Order Fill would result in exceeding maximum position size under current configurable leverage tier" '2090': errors.RateLimitExceeded, // "Max open orders exceeded" '2100': errors.BadRequest, // "Invalid initial leverage" '2101': errors.BadRequest, // "Vaults cannot configure leverage" '2102': errors.OperationRejected, // "Margin type change failed, has open position for this instrument" '2103': errors.OperationRejected, // "Margin type change failed, has open orders for this instrument" '2104': errors.BadRequest, // "Margin type not supported" '2105': errors.BadRequest, // "Margin type change failed" '2107': errors.BadRequest, // "Attempted to set leverage below minimum" '2108': errors.BadRequest, // "Attempted to set leverage above maximum" '2110': errors.InvalidOrder, // "Invalid trigger by" '2111': errors.InvalidOrder, // "Unsupported trigger by" '2112': errors.InvalidOrder, // "Invalid trigger order" '2113': errors.InvalidOrder, // "Trigger price must be non-zero" '2114': errors.InvalidOrder, // "Invalid position linked TPSL orders, position linked TPSL must be a reduce-only order" '2115': errors.InvalidOrder, // "Invalid position linked TPSL orders, position linked TPSL must not have smaller size than the position" '2116': errors.InvalidOrder, // "Position linked TPSL order for this asset already exists" '2117': errors.InvalidOrder, // "Position linked TPSL orders must be created from web or mobile clients" '2300': errors.OperationRejected, // "Order cancel time-to-live settings currently disabled." '2301': errors.OperationRejected, // "Order cancel time-to-live exceeds maximum allowed value." '2400': errors.OperationRejected, // "Reduce only order with no position" '2401': errors.OperationRejected, // "Reduce only order must not increase position size" '2402': errors.OperationRejected, // "Reduce only order size exceeds maximum allowed value" '3000': errors.BadSymbol, // "Instrument is invalid" '3004': errors.OperationRejected, // "Instrument does not have a valid maintenance margin configuration" '3005': errors.OperationRejected, // "Instrument's underlying currency does not have a valid balance decimal configuration" '3006': errors.OperationRejected, // "Instrument's quote currency does not have a valid balance decimal configuration" '3021': errors.BadRequest, // "Either order ID or client order ID must be supplied" '3031': errors.BadRequest, // "Depth is invalid" '4000': errors.InsufficientFunds, // "Insufficient balance to complete transfer" '4002': errors.OperationFailed, // "Transfer failed with an unrefined failure reason, please report to GRVT" '4010': errors.OperationRejected, // "This wallet is not supported. Please try another wallet." '5000': errors.OperationRejected, // "Transfer Metadata does not match the expected structure." '5001': errors.OperationRejected, // "Transfer Provider does not match the expected provider." '5002': errors.OperationRejected, // "Direction of the transfer does not match the expected direction." '5003': errors.OperationRejected, // "Endpoint account ID is invalid." '5004': errors.OperationRejected, // "Funding account does not exist in our system." '5005': errors.OperationRejected, // "Invalid ChainID for the transfer request." '6000': errors.OperationRejected, // "Countdown time is bigger than 300s supported" '6100': errors.OperationRejected, // "Derisk MM Ratio is out of range" '7000': errors.OperationRejected, // "Vault ID provided is invalid and does not belong to any vault" '7001': errors.InsufficientFunds, // "Vault does not have sufficient LP token balance" '7002': errors.OperationFailed, // "User has an ongoing redemption" '7003': errors.OperationRejected, // "This vault has been delisted/closed." '7004': errors.OperationRejected, // "This investment would cause the vault to exceed its valuation cap." '7005': errors.InsufficientFunds, // "You are attempting to burn more vault tokens than you own." '7006': errors.OperationFailed, // "You are attempting to burn vault tokens whilst having an active redemption request." '7007': errors.PermissionDenied, // "The investor is not an LP for this vault." '7100': errors.OperationFailed, // "Unknown transaction type" '7101': errors.OperationRejected, // "Transfer account not found" '7102': errors.OperationRejected, // "Transfer sub-account not found" '7103': errors.OperationRejected, // "Charged trading fee below the config minimum" '7201': errors.OperationRejected, // "Attempted to create a limit order at a price outside of asset's price protection band." '7450': errors.OperationRejected, // "Add margin failed" '7451': errors.OperationRejected, // "Add margin to empty position" '7452': errors.OperationRejected, // "Add margin to non isolated position" '7453': errors.OperationRejected, // "Max addable amount exceeded" '7454': errors.OperationRejected, // "Max removable amount exceeded" '7455': errors.OperationRejected, // "Not isolated margin position" '7500': errors.OperationRejected, // "Builder Fee exceeds the allowed program limit." '7501': errors.BadRequest, // "Builder Fee can't be negative." '7502': errors.OperationRejected, // "Builder Account does not exist." '7503': errors.OperationRejected, // "Builder is already authorized for this account with the given fee." '7504': errors.OperationRejected, // "Builder is not authorized for the specified user.","status":400 }, 'broad': {}, }, }); } eipDefinitions() { return { 'EIP712_ORDER_TYPE': { 'Order': [ { 'name': 'subAccountID', 'type': 'uint64' }, { 'name': 'isMarket', 'type': 'bool' }, { 'name': 'timeInForce', 'type': 'uint8' }, { 'name': 'postOnly', 'type': 'bool' }, { 'name': 'reduceOnly', 'type': 'bool' }, { 'name': 'legs', 'type': 'OrderLeg[]' }, { 'name': 'nonce', 'type': 'uint32' }, { 'name': 'expiration', 'type': 'int64' }, ], 'OrderLeg': [ { 'name': 'assetID', 'type': 'uint256' }, { 'name': 'contractSize', 'type': 'uint64' }, { 'name': 'limitPrice', 'type': 'uint64' }, { 'name': 'isBuyingContract', 'type': 'bool' }, ], }, 'EIP712_ORDER_WITH_BUILDER_TYPE': { 'OrderWithBuilderFee': [ { 'name': 'subAccountID', 'type': 'uint64' }, { 'name': 'isMarket', 'type': 'bool' }, { 'name': 'timeInForce', 'type': 'uint8' }, { 'name': 'postOnly', 'type': 'bool' }, { 'name': 'reduceOnly', 'type': 'bool' }, { 'name': 'legs', 'type': 'OrderLeg[]' }, { 'name': 'builder', 'type': 'address' }, { 'name': 'builderFee', 'type': 'uint32' }, { 'name': 'nonce', 'type': 'uint32' }, { 'name': 'expiration', 'type': 'int64' }, ], 'OrderLeg': [ { 'name': 'assetID', 'type': 'uint256' }, { 'name': 'contractSize', 'type': 'uint64' }, { 'name': 'limitPrice', 'type': 'uint64' }, { 'name': 'isBuyingContract', 'type': 'bool' }, ], }, 'EIP712_TRANSFER_TYPE': { 'Transfer': [ { 'name': 'fromAccount', 'type': 'address' }, { 'name': 'fromSubAccount', 'type': 'uint64' }, { 'name': 'toAccount', 'type': 'address' }, { 'name': 'toSubAccount', 'type': 'uint64' }, { 'name': 'tokenCurrency', 'type': 'uint8' }, { 'name': 'numTokens', 'type': 'uint64' }, { 'name': 'nonce', 'type': 'uint32' }, { 'name': 'expiration', 'type': 'int64' }, ], }, 'EIP712_WITHDRAWAL_TYPE': { 'Withdrawal': [ { 'name': 'fromAccount', 'type': 'address' }, { 'name': 'toEthAddress', 'type': 'address' }, { 'name': 'tokenCurrency', 'type': 'uint8' }, { 'name': 'numTokens', 'type': 'uint64' }, { 'name': 'nonce', 'type': 'uint32' }, { 'name': 'expiration', 'type': 'int64' }, ], }, 'EIP712_BUILDER_APPROVAL_TYPE': { 'AuthorizeBuilder': [ { 'name': 'mainAccountID', 'type': 'address' }, { 'name': 'builderAccountID', 'type': 'address' }, { 'name': 'maxFutureFeeRate', 'type': 'uint32' }, { 'name': 'maxSpotFeeRate', 'type': 'uint32' }, { 'name': 'nonce', 'type': 'uint32' }, { 'name': 'expiration', 'type': 'int64' }, ], }, 'EIP712_WALLETLOGIN_TYPE': { 'WalletLogin': [ { 'name': 'signer', 'type': 'address' }, { 'name': 'nonce', 'type': 'uint32' }, { 'name': 'expiration', 'type': 'int64' }, ], }, }; } usesPrivateKey() { const privateKeyDefined = this.privateKey !== undefined && this.privateKey !== ''; const apiKeyDefined = this.apiKey !== undefined && this.apiKey !== ''; if (privateKeyDefined && apiKeyDefined) { throw new errors.ExchangeError('You should provide either "privateKey" or "apikey & secret"'); } return privateKeyDefined; } /** * @method * @name grvt#signIn * @description sign in, must be called prior to using other authenticated methods * @see https://api-docs.grvt.io/#authentication * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns response from exchange */ async signIn(params = {}) { // if (this.usesPrivateKey ()) { // await this.signInWithPrivateKey (params); // await this.initializeClient (params); // } else { // await this.signInWithApiKey (params); // } if (this.privateKey === undefined || this.privateKey === '') { throw new errors.PermissionDenied('Private key is required for this operation. If you used joined GRVT through email registration instead of Web3 wallet, then read: https://github.com/ccxt/ccxt/wiki/FAQ#how-to-use-the-grvt-exchange-in-ccxt'); } await this.signInWithPrivateKey(params); await this.initializeClient(params); await this.loadAccountInfos(); return true; } async signInWithApiKey(params = {}) { const now = this.milliseconds(); // expires in 24 hours as CS suggested const expires = this.safeInteger(this.options, 'signInExpiration', 0); // if previous sign-in not expired (give 10 seconds margin) if (expires !== undefined && expires > now + 10000) { return {}; } const request = { 'api_key': this.apiKey, }; const response = await this.privateEdgePostAuthApiKeyLogin(this.extend(request, params)); // // { // "location": "", // "status": "success" // } // this.options['signInExpiration'] = now + 86400000; // 24 hours return response; } async signInWithPrivateKey(params = {}) { this.checkRequiredCredentials(); const now = this.milliseconds(); // expires in 24 hours as CS suggested const expires = this.safeInteger(this.options, 'signInExpiration', 0); // if previous sign-in not expired (give 10 seconds margin) if (expires !== undefined && expires > now + 10000) { return {}; } const walletAddress = this.ethGetAddressFromPrivateKey(this.privateKey); let request = { 'address': walletAddress, 'signature': this.defaultSignature(), }; request = this.createSignedRequest(request, 'EIP712_WALLETLOGIN_TYPE'); const response = await this.privateEdgePostAuthWalletLogin(this.extend(request, params)); // // { // "location": "", // "status": "success" // } // this.options['signInExpiration'] = now + 86400000; // 24 hours return response; } async initializeClient(params = {}) { const builderFee = this.safeBool(params, 'builderFee', this.safeBool(this.options, 'builderFee', true)); // we shouldn't omit here if (!builderFee) { return false; // skip if builder fee is not enabled } const approvedBuilderFee = this.safeBool(this.options, 'approvedBuilderFee', false); if (approvedBuilderFee) { return true; // skip if builder fee is already approved } const results = await Promise.all([this.privateTradingPostFullV1GetAuthorizedBuilders(), this.loadAccountInfos()]); // // { // "results": [{ // "builder_account_id": "GRVT_MAIN_ACCOUNT_ID_HERE", // "max_futures_fee_rate": 0.001, // "max_spot_fee_rate": 0.0001 // }] // } // const currentBuilders = results[0]; const approvedBuilder = this.safeList(currentBuilders, 'results', []); const length = approvedBuilder.length; let found = false; for (let i = 0; i < length; i++) { const builderInfo = this.safeDict(approvedBuilder, i, {}); const builderAccountId = this.safeString(builderInfo, 'builder_account_id'); if (builderAccountId === this.safeString(this.options, 'builder')) { found = true; break; } } if (found) { this.options['approvedBuilderFee'] = true; } else { try { const defaultFromAccountId = this.safeString(this.options, 'userMainAccountId'); // this.ethGetAddressFromPrivateKey (this.secret); // this.safeString (this.options, 'userMainAccountId'); let request = { 'main_account_id': defaultFromAccountId, 'builder_account_id': this.safeString(this.options, 'builder'), 'max_futures_fee_rate': this.safeString(this.options, 'builderRate'), 'max_spot_fee_rate': this.safeString(this.options, 'builderRate'), 'signature': this.defaultSignature(), }; request = this.createSignedRequest(request, 'EIP712_BUILDER_APPROVAL_TYPE'); const authResponse = await this.privateTradingPostFullV1AuthorizeBuilder(this.extend(request, params)); // // { // "result": { // "ack": "true", // "tx_id":"0" // } // } // const authResult = this.safeDict(authResponse, 'result'); const ack = this.safeBool(authResult, 'ack'); if (!ack) { throw new errors.ExchangeError('Builder authorization failed, ' + this.json(authResponse)); } this.options['approvedBuilderFee'] = true; } catch (e) { this.options['builderFee'] = false; // disable builder fee if an error occurs } } return undefined; // just c# } /** * @method * @name grvt#fetchMarkets * @description retrieves data on all markets * @see https://api-docs.grvt.io/market_data_api/#get-instrument-prod * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchMarkets(params = {}) { const marketsPromise = this.publicMarketPostFullV1AllInstruments(params); // // { // "result": [ // { // "instrument": "AAVE_USDT_Perp", // "instrument_hash": "0x032201", // "base": "AAVE", // "quote": "USDT", // "kind": "PERPETUAL", // "venues": [ // "ORDERBOOK", // "RFQ" // ], // "settlement_period": "PERPETUAL", // "base_decimals": "9", // "quote_decimals": "6", // "tick_size": "0.01", // "min_size": "0.1", // "create_time": "1764303867576216941", // "max_position_size": "3000.0", // "funding_interval_hours": "8", // "adjusted_funding_rate_cap": "0.75", // "adjusted_funding_rate_floor": "-0.75" // }, // ... // const promises = [marketsPromise]; if (!this.isEmptyString(this.apiKey) || !this.isEmptyString(this.privateKey)) { promises.push(this.signIn()); } const results = await Promise.all(promises); const response = results[0]; const result = this.safeList(response, 'result', []); return this.parseMarkets(result); } parseMarket(market) { // // { // "instrument": "BTC_USDT_Perp", // "instrument_hash": "0x030501", // "base": "BTC", // "quote": "USDT", // "kind": "PERPETUAL", // "venues": [ // "ORDERBOOK", // "RFQ" // ], // "settlement_period": "PERPETUAL", // "base_decimals": 9, // "quote_decimals": 6, // "tick_size": "0.1", // "min_size": "0.001", // "create_time": "1768040726362828205", // "max_position_size": "1000.0", // "funding_interval_hours": 8, // "adjusted_funding_rate_cap": "0.3", // "adjusted_funding_rate_floor": "-0.3", // "min_notional": "100.0" // } // const marketId = this.safeString(market, 'instrument'); const baseId = this.safeString(market, 'base'); const quoteId = this.safeString(market, 'quote'); const settleId = quoteId; const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const settle = this.safeCurrencyCode(settleId); const symbol = base + '/' + quote + ':' + settle; let type = undefined; const typeRaw = this.safeString(market, 'kind'); if (typeRaw === 'PERPETUAL') { type = 'swap'; } const isSpot = (type === 'spot'); const isSwap = (type === 'swap'); const isFuture = (type === 'future'); const isContract = isSwap || isFuture; return { 'id': marketId, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': isSpot, 'margin': false, 'swap': isSwap, 'future': isFuture, 'option': false, 'active': undefined, // todo: ask support to add 'contract': isContract, 'linear': isSwap ? true : undefined, 'inverse': isSwap ? false : undefined, 'contractSize': this.parseNumber('1'), // tbd, vague response from support 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.safeNumber(market, 'min_size'), // confirmed, not 'base_decimals' 'price': this.safeNumber(market, 'tick_size'), 'base': this.parseNumber(this.parsePrecision(this.safeString(market, 'base_decimals'))), 'quote': this.parseNumber(this.parsePrecision(this.safeString(market, 'quote_decimals'))), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.safeNumber(market, 'min_size'), 'max': this.safeNumber(market, 'max_position_size'), }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber(market, 'min_notional'), 'max': undefined, }, }, 'created': this.safeIntegerProduct(market, 'create_time', 0.000001), 'info': market, }; } /** * @method * @name grvt#fetchCurrencies * @description fetches all available currencies on an exchange * @see https://api-docs.grvt.io/market_data_api/#get-currency-response * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an associative dictionary of currencies */ async fetchCurrencies(params = {}) { const request = { '': '' }; // workaround for php [] empty arr const response = await this.publicMarketPostFullV1Currency(request); // // { // "result": [ // { // "id": "4", // "symbol": "ETH", // "balance_decimals": "9", // "quantity_multiplier": "1000000000" // }, // .. // const responseResult = this.safeList(response, 'result', []); return this.parseCurrencies(responseResult); } parseCurrency(rawCurrency) { // // { // "id": "4", // "symbol": "ETH", // "balance_decimals": "9", // "quantity_multiplier": "1000000000" // }, // const id = this.safeString(rawCurrency, 'symbol'); const code = this.safeCurrencyCode(id); return this.safeCurrencyStructure({ 'info': rawCurrency, 'id': id, 'code': code, 'name': undefined, 'active': undefined, 'deposit': undefined, 'withdraw': undefined, 'fee': undefined, 'precision': this.parseNumber(this.parsePrecision(this.safeString(rawCurrency, 'balance_decimals'))), 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': undefined, 'max': undefined, }, }, 'type': 'crypto', // only crypto for now 'networks': undefined, 'numericId': this.safeInteger(rawCurrency, 'id'), }); } /** * @method * @name grvt#fetchTicker * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @see https://api-docs.grvt.io/market_data_api/#ticker_1 * @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 = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); const request = { 'instrument': this.marketId(symbol), }; const response = await this.publicMarketPostFullV1Ticker(this.extend(request, params)); // // { // "result": { // "event_time": "1764774730025055205", // "instrument": "BTC_USDT_Perp", // "mark_price": "92697.300078773", // "index_price": "92727.818122278", // "last_price": "92683.0", // "last_size": "0.001", // "mid_price": "92682.95", // "best_bid_price": "92682.9", // "best_bid_size": "5.332", // "best_ask_price": "92683.0", // "best_ask_size": "0.009", // "funding_rate_8h_curr": "0.0037", // "funding_rate_8h_avg": "0.0037", // "interest_rate": "0.0", // "forward_price": "0.0", // "buy_volume_24h_b": "2893.898", // "sell_volume_24h_b": "2907.847", // "buy_volume_24h_q": "266955739.1606", // "sell_volume_24h_q": "268170211.7109", // "high_price": "93908.3", // "low_price": "89900.1", // "open_price": "90129.2", // "open_interest": "1523.218935908", // "long_short_ratio": "1.472543", // "funding_rate": "0.0037", // "next_funding_time": "1764777600000000000" // } // } // const result = this.safeDict(response, 'result', {}); return this.parseTicker(result, market); } parseTicker(ticker, market = undefined) { // // { // "event_time": "1764774730025055205", // "instrument": "BTC_USDT_Perp", // "mark_price": "92697.300078773", // "index_price": "92727.818122278", // "last_price": "92683.0", // "last_size": "0.001", // "mid_price": "92682.95", // "best_bid_price": "92682.9", // "best_bid_size": "5.332", // "best_ask_price": "92683.0", // "best_ask_size": "0.009", // "funding_rate_8h_curr": "0.0037", // "funding_rate_8h_avg": "0.0037", // "interest_rate": "0.0", // "forward_price": "0.0", // "buy_volume_24h_b": "2893.898", // "sell_volume_24h_b": "2907.847", // "buy_volume_24h_q": "266955739.1606", // "sell_volume_24h_q": "268170211.7109", // "high_price": "93908.3", // "low_price": "89900.1", // "open_price": "90129.2", // "open_interest": "1523.218935908", // "long_short_ratio": "1.472543", // "funding_rate": "0.0037", // "next_funding_time": "1764777600000000000" // } // const marketId = this.safeString(ticker, 'instrument'); const timestamp = this.safeIntegerProduct(ticker, 'event_time', 0.000001); return this.safeTicker({ 'info': ticker, 'symbol': this.safeSymbol(marketId, market), 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'open': this.safeString(ticker, 'open_price'), 'high': this.safeString(ticker, 'high_price'), 'low': this.safeString(ticker, 'low_price'), 'last': this.safeString(ticker, 'last_price'), 'bid': this.safeString(ticker, 'best_bid_price'), 'bidVolume': this.safeString(ticker, 'best_bid_size'), 'ask': this.safeString(ticker, 'best_ask_price'), 'askVolume': this.safeString(ticker, 'best_ask_size'), 'change': undefined, 'percentage': undefined, 'baseVolume': this.safeString(ticker, 'buy_volume_24h_b'), 'quoteVolume': this.safeString(ticker, 'buy_volume_24h_q'), 'markPrice': this.safeString(ticker, 'mark_price'), 'indexPrice': this.safeString(ticker, 'index_price'), 'vwap': undefined, 'average': undefined, 'previousClose': undefined, }); } /** * @method * @name grvt#fetchOrderBook * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @see https://api-docs.grvt.io/market_data_api/#orderbook-levels * @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 * @param {string} [params.loc] crypto location, default: us * @returns {object} an [order book structure]{@link https://docs.ccxt.com/?id=order-book-structure} */ async fetchOrderBook(symbol, limit = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const request = { 'instrument': this.marketId(symbol), }; if (limit === undefined) { limit = 100; } if (limit <= 500) { request['depth'] = this.findNearestCeiling([10, 50, 100, 500], limit); } const response = await this.publicMarketPostFullV1Book(this.extend(request, params)); // // { // "result": { // "event_time": "1764777396650000000", // "instrument": "BTC_USDT_Perp", // "bids": [ // { "price": "92336.0", "size": "0.005", "num_orders": "1" }, // ... // ], // "asks": [ // { "price": "92336.1", "size": "5.711", "num_orders": "37" }, // ... // ] // } // } // const result = this.safeDict(response, 'result', {}); const timestamp = this.parse8601(this.safeString(result, 'event_time')); const marketId = this.safeString(result, 'instrument'); return this.parseOrderBook(result, this.safeSymbol(marketId), timestamp, 'bids', 'asks', 'price', 'size'); } /** * @method * @name grvt#fetchTrades * @description get the list of most recent trades for a particular symbol * @see https://api-docs.grvt.io/market_data_api/#trade_1 * @param {string} symbol unified symbol of the market * @param {int} [since] timestamp in ms of the earliest item to fetch * @param {int} [limit] the maximum amount of items to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] timestamp in ms for the ending date filter, default is the current time * @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=public-trades} */ async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { if (this.markets === undefined) { await this.loadMarkets(); } const market = this.market(symbol); let request = { 'instrument': market['id'], }; if (limit !== undefined) { request['limit'] = Math.min(limit, 1000); } [request, params] = this.handleUntilOptionString('end_time', request, params, 1000000); if (since !== undefined) { request['start_time'] = this.numberToString(since * 1000000); } const response = await this.publicMarketPostFullV1TradeHistory(this.extend(request, params)); // // { // "next": "eyJ0cmFkZUlkIjo2NDc5MTAyMywidHJhZGVJbmRleCI6MX0", // "result": [ // { // "event_time": "1764779531332118705", // "i