ccxt-xigua
Version:
1,842 lines • 85.6 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeNotAvailable, ExchangeError, DDoSProtection, BadSymbol, InvalidOrder, ArgumentsRequired, AuthenticationError, OrderNotFound, PermissionDenied, InsufficientFunds, BadRequest, NotSupported } = require ('./base/errors');
const { TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class whitebit extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'whitebit',
'name': 'WhiteBit',
'version': 'v2',
'countries': [ 'EE' ],
'rateLimit': 500,
'has': {
'CORS': undefined,
'spot': true,
'margin': undefined, // has but unimplemented
'swap': false,
'future': false,
'option': false,
'cancelAllOrders': false,
'cancelOrder': true,
'cancelOrders': false,
'createDepositAddress': undefined,
'createLimitOrder': undefined,
'createMarketOrder': undefined,
'createOrder': true,
'createStopLimitOrder': true,
'createStopMarketOrder': true,
'createStopOrder': true,
'editOrder': undefined,
'fetchBalance': true,
'fetchBidsAsks': undefined,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDeposit': true,
'fetchDepositAddress': true,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchMarginMode': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrders': true,
'fetchOrderBook': true,
'fetchOrderTrades': true,
'fetchPositionMode': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': true,
'fetchTransactionFees': true,
'setLeverage': true,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/66732963-8eb7dd00-ee66-11e9-849b-10d9282bb9e0.jpg',
'api': {
'v1': {
'public': 'https://whitebit.com/api/v1/public',
'private': 'https://whitebit.com/api/v1',
},
'v2': {
'public': 'https://whitebit.com/api/v2/public',
},
'v4': {
'public': 'https://whitebit.com/api/v4/public',
'private': 'https://whitebit.com/api/v4',
},
},
'www': 'https://www.whitebit.com',
'doc': 'https://github.com/whitebit-exchange/api-docs',
'fees': 'https://whitebit.com/fee-schedule',
'referral': 'https://whitebit.com/referral/d9bdf40e-28f2-4b52-b2f9-cd1415d82963',
},
'api': {
'web': {
'get': [
'v1/healthcheck',
],
},
'v1': {
'public': {
'get': [
'markets',
'tickers',
'ticker',
'symbols',
'depth/result',
'history',
'kline',
],
},
'private': {
'post': [
'account/balance',
'order/new',
'order/cancel',
'orders',
'account/order_history',
'account/executed_history',
'account/executed_history/all',
'account/order',
],
},
},
'v2': {
'public': {
'get': [
'markets',
'ticker',
'assets',
'fee',
'depth/{market}',
'trades/{market}',
],
},
},
'v4': {
'public': {
'get': [
'assets',
'fee',
'orderbook/{market}',
'ticker',
'trades/{market}',
'time',
'ping',
],
},
'private': {
'post': [
'collateral-account/leverage',
'main-account/address',
'main-account/balance',
'main-account/create-new-address',
'main-account/codes',
'main-account/codes/apply',
'main-account/codes/my',
'main-account/codes/history',
'main-account/fiat-deposit-url',
'main-account/history',
'main-account/withdraw',
'main-account/withdraw-pay',
'main-account/transfer',
'trade-account/balance',
'trade-account/executed-history',
'trade-account/order',
'trade-account/order/history',
'order/new',
'order/market',
'order/stock_market',
'order/stop_limit',
'order/stop_market',
'order/cancel',
'orders',
'profile/websocket_token',
],
},
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'taker': this.parseNumber ('0.001'),
'maker': this.parseNumber ('0.001'),
},
},
'options': {
'createMarketBuyOrderRequiresPrice': true,
'fiatCurrencies': [ 'EUR', 'USD', 'RUB', 'UAH' ],
'accountsByType': {
'main': 'main',
'spot': 'trade',
'margin': 'margin', // api does not suppot transfers to margin
},
'transfer': {
'fillTransferResponseFromRequest': true,
},
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {
'Unauthorized request.': AuthenticationError, // {"code":10,"message":"Unauthorized request."}
'The market format is invalid.': BadSymbol, // {"code":0,"message":"Validation failed","errors":{"market":["The market format is invalid."]}}
'Market is not available': BadSymbol, // {"success":false,"message":{"market":["Market is not available"]},"result":[]}
'Invalid payload.': BadRequest, // {"code":9,"message":"Invalid payload."}
'Amount must be greater than 0': InvalidOrder, // {"code":0,"message":"Validation failed","errors":{"amount":["Amount must be greater than 0"]}}
'The order id field is required.': InvalidOrder, // {"code":0,"message":"Validation failed","errors":{"orderId":["The order id field is required."]}}
'Not enough balance': InsufficientFunds, // {"code":0,"message":"Validation failed","errors":{"amount":["Not enough balance"]}}
'This action is unauthorized.': PermissionDenied, // {"code":0,"message":"This action is unauthorized."}
'This API Key is not authorized to perform this action.': PermissionDenied, // {"code":4,"message":"This API Key is not authorized to perform this action."}
'Unexecuted order was not found.': OrderNotFound, // {"code":2,"message":"Inner validation failed","errors":{"order_id":["Unexecuted order was not found."]}}
'503': ExchangeNotAvailable, // {"response":null,"status":503,"errors":{"message":[""]},"notification":null,"warning":null,"_token":null},
'422': OrderNotFound, // {"response":null,"status":422,"errors":{"orderId":["Finished order id 1295772653 not found on your account"]},"notification":null,"warning":"Finished order id 1295772653 not found on your account","_token":null}
},
'broad': {
'Given amount is less than min amount': InvalidOrder, // {"code":0,"message":"Validation failed","errors":{"amount":["Given amount is less than min amount 200000"],"total":["Total is less than 5.05"]}}
'Total is less than': InvalidOrder, // {"code":0,"message":"Validation failed","errors":{"amount":["Given amount is less than min amount 200000"],"total":["Total is less than 5.05"]}}
'fee must be no less than': InvalidOrder, // {"code":0,"message":"Validation failed","errors":{"amount":["Total amount + fee must be no less than 5.05505"]}}
'Enable your key in API settings': PermissionDenied, // {"code":2,"message":"This action is unauthorized. Enable your key in API settings"}
},
},
});
}
async fetchMarkets (params = {}) {
/**
* @method
* @name whitebit#fetchMarkets
* @description retrieves data on all markets for whitebit
* @param {dict} params extra parameters specific to the exchange api endpoint
* @returns {[dict]} an array of objects representing market data
*/
const response = await this.v2PublicGetMarkets (params);
//
// {
// "success": true,
// "message": "",
// "result": [
// {
// "name":
// "C98_USDT",
// "stock":"C98",
// "money":"USDT",
// "stockPrec":"3",
// "moneyPrec":"5",
// "feePrec":"6",
// "makerFee":"0.001",
// "takerFee":"0.001",
// "minAmount":"2.5",
// "minTotal":"5.05",
// "tradesEnabled":true
// },
// ...
// ]
// }
//
const markets = this.safeValue (response, 'result', []);
const result = [];
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const id = this.safeString (market, 'name');
const baseId = this.safeString (market, 'stock');
const quoteId = this.safeString (market, 'money');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const active = this.safeValue (market, 'tradesEnabled');
const entry = {
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': undefined,
'swap': false,
'future': false,
'option': false,
'active': active,
'contract': false,
'linear': undefined,
'inverse': undefined,
'taker': this.safeNumber (market, 'makerFee'),
'maker': this.safeNumber (market, 'takerFee'),
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber (this.parsePrecision (this.safeString (market, 'stockPrec'))),
'price': this.parseNumber (this.parsePrecision (this.safeString (market, 'moneyPrec'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber (market, 'minAmount'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': this.safeNumber (market, 'minTotal'),
'max': undefined,
},
},
'info': market,
};
result.push (entry);
}
return result;
}
async fetchCurrencies (params = {}) {
/**
* @method
* @name whitebit#fetchCurrencies
* @description fetches all available currencies on an exchange
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} an associative dictionary of currencies
*/
const response = await this.v4PublicGetAssets (params);
//
// "BTC": {
// "name": "Bitcoin",
// "unified_cryptoasset_id": 1,
// "can_withdraw": true,
// "can_deposit": true,
// "min_withdraw": "0.001",
// "max_withdraw": "2",
// "maker_fee": "0.1",
// "taker_fee": "0.1",
// "min_deposit": "0.0001",
// "max_deposit": "0",
// },
//
const ids = Object.keys (response);
const result = {};
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const currency = response[id];
// breaks down in Python due to utf8 encoding issues on the exchange side
// const name = this.safeString (currency, 'name');
const canDeposit = this.safeValue (currency, 'can_deposit', true);
const canWithdraw = this.safeValue (currency, 'can_withdraw', true);
const active = canDeposit && canWithdraw;
const code = this.safeCurrencyCode (id);
result[code] = {
'id': id,
'code': code,
'info': currency, // the original payload
'name': undefined, // see the comment above
'active': active,
'deposit': canDeposit,
'withdraw': canWithdraw,
'fee': undefined,
'precision': undefined,
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': this.safeNumber (currency, 'min_withdraw'),
'max': this.safeNumber (currency, 'max_withdraw'),
},
},
};
}
return result;
}
async fetchTransactionFees (codes = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchTransactionFees
* @description fetch transaction fees
* @param {[str]|undefined} codes not used by fetchTransactionFees ()
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a list of [fee structures]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure}
*/
await this.loadMarkets ();
const response = await this.v4PublicGetFee (params);
//
// {
// "1INCH":{
// "is_depositable":true,
// "is_withdrawal":true,
// "ticker":"1INCH",
// "name":"1inch",
// "providers":[
// ],
// "withdraw":{
// "max_amount":"0",
// "min_amount":"21.5",
// "fixed":"17.5",
// "flex":null
// },
// "deposit":{
// "max_amount":"0",
// "min_amount":"19.5",
// "fixed":null,
// "flex":null
// }
// },
// {...}
// }
//
const currenciesIds = Object.keys (response);
const withdrawFees = {};
const depositFees = {};
for (let i = 0; i < currenciesIds.length; i++) {
const currency = currenciesIds[i];
const data = response[currency];
const code = this.safeCurrencyCode (currency);
const withdraw = this.safeValue (data, 'withdraw', {});
withdrawFees[code] = this.safeString (withdraw, 'fixed');
const deposit = this.safeValue (data, 'deposit', {});
depositFees[code] = this.safeString (deposit, 'fixed');
}
return {
'withdraw': withdrawFees,
'deposit': depositFees,
'info': response,
};
}
async fetchTradingFees (params = {}) {
/**
* @method
* @name whitebit#fetchTradingFees
* @description fetch the trading fees for multiple markets
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a dictionary of [fee structures]{@link https://docs.ccxt.com/en/latest/manual.html#fee-structure} indexed by market symbols
*/
const response = await this.v4PublicGetAssets (params);
//
// {
// '1INCH': {
// name: '1inch',
// unified_cryptoasset_id: '8104',
// can_withdraw: true,
// can_deposit: true,
// min_withdraw: '33',
// max_withdraw: '0',
// maker_fee: '0.1',
// taker_fee: '0.1',
// min_deposit: '30',
// max_deposit: '0'
// },
// ...
// }
//
const result = {};
for (let i = 0; i < this.symbols.length; i++) {
const symbol = this.symbols[i];
const market = this.market (symbol);
const fee = this.safeValue (response, market['baseId'], {});
let makerFee = this.safeString (fee, 'maker_fee');
let takerFee = this.safeString (fee, 'taker_fee');
makerFee = Precise.stringDiv (makerFee, '100');
takerFee = Precise.stringDiv (takerFee, '100');
result[symbol] = {
'info': fee,
'symbol': market['symbol'],
'percentage': true,
'tierBased': false,
'maker': this.parseNumber (makerFee),
'taker': this.parseNumber (takerFee),
};
}
return result;
}
async fetchTicker (symbol, params = {}) {
/**
* @method
* @name whitebit#fetchTicker
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {str} symbol unified symbol of the market to fetch the ticker for
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a [ticker structure]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
const response = await this.v1PublicGetTicker (this.extend (request, params));
//
// {
// "success":true,
// "message":"",
// "result": {
// "bid":"0.021979",
// "ask":"0.021996",
// "open":"0.02182",
// "high":"0.022039",
// "low":"0.02161",
// "last":"0.021987",
// "volume":"2810.267",
// "deal":"61.383565474",
// "change":"0.76",
// },
// }
//
const ticker = this.safeValue (response, 'result', {});
return this.parseTicker (ticker, market);
}
parseTicker (ticker, market = undefined) {
//
// FetchTicker (v1)
//
// {
// "bid": "0.021979",
// "ask": "0.021996",
// "open": "0.02182",
// "high": "0.022039",
// "low": "0.02161",
// "last": "0.021987",
// "volume": "2810.267",
// "deal": "61.383565474",
// "change": "0.76",
// }
//
// FetchTickers (v4)
//
// "BCH_RUB": {
// "base_id": 1831,
// "quote_id": 0,
// "last_price": "32830.21",
// "quote_volume": "1494659.8024096",
// "base_volume": "46.1083",
// "isFrozen": false,
// "change": "2.12" // in percent
// }
//
market = this.safeMarket (undefined, market);
const last = this.safeString (ticker, 'last_price');
return this.safeTicker ({
'symbol': market['symbol'],
'timestamp': undefined,
'datetime': undefined,
'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': undefined,
'open': this.safeString (ticker, 'open'),
'close': last,
'last': last,
'previousClose': undefined,
'change': undefined,
'percentage': this.safeString (ticker, 'change'),
'average': undefined,
'baseVolume': this.safeString2 (ticker, 'base_volume', 'volume'),
'quoteVolume': this.safeString2 (ticker, 'quote_volume', 'deal'),
'info': ticker,
}, market);
}
async fetchTickers (symbols = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchTickers
* @description fetches price tickers for multiple markets, statistical calculations with the information calculated over the past 24 hours each market
* @param {[str]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} an array of [ticker structures]{@link https://docs.ccxt.com/en/latest/manual.html#ticker-structure}
*/
await this.loadMarkets ();
const response = await this.v4PublicGetTicker (params);
//
// "BCH_RUB": {
// "base_id":1831,
// "quote_id":0,
// "last_price":"32830.21",
// "quote_volume":"1494659.8024096",
// "base_volume":"46.1083",
// "isFrozen":false,
// "change":"2.12"
// },
//
const marketIds = Object.keys (response);
const result = {};
for (let i = 0; i < marketIds.length; i++) {
const marketId = marketIds[i];
const market = this.safeMarket (marketId);
const ticker = this.parseTicker (response[marketId], market);
const symbol = ticker['symbol'];
result[symbol] = ticker;
}
return this.filterByArray (result, 'symbol', symbols);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {str} symbol unified symbol of the market to fetch the order book for
* @param {int|undefined} limit the maximum amount of order book entries to return
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symbols
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
if (limit !== undefined) {
request['depth'] = limit; // default = 50, maximum = 100
}
const response = await this.v4PublicGetOrderbookMarket (this.extend (request, params));
//
// {
// "timestamp": 1594391413,
// "asks": [
// [
// "9184.41",
// "0.773162"
// ],
// [ ... ]
// ],
// "bids": [
// [
// "9181.19",
// "0.010873"
// ],
// [ ... ]
// ]
// }
//
const timestamp = this.safeInteger (response, 'timestamp');
return this.parseOrderBook (response, symbol, timestamp);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @param {str} symbol unified symbol of the market to fetch trades for
* @param {int|undefined} since timestamp in ms of the earliest trade to fetch
* @param {int|undefined} limit the maximum amount of trades to fetch
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
const response = await this.v4PublicGetTradesMarket (this.extend (request, params));
//
// [
// {
// "tradeID": 158056419,
// "price": "9186.13",
// "quote_volume": "0.0021",
// "base_volume": "9186.13",
// "trade_timestamp": 1594391747,
// "type": "sell"
// },
// ],
//
return this.parseTrades (response, market, since, limit);
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchMyTrades
* @description fetch all trades made by the user
* @param {str} symbol unified symbol of the market to fetch trades for
* @param {int|undefined} since timestamp in ms of the earliest trade to fetch
* @param {int|undefined} limit the maximum amount of trades to fetch
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html?#public-trades}
*/
await this.loadMarkets ();
let market = undefined;
const request = {};
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
const response = await this.v4PrivatePostTradeAccountExecutedHistory (this.extend (request, params));
//
// when no symbol is provided
//
// {
// "USDC_USDT":[
// {
// "id":"1343815269",
// "clientOrderId":"",
// "time":"1641051917.532965",
// "side":"sell",
// "role":"2",
// "amount":"9.986",
// "price":"0.9995",
// "deal":"9.981007",
// "fee":"0.009981007",
// "orderId":"58166729555"
// },
// ]
// }
//
// when a symbol is provided
//
// [
// {
// 'id': 1343815269,
// 'clientOrderId': '',
// 'time': 1641051917.532965,
// 'side': 'sell',
// 'role': 2,
// 'amount': '9.986',
// 'price': '0.9995',
// 'deal': '9.981007',
// 'fee': '0.009981007',
// 'orderId': 58166729555,
// },
// ]
//
if (Array.isArray (response)) {
return this.parseTrades (response, market, since, limit);
} else {
let results = [];
const keys = Object.keys (response);
for (let i = 0; i < keys.length; i++) {
const marketId = keys[i];
const market = this.safeMarket (marketId, undefined, '_');
const rawTrades = this.safeValue (response, marketId, []);
const parsed = this.parseTrades (rawTrades, market, since, limit);
results = this.arrayConcat (results, parsed);
}
results = this.sortBy2 (results, 'timestamp', 'id');
const tail = (since === undefined);
return this.filterBySinceLimit (results, since, limit, 'timestamp', tail);
}
}
parseTrade (trade, market = undefined) {
//
// fetchTradesV4
//
// {
// "tradeID": 158056419,
// "price": "9186.13",
// "quote_volume": "0.0021",
// "base_volume": "9186.13",
// "trade_timestamp": 1594391747,
// "type": "sell"
// }
//
// orderTrades (v4Private)
//
// {
// "time": 1593342324.613711,
// "fee": "0.00000419198",
// "price": "0.00000701",
// "amount": "598",
// "id": 149156519, // trade id
// "dealOrderId": 3134995325, // orderId
// "clientOrderId": "customId11",
// "role": 2, // 1 = maker, 2 = taker
// "deal": "0.00419198" // amount in money
// }
//
// fetchMyTrades
//
// {
// 'id': 1343815269,
// 'clientOrderId': '',
// 'time': 1641051917.532965,
// 'side': 'sell',
// 'role': 2,
// 'amount': '9.986',
// 'price': '0.9995',
// 'deal': '9.981007',
// 'fee': '0.009981007',
// 'orderId': 58166729555,
// }
//
market = this.safeMarket (undefined, market);
const timestamp = this.safeTimestamp2 (trade, 'time', 'trade_timestamp');
const orderId = this.safeString2 (trade, 'dealOrderId', 'orderId');
const cost = this.safeString (trade, 'deal');
const price = this.safeString (trade, 'price');
const amount = this.safeString2 (trade, 'amount', 'base_volume');
const id = this.safeString2 (trade, 'id', 'tradeID');
const side = this.safeString2 (trade, 'type', 'side');
const symbol = market['symbol'];
const role = this.safeInteger (trade, 'role');
let takerOrMaker = undefined;
if (role !== undefined) {
takerOrMaker = (role === 1) ? 'maker' : 'taker';
}
let fee = undefined;
const feeCost = this.safeString (trade, 'fee');
if (feeCost !== undefined) {
fee = {
'cost': feeCost,
'currency': market['quote'],
};
}
return this.safeTrade ({
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'id': id,
'order': orderId,
'type': undefined,
'takerOrMaker': takerOrMaker,
'side': side,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
}, market);
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @param {str} symbol unified symbol of the market to fetch OHLCV data for
* @param {str} timeframe the length of time each candle represents
* @param {int|undefined} since timestamp in ms of the earliest candle to fetch
* @param {int|undefined} limit the maximum amount of candles to fetch
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[[int]]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'interval': this.timeframes[timeframe],
};
if (since !== undefined) {
const maxLimit = 1440;
if (limit === undefined) {
limit = maxLimit;
}
limit = Math.min (limit, maxLimit);
const start = parseInt (since / 1000);
const duration = this.parseTimeframe (timeframe);
const end = this.sum (start, duration * limit);
request['start'] = start;
request['end'] = end;
}
if (limit !== undefined) {
request['limit'] = limit; // max 1440
}
const response = await this.v1PublicGetKline (this.extend (request, params));
//
// {
// "success":true,
// "message":"",
// "result":[
// [1591488000,"0.025025","0.025025","0.025029","0.025023","6.181","0.154686629"],
// [1591488060,"0.025028","0.025033","0.025035","0.025026","8.067","0.201921167"],
// [1591488120,"0.025034","0.02505","0.02505","0.025034","20.089","0.503114696"],
// ]
// }
//
const result = this.safeValue (response, 'result', []);
return this.parseOHLCVs (result, market, timeframe, since, limit);
}
parseOHLCV (ohlcv, market = undefined) {
//
// [
// 1591488000,
// "0.025025",
// "0.025025",
// "0.025029",
// "0.025023",
// "6.181",
// "0.154686629"
// ]
//
return [
this.safeTimestamp (ohlcv, 0), // timestamp
this.safeNumber (ohlcv, 1), // open
this.safeNumber (ohlcv, 3), // high
this.safeNumber (ohlcv, 4), // low
this.safeNumber (ohlcv, 2), // close
this.safeNumber (ohlcv, 5), // volume
];
}
async fetchStatus (params = {}) {
/**
* @method
* @name whitebit#fetchStatus
* @description the latest known information on the availability of the exchange API
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a [status structure]{@link https://docs.ccxt.com/en/latest/manual.html#exchange-status-structure}
*/
const response = await this.v4PublicGetPing (params);
//
// [
// "pong"
// ]
//
const status = this.safeString (response, 0);
return {
'status': (status === 'pong') ? 'ok' : status,
'updated': undefined,
'eta': undefined,
'url': undefined,
'info': response,
};
}
async fetchTime (params = {}) {
/**
* @method
* @name whitebit#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
const response = await this.v4PublicGetTime (params);
//
// {
// "time":1635467280514
// }
//
return this.safeInteger (response, 'time');
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
/**
* @method
* @name whitebit#createOrder
* @description create a trade order
* @param {str} symbol unified symbol of the market to create an order in
* @param {str} type 'market' or 'limit'
* @param {str} side 'buy' or 'sell'
* @param {float} amount how much of currency you want to trade in units of base currency
* @param {float|undefined} price the price at which the order is to be fullfilled, in units of the quote currency, ignored in market orders
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} an [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
let method = undefined;
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'side': side,
'amount': this.amountToPrecision (symbol, amount),
};
const stopPrice = this.safeNumber2 (params, 'stopPrice', 'activationPrice');
if (stopPrice !== undefined) {
// it's a stop order
request['activation_price'] = this.priceToPrecision (symbol, stopPrice);
if (type === 'limit' || type === 'stopLimit') {
// it's a stop-limit-order
method = 'v4PrivateOPostOrderStopLimit';
} else if (type === 'market' || type === 'stopMarket') {
// it's a stop-market-order
method = 'v4PrivatePostOrderStopMarket';
}
} else {
if (type === 'market') {
// it's a regular market order
method = 'v4PrivatePostOrderMarket';
}
if (type === 'limit') {
// it's a regular limit order
method = 'v4PrivatePostOrderNew';
}
}
// aggregate common assignments regardless stop or not
if (type === 'limit' || type === 'stopLimit') {
if (price === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder() requires a price argument for a stopLimit order');
}
const convertedPrice = this.priceToPrecision (symbol, price);
request['price'] = convertedPrice;
}
if (type === 'market' || type === 'stopMarket') {
if (side === 'buy') {
let cost = this.safeNumber (params, 'cost');
const createMarketBuyOrderRequiresPrice = this.safeValue (this.options, 'createMarketBuyOrderRequiresPrice', true);
if (createMarketBuyOrderRequiresPrice) {
if (price !== undefined) {
if (cost === undefined) {
cost = amount * price;
}
} else if (cost === undefined) {
throw new InvalidOrder (this.id + " createOrder() requires the price argument for market buy orders to calculate total order cost (amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = false and supply the total cost value in the 'amount' argument or in the 'cost' extra parameter (the exchange-specific behaviour)");
}
} else {
cost = (cost === undefined) ? amount : cost;
}
request['amount'] = this.costToPrecision (symbol, cost);
}
}
if (method === undefined) {
throw new ArgumentsRequired (this.id + ' createOrder() requires one of the following order types: market, limit, stopLimit or stopMarket');
}
const response = await this[method] (this.extend (request, params));
return this.parseOrder (response);
}
async cancelOrder (id, symbol = undefined, params = {}) {
/**
* @method
* @name whitebit#cancelOrder
* @description cancels an open order
* @param {str} id order id
* @param {str} symbol unified symbol of the market the order was made in
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} An [order structure]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' cancelOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'orderId': parseInt (id),
};
return await this.v4PrivatePostOrderCancel (this.extend (request, params));
}
parseBalance (response) {
const balanceKeys = Object.keys (response);
const result = { };
for (let i = 0; i < balanceKeys.length; i++) {
const id = balanceKeys[i];
const balance = response[id];
const code = this.safeCurrencyCode (id);
const account = this.account ();
account['free'] = this.safeString (balance, 'available');
account['used'] = this.safeString (balance, 'freeze');
result[code] = account;
}
return this.safeBalance (result);
}
async fetchBalance (params = {}) {
/**
* @method
* @name whitebit#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a [balance structure]{@link https://docs.ccxt.com/en/latest/manual.html?#balance-structure}
*/
await this.loadMarkets ();
const response = await this.v4PrivatePostTradeAccountBalance (params);
//
// {
// "BTC": { "available": "0.123", "freeze": "1" },
// "XMR": { "available": "3013", "freeze": "100" },
// }
//
return this.parseBalance (response);
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchOpenOrders
* @description fetch all unfilled currently open orders
* @param {str} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch open orders for
* @param {int|undefined} limit the maximum number of open orders structures to retrieve
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[dict]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure}
*/
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOpenOrders() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
};
if (limit !== undefined) {
request['limit'] = limit; // default 50 max 100
}
const response = await this.v4PrivatePostOrders (this.extend (request, params));
//
// [
// {
// "orderId": 3686033640,
// "clientOrderId": "customId11",
// "market": "BTC_USDT",
// "side": "buy",
// "type": "limit",
// "timestamp": 1594605801.49815, // current timestamp of unexecuted order
// "dealMoney": "0", // executed amount in money
// "dealStock": "0", // executed amount in stock
// "amount": "2.241379", // active order amount
// "takerFee": "0.001",
// "makerFee": "0.001",
// "left": "2.241379", // unexecuted amount in stock
// "dealFee": "0", // executed fee by deal
// "price": "40000"
// },
// ]
//
return this.parseOrders (response, market, since, limit, { 'status': 'open' });
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchClosedOrders
* @description fetches information on multiple closed orders made by the user
* @param {str|undefined} symbol unified market symbol of the market orders were made in
* @param {int|undefined} since the earliest time in ms to fetch orders for
* @param {int|undefined} limit the maximum number of orde structures to retrieve
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[dict]} a list of [order structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-structure
*/
await this.loadMarkets ();
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
symbol = market['symbol'];
request['market'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default 50 max 100
}
const response = await this.v4PrivatePostTradeAccountOrderHistory (this.extend (request, params));
//
// {
// "BTC_USDT": [
// {
// "id": 160305483,
// "clientOrderId": "customId11",
// "time": 1594667731.724403,
// "side": "sell",
// "role": 2, // 1 = maker, 2 = taker
// "amount": "0.000076",
// "price": "9264.21",
// "deal": "0.70407996",
// "fee": "0.00070407996"
// },
// ],
// }
//
const marketIds = Object.keys (response);
let results = [];
for (let i = 0; i < marketIds.length; i++) {
const marketId = marketIds[i];
const market = this.safeMarket (marketId, undefined, '_');
const orders = response[marketId];
for (let j = 0; j < orders.length; j++) {
const order = this.parseOrder (orders[j], market);
results.push (this.extend (order, { 'status': 'filled' }));
}
}
results = this.sortBy (results, 'timestamp');
results = this.filterBySymbolSinceLimit (results, symbol, since, limit, since === undefined);
return results;
}
parseOrder (order, market = undefined) {
//
// createOrder, fetchOpenOrders
//
// {
// "orderId": 4180284841,
// "clientOrderId": "order1987111",
// "market": "BTC_USDT",
// "side": "buy",
// "type": "stop limit",
// "timestamp": 1595792396.165973,
// "dealMoney": "0", // if order finished - amount in money currency that finished
// "dealStock": "0", // if order finished - amount in stock currency that finished
// "amount": "0.001",
// "takerFee": "0.001",
// "makerFee": "0.001",
// "left": "0.001", // remaining amount
// "dealFee": "0", // fee in money that you pay if order is finished
// "price": "40000",
// "activation_price": "40000" // activation price -> only for stopLimit, stopMarket
// }
//
// fetchClosedOrders
//
// {
// "market": "BTC_USDT"
// "amount": "0.0009",
// "price": "40000",
// "type": "limit",
// "id": 4986126152,
// "clientOrderId": "customId11",
// "side": "sell",
// "ctime": 1597486960.311311, // timestamp of order creation
// "takerFee": "0.001",
// "ftime": 1597486960.311332, // executed order timestamp
// "makerFee": "0.001",
// "dealFee": "0.041258268", // paid fee if order is finished
// "dealStock": "0.0009", // amount in stock currency that finished
// "dealMoney": "41.258268" // amount in money currency that finished
// }
//
const marketId = this.safeString (order, 'market');
market = this.safeMarket (marketId, market, '_');
const symbol = market['symbol'];
const side = this.safeString (order, 'side');
const filled = this.safeString (order, 'dealStock');
const remaining = this.safeString (order, 'left');
const clientOrderId = this.safeString (order, 'clientOrderId');
let price = this.safeString (order, 'price');
const stopPrice = this.safeString (order, 'activation_price');
const orderId = this.safeString2 (order, 'orderId', 'id');
const type = this.safeString (order, 'type');
let amount = this.safeString (order, 'amount');
let cost = undefined;
if (price === '0') {
// api error to be solved
price = undefined;
}
if (side === 'buy' && type.indexOf ('market') >= 0) {
// in these cases the amount is in the quote currency meaning it's the cost
cost = amount;
amount = undefined;
if (price !== undefined) {
// if the price is available we can do this conversion
// from amount in quote currency to base currency
amount = Precise.stringDiv (cost, price);
}
}
const dealFee = this.safeString (order, 'dealFee');
let fee = undefined;
if (dealFee !== undefined) {
fee = {
'cost': this.parseNumber (dealFee),
'currency': market['quote'],
};
}
const timestamp = this.safeTimestamp2 (order, 'ctime', 'timestamp');
const lastTradeTimestamp = this.safeTimestamp (order, 'ftime');
return this.safeOrder ({
'info': order,
'id': orderId,
'symbol': symbol,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': lastTradeTimestamp,
'timeInForce': undefined,
'postOnly': undefined,
'status': undefined,
'side': side,
'price': price,
'type': type,
'stopPrice': stopPrice,
'amount': amount,
'filled': filled,
'remaining': remaining,
'average': undefined,
'cost': cost,
'fee': fee,
'trades': undefined,
}, market);
}
async fetchOrderTrades (id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchOrderTrades
* @description fetch all the trades made from a single order
* @param {str} id order id
* @param {str|undefined} symbol unified market symbol
* @param {int|undefined} since the earliest time in ms to fetch trades for
* @param {int|undefined} limit the maximum number of trades to retrieve
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[dict]} a list of [trade structures]{@link https://docs.ccxt.com/en/latest/manual.html#trade-structure}
*/
await this.loadMarkets ();
const request = {
'orderId': parseInt (id),
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default 50, max 100
}
const response = await this.v4PrivatePostTradeAccountOrder (this.extend (request, params));
//
// {
// "records": [
// {
// "time": 1593342324.613711,
// "fee": "0.00000419198",
// "price": "0.00000701",
// "amount": "598",
// "id": 149156519, // trade id
// "dealOrderId": 3134995325, // orderId
// "clientOrderId": "customId11", // empty string if not specified
// "role": 2, // 1 = maker, 2 = taker
// "deal": "0.00419198"
// }
// ],
// "offset": 0,
// "limit": 100
// }
//
const data = this.safeValue (response, 'records', []);
return this.parseTrades (data, market);
}
async fetchDepositAddress (code, params = {}) {
/**
* @method
* @name whitebit#fetchDepositAddress
* @description fetch the deposit address for a currency associated with this account
* @param {str} code unified currency code
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} an [address structure]{@link https://docs.ccxt.com/en/latest/manual.html#address-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'ticker': currency['id'],
};
let method = 'v4PrivatePostMainAccountAddress';
if (this.isFiat (code)) {
method = 'v4PrivatePostMainAccountFiatDepositUrl';
const provider = this.safeNumber (params, 'provider');
if (provider === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDepositAddress() requires a provider when the ticker is fiat');
}
request['provider'] = provider;
const amount = this.safeNumber (params, 'amount');
if (amount === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDepositAddress() requires an amount when the ticker is fiat');
}
request['amount'] = amount;
const uniqueId = this.safeValue (params, 'uniqueId');
if (uniqueId === undefined) {
throw new ArgumentsRequired (this.id + ' fetchDepositAddress() requires an uniqueId when the ticker is fiat');
}
}
const response = await this[method] (this.extend (request, params));
//
// fiat
//
// {
// "url": "https://someaddress.com"
// }
//
// crypto
//
// {
// "account": {
// "address": "GDTSOI56XNVAKJNJBLJGRNZIVOCIZJRBIDKTWSCYEYNFAZEMBLN75RMN",
// "memo": "48565488244493"
// },
// "required": {
// "fixedFee": "0",
// "flexFee": {
// "maxFee": "0",
// "minFee": "0",
// "percent": "0"
// },
// "maxAmount": "0",
// "minAmount": "1"
// }
// }
//
const url = this.safeString (response, 'url');
const account = this.safeValue (response, 'account', {});
const address = this.safeString (account, 'address', url);
const tag = this.safeString (account, 'memo');
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'network': undefined,
'info': response,
};
}
async setLeverage (leverage, symbol = undefined, params = {}) {
/**
* @method
* @name whitebit#setLeverage
* @description set the level of leverage for a market
* @param {float} leverage the rate of leverage
* @param {str} symbol unified market symbol
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} response from the exchange
*/
await this.loadMarkets ();
if (symbol !== undefined) {
throw new NotSupported (this.id + ' setLeverage() does not allow to set per symbol');
}
if ((leverage < 1) || (leverage > 20)) {
throw new BadRequest (this.id + ' setLeverage() leverage should be between 1 and 20');
}
const request = {
'leverage': leverage,
};
return await this.v4PrivatePostCollateralAccountLeverage (this.extend (request, params));
// {
// "leverage": 5
// }
}
async transfer (code, amount, fromAccount, toAccount, params = {}) {
/**
* @method
* @name whitebit#transfer
* @description transfer currency internally between wallets on the same account
* @param {str} code unified currency code
* @param {float} amount amount to transfer
* @param {str} fromAccount account to transfer from
* @param {str} toAccount account to transfer to
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a [transfer structure]{@link https://docs.ccxt.com/en/latest/manual.html#transfer-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code);
const accountsByType = this.safeValue (this.options, 'accountsByType');
const fromAccountId = this.safeString (accountsByType, fromAccount, fromAccount);
const toAccountId = this.safeString (accountsByType, toAccount, toAccount);
let type = undefined;
if (fromAccountId === 'main' && toAccountId === 'trade') {
type = 'deposit';
} else if (fromAccountId === 'trade' && toAccountId === 'main') {
type = 'withdraw';
}
if (type === undefined) {
throw new ExchangeError (this.id + ' transfer() only allows transfers between main account and spot account');
}
const request = {
'ticker': currency['id'],
'method': type,
'amount': this.currencyToPrecision (code, amount),
};
const response = await this.v4PrivatePostMainAccountTransfer (this.extend (request, params));
//
// []
//
const transfer = this.parseTransfer (response, currency);
const transferOptions = this.safeValue (this.options, 'transfer', {});
const fillTransferResponseFromRequest = this.safeValue (transferOptions, 'fillTransferResponseFromRequest', true);
if (fillTransferResponseFromRequest) {
transfer['amount'] = amount;
transfer['fromAccount'] = fromAccount;
transfer['toAccount'] = toAccount;
}
return transfer;
}
parseTransfer (transfer, currency) {
//
// []
//
return {
'info': transfer,
'id': undefined,
'timestamp': undefined,
'datetime': undefined,
'currency': this.safeCurrencyCode (undefined, currency),
'amount': undefined,
'fromAccount': undefined,
'toAccount': undefined,
'status': 'pending',
};
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
/**
* @method
* @name whitebit#withdraw
* @description make a withdrawal
* @param {str} code unified currency code
* @param {float} amount the amount to withdraw
* @param {str} address the address to withdraw to
* @param {str|undefined} tag
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a [transaction structure]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
await this.loadMarkets ();
const currency = this.currency (code); // check if it has canDeposit
const request = {
'ticker': currency['id'],
'amount': this.currencyToPrecision (code, amount),
'address': address,
};
let uniqueId = this.safeValue (params, 'uniqueId');
if (uniqueId === undefined) {
uniqueId = this.uuid22 ();
}
request['uniqueId'] = uniqueId;
if (tag !== undefined) {
request['memo'] = tag;
}
if (this.isFiat (code)) {
const provider = this.safeValue (params, 'provider');
if (provider === undefined) {
throw new ArgumentsRequired (this.id + ' withdraw() requires a provider when the ticker is fiat');
}
request['provider'] = provider;
}
const response = await this.v4PrivatePostMainAccountWithdraw (this.extend (request, params));
//
// empty array with a success status
// go to deposit/withdraw history and check you request status by uniqueId
//
// []
//
return this.extend ({ 'id': uniqueId }, this.parseTransaction (response, currency));
}
parseTransaction (transaction, currency = undefined) {
//
// {
// "address": "3ApEASLcrQtZpg1TsssFgYF5V5YQJAKvuE", // deposit address
// "uniqueId": null, // unique Id of deposit
// "createdAt": 1593437922, // timestamp of deposit
// "currency": "Bitcoin", // deposit currency
// "ticker": "BTC", // deposit currency ticker
// "method": 1, // called method 1 - deposit, 2 - withdraw
// "amount": "0.0006", // amount of deposit
// "description": "", // deposit description
// "memo": "", // deposit memo
// "fee": "0", // deposit fee
// "status": 15, // transactions status
// "network": null, // if currency is multinetwork
// "transactionHash": "a275a514013e4e0f927fd0d1bed215e7f6f2c4c6ce762836fe135ec22529d886", // deposit transaction hash
// "details": {
// "partial": { // details about partially successful withdrawals
// "requestAmount": "50000", // requested withdrawal amount
// "processedAmount": "39000", // processed withdrawal amount
// "processedFee": "273", // fee for processed withdrawal amount
// "normalizeTransaction": "" // deposit id
// }
// },
// "confirmations": { // if transaction status == 15 you can see this object
// "actual": 1, // current block confirmations
// "required": 2 // required block confirmation for successful deposit
// }
// }
//
currency = this.safeCurrency (undefined, currency);
const address = this.safeString (transaction, 'address');
const timestamp = this.safeTimestamp (transaction, 'createdAt');
const currencyId = this.safeString (transaction, 'ticker');
const status = this.safeString (transaction, 'status');
const method = this.safeString (transaction, 'method');
return {
'id': this.safeString (transaction, 'uniqueId'),
'txid': this.safeString (transaction, 'transactionHash'),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'network': this.safeString (transaction, 'network'),
'addressFrom': (method === '1') ? address : undefined,
'address': address,
'addressTo': (method === '2') ? address : undefined,
'amount': this.safeNumber (transaction, 'amount'),
'type': (method === '1') ? 'deposit' : 'withdrawal',
'currency': this.safeCurrencyCode (currencyId, currency),
'status': this.parseTransactionStatus (status),
'updated': undefined,
'tagFrom': undefined,
'tag': undefined,
'tagTo': undefined,
'comment': this.safeString (transaction, 'description'),
'fee': {
'cost': this.safeNumber (transaction, 'fee'),
'currency': this.safeCurrencyCode (currencyId, currency),
},
'info': transaction,
};
}
parseTransactionStatus (status) {
const statuses = {
'1': 'pending',
'2': 'pending',
'3': 'ok',
'4': 'canceled',
'5': 'pending',
'6': 'pending',
'7': 'ok',
'9': 'canceled',
'10': 'pending',
'11': 'pending',
'12': 'pending',
'13': 'pending',
'14': 'pending',
'15': 'pending',
'16': 'pending',
'17': 'pending',
};
return this.safeString (statuses, status, status);
}
async fetchDeposit (id, code = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchDeposit
* @description fetch information on a deposit
* @param {str} id deposit id
* @param {str|undefined} code not used by whitebit fetchDeposit ()
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {dict} a [transaction structure]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
await this.loadMarkets ();
let currency = undefined;
const request = {
'transactionMethod': 1,
'uniqueId': id,
'limit': 1,
'offset': 0,
};
if (code !== undefined) {
currency = this.currency (code);
request['ticker'] = currency['id'];
}
const response = await this.v4PrivatePostMainAccountHistory (this.extend (request, params));
//
// {
// "limit": 100,
// "offset": 0,
// "records": [
// {
// "address": "3ApEASLcrQtZpg1TsssFgYF5V5YQJAKvuE", // deposit address
// "uniqueId": null, // unique Id of deposit
// "createdAt": 1593437922, // timestamp of deposit
// "currency": "Bitcoin", // deposit currency
// "ticker": "BTC", // deposit currency ticker
// "method": 1, // called method 1 - deposit, 2 - withdraw
// "amount": "0.0006", // amount of deposit
// "description": "", // deposit description
// "memo": "", // deposit memo
// "fee": "0", // deposit fee
// "status": 15, // transactions status
// "network": null, // if currency is multinetwork
// "transactionHash": "a275a514013e4e0f927fd0d1bed215e7f6f2c4c6ce762836fe135ec22529d886", // deposit transaction hash
// "details": {
// "partial": { // details about partially successful withdrawals
// "requestAmount": "50000", // requested withdrawal amount
// "processedAmount": "39000", // processed withdrawal amount
// "processedFee": "273", // fee for processed withdrawal amount
// "normalizeTransaction": "" // deposit id
// }
// },
// "confirmations": { // if transaction status == 15 you can see this object
// "actual": 1, // current block confirmations
// "required": 2 // required block confirmation for successful deposit
// }
// },
// {...},
// ],
// "total": 300 // total number of transactions, use this for calculating ‘limit’ and ‘offset'
// }
//
const records = this.safeValue (response, 'records', []);
const first = this.safeValue (records, 0, {});
return this.parseTransaction (first, currency);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name whitebit#fetchDeposits
* @description fetch all deposits made to an account
* @param {str|undefined} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch deposits for
* @param {int|undefined} limit the maximum number of deposits structures to retrieve
* @param {dict} params extra parameters specific to the whitebit api endpoint
* @returns {[dict]} a list of [transaction structures]{@link https://docs.ccxt.com/en/latest/manual.html#transaction-structure}
*/
await this.loadMarkets ();
let currency = undefined;
const request = {
'transactionMethod': 1,
'limit': 100,
'offset': 0,
};
if (code !== undefined) {
currency = this.currency (code);
request['ticker'] = currency['id'];
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.v4PrivatePostMainAccountHistory (this.extend (request, params));
//
// {
// "limit": 100,
// "offset": 0,
// "records": [
// {
// "address": "3ApEASLcrQtZpg1TsssFgYF5V5YQJAKvuE", // deposit address
// "uniqueId": null, // unique Id of deposit
// "createdAt": 1593437922, // timestamp of deposit
// "currency": "Bitcoin", // deposit currency
// "ticker": "BTC", // deposit currency ticker
// "method": 1, // called method 1 - deposit, 2 - withdraw
// "amount": "0.0006", // amount of deposit
// "description": "", // deposit description
// "memo": "", // deposit memo
// "fee": "0", // deposit fee
// "status": 15, // transactions status
// "network": null, // if currency is multinetwork
// "transactionHash": "a275a514013e4e0f927fd0d1bed215e7f6f2c4c6ce762836fe135ec22529d886", // deposit transaction hash
// "details": {
// "partial": { // details about partially successful withdrawals
// "requestAmount": "50000", // requested withdrawal amount
// "processedAmount": "39000", // processed withdrawal amount
// "processedFee": "273", // fee for processed withdrawal amount
// "normalizeTransaction": "" // deposit id
// }
// },
// "confirmations": { // if transaction status == 15 you can see this object
// "actual": 1, // current block confirmations
// "required": 2 // required block confirmation for successful deposit
// }
// },
// {...},
// ],
// "total": 300 // total number of transactions, use this for calculating ‘limit’ and ‘offset'
// }
//
const records = this.safeValue (response, 'records', []);
return this.parseTransactions (records, currency, since, limit);
}
isFiat (currency) {
const fiatCurrencies = this.safeValue (this.options, 'fiatCurrencies', []);
return this.inArray (currency, fiatCurrencies);
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
const query = this.omit (params, this.extractParams (path));
const version = this.safeValue (api, 0);
const accessibility = this.safeValue (api, 1);
const pathWithParams = '/' + this.implodeParams (path, params);
let url = this.urls['api'][version][accessibility] + pathWithParams;
if (accessibility === 'public') {
if (Object.keys (query).length) {
url += '?' + this.urlencode (query);
}
}
if (accessibility === 'private') {
this.checkRequiredCredentials ();
const nonce = this.nonce ().toString ();
const secret = this.stringToBinary (this.encode (this.secret));
const request = '/' + 'api' + '/' + version + pathWithParams;
body = this.json (this.extend ({ 'request': request, 'nonce': nonce }, params));
const payload = this.stringToBase64 (body);
const signature = this.hmac (payload, secret, 'sha512');
headers = {
'Content-Type': 'application/json',
'X-TXC-APIKEY': this.apiKey,
'X-TXC-PAYLOAD': this.decode (payload),
'X-TXC-SIGNATURE': signature,
};
}
return { 'url': url, 'method': method, 'body': body, 'headers': headers };
}
handleErrors (code, reason, url, method, headers, body, response, requestHeaders, requestBody) {
if ((code === 418) || (code === 429)) {
throw new DDoSProtection (this.id + ' ' + code.toString () + ' ' + reason + ' ' + body);
}
if (code === 404) {
throw new ExchangeError (this.id + ' ' + code.toString () + ' endpoint not found');
}
if (response !== undefined) {
// For cases where we have a meaningful status
// {"response":null,"status":422,"errors":{"orderId":["Finished order id 435453454535 not found on your account"]},"notification":null,"warning":"Finished order id 435453454535 not found on your account","_token":null}
const status = this.safeInteger (response, 'status');
// {"code":10,"message":"Unauthorized request."}
const message = this.safeString (response, 'message');
// For these cases where we have a generic code variable error key
// {"code":0,"message":"Validation failed","errors":{"amount":["Amount must be greater than 0"]}}
const code = this.safeInteger (response, 'code');
const hasErrorStatus = status !== undefined && status !== '200';
if (hasErrorStatus || code !== undefined) {
const feedback = this.id + ' ' + body;
let errorInfo = message;
if (hasErrorStatus) {
errorInfo = status;
} else {
const errorObject = this.safeValue (response, 'errors');
if (errorObject !== undefined) {
const errorKey = Object.keys (errorObject)[0];
const errorMessageArray = this.safeValue (errorObject, errorKey, []);
const errorMessageLength = errorMessageArray.length;
errorInfo = (errorMessageLength > 0) ? errorMessageArray[0] : body;
}
}
this.throwExactlyMatchedException (this.exceptions['exact'], errorInfo, feedback);
this.throwBroadlyMatchedException (this.exceptions['broad'], body, feedback);
throw new ExchangeError (feedback);
}
}
}
};