ccxt
Version:
1,181 lines (1,178 loc) • 102 kB
JavaScript
// ----------------------------------------------------------------------------
// 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/bitstamp.js';
import { AuthenticationError, BadRequest, ExchangeError, NotSupported, PermissionDenied, InvalidNonce, OrderNotFound, InsufficientFunds, InvalidAddress, InvalidOrder, OnMaintenance, ExchangeNotAvailable } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class bitstamp
* @augments Exchange
*/
export default class bitstamp extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'bitstamp',
'name': 'Bitstamp',
'countries': ['GB'],
// 8000 requests per 10 minutes = 8000 / 600 = 13.33333333 requests per second => 1000ms / 13.33333333 = 75ms between requests on average
'rateLimit': 75,
'version': 'v2',
'userAgent': this.userAgents['chrome'],
'pro': true,
'has': {
'CORS': true,
'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,
'createReduceOnlyOrder': false,
'createStopLimitOrder': false,
'createStopMarketOrder': false,
'createStopOrder': false,
'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,
'fetchDepositsWithdrawals': 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,
'fetchLedger': true,
'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,
'fetchPosition': false,
'fetchPositionHistory': false,
'fetchPositionMode': false,
'fetchPositions': false,
'fetchPositionsForSymbol': false,
'fetchPositionsHistory': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': true,
'fetchTransactionFees': true,
'fetchTransactions': 'emulated',
'fetchVolatilityHistory': false,
'fetchWithdrawals': true,
'reduceMargin': false,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'setLeverage': false,
'setMargin': false,
'setMarginMode': false,
'setPositionMode': false,
'transfer': true,
'withdraw': true,
},
'urls': {
'logo': 'https://github.com/user-attachments/assets/d5480572-1fee-43cb-b900-d38c522d0024',
'api': {
'public': 'https://www.bitstamp.net/api',
'private': 'https://www.bitstamp.net/api',
},
'www': 'https://www.bitstamp.net',
'doc': 'https://www.bitstamp.net/api',
},
'timeframes': {
'1m': '60',
'3m': '180',
'5m': '300',
'15m': '900',
'30m': '1800',
'1h': '3600',
'2h': '7200',
'4h': '14400',
'6h': '21600',
'12h': '43200',
'1d': '86400',
'1w': '259200',
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
},
'api': {
'public': {
'get': {
'ohlc/{pair}/': 1,
'order_book/{pair}/': 1,
'ticker/': 1,
'ticker_hour/{pair}/': 1,
'ticker/{pair}/': 1,
'transactions/{pair}/': 1,
'trading-pairs-info/': 1,
'currencies/': 1,
'eur_usd/': 1,
'travel_rule/vasps/': 1,
},
},
'private': {
'get': {
'travel_rule/contacts/': 1,
'contacts/{contact_uuid}/': 1,
'earn/subscriptions/': 1,
'earn/transactions/': 1,
},
'post': {
'account_balances/': 1,
'account_balances/{currency}/': 1,
'balance/': 1,
'balance/{pair}/': 1,
'bch_withdrawal/': 1,
'bch_address/': 1,
'user_transactions/': 1,
'user_transactions/{pair}/': 1,
'crypto-transactions/': 1,
'open_order': 1,
'open_orders/all/': 1,
'open_orders/{pair}/': 1,
'order_status/': 1,
'cancel_order/': 1,
'cancel_all_orders/': 1,
'cancel_all_orders/{pair}/': 1,
'buy/{pair}/': 1,
'buy/market/{pair}/': 1,
'buy/instant/{pair}/': 1,
'sell/{pair}/': 1,
'sell/market/{pair}/': 1,
'sell/instant/{pair}/': 1,
'transfer-to-main/': 1,
'transfer-from-main/': 1,
'my_trading_pairs/': 1,
'fees/trading/': 1,
'fees/trading/{market_symbol}': 1,
'fees/withdrawal/': 1,
'fees/withdrawal/{currency}/': 1,
'withdrawal-requests/': 1,
'withdrawal/open/': 1,
'withdrawal/status/': 1,
'withdrawal/cancel/': 1,
'liquidation_address/new/': 1,
'liquidation_address/info/': 1,
'btc_unconfirmed/': 1,
'websockets_token/': 1,
// individual coins
'btc_withdrawal/': 1,
'btc_address/': 1,
'ripple_withdrawal/': 1,
'ripple_address/': 1,
'ltc_withdrawal/': 1,
'ltc_address/': 1,
'eth_withdrawal/': 1,
'eth_address/': 1,
'xrp_withdrawal/': 1,
'xrp_address/': 1,
'xlm_withdrawal/': 1,
'xlm_address/': 1,
'pax_withdrawal/': 1,
'pax_address/': 1,
'link_withdrawal/': 1,
'link_address/': 1,
'usdc_withdrawal/': 1,
'usdc_address/': 1,
'omg_withdrawal/': 1,
'omg_address/': 1,
'dai_withdrawal/': 1,
'dai_address/': 1,
'knc_withdrawal/': 1,
'knc_address/': 1,
'mkr_withdrawal/': 1,
'mkr_address/': 1,
'zrx_withdrawal/': 1,
'zrx_address/': 1,
'gusd_withdrawal/': 1,
'gusd_address/': 1,
'aave_withdrawal/': 1,
'aave_address/': 1,
'bat_withdrawal/': 1,
'bat_address/': 1,
'uma_withdrawal/': 1,
'uma_address/': 1,
'snx_withdrawal/': 1,
'snx_address/': 1,
'uni_withdrawal/': 1,
'uni_address/': 1,
'yfi_withdrawal/': 1,
'yfi_address/': 1,
'audio_withdrawal/': 1,
'audio_address/': 1,
'crv_withdrawal/': 1,
'crv_address/': 1,
'algo_withdrawal/': 1,
'algo_address/': 1,
'comp_withdrawal/': 1,
'comp_address/': 1,
'grt_withdrawal/': 1,
'grt_address/': 1,
'usdt_withdrawal/': 1,
'usdt_address/': 1,
'eurt_withdrawal/': 1,
'eurt_address/': 1,
'matic_withdrawal/': 1,
'matic_address/': 1,
'sushi_withdrawal/': 1,
'sushi_address/': 1,
'chz_withdrawal/': 1,
'chz_address/': 1,
'enj_withdrawal/': 1,
'enj_address/': 1,
'alpha_withdrawal/': 1,
'alpha_address/': 1,
'ftt_withdrawal/': 1,
'ftt_address/': 1,
'storj_withdrawal/': 1,
'storj_address/': 1,
'axs_withdrawal/': 1,
'axs_address/': 1,
'sand_withdrawal/': 1,
'sand_address/': 1,
'hbar_withdrawal/': 1,
'hbar_address/': 1,
'rgt_withdrawal/': 1,
'rgt_address/': 1,
'fet_withdrawal/': 1,
'fet_address/': 1,
'skl_withdrawal/': 1,
'skl_address/': 1,
'cel_withdrawal/': 1,
'cel_address/': 1,
'sxp_withdrawal/': 1,
'sxp_address/': 1,
'ada_withdrawal/': 1,
'ada_address/': 1,
'slp_withdrawal/': 1,
'slp_address/': 1,
'ftm_withdrawal/': 1,
'ftm_address/': 1,
'perp_withdrawal/': 1,
'perp_address/': 1,
'dydx_withdrawal/': 1,
'dydx_address/': 1,
'gala_withdrawal/': 1,
'gala_address/': 1,
'shib_withdrawal/': 1,
'shib_address/': 1,
'amp_withdrawal/': 1,
'amp_address/': 1,
'sgb_withdrawal/': 1,
'sgb_address/': 1,
'avax_withdrawal/': 1,
'avax_address/': 1,
'wbtc_withdrawal/': 1,
'wbtc_address/': 1,
'ctsi_withdrawal/': 1,
'ctsi_address/': 1,
'cvx_withdrawal/': 1,
'cvx_address/': 1,
'imx_withdrawal/': 1,
'imx_address/': 1,
'nexo_withdrawal/': 1,
'nexo_address/': 1,
'ust_withdrawal/': 1,
'ust_address/': 1,
'ant_withdrawal/': 1,
'ant_address/': 1,
'gods_withdrawal/': 1,
'gods_address/': 1,
'rad_withdrawal/': 1,
'rad_address/': 1,
'band_withdrawal/': 1,
'band_address/': 1,
'inj_withdrawal/': 1,
'inj_address/': 1,
'rly_withdrawal/': 1,
'rly_address/': 1,
'rndr_withdrawal/': 1,
'rndr_address/': 1,
'vega_withdrawal/': 1,
'vega_address/': 1,
'1inch_withdrawal/': 1,
'1inch_address/': 1,
'ens_withdrawal/': 1,
'ens_address/': 1,
'mana_withdrawal/': 1,
'mana_address/': 1,
'lrc_withdrawal/': 1,
'lrc_address/': 1,
'ape_withdrawal/': 1,
'ape_address/': 1,
'mpl_withdrawal/': 1,
'mpl_address/': 1,
'euroc_withdrawal/': 1,
'euroc_address/': 1,
'sol_withdrawal/': 1,
'sol_address/': 1,
'dot_withdrawal/': 1,
'dot_address/': 1,
'near_withdrawal/': 1,
'near_address/': 1,
'doge_withdrawal/': 1,
'doge_address/': 1,
'flr_withdrawal/': 1,
'flr_address/': 1,
'dgld_withdrawal/': 1,
'dgld_address/': 1,
'ldo_withdrawal/': 1,
'ldo_address/': 1,
'travel_rule/contacts/': 1,
'earn/subscribe/': 1,
'earn/subscriptions/setting/': 1,
'earn/unsubscribe': 1,
'wecan_withdrawal/': 1,
'wecan_address/': 1,
'trac_withdrawal/': 1,
'trac_address/': 1,
'eurcv_withdrawal/': 1,
'eurcv_address/': 1,
'pyusd_withdrawal/': 1,
'pyusd_address/': 1,
'lmwr_withdrawal/': 1,
'lmwr_address/': 1,
'pepe_withdrawal/': 1,
'pepe_address/': 1,
'blur_withdrawal/': 1,
'blur_address/': 1,
'vext_withdrawal/': 1,
'vext_address/': 1,
'cspr_withdrawal/': 1,
'cspr_address/': 1,
'vchf_withdrawal/': 1,
'vchf_address/': 1,
'veur_withdrawal/': 1,
'veur_address/': 1,
'truf_withdrawal/': 1,
'truf_address/': 1,
'wif_withdrawal/': 1,
'wif_address/': 1,
'smt_withdrawal/': 1,
'smt_address/': 1,
'sui_withdrawal/': 1,
'sui_address/': 1,
'jup_withdrawal/': 1,
'jup_address/': 1,
'ondo_withdrawal/': 1,
'ondo_address/': 1,
'boba_withdrawal/': 1,
'boba_address/': 1,
'pyth_withdrawal/': 1,
'pyth_address/': 1,
},
},
},
'fees': {
'trading': {
'tierBased': true,
'percentage': true,
'taker': this.parseNumber('0.004'),
'maker': this.parseNumber('0.004'),
'tiers': {
'taker': [
[this.parseNumber('0'), this.parseNumber('0.004')],
[this.parseNumber('10000'), this.parseNumber('0.003')],
[this.parseNumber('100000'), this.parseNumber('0.002')],
[this.parseNumber('500000'), this.parseNumber('0.0018')],
[this.parseNumber('1500000'), this.parseNumber('0.0016')],
[this.parseNumber('5000000'), this.parseNumber('0.0012')],
[this.parseNumber('20000000'), this.parseNumber('0.001')],
[this.parseNumber('50000000'), this.parseNumber('0.0008')],
[this.parseNumber('100000000'), this.parseNumber('0.0006')],
[this.parseNumber('250000000'), this.parseNumber('0.0005')],
[this.parseNumber('1000000000'), this.parseNumber('0.0003')],
],
'maker': [
[this.parseNumber('0'), this.parseNumber('0.003')],
[this.parseNumber('10000'), this.parseNumber('0.002')],
[this.parseNumber('100000'), this.parseNumber('0.001')],
[this.parseNumber('500000'), this.parseNumber('0.0008')],
[this.parseNumber('1500000'), this.parseNumber('0.0006')],
[this.parseNumber('5000000'), this.parseNumber('0.0003')],
[this.parseNumber('20000000'), this.parseNumber('0.002')],
[this.parseNumber('50000000'), this.parseNumber('0.0001')],
[this.parseNumber('100000000'), this.parseNumber('0')],
[this.parseNumber('250000000'), this.parseNumber('0')],
[this.parseNumber('1000000000'), this.parseNumber('0')],
],
},
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {},
'deposit': {
'BTC': 0,
'BCH': 0,
'LTC': 0,
'ETH': 0,
'XRP': 0,
'XLM': 0,
'PAX': 0,
'USD': 7.5,
'EUR': 0,
},
},
},
'precisionMode': TICK_SIZE,
'commonCurrencies': {
'UST': 'USTC',
},
// exchange-specific options
'options': {
'networksById': {
'bitcoin-cash': 'BCH',
'bitcoin': 'BTC',
'ethereum': 'ERC20',
'litecoin': 'LTC',
'stellar': 'XLM',
'xrpl': 'XRP',
'tron': 'TRC20',
'algorand': 'ALGO',
'flare': 'FLR',
'hedera': 'HBAR',
'cardana': 'ADA',
'songbird': 'FLR',
'avalanche-c-chain': 'AVAX',
'solana': 'SOL',
'polkadot': 'DOT',
'near': 'NEAR',
'doge': 'DOGE',
'sui': 'SUI',
'casper': 'CSRP',
},
},
'exceptions': {
'exact': {
'No permission found': PermissionDenied,
'API key not found': AuthenticationError,
'IP address not allowed': PermissionDenied,
'Invalid nonce': InvalidNonce,
'Invalid signature': AuthenticationError,
'Authentication failed': AuthenticationError,
'Missing key, signature and nonce parameters': AuthenticationError,
'Wrong API key format': AuthenticationError,
'Your account is frozen': PermissionDenied,
'Please update your profile with your FATCA information, before using API.': PermissionDenied,
'Order not found.': OrderNotFound,
'Price is more than 20% below market price.': InvalidOrder,
"Bitstamp.net is under scheduled maintenance. We'll be back soon.": OnMaintenance,
'Order could not be placed.': ExchangeNotAvailable,
'Invalid offset.': BadRequest,
},
'broad': {
'Minimum order size is': InvalidOrder,
'Check your account balance for details.': InsufficientFunds,
'Ensure this value has at least': InvalidAddress,
'Ensure that there are no more than': InvalidOrder, // {"status": "error", "reason": {"amount": ["Ensure that there are no more than 0 decimal places."], "__all__": [""]}}
},
},
'features': {
'spot': {
'sandbox': false,
'createOrder': {
'marginMode': false,
'triggerPrice': false,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': true,
},
'hedged': false,
'trailing': false,
'leverage': false,
'marketBuyByCost': false,
'marketBuyRequiresPrice': false,
'selfTradePrevention': false,
'iceberg': false,
},
'createOrders': undefined,
'fetchMyTrades': {
'marginMode': false,
'limit': 1000,
'daysBack': undefined,
'untilDays': 30,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': undefined,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': undefined,
'fetchClosedOrders': undefined,
'fetchOHLCV': {
'limit': 1000,
},
},
'swap': {
'linear': undefined,
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
});
}
/**
* @method
* @name bitstamp#fetchMarkets
* @description retrieves data on all markets for bitstamp
* @see https://www.bitstamp.net/api/#tag/Market-info/operation/GetTradingPairsInfo
* @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.fetchMarketsFromCache(params);
//
// [
// {
// "trading": "Enabled",
// "base_decimals": 8,
// "url_symbol": "btcusd",
// "name": "BTC/USD",
// "instant_and_market_orders": "Enabled",
// "minimum_order": "20.0 USD",
// "counter_decimals": 2,
// "description": "Bitcoin / U.S. dollar"
// }
// ]
//
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const name = this.safeString(market, 'name');
let [base, quote] = name.split('/');
const baseId = base.toLowerCase();
const quoteId = quote.toLowerCase();
base = this.safeCurrencyCode(base);
quote = this.safeCurrencyCode(quote);
const minimumOrder = this.safeString(market, 'minimum_order');
const parts = minimumOrder.split(' ');
const status = this.safeString(market, 'trading');
result.push({
'id': this.safeString(market, 'url_symbol'),
'marketId': baseId + '_' + quoteId,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'future': false,
'swap': false,
'option': false,
'active': (status === 'Enabled'),
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber(this.parsePrecision(this.safeString(market, 'base_decimals'))),
'price': this.parseNumber(this.parsePrecision(this.safeString(market, 'counter_decimals'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': this.safeNumber(parts, 0),
'max': undefined,
},
},
'created': undefined,
'info': market,
});
}
return result;
}
constructCurrencyObject(id, code, name, precision, minCost, originalPayload) {
let currencyType = 'crypto';
const description = this.describe();
if (this.isFiat(code)) {
currencyType = 'fiat';
}
const tickSize = this.parseNumber(this.parsePrecision(this.numberToString(precision)));
return {
'id': id,
'code': code,
'info': originalPayload,
'type': currencyType,
'name': name,
'active': true,
'deposit': undefined,
'withdraw': undefined,
'fee': this.safeNumber(description['fees']['funding']['withdraw'], code),
'precision': tickSize,
'limits': {
'amount': {
'min': tickSize,
'max': undefined,
},
'price': {
'min': tickSize,
'max': undefined,
},
'cost': {
'min': minCost,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': undefined,
},
},
'networks': {},
};
}
async fetchMarketsFromCache(params = {}) {
// this method is now redundant
// currencies are now fetched before markets
const options = this.safeValue(this.options, 'fetchMarkets', {});
const timestamp = this.safeInteger(options, 'timestamp');
const expires = this.safeInteger(options, 'expires', 1000);
const now = this.milliseconds();
if ((timestamp === undefined) || ((now - timestamp) > expires)) {
const response = await this.publicGetTradingPairsInfo(params);
this.options['fetchMarkets'] = this.extend(options, {
'response': response,
'timestamp': now,
});
}
return this.safeValue(this.options['fetchMarkets'], 'response');
}
/**
* @method
* @name bitstamp#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://www.bitstamp.net/api/#tag/Market-info/operation/GetTradingPairsInfo
* @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.fetchMarketsFromCache(params);
//
// [
// {
// "trading": "Enabled",
// "base_decimals": 8,
// "url_symbol": "btcusd",
// "name": "BTC/USD",
// "instant_and_market_orders": "Enabled",
// "minimum_order": "20.0 USD",
// "counter_decimals": 2,
// "description": "Bitcoin / U.S. dollar"
// },
// ]
//
const result = {};
for (let i = 0; i < response.length; i++) {
const market = response[i];
const name = this.safeString(market, 'name');
let [base, quote] = name.split('/');
const baseId = base.toLowerCase();
const quoteId = quote.toLowerCase();
base = this.safeCurrencyCode(base);
quote = this.safeCurrencyCode(quote);
const description = this.safeString(market, 'description');
const [baseDescription, quoteDescription] = description.split(' / ');
const minimumOrder = this.safeString(market, 'minimum_order');
const parts = minimumOrder.split(' ');
const cost = parts[0];
if (!(base in result)) {
const baseDecimals = this.safeInteger(market, 'base_decimals');
result[base] = this.constructCurrencyObject(baseId, base, baseDescription, baseDecimals, undefined, market);
}
if (!(quote in result)) {
const counterDecimals = this.safeInteger(market, 'counter_decimals');
result[quote] = this.constructCurrencyObject(quoteId, quote, quoteDescription, counterDecimals, this.parseNumber(cost), market);
}
}
return result;
}
/**
* @method
* @name bitstamp#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://www.bitstamp.net/api/#tag/Order-book/operation/GetOrderBook
* @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 = {
'pair': market['id'],
};
const response = await this.publicGetOrderBookPair(this.extend(request, params));
//
// {
// "timestamp": "1583652948",
// "microtimestamp": "1583652948955826",
// "bids": [
// [ "8750.00", "1.33685271" ],
// [ "8749.39", "0.07700000" ],
// [ "8746.98", "0.07400000" ],
// ]
// "asks": [
// [ "8754.10", "1.51995636" ],
// [ "8754.71", "1.40000000" ],
// [ "8754.72", "2.50000000" ],
// ]
// }
//
const microtimestamp = this.safeInteger(response, 'microtimestamp');
const timestamp = this.parseToInt(microtimestamp / 1000);
const orderbook = this.parseOrderBook(response, market['symbol'], timestamp);
orderbook['nonce'] = microtimestamp;
return orderbook;
}
parseTicker(ticker, market = undefined) {
//
// {
// "timestamp": "1686068944",
// "high": "26252",
// "last": "26216",
// "bid": "26208",
// "vwap": "25681",
// "volume": "3563.13819902",
// "low": "25350",
// "ask": "26211",
// "open": "25730",
// "open_24": "25895",
// "percent_change_24": "1.24",
// "pair": "BTC/USD"
// }
//
const marketId = this.safeString(ticker, 'pair');
const symbol = this.safeSymbol(marketId, market, undefined);
const timestamp = this.safeTimestamp(ticker, 'timestamp');
const vwap = this.safeString(ticker, 'vwap');
const baseVolume = this.safeString(ticker, 'volume');
const quoteVolume = Precise.stringMul(baseVolume, vwap);
const last = this.safeString(ticker, 'last');
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': undefined,
'ask': this.safeString(ticker, 'ask'),
'askVolume': undefined,
'vwap': vwap,
'open': this.safeString(ticker, 'open'),
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': undefined,
'average': undefined,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'info': ticker,
}, market);
}
/**
* @method
* @name bitstamp#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @see https://www.bitstamp.net/api/#tag/Tickers/operation/GetMarketTicker
* @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 = {
'pair': market['id'],
};
const ticker = await this.publicGetTickerPair(this.extend(request, params));
//
// {
// "timestamp": "1686068944",
// "high": "26252",
// "last": "26216",
// "bid": "26208",
// "vwap": "25681",
// "volume": "3563.13819902",
// "low": "25350",
// "ask": "26211",
// "open": "25730",
// "open_24": "25895",
// "percent_change_24": "1.24"
// }
//
return this.parseTicker(ticker, market);
}
/**
* @method
* @name bitstamp#fetchTickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @see https://www.bitstamp.net/api/#tag/Tickers/operation/GetCurrencyPairTickers
* @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.publicGetTicker(params);
//
// {
// "timestamp": "1686068944",
// "high": "26252",
// "last": "26216",
// "bid": "26208",
// "vwap": "25681",
// "volume": "3563.13819902",
// "low": "25350",
// "ask": "26211",
// "open": "25730",
// "open_24": "25895",
// "percent_change_24": "1.24",
// "pair": "BTC/USD"
// }
//
return this.parseTickers(response, symbols);
}
getCurrencyIdFromTransaction(transaction) {
//
// {
// "fee": "0.00000000",
// "btc_usd": "0.00",
// "datetime": XXX,
// "usd": 0.0,
// "btc": 0.0,
// "eth": "0.05000000",
// "type": "0",
// "id": XXX,
// "eur": 0.0
// }
//
const currencyId = this.safeStringLower(transaction, 'currency');
if (currencyId !== undefined) {
return currencyId;
}
transaction = this.omit(transaction, [
'fee',
'price',
'datetime',
'type',
'status',
'id',
]);
const ids = Object.keys(transaction);
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
if (id.indexOf('_') < 0) {
const value = this.safeInteger(transaction, id);
if ((value !== undefined) && (value !== 0)) {
return id;
}
}
}
return undefined;
}
getMarketFromTrade(trade) {
trade = this.omit(trade, [
'fee',
'price',
'datetime',
'tid',
'type',
'order_id',
'side',
]);
const currencyIds = Object.keys(trade);
const numCurrencyIds = currencyIds.length;
if (numCurrencyIds > 2) {
throw new ExchangeError(this.id + ' getMarketFromTrade() too many keys: ' + this.json(currencyIds) + ' in the trade: ' + this.json(trade));
}
if (numCurrencyIds === 2) {
let marketId = currencyIds[0] + currencyIds[1];
if (marketId in this.markets_by_id) {
return this.safeMarket(marketId);
}
marketId = currencyIds[1] + currencyIds[0];
if (marketId in this.markets_by_id) {
return this.safeMarket(marketId);
}
}
return undefined;
}
parseTrade(trade, market = undefined) {
//
// fetchTrades (public)
//
// {
// "date": "1637845199",
// "tid": "209895701",
// "amount": "0.00500000",
// "type": "0", // Transaction type: 0 - buy; 1 - sell
// "price": "4451.25"
// }
//
// fetchMyTrades, trades returned within fetchOrder (private)
//
// {
// "fee": "0.11128",
// "eth_usdt": 4451.25,
// "datetime": "2021-11-25 12:59:59.322000",
// "usdt": "-22.26",
// "order_id": 1429545880227846,
// "usd": 0,
// "btc": 0,
// "eth": "0.00500000",
// "type": "2", // Transaction type: 0 - deposit; 1 - withdrawal; 2 - market trade; 14 - sub account transfer; 25 - credited with staked assets; 26 - sent assets to staking; 27 - staking reward; 32 - referral reward; 35 - inter account transfer.
// "id": 209895701,
// "eur": 0
// }
//
// from fetchOrder (private)
//
// {
// "fee": "0.11128",
// "price": "4451.25000000",
// "datetime": "2021-11-25 12:59:59.322000",
// "usdt": "22.25625000",
// "tid": 209895701,
// "eth": "0.00500000",
// "type": 2 // Transaction type: 0 - deposit; 1 - withdrawal; 2 - market trade
// }
//
const id = this.safeString2(trade, 'id', 'tid');
let symbol = undefined;
let side = undefined;
let priceString = this.safeString(trade, 'price');
let amountString = this.safeString(trade, 'amount');
const orderId = this.safeString(trade, 'order_id');
const type = undefined;
let costString = this.safeString(trade, 'cost');
let rawMarketId = undefined;
if (market === undefined) {
const keys = Object.keys(trade);
for (let i = 0; i < keys.length; i++) {
const currentKey = keys[i];
if (currentKey !== 'order_id' && currentKey.indexOf('_') >= 0) {
rawMarketId = currentKey;
market = this.safeMarket(rawMarketId, market, '_');
}
}
}
// if the market is still not defined
// try to deduce it from used keys
if (market === undefined) {
market = this.getMarketFromTrade(trade);
}
const feeCostString = this.safeString(trade, 'fee');
const feeCurrency = market['quote'];
const priceId = (rawMarketId !== undefined) ? rawMarketId : market['marketId'];
priceString = this.safeString(trade, priceId, priceString);
amountString = this.safeString(trade, market['baseId'], amountString);
costString = this.safeString(trade, market['quoteId'], costString);
symbol = market['symbol'];
const datetimeString = this.safeString2(trade, 'date', 'datetime');
let timestamp = undefined;
if (datetimeString !== undefined) {
if (datetimeString.indexOf(' ') >= 0) {
// iso8601
timestamp = this.parse8601(datetimeString);
}
else {
// string unix epoch in seconds
timestamp = parseInt(datetimeString);
timestamp = timestamp * 1000;
}
}
// if it is a private trade
if ('id' in trade) {
if (amountString !== undefined) {
const isAmountNeg = Precise.stringLt(amountString, '0');
if (isAmountNeg) {
side = 'sell';
amountString = Precise.stringNeg(amountString);
}
else {
side = 'buy';
}
}
}
else {
side = this.safeString(trade, 'type');
if (side === '1') {
side = 'sell';
}
else if (side === '0') {
side = 'buy';
}
else {
side = undefined;
}
}
if (costString !== undefined) {
costString = Precise.stringAbs(costString);
}
let fee = undefined;
if (feeCostString !== undefined) {
fee = {
'cost': feeCostString,
'currency': feeCurrency,
};
}
return this.safeTrade({
'id': id,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': symbol,
'order': orderId,
'type': type,
'side': side,
'takerOrMaker': undefined,
'price': priceString,
'amount': amountString,
'cost': costString,
'fee': fee,
}, market);
}
/**
* @method
* @name bitstamp#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://www.bitstamp.net/api/#tag/Transactions-public/operation/GetTransactions
* @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
* @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);
const request = {
'pair': market['id'],
'time': 'hour',
};
const response = await this.publicGetTransactionsPair(this.extend(request, params));
//
// [
// {
// "date": "1551814435",
// "tid": "83581898",
// "price": "0.03532850",
// "type": "1",
// "amount": "0.85945907"
// },
// {
// "date": "1551814434",
// "tid": "83581896",
// "price": "0.03532851",
// "type": "1",
// "amount": "11.34130961"
// },
// ]
//
return this.parseTrades(response, market, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// {
// "high": "9064.77",
// "timestamp": "1593961440",