UNPKG

ccxt

Version:

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

1,138 lines (1,137 loc) • 152 kB
// --------------------------------------------------------------------------- import Exchange from './abstract/grvt.js'; import { ExchangeError, ArgumentsRequired, InsufficientFunds, InvalidOrder, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, BadRequest, BadSymbol, OperationFailed, OperationRejected } from './base/errors.js'; import { Precise } from './base/Precise.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'; import { TICK_SIZE } from './base/functions/number.js'; // --------------------------------------------------------------------------- /** * @class grvt * @augments Exchange */ export default class grvt extends Exchange { describe() { const rlOthers = 40; const rlOrders = 20; return this.deepExtend(super.describe(), { 'id': 'grvt', 'name': 'GRVT', 'countries': ['SG'], '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/7a2e8108-29f6-45d1-822d-48eb1c8cbbe6', '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, 'full/v1/get_position_margin_limits': rlOthers, 'full/v1/set_position_config': rlOthers, '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, 'full/v1/get_authorized_builders': rlOthers, 'full/v1/builder_fill_history': rlOthers, }, }, }, // exchange-specific options 'options': { 'accountId': undefined, // 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': 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, 'exceptions': { 'exact': { '1000': AuthenticationError, '1001': PermissionDenied, '1002': OperationFailed, '1003': BadRequest, '1004': OperationRejected, '1005': OperationFailed, '1006': RateLimitExceeded, '1008': PermissionDenied, '1009': OperationRejected, '1012': BadRequest, '1400': PermissionDenied, '2000': PermissionDenied, '2001': InvalidNonce, '2002': BadRequest, '2003': PermissionDenied, '2004': InvalidNonce, '2005': BadRequest, '2006': BadRequest, '2007': BadRequest, '2008': BadRequest, '2010': InvalidOrder, '2011': InvalidOrder, '2012': InvalidOrder, '2020': InvalidOrder, '2021': InvalidOrder, '2030': InvalidOrder, '2031': InvalidOrder, '2032': InvalidOrder, '2040': InvalidOrder, '2041': InvalidOrder, '2042': InvalidOrder, '2050': InvalidOrder, '2051': InvalidOrder, '2060': BadSymbol, '2061': BadSymbol, '2062': InvalidOrder, '2063': InvalidOrder, '2064': InvalidOrder, '2065': InvalidOrder, '2070': InvalidOrder, '2080': InsufficientFunds, '2081': OperationRejected, '2082': InvalidOrder, '2083': OperationRejected, '2090': RateLimitExceeded, '2100': BadRequest, '2101': BadRequest, '2102': OperationRejected, '2103': OperationRejected, '2104': BadRequest, '2105': BadRequest, '2107': BadRequest, '2108': BadRequest, '2110': InvalidOrder, '2111': InvalidOrder, '2112': InvalidOrder, '2113': InvalidOrder, '2114': InvalidOrder, '2115': InvalidOrder, '2116': InvalidOrder, '2117': InvalidOrder, '2300': OperationRejected, '2301': OperationRejected, '2400': OperationRejected, '2401': OperationRejected, '2402': OperationRejected, '3000': BadSymbol, '3004': OperationRejected, '3005': OperationRejected, '3006': OperationRejected, '3021': BadRequest, '3031': BadRequest, '4000': InsufficientFunds, '4002': OperationFailed, '4010': OperationRejected, '5000': OperationRejected, '5001': OperationRejected, '5002': OperationRejected, '5003': OperationRejected, '5004': OperationRejected, '5005': OperationRejected, '6000': OperationRejected, '6100': OperationRejected, '7000': OperationRejected, '7001': InsufficientFunds, '7002': OperationFailed, '7003': OperationRejected, '7004': OperationRejected, '7005': InsufficientFunds, '7006': OperationFailed, '7007': PermissionDenied, '7100': OperationFailed, '7101': OperationRejected, '7102': OperationRejected, '7103': OperationRejected, '7201': OperationRejected, '7450': OperationRejected, '7451': OperationRejected, '7452': OperationRejected, '7453': OperationRejected, '7454': OperationRejected, '7455': OperationRejected, '7500': OperationRejected, '7501': BadRequest, '7502': OperationRejected, '7503': OperationRejected, '7504': 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 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 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 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, 'contract': isContract, 'linear': isSwap ? true : undefined, 'inverse': isSwap ? false : undefined, 'contractSize': this.parseNumber('1'), 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.safeNumber(market, 'min_size'), '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', '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 = {}) { 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'); return this.safeTicker({ 'info': ticker, 'symbol': this.safeSymbol(marketId, market), '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} A dictionary of [order book structures]{@link https://github.com/ccxt/ccxt/wiki/Manual#order-book-structure} indexed by market symbols */ async fetchOrderBook(symbol, limit = undefined, params = {}) { 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 = {}) { 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", // "instrument": "ETH_USDT_Perp", // "is_taker_buyer": false, // "size": "23.73", // "price": "3089.88", // "mark_price": "3089.360002315", // "index_price": "3090.443723246", // "interest_rate": "0.0", // "forward_price": "0.0", // "trade_id": "64796657-1", // "venue": "ORDERBOOK", // "is_rpi": false // }, // ... // const result = this.safeList(response, 'result', []); return this.parseTrades(result, market, since, limit); } parseTrade(trade, market = undefined) { // // fetchTrades // // { // "event_time": "1764779531332118705", // "instrument": "ETH_USDT_Perp", // "size": "23.73", // "price": "3089.88", // "is_rpi": false, // "mark_price": "3089.360002315", // "index_price": "3090.443723246", // "interest_rate": "0.0", // "forward_price": "0.0", // "trade_id": "64796657-1", // "venue": "ORDERBOOK", // "is_taker_buyer": false // } // // fetchMyTrades // // { // "event_time": "1764945709702747558", // "instrument": "BTC_USDT_Perp", // "size": "0.001", // "price": "90000.0", // "is_rpi": false // "mark_price": "90050.164063298", // "index_price": "90089.803654938", // "interest_rate": "0.0", // "forward_price": "0.0", // "trade_id": "65424692-2", // "venue": "ORDERBOOK", // "is_buyer": true, // "is_taker": false, // "broker": "UNSPECIFIED", // "realized_pnl": "0.0", // "fee": "-0.00009", // "fee_rate": "0.0", // "order_id": "0x01010105034cddc7000000006621285c", // "client_order_id": "1375879248", // "signer": "0x42c9f56f2c9da534f64b8806d64813b29c62a01d", // "sub_account_id": "2147050003876484", // } // const marketId = this.safeString(trade, 'instrument'); market = this.safeMarket(marketId, market); const timestamp = this.safeIntegerProduct(trade, 'event_time', 0.000001); let takerOrMaker = undefined; const isTakerBuyer = this.safeBool(trade, 'is_taker_buyer'); let side = undefined; if (isTakerBuyer !== undefined) { side = isTakerBuyer ? 'buy' : 'sell'; takerOrMaker = 'taker'; } else { takerOrMaker = this.safeBool(trade, 'is_taker') ? 'taker' : 'maker'; side = this.safeBool(trade, 'is_buyer') ? 'buy' : 'sell'; } let fee = undefined; const feeString = this.safeString(trade, 'fee'); if (feeString !== undefined) { fee = { 'cost': this.parseNumber(feeString), 'currency': market['quote'], 'rate': this.safeNumber(trade, 'fee_rate'), }; } return this.safeTrade({ 'info': trade, 'id': this.safeString(trade, 'trade_id'), 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': market['symbol'], 'side': side, 'takerOrMaker': takerOrMaker, 'price': this.safeString(trade, 'price'), 'amount': this.safeString(trade, 'size'), 'cost': undefined, 'fee': fee, 'order': this.safeString(trade, 'order_id'), }, market); } /** * @method * @name grvt#fetchOHLCV * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @see https://api-docs.grvt.io/market_data_api/#candlestick_1 * @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 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 * @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params) * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume */ async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { const maxLimit = 1000; await this.loadMarkets(); let paginate = false; [paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate', false); if (paginate) { return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, maxLimit); } const market = this.market(symbol); let request = { 'instrument': market['id'], 'interval': this.safeString(this.timeframes, timeframe, timeframe), }; const priceTypeMap = { 'last': 'TRADE', 'mark': 'MARK', 'index': 'INDEX', // 'median': 'MEDIAN', }; const selectedPriceType = this.safeString(params, 'priceType', 'last'); request['type'] = this.safeString(priceTypeMap, selectedPriceType); 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);