UNPKG

ccxt

Version:

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

1,165 lines (1,162 loc) • 96.5 kB
// ---------------------------------------------------------------------------- // PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN: // https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code // EDIT THE CORRESPONDENT .ts FILE INSTEAD // ---------------------------------------------------------------------------- import Exchange from './abstract/bitvavo.js'; import { ExchangeError, BadSymbol, AuthenticationError, InsufficientFunds, InvalidOrder, ArgumentsRequired, OrderNotFound, InvalidAddress, BadRequest, RateLimitExceeded, PermissionDenied, ExchangeNotAvailable, AccountSuspended, OnMaintenance } from './base/errors.js'; import { SIGNIFICANT_DIGITS, DECIMAL_PLACES, TRUNCATE, ROUND } from './base/functions/number.js'; import { Precise } from './base/Precise.js'; import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; // ---------------------------------------------------------------------------- /** * @class bitvavo * @augments Exchange */ export default class bitvavo extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'bitvavo', 'name': 'Bitvavo', 'countries': ['NL'], 'rateLimit': 60, 'version': 'v2', 'certified': false, 'pro': true, 'has': { 'CORS': undefined, 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'addMargin': false, 'borrowCrossMargin': false, 'borrowIsolatedMargin': false, 'borrowMargin': false, 'cancelAllOrders': true, 'cancelOrder': true, 'closeAllPositions': false, 'closePosition': false, 'createOrder': true, 'createOrderWithTakeProfitAndStopLoss': false, 'createOrderWithTakeProfitAndStopLossWs': false, 'createPostOnlyOrder': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchBorrowInterest': false, 'fetchBorrowRate': false, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchBorrowRates': false, 'fetchBorrowRatesPerSymbol': false, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchDepositAddresses': false, 'fetchDepositAddressesByNetwork': false, 'fetchDeposits': true, 'fetchDepositWithdrawFee': 'emulated', 'fetchDepositWithdrawFees': true, 'fetchFundingHistory': false, 'fetchFundingInterval': false, 'fetchFundingIntervals': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchGreeks': false, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchIsolatedPositions': false, 'fetchLeverage': false, 'fetchLeverages': false, 'fetchLeverageTiers': false, 'fetchLiquidations': false, 'fetchLongShortRatio': false, 'fetchLongShortRatioHistory': false, 'fetchMarginAdjustmentHistory': false, 'fetchMarginMode': false, 'fetchMarginModes': false, 'fetchMarketLeverageTiers': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchMarkPrices': false, 'fetchMyLiquidations': false, 'fetchMySettlementHistory': false, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterest': false, 'fetchOpenInterestHistory': false, 'fetchOpenInterests': false, 'fetchOpenOrders': true, 'fetchOption': false, 'fetchOptionChain': false, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': true, 'fetchPosition': false, 'fetchPositionHistory': false, 'fetchPositionMode': false, 'fetchPositions': false, 'fetchPositionsForSymbol': false, 'fetchPositionsHistory': false, 'fetchPositionsRisk': false, 'fetchPremiumIndexOHLCV': false, 'fetchSettlementHistory': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': true, 'fetchTransfer': false, 'fetchTransfers': false, 'fetchVolatilityHistory': false, 'fetchWithdrawals': true, 'reduceMargin': false, 'repayCrossMargin': false, 'repayIsolatedMargin': false, 'repayMargin': false, 'setLeverage': false, 'setMargin': false, 'setMarginMode': false, 'setPositionMode': false, 'transfer': false, 'withdraw': true, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/d213155c-8c71-4701-9bd5-45351febc2a8', 'api': { 'public': 'https://api.bitvavo.com', 'private': 'https://api.bitvavo.com', }, 'www': 'https://bitvavo.com/', 'doc': 'https://docs.bitvavo.com/', 'fees': 'https://bitvavo.com/en/fees', 'referral': 'https://bitvavo.com/?a=24F34952F7', }, 'api': { 'public': { 'get': { 'time': 1, 'markets': 1, 'assets': 1, '{market}/book': 1, '{market}/trades': 5, '{market}/candles': 1, 'ticker/price': 1, 'ticker/book': 1, 'ticker/24h': { 'cost': 1, 'noMarket': 25 }, }, }, 'private': { 'get': { 'account': 1, 'order': 1, 'orders': 5, 'ordersOpen': { 'cost': 1, 'noMarket': 25 }, 'trades': 5, 'balance': 5, 'deposit': 1, 'depositHistory': 5, 'withdrawalHistory': 5, }, 'post': { 'order': 1, 'withdrawal': 1, }, 'put': { 'order': 1, }, 'delete': { 'order': 1, 'orders': 1, }, }, }, 'fees': { 'trading': { 'tierBased': true, 'percentage': true, 'taker': this.parseNumber('0.0025'), 'maker': this.parseNumber('0.002'), 'tiers': { 'taker': [ [this.parseNumber('0'), this.parseNumber('0.0025')], [this.parseNumber('100000'), this.parseNumber('0.0020')], [this.parseNumber('250000'), this.parseNumber('0.0016')], [this.parseNumber('500000'), this.parseNumber('0.0012')], [this.parseNumber('1000000'), this.parseNumber('0.0010')], [this.parseNumber('2500000'), this.parseNumber('0.0008')], [this.parseNumber('5000000'), this.parseNumber('0.0006')], [this.parseNumber('10000000'), this.parseNumber('0.0005')], [this.parseNumber('25000000'), this.parseNumber('0.0004')], ], 'maker': [ [this.parseNumber('0'), this.parseNumber('0.0015')], [this.parseNumber('100000'), this.parseNumber('0.0010')], [this.parseNumber('250000'), this.parseNumber('0.0008')], [this.parseNumber('500000'), this.parseNumber('0.0006')], [this.parseNumber('1000000'), this.parseNumber('0.0005')], [this.parseNumber('2500000'), this.parseNumber('0.0004')], [this.parseNumber('5000000'), this.parseNumber('0.0004')], [this.parseNumber('10000000'), this.parseNumber('0.0003')], [this.parseNumber('25000000'), this.parseNumber('0.0003')], ], }, }, }, 'requiredCredentials': { 'apiKey': true, 'secret': true, }, 'features': { 'spot': { 'sandbox': false, 'createOrder': { 'marginMode': false, 'triggerPrice': true, 'triggerPriceType': undefined, 'triggerDirection': undefined, 'stopLossPrice': true, 'takeProfitPrice': true, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'IOC': true, 'FOK': true, 'PO': true, 'GTD': false, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyRequiresPrice': false, 'marketBuyByCost': true, 'selfTradePrevention': true, 'iceberg': false, }, 'createOrders': undefined, 'fetchMyTrades': { 'marginMode': false, 'limit': 1000, 'daysBack': 100000, 'untilDays': 100000, 'symbolRequired': true, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchOrders': { 'marginMode': true, 'limit': 1000, 'daysBack': 100000, 'untilDays': 100000, 'trigger': false, 'trailing': false, 'symbolRequired': true, }, 'fetchClosedOrders': undefined, 'fetchOHLCV': { 'limit': 1440, }, }, 'swap': { 'linear': undefined, 'inverse': undefined, }, 'future': { 'linear': undefined, 'inverse': undefined, }, }, 'exceptions': { 'exact': { '101': ExchangeError, '102': BadRequest, '103': RateLimitExceeded, '104': RateLimitExceeded, '105': PermissionDenied, '107': ExchangeNotAvailable, '108': ExchangeNotAvailable, '109': ExchangeNotAvailable, '110': BadRequest, '200': BadRequest, '201': BadRequest, '202': BadRequest, '203': BadSymbol, '204': BadRequest, '205': BadRequest, '206': BadRequest, '210': InvalidOrder, '211': InvalidOrder, '212': InvalidOrder, '213': InvalidOrder, '214': InvalidOrder, '215': InvalidOrder, '216': InsufficientFunds, '217': InvalidOrder, '230': ExchangeError, '231': ExchangeError, '232': BadRequest, '233': InvalidOrder, '234': InvalidOrder, '235': ExchangeError, '236': BadRequest, '240': OrderNotFound, '300': AuthenticationError, '301': AuthenticationError, '302': AuthenticationError, '303': AuthenticationError, '304': AuthenticationError, // "304": AuthenticationError, // Authentication is required for this endpoint. '305': AuthenticationError, '306': AuthenticationError, '307': PermissionDenied, '308': AuthenticationError, '309': AuthenticationError, '310': PermissionDenied, '311': PermissionDenied, '312': PermissionDenied, '315': BadRequest, '317': AccountSuspended, '400': ExchangeError, '401': ExchangeError, '402': PermissionDenied, '403': PermissionDenied, '404': OnMaintenance, '405': ExchangeError, '406': BadRequest, '407': ExchangeError, '408': InsufficientFunds, '409': InvalidAddress, '410': ExchangeError, '411': BadRequest, '412': InvalidAddress, '413': InvalidAddress, '414': ExchangeError, // You cannot withdraw assets within 2 minutes of logging in. }, 'broad': { 'start parameter is invalid': BadRequest, 'symbol parameter is invalid': BadSymbol, 'amount parameter is invalid': InvalidOrder, 'orderId parameter is invalid': InvalidOrder, // {"errorCode":205,"error":"orderId parameter is invalid."} }, }, 'options': { 'currencyToPrecisionRoundingMode': TRUNCATE, 'BITVAVO-ACCESS-WINDOW': 10000, 'networks': { 'ERC20': 'ETH', 'TRC20': 'TRX', }, 'operatorId': undefined, 'fiatCurrencies': ['EUR'], // only fiat atm }, 'precisionMode': SIGNIFICANT_DIGITS, 'commonCurrencies': { 'MIOTA': 'IOTA', // https://github.com/ccxt/ccxt/issues/7487 }, }); } amountToPrecision(symbol, amount) { // https://docs.bitfinex.com/docs/introduction#amount-precision // The amount field allows up to 8 decimals. // Anything exceeding this will be rounded to the 8th decimal. return this.decimalToPrecision(amount, TRUNCATE, this.markets[symbol]['precision']['amount'], DECIMAL_PLACES); } priceToPrecision(symbol, price) { price = this.decimalToPrecision(price, ROUND, this.markets[symbol]['precision']['price'], this.precisionMode); // https://docs.bitfinex.com/docs/introduction#price-precision // The precision level of all trading prices is based on significant figures. // All pairs on Bitfinex use up to 5 significant digits and up to 8 decimals (e.g. 1.2345, 123.45, 1234.5, 0.00012345). // Prices submit with a precision larger than 5 will be cut by the API. return this.decimalToPrecision(price, TRUNCATE, 8, DECIMAL_PLACES); } /** * @method * @name bitvavo#fetchTime * @description fetches the current integer timestamp in milliseconds from the exchange server * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {int} the current integer timestamp in milliseconds from the exchange server */ async fetchTime(params = {}) { const response = await this.publicGetTime(params); // // { "time": 1590379519148 } // return this.safeInteger(response, 'time'); } /** * @method * @name bitvavo#fetchMarkets * @see https://docs.bitvavo.com/#tag/General/paths/~1markets/get * @description retrieves data on all markets for bitvavo * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchMarkets(params = {}) { const response = await this.publicGetMarkets(params); // // [ // { // "market":"ADA-BTC", // "status":"trading", // "trading" "halted" "auction" // "base":"ADA", // "quote":"BTC", // "pricePrecision":5, // "minOrderInBaseAsset":"100", // "minOrderInQuoteAsset":"0.001", // "orderTypes": [ "market", "limit" ] // } // ] // return this.parseMarkets(response); } parseMarkets(markets) { const currencies = this.currencies; const currenciesById = this.indexBy(currencies, 'id'); const result = []; const fees = this.fees; for (let i = 0; i < markets.length; i++) { const market = markets[i]; const id = this.safeString(market, 'market'); const baseId = this.safeString(market, 'base'); const quoteId = this.safeString(market, 'quote'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const status = this.safeString(market, 'status'); const baseCurrency = this.safeValue(currenciesById, baseId); const basePrecision = this.safeInteger(baseCurrency, 'precision'); result.push(this.safeMarketStructure({ 'id': id, 'symbol': base + '/' + quote, 'base': base, 'quote': quote, 'settle': undefined, 'baseId': baseId, 'quoteId': quoteId, 'settleId': undefined, 'type': 'spot', 'spot': true, 'margin': false, 'swap': false, 'future': false, 'option': false, 'active': (status === 'trading'), 'contract': false, 'linear': undefined, 'inverse': undefined, 'contractSize': undefined, 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'taker': fees['trading']['taker'], 'maker': fees['trading']['maker'], 'precision': { 'amount': this.safeInteger(baseCurrency, 'decimals', basePrecision), 'price': this.safeInteger(market, 'pricePrecision'), }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': this.safeNumber(market, 'minOrderInBaseAsset'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber(market, 'minOrderInQuoteAsset'), 'max': undefined, }, }, 'created': undefined, 'info': market, })); } return result; } /** * @method * @name bitvavo#fetchCurrencies * @see https://docs.bitvavo.com/#tag/General/paths/~1assets/get * @description fetches all available currencies on an exchange * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an associative dictionary of currencies */ async fetchCurrencies(params = {}) { const response = await this.publicGetAssets(params); // // [ // { // "symbol": "USDT", // "displayTicker": "USDT", // "name": "Tether", // "slug": "tether", // "popularity": -1, // "decimals": 6, // "depositFee": "0", // "depositConfirmations": 64, // "depositStatus": "OK", // "withdrawalFee": "3.2", // "withdrawalMinAmount": "3.2", // "withdrawalStatus": "OK", // "networks": [ // "ETH" // ], // "light": { // "color": "#009393", // "icon": { "hash": "4ad7c699", "svg": "https://...", "webp16": "https://...", "webp32": "https://...", "webp64": "https://...", "webp128": "https://...", "webp256": "https://...", "png16": "https://...", "png32": "https://...", "png64": "https://...", "png128": "https://...", "png256": "https://..." // } // }, // "dark": { // "color": "#009393", // "icon": { "hash": "4ad7c699", "svg": "https://...", "webp16": "https://...", "webp32": "https://...", "webp64": "https://...", "webp128": "https://...", "webp256": "https://...", "png16": "https://...", "png32": "https://...", "png64": "https://...", "png128": "https://...", "png256": "https://..." // } // }, // "visibility": "PUBLIC", // "message": "" // }, // ] // return this.parseCurrenciesCustom(response); } parseCurrenciesCustom(currencies) { // // [ // { // "symbol": "USDT", // "displayTicker": "USDT", // "name": "Tether", // "slug": "tether", // "popularity": -1, // "decimals": 6, // "depositFee": "0", // "depositConfirmations": 64, // "depositStatus": "OK", // "withdrawalFee": "3.2", // "withdrawalMinAmount": "3.2", // "withdrawalStatus": "OK", // "networks": [ // "ETH" // ], // "light": { // "color": "#009393", // "icon": { "hash": "4ad7c699", "svg": "https://...", "webp16": "https://...", "webp32": "https://...", "webp64": "https://...", "webp128": "https://...", "webp256": "https://...", "png16": "https://...", "png32": "https://...", "png64": "https://...", "png128": "https://...", "png256": "https://..." // } // }, // "dark": { // "color": "#009393", // "icon": { "hash": "4ad7c699", "svg": "https://...", "webp16": "https://...", "webp32": "https://...", "webp64": "https://...", "webp128": "https://...", "webp256": "https://...", "png16": "https://...", "png32": "https://...", "png64": "https://...", "png128": "https://...", "png256": "https://..." // } // }, // "visibility": "PUBLIC", // "message": "" // }, // ] // const fiatCurrencies = this.safeList(this.options, 'fiatCurrencies', []); const result = {}; for (let i = 0; i < currencies.length; i++) { const currency = currencies[i]; const id = this.safeString(currency, 'symbol'); const code = this.safeCurrencyCode(id); const isFiat = this.inArray(code, fiatCurrencies); const networks = {}; const networksArray = this.safeList(currency, 'networks', []); const deposit = this.safeString(currency, 'depositStatus') === 'OK'; const withdrawal = this.safeString(currency, 'withdrawalStatus') === 'OK'; const active = deposit && withdrawal; const withdrawFee = this.safeNumber(currency, 'withdrawalFee'); const precision = this.safeInteger(currency, 'decimals', 8); const minWithdraw = this.safeNumber(currency, 'withdrawalMinAmount'); // btw, absolutely all of them have 1 network atm for (let j = 0; j < networksArray.length; j++) { const networkId = networksArray[j]; const networkCode = this.networkIdToCode(networkId); networks[networkCode] = { 'info': currency, 'id': networkId, 'network': networkCode, 'active': active, 'deposit': deposit, 'withdraw': withdrawal, 'fee': withdrawFee, 'precision': precision, 'limits': { 'withdraw': { 'min': minWithdraw, 'max': undefined, }, }, }; } result[code] = this.safeCurrencyStructure({ 'info': currency, 'id': id, 'code': code, 'name': this.safeString(currency, 'name'), 'active': active, 'deposit': deposit, 'withdraw': withdrawal, 'networks': networks, 'fee': withdrawFee, 'precision': precision, 'type': isFiat ? 'fiat' : 'crypto', 'limits': { 'amount': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': minWithdraw, 'max': undefined, }, }, }); } // set currencies here to avoid calling publicGetAssets twice this.currencies = this.mapToSafeMap(this.deepExtend(this.currencies, result)); return result; } /** * @method * @name bitvavo#fetchTicker * @see https://docs.bitvavo.com/#tag/Market-Data/paths/~1ticker~124h/get * @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market * @param {string} symbol unified symbol of the market to fetch the ticker for * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async fetchTicker(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'market': market['id'], }; const response = await this.publicGetTicker24h(this.extend(request, params)); // // { // "market":"ETH-BTC", // "open":"0.022578", // "high":"0.023019", // "low":"0.022572", // "last":"0.023019", // "volume":"25.16366324", // "volumeQuote":"0.57333305", // "bid":"0.023039", // "bidSize":"0.53500578", // "ask":"0.023041", // "askSize":"0.47859202", // "timestamp":1590381666900 // } // return this.parseTicker(response, market); } parseTicker(ticker, market = undefined) { // // fetchTicker // // { // "market":"ETH-BTC", // "open":"0.022578", // "high":"0.023019", // "low":"0.022573", // "last":"0.023019", // "volume":"25.16366324", // "volumeQuote":"0.57333305", // "bid":"0.023039", // "bidSize":"0.53500578", // "ask":"0.023041", // "askSize":"0.47859202", // "timestamp":1590381666900 // } // const marketId = this.safeString(ticker, 'market'); const symbol = this.safeSymbol(marketId, market, '-'); const timestamp = this.safeInteger(ticker, 'timestamp'); const last = this.safeString(ticker, 'last'); const baseVolume = this.safeString(ticker, 'volume'); const quoteVolume = this.safeString(ticker, 'volumeQuote'); const open = this.safeString(ticker, 'open'); return this.safeTicker({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'high': this.safeString(ticker, 'high'), 'low': this.safeString(ticker, 'low'), 'bid': this.safeString(ticker, 'bid'), 'bidVolume': this.safeString(ticker, 'bidSize'), 'ask': this.safeString(ticker, 'ask'), 'askVolume': this.safeString(ticker, 'askSize'), 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }, market); } /** * @method * @name bitvavo#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async fetchTickers(symbols = undefined, params = {}) { await this.loadMarkets(); const response = await this.publicGetTicker24h(params); // // [ // { // "market":"ADA-BTC", // "open":"0.0000059595", // "high":"0.0000059765", // "low":"0.0000059595", // "last":"0.0000059765", // "volume":"2923.172", // "volumeQuote":"0.01743483", // "bid":"0.0000059515", // "bidSize":"1117.630919", // "ask":"0.0000059585", // "askSize":"809.999739", // "timestamp":1590382266324 // } // ] // return this.parseTickers(response, symbols); } /** * @method * @name bitvavo#fetchTrades * @see https://docs.bitvavo.com/#tag/Market-Data/paths/~1{market}~1trades/get * @description get the list of most recent trades for a particular symbol * @param {string} symbol unified symbol of the market to fetch trades for * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] the latest time in ms to fetch entries for * @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 {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 paginate = false; [paginate, params] = this.handleOptionAndParams(params, 'fetchTrades', 'paginate'); if (paginate) { return await this.fetchPaginatedCallDynamic('fetchTrades', symbol, since, limit, params); } let request = { 'market': market['id'], // "limit": 500, // default 500, max 1000 // "start": since, // "end": this.milliseconds (), // "tradeIdFrom": "57b1159b-6bf5-4cde-9e2c-6bd6a5678baf", // "tradeIdTo": "57b1159b-6bf5-4cde-9e2c-6bd6a5678baf", }; if (limit !== undefined) { request['limit'] = Math.min(limit, 1000); } if (since !== undefined) { request['start'] = since; } [request, params] = this.handleUntilOption('end', request, params); const response = await this.publicGetMarketTrades(this.extend(request, params)); // // [ // { // "id":"94154c98-6e8b-4e33-92a8-74e33fc05650", // "timestamp":1590382761859, // "amount":"0.06026079", // "price":"8095.3", // "side":"buy" // } // ] // return this.parseTrades(response, market, since, limit); } parseTrade(trade, market = undefined) { // // fetchTrades (public) // // { // "id":"94154c98-6e8b-4e33-92a8-74e33fc05650", // "timestamp":1590382761859, // "amount":"0.06026079", // "price":"8095.3", // "side":"buy" // } // // createOrder, fetchOpenOrders, fetchOrders, editOrder (private) // // { // "id":"b0c86aa5-6ed3-4a2d-ba3a-be9a964220f4", // "timestamp":1590505649245, // "amount":"0.249825", // "price":"183.49", // "taker":true, // "fee":"0.12038925", // "feeCurrency":"EUR", // "settled":true // } // // fetchMyTrades (private) // // { // "id":"b0c86aa5-6ed3-4a2d-ba3a-be9a964220f4", // "orderId":"af76d6ce-9f7c-4006-b715-bb5d430652d0", // "timestamp":1590505649245, // "market":"ETH-EUR", // "side":"sell", // "amount":"0.249825", // "price":"183.49", // "taker":true, // "fee":"0.12038925", // "feeCurrency":"EUR", // "settled":true // } // // watchMyTrades (private) // // { // "event": "fill", // "timestamp": 1590964470132, // "market": "ETH-EUR", // "orderId": "85d082e1-eda4-4209-9580-248281a29a9a", // "fillId": "861d2da5-aa93-475c-8d9a-dce431bd4211", // "side": "sell", // "amount": "0.1", // "price": "211.46", // "taker": true, // "fee": "0.056", // "feeCurrency": "EUR" // } // const priceString = this.safeString(trade, 'price'); const amountString = this.safeString(trade, 'amount'); const timestamp = this.safeInteger(trade, 'timestamp'); const side = this.safeString(trade, 'side'); const id = this.safeString2(trade, 'id', 'fillId'); const marketId = this.safeString(trade, 'market'); const symbol = this.safeSymbol(marketId, market, '-'); const taker = this.safeValue(trade, 'taker'); let takerOrMaker = undefined; if (taker !== undefined) { takerOrMaker = taker ? 'taker' : 'maker'; } const feeCostString = this.safeString(trade, 'fee'); let fee = undefined; if (feeCostString !== undefined) { const feeCurrencyId = this.safeString(trade, 'feeCurrency'); const feeCurrencyCode = this.safeCurrencyCode(feeCurrencyId); fee = { 'cost': feeCostString, 'currency': feeCurrencyCode, }; } const orderId = this.safeString(trade, 'orderId'); return this.safeTrade({ 'info': trade, 'id': id, 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'order': orderId, 'type': undefined, 'side': side, 'takerOrMaker': takerOrMaker, 'price': priceString, 'amount': amountString, 'cost': undefined, 'fee': fee, }, market); } /** * @method * @name bitvavo#fetchTradingFees * @see https://docs.bitvavo.com/#tag/Account/paths/~1account/get * @description fetch the trading fees for multiple markets * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols */ async fetchTradingFees(params = {}) { await this.loadMarkets(); const response = await this.privateGetAccount(params); // // { // "fees": { // "taker": "0.0025", // "maker": "0.0015", // "volume": "10000.00" // } // } // return this.parseTradingFees(response); } parseTradingFees(fees, market = undefined) { // // { // "fees": { // "taker": "0.0025", // "maker": "0.0015", // "volume": "10000.00" // } // } // const feesValue = this.safeValue(fees, 'fees'); const maker = this.safeNumber(feesValue, 'maker'); const taker = this.safeNumber(feesValue, 'taker'); const result = {}; for (let i = 0; i < this.symbols.length; i++) { const symbol = this.symbols[i]; result[symbol] = { 'info': fees, 'symbol': symbol, 'maker': maker, 'taker': taker, 'percentage': true, 'tierBased': true, }; } return result; } /** * @method * @name bitvavo#fetchOrderBook * @see https://docs.bitvavo.com/#tag/Market-Data/paths/~1{market}~1book/get * @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data * @param {string} symbol unified symbol of the market to fetch the order book for * @param {int} [limit] the maximum amount of order book entries to return * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols */ async fetchOrderBook(symbol, limit = undefined, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'market': market['id'], }; if (limit !== undefined) { request['depth'] = limit; } const response = await this.publicGetMarketBook(this.extend(request, params)); // // { // "market":"BTC-EUR", // "nonce":35883831, // "bids":[ // ["8097.4","0.6229099"], // ["8097.2","0.64151283"], // ["8097.1","0.24966294"], // ], // "asks":[ // ["8097.5","1.36916911"], // ["8098.8","0.33462248"], // ["8099.3","1.12908646"], // ] // } // const orderbook = this.parseOrderBook(response, market['symbol']); orderbook['nonce'] = this.safeInteger(response, 'nonce'); return orderbook; } parseOHLCV(ohlcv, market = undefined) { // // [ // 1590383700000, // "8088.5", // "8088.5", // "8088.5", // "8088.5", // "0.04788623" // ] // return [ this.safeInteger(ohlcv, 0), this.safeNumber(ohlcv, 1), this.safeNumber(ohlcv, 2), this.safeNumber(ohlcv, 3), this.safeNumber(ohlcv, 4), this.safeNumber(ohlcv, 5), ]; } fetchOHLCVRequest(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { const market = this.market(symbol); let request = { 'market': market['id'], 'interval': this.safeString(this.timeframes, timeframe, timeframe), // "limit": 1440, // default 1440, max 1440 // "start": since, // "end": this.milliseconds (), }; if (since !== undefined) { // https://github.com/ccxt/ccxt/issues/9227 const duration = this.parseTimeframe(timeframe); request['start'] = since; if (limit === undefined) { limit = 1440; } else { limit = Math.min(limit, 1440); } request['end'] = this.sum(since, limit * duration * 1000); } [request, params] = this.handleUntilOption('end', request, params); if (limit !== undefined) { request['limit'] = limit; // default 1440, max 1440 } return this.extend(request, params); } /** * @method * @name bitvavo#fetchOHLCV * @see https://docs.bitvavo.com/#tag/Market-Data/paths/~1{market}~1candles/get * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @param {string} symbol unified symbol of the market to fetch OHLCV data for * @param {string} timeframe the length of time each candle represents * @param {int} [since] timestamp in ms of the earliest candle to fetch * @param {int} [limit] the maximum amount of candles to fetch * @param {object} [params] extra parameters specific to the exchange API endpoint * @param {int} [params.until] the latest time in ms to fetch entries for * @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 = {}) { await this.loadMarkets(); const market = this.market(symbol); let paginate = false; [paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate'); if (paginate) { return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 1440); } const request = this.fetchOHLCVRequest(symbol, timeframe, since, limit, params); const response = await this.publicGetMarketCandles(request); // // [ // [1590383700000,"8088.5","8088.5","8088.5","8088.5","0.04788623"], // [1590383580000,"8091.3","8091.5","8091.3","8091.5","0.04931221"], // [1590383520000,"8090.3","8092.7","8090.3","8092.5","0.04001286"], // ] // return this.parseOHLCVs(response, market, timeframe, since, limit); } parseBalance(response) { const result = { 'info': response, 'timestamp': undefined, 'datetime': undefined, }; for (let i = 0; i < response.length; i++) { const balance = response[i]; const currencyId = this.safeString(balance, 'symbol'); const code = this.safeCurrencyCode(currencyId); const account = this.account(); account['free'] = this.safeString(balance, 'available'); account['used'] = this.safeString(balance, 'inOrder'); result[code] = account; } return this.safeBalance(result); } /** * @method * @name bitvavo#fetchBalance * @see https://docs.bitvavo.com/#tag/Account/paths/~1balance/get * @description query for balance and get the amount of funds available for trading or funds locked in orders * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async fetchBalance(params = {}) { await this.loadMarkets(); const response = await this.privateGetBalance(params); // // [ // { // "symbol": "BTC", // "available": "1.57593193", // "inOrder": "0.74832374" // } // ] // return this.parseBalance(response); } /** * @method * @name bitvavo#fetchDepositAddress * @description fetch the deposit address for a currency associated with this account * @param {string} code unified currency code * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure} */ async fetchDepositAddress(code, params = {}) { await this.loadMarkets(); const currency = this.currency(code); const request = { 'symbol': currency['id'], }; const response = await this.privateGetDeposit(this.extend(request, params)); // // { // "address": "0x449889e3234514c45d57f7c5a571feba0c7ad567", // "paymentId": "10002653" // } // const address = this.safeString(response, 'address'); const tag = this.safeString(response, 'paymentId'); this.checkAddress(address); return { 'info': response, 'currency': code, 'network': undefined, 'address': address, 'tag': tag, }; } createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) { const market = this.market(symbol); const request = {