ccxt
Version:
1,128 lines (1,124 loc) • 153 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var grvt$1 = require('./abstract/grvt.js');
var errors = require('./base/errors.js');
var Precise = require('./base/Precise.js');
var sha3 = require('./static_dependencies/noble-hashes/sha3.js');
var secp256k1 = require('./static_dependencies/noble-curves/secp256k1.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'],
'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': 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,
'exceptions': {
'exact': {
'1000': errors.AuthenticationError,
'1001': errors.PermissionDenied,
'1002': errors.OperationFailed,
'1003': errors.BadRequest,
'1004': errors.OperationRejected,
'1005': errors.OperationFailed,
'1006': errors.RateLimitExceeded,
'1008': errors.PermissionDenied,
'1009': errors.OperationRejected,
'1012': errors.BadRequest,
'1400': errors.PermissionDenied,
'2000': errors.PermissionDenied,
'2001': errors.InvalidNonce,
'2002': errors.BadRequest,
'2003': errors.PermissionDenied,
'2004': errors.InvalidNonce,
'2005': errors.BadRequest,
'2006': errors.BadRequest,
'2007': errors.BadRequest,
'2008': errors.BadRequest,
'2010': errors.InvalidOrder,
'2011': errors.InvalidOrder,
'2012': errors.InvalidOrder,
'2020': errors.InvalidOrder,
'2021': errors.InvalidOrder,
'2030': errors.InvalidOrder,
'2031': errors.InvalidOrder,
'2032': errors.InvalidOrder,
'2040': errors.InvalidOrder,
'2041': errors.InvalidOrder,
'2042': errors.InvalidOrder,
'2050': errors.InvalidOrder,
'2051': errors.InvalidOrder,
'2060': errors.BadSymbol,
'2061': errors.BadSymbol,
'2062': errors.InvalidOrder,
'2063': errors.InvalidOrder,
'2064': errors.InvalidOrder,
'2065': errors.InvalidOrder,
'2070': errors.InvalidOrder,
'2080': errors.InsufficientFunds,
'2081': errors.OperationRejected,
'2082': errors.InvalidOrder,
'2083': errors.OperationRejected,
'2090': errors.RateLimitExceeded,
'2100': errors.BadRequest,
'2101': errors.BadRequest,
'2102': errors.OperationRejected,
'2103': errors.OperationRejected,
'2104': errors.BadRequest,
'2105': errors.BadRequest,
'2107': errors.BadRequest,
'2108': errors.BadRequest,
'2110': errors.InvalidOrder,
'2111': errors.InvalidOrder,
'2112': errors.InvalidOrder,
'2113': errors.InvalidOrder,
'2114': errors.InvalidOrder,
'2115': errors.InvalidOrder,
'2116': errors.InvalidOrder,
'2117': errors.InvalidOrder,
'2300': errors.OperationRejected,
'2301': errors.OperationRejected,
'2400': errors.OperationRejected,
'2401': errors.OperationRejected,
'2402': errors.OperationRejected,
'3000': errors.BadSymbol,
'3004': errors.OperationRejected,
'3005': errors.OperationRejected,
'3006': errors.OperationRejected,
'3021': errors.BadRequest,
'3031': errors.BadRequest,
'4000': errors.InsufficientFunds,
'4002': errors.OperationFailed,
'4010': errors.OperationRejected,
'5000': errors.OperationRejected,
'5001': errors.OperationRejected,
'5002': errors.OperationRejected,
'5003': errors.OperationRejected,
'5004': errors.OperationRejected,
'5005': errors.OperationRejected,
'6000': errors.OperationRejected,
'6100': errors.OperationRejected,
'7000': errors.OperationRejected,
'7001': errors.InsufficientFunds,
'7002': errors.OperationFailed,
'7003': errors.OperationRejected,
'7004': errors.OperationRejected,
'7005': errors.InsufficientFunds,
'7006': errors.OperationFailed,
'7007': errors.PermissionDenied,
'7100': errors.OperationFailed,
'7101': errors.OperationRejected,
'7102': errors.OperationRejected,
'7103': errors.OperationRejected,
'7201': errors.OperationRejected,
'7450': errors.OperationRejected,
'7451': errors.OperationRejected,
'7452': errors.OperationRejected,
'7453': errors.OperationRejected,
'7454': errors.OperationRejected,
'7455': errors.OperationRejected,
'7500': errors.OperationRejected,
'7501': errors.BadRequest,
'7502': errors.OperationRejected,
'7503': errors.OperationRejected,
'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,
'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),