UNPKG

ccxt-look

Version:

A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges

1,228 lines (1,198 loc) 55.6 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { ExchangeNotAvailable, ExchangeError, DDoSProtection, BadSymbol, InvalidOrder, ArgumentsRequired, AuthenticationError, OrderNotFound, PermissionDenied, InsufficientFunds, BadRequest } = require ('./base/errors'); 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, 'cancelOrder': true, 'createDepositAddress': undefined, 'createLimitOrder': undefined, 'createMarketOrder': undefined, 'createOrder': true, 'createStopLimitOrder': true, 'createStopMarketOrder': true, 'createStopOrder': true, 'editOrder': undefined, 'fetchBalance': true, 'fetchBidsAsks': undefined, 'fetchClosedOrders': true, 'fetchCurrencies': true, 'fetchDepositAddress': true, 'fetchFundingFees': true, 'fetchFundingHistory': false, 'fetchFundingRate': false, 'fetchFundingRateHistory': false, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchMarkets': true, 'fetchMarkOHLCV': false, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrderBook': true, 'fetchOrderTrades': true, 'fetchPremiumIndexOHLCV': false, 'fetchTicker': true, 'fetchTickers': true, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': 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': [ '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', ], }, }, }, '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, }, }, '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 = {}) { 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.safeInteger (market, 'stockPrec'), 'price': this.safeInteger (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 = {}) { 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 fetchFundingFees (params = {}) { 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 = {}) { 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 = {}) { 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, false); } async fetchTickers (symbols = undefined, params = {}) { 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 = {}) { 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.safeString (response, 'timestamp'); return this.parseOrderBook (response, symbol, timestamp); } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { 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); } 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 // } // const timestamp = this.safeTimestamp2 (trade, 'time', 'trade_timestamp'); const orderId = this.safeString (trade, 'dealOrderId'); 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.safeString (trade, 'type'); const symbol = this.safeSymbol (undefined, market); 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) { const safeMarket = this.safeMarket (undefined, market); const quote = safeMarket['quote']; fee = { 'cost': feeCost, 'currency': 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 = {}) { 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 = {}) { const response = await this.v4PublicGetPing (params); // // [ // "pong" // ] // const status = this.safeString (response, 0); return { 'status': (status === 'pong') ? 'ok' : status, 'updated': this.milliseconds (), 'eta': undefined, 'url': undefined, 'info': response, }; } async fetchTime (params = {}) { const response = await this.v4PublicGetTime (params); // // { // "time":1635467280514 // } // return this.safeInteger (response, 'time'); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { 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 = {}) { 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 = {}) { 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 = {}) { 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 = {}) { 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'); let clientOrderId = this.safeString (order, 'clientOrderId'); if (clientOrderId === '') { clientOrderId = undefined; } 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 = {}) { 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 = {}) { 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 transfer (code, amount, fromAccount, toAccount, params = {}) { 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 = {}) { 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 =