consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,201 lines (1,166 loc) • 52.2 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ExchangeNotAvailable, BadResponse, BadRequest, InvalidOrder, InsufficientFunds, AuthenticationError, ArgumentsRequired, InvalidAddress, RateLimitExceeded, DDoSProtection, BadSymbol } = require ('./base/errors');
const { TRUNCATE, TICK_SIZE } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class probit extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'probit',
'name': 'ProBit',
'countries': [ 'SC', 'KR' ], // Seychelles, South Korea
'rateLimit': 250, // ms
'has': {
'CORS': true,
'fetchTime': true,
'fetchMarkets': true,
'fetchCurrencies': true,
'fetchTickers': true,
'fetchTicker': true,
'fetchOHLCV': true,
'fetchOrderBook': true,
'fetchTrades': true,
'fetchBalance': true,
'createOrder': true,
'createMarketOrder': true,
'cancelOrder': true,
'fetchOrder': true,
'fetchOpenOrders': true,
'fetchClosedOrders': true,
'fetchMyTrades': true,
'fetchDepositAddress': true,
'withdraw': true,
'signIn': true,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'10m': '10m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'4h': '4h',
'6h': '6h',
'12h': '12h',
'1d': '1D',
'1w': '1W',
'1M': '1M',
},
'version': 'v1',
'urls': {
'logo': 'https://user-images.githubusercontent.com/51840849/79268032-c4379480-7ea2-11ea-80b3-dd96bb29fd0d.jpg',
'api': {
'accounts': 'https://accounts.probit.com',
'public': 'https://api.probit.com/api/exchange',
'private': 'https://api.probit.com/api/exchange',
},
'www': 'https://www.probit.com',
'doc': [
'https://docs-en.probit.com',
'https://docs-ko.probit.com',
],
'fees': 'https://support.probit.com/hc/en-us/articles/360020968611-Trading-Fees',
'referral': 'https://www.probit.com/r/34608773',
},
'api': {
'public': {
'get': [
'market',
'currency',
'currency_with_platform',
'time',
'ticker',
'order_book',
'trade',
'candle',
],
},
'private': {
'post': [
'new_order',
'cancel_order',
'withdrawal',
],
'get': [
'balance',
'order',
'open_order',
'order_history',
'trade_history',
'deposit_address',
],
},
'accounts': {
'post': [
'token',
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.2 / 100,
'taker': 0.2 / 100,
},
},
'exceptions': {
'exact': {
'UNAUTHORIZED': AuthenticationError,
'INVALID_ARGUMENT': BadRequest, // Parameters are not a valid format, parameters are empty, or out of range, or a parameter was sent when not required.
'TRADING_UNAVAILABLE': ExchangeNotAvailable,
'NOT_ENOUGH_BALANCE': InsufficientFunds,
'NOT_ALLOWED_COMBINATION': BadRequest,
'INVALID_ORDER': InvalidOrder, // Requested order does not exist, or it is not your order
'RATE_LIMIT_EXCEEDED': RateLimitExceeded, // You are sending requests too frequently. Please try it later.
'MARKET_UNAVAILABLE': ExchangeNotAvailable, // Market is closed today
'INVALID_MARKET': BadSymbol, // Requested market is not exist
'MARKET_CLOSED': BadSymbol, // {"errorCode":"MARKET_CLOSED"}
'INVALID_CURRENCY': BadRequest, // Requested currency is not exist on ProBit system
'TOO_MANY_OPEN_ORDERS': DDoSProtection, // Too many open orders
'DUPLICATE_ADDRESS': InvalidAddress, // Address already exists in withdrawal address list
},
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
},
'precisionMode': TICK_SIZE,
'options': {
'createMarketBuyOrderRequiresPrice': true,
'timeInForce': {
'limit': 'gtc',
'market': 'ioc',
},
},
'commonCurrencies': {
'AUTO': 'Cube',
'BTCBEAR': 'BEAR',
'BTCBULL': 'BULL',
'CBC': 'CryptoBharatCoin',
'EPS': 'Epanus', // conflict with EPS Ellipsis https://github.com/ccxt/ccxt/issues/8909
'HBC': 'Hybrid Bank Cash',
'UNI': 'UNICORN Token',
},
});
}
async fetchMarkets (params = {}) {
const response = await this.publicGetMarket (params);
//
// {
// "data":[
// {
// "id":"MONA-USDT",
// "base_currency_id":"MONA",
// "quote_currency_id":"USDT",
// "min_price":"0.001",
// "max_price":"9999999999999999",
// "price_increment":"0.001",
// "min_quantity":"0.0001",
// "max_quantity":"9999999999999999",
// "quantity_precision":4,
// "min_cost":"1",
// "max_cost":"9999999999999999",
// "cost_precision":8,
// "taker_fee_rate":"0.2",
// "maker_fee_rate":"0.2",
// "show_in_ui":true,
// "closed":false
// },
// ]
// }
//
const markets = this.safeValue (response, 'data', []);
const result = [];
for (let i = 0; i < markets.length; i++) {
const market = markets[i];
const id = this.safeString (market, 'id');
const baseId = this.safeString (market, 'base_currency_id');
const quoteId = this.safeString (market, 'quote_currency_id');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const closed = this.safeValue (market, 'closed', false);
const active = !closed;
const amountPrecision = this.safeString (market, 'quantity_precision');
const costPrecision = this.safeString (market, 'cost_precision');
const amountTickSize = this.parsePrecision (amountPrecision);
const costTickSize = this.parsePrecision (costPrecision);
const precision = {
'amount': this.parseNumber (amountTickSize),
'price': this.safeNumber (market, 'price_increment'),
'cost': this.parseNumber (costTickSize),
};
const takerFeeRate = this.safeString (market, 'taker_fee_rate');
const taker = Precise.stringDiv (takerFeeRate, '100');
const makerFeeRate = this.safeString (market, 'maker_fee_rate');
const maker = Precise.stringDiv (makerFeeRate, '100');
result.push ({
'id': id,
'info': market,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'precision': precision,
'taker': this.parseNumber (taker),
'maker': this.parseNumber (maker),
'limits': {
'amount': {
'min': this.safeNumber (market, 'min_quantity'),
'max': this.safeNumber (market, 'max_quantity'),
},
'price': {
'min': this.safeNumber (market, 'min_price'),
'max': this.safeNumber (market, 'max_price'),
},
'cost': {
'min': this.safeNumber (market, 'min_cost'),
'max': this.safeNumber (market, 'max_cost'),
},
},
});
}
return result;
}
async fetchCurrencies (params = {}) {
const response = await this.publicGetCurrencyWithPlatform (params);
//
// {
// "data":[
// {
// "id":"USDT",
// "display_name":{"ko-kr":"테더","en-us":"Tether"},
// "show_in_ui":true,
// "platform":[
// {
// "id":"ETH",
// "priority":1,
// "deposit":true,
// "withdrawal":true,
// "currency_id":"USDT",
// "precision":6,
// "min_confirmation_count":15,
// "require_destination_tag":false,
// "display_name":{"name":{"ko-kr":"ERC-20","en-us":"ERC-20"}},
// "min_deposit_amount":"0",
// "min_withdrawal_amount":"1",
// "withdrawal_fee":[
// {"amount":"0.01","priority":2,"currency_id":"ETH"},
// {"amount":"1.5","priority":1,"currency_id":"USDT"},
// ],
// "deposit_fee":{},
// "suspended_reason":"",
// "deposit_suspended":false,
// "withdrawal_suspended":false
// },
// {
// "id":"OMNI",
// "priority":2,
// "deposit":true,
// "withdrawal":true,
// "currency_id":"USDT",
// "precision":6,
// "min_confirmation_count":3,
// "require_destination_tag":false,
// "display_name":{"name":{"ko-kr":"OMNI","en-us":"OMNI"}},
// "min_deposit_amount":"0",
// "min_withdrawal_amount":"5",
// "withdrawal_fee":[{"amount":"5","priority":1,"currency_id":"USDT"}],
// "deposit_fee":{},
// "suspended_reason":"wallet_maintenance",
// "deposit_suspended":false,
// "withdrawal_suspended":false
// }
// ],
// "stakeable":false,
// "unstakeable":false,
// "auto_stake":false,
// "auto_stake_amount":"0"
// }
// ]
// }
//
const currencies = this.safeValue (response, 'data');
const result = {};
for (let i = 0; i < currencies.length; i++) {
const currency = currencies[i];
const id = this.safeString (currency, 'id');
const code = this.safeCurrencyCode (id);
const displayName = this.safeValue (currency, 'display_name');
const name = this.safeString (displayName, 'en-us');
const platforms = this.safeValue (currency, 'platform', []);
const platformsByPriority = this.sortBy (platforms, 'priority');
const platform = this.safeValue (platformsByPriority, 0, {});
const precision = this.safeInteger (platform, 'precision');
const depositSuspended = this.safeValue (platform, 'deposit_suspended');
const withdrawalSuspended = this.safeValue (platform, 'withdrawal_suspended');
const active = !(depositSuspended && withdrawalSuspended);
const withdrawalFees = this.safeValue (platform, 'withdrawal_fee', {});
const fees = [];
// sometimes the withdrawal fee is an empty object
// [ { 'amount': '0.015', 'priority': 1, 'currency_id': 'ETH' }, {} ]
for (let j = 0; j < withdrawalFees.length; j++) {
const withdrawalFee = withdrawalFees[j];
const amount = this.safeNumber (withdrawalFee, 'amount');
const priority = this.safeInteger (withdrawalFee, 'priority');
if ((amount !== undefined) && (priority !== undefined)) {
fees.push (withdrawalFee);
}
}
const withdrawalFeesByPriority = this.sortBy (fees, 'priority');
const withdrawalFee = this.safeValue (withdrawalFeesByPriority, 0, {});
const fee = this.safeNumber (withdrawalFee, 'amount');
result[code] = {
'id': id,
'code': code,
'info': currency,
'name': name,
'active': active,
'fee': fee,
'precision': precision,
'limits': {
'amount': {
'min': Math.pow (10, -precision),
'max': Math.pow (10, precision),
},
'deposit': {
'min': this.safeNumber (platform, 'min_deposit_amount'),
'max': undefined,
},
'withdraw': {
'min': this.safeNumber (platform, 'min_withdrawal_amount'),
'max': undefined,
},
},
};
}
return result;
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetBalance (params);
//
// {
// data: [
// {
// "currency_id":"XRP",
// "total":"100",
// "available":"0",
// }
// ]
// }
//
const result = {
'info': response,
'timestamp': undefined,
'datetime': undefined,
};
const data = this.safeValue (response, 'data');
for (let i = 0; i < data.length; i++) {
const balance = data[i];
const currencyId = this.safeString (balance, 'currency_id');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['total'] = this.safeString (balance, 'total');
account['free'] = this.safeString (balance, 'available');
result[code] = account;
}
return this.parseBalance (result, false);
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market_id': market['id'],
};
const response = await this.publicGetOrderBook (this.extend (request, params));
//
// {
// data: [
// { side: 'buy', price: '0.000031', quantity: '10' },
// { side: 'buy', price: '0.00356007', quantity: '4.92156877' },
// { side: 'sell', price: '0.1857', quantity: '0.17' },
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const dataBySide = this.groupBy (data, 'side');
return this.parseOrderBook (dataBySide, symbol, undefined, 'buy', 'sell', 'price', 'quantity');
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
if (symbols !== undefined) {
const marketIds = this.marketIds (symbols);
request['market_ids'] = marketIds.join (',');
}
const response = await this.publicGetTicker (this.extend (request, params));
//
// {
// "data":[
// {
// "last":"0.022902",
// "low":"0.021693",
// "high":"0.024093",
// "change":"-0.000047",
// "base_volume":"15681.986",
// "quote_volume":"360.514403624",
// "market_id":"ETH-BTC",
// "time":"2020-04-12T18:43:38.000Z"
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseTickers (data, symbols);
}
async fetchTicker (symbol, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market_ids': market['id'],
};
const response = await this.publicGetTicker (this.extend (request, params));
//
// {
// "data":[
// {
// "last":"0.022902",
// "low":"0.021693",
// "high":"0.024093",
// "change":"-0.000047",
// "base_volume":"15681.986",
// "quote_volume":"360.514403624",
// "market_id":"ETH-BTC",
// "time":"2020-04-12T18:43:38.000Z"
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const ticker = this.safeValue (data, 0);
if (ticker === undefined) {
throw new BadResponse (this.id + ' fetchTicker() returned an empty response');
}
return this.parseTicker (ticker, market);
}
parseTicker (ticker, market = undefined) {
//
// {
// "last":"0.022902",
// "low":"0.021693",
// "high":"0.024093",
// "change":"-0.000047",
// "base_volume":"15681.986",
// "quote_volume":"360.514403624",
// "market_id":"ETH-BTC",
// "time":"2020-04-12T18:43:38.000Z"
// }
//
const timestamp = this.parse8601 (this.safeString (ticker, 'time'));
const marketId = this.safeString (ticker, 'market_id');
const symbol = this.safeSymbol (marketId, market, '-');
const close = this.safeNumber (ticker, 'last');
const change = this.safeNumber (ticker, 'change');
let percentage = undefined;
let open = undefined;
if (change !== undefined) {
if (close !== undefined) {
open = close - change;
percentage = (change / open) * 100;
}
}
const baseVolume = this.safeNumber (ticker, 'base_volume');
const quoteVolume = this.safeNumber (ticker, 'quote_volume');
const vwap = this.vwap (baseVolume, quoteVolume);
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeNumber (ticker, 'high'),
'low': this.safeNumber (ticker, 'low'),
'bid': undefined,
'bidVolume': undefined,
'ask': undefined,
'askVolume': undefined,
'vwap': vwap,
'open': open,
'close': close,
'last': close,
'previousClose': undefined, // previous day close
'change': change,
'percentage': percentage,
'average': undefined,
'baseVolume': baseVolume,
'quoteVolume': quoteVolume,
'info': ticker,
};
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
const request = {
'limit': 100,
'start_time': this.iso8601 (0),
'end_time': this.iso8601 (this.milliseconds ()),
};
if (symbol !== undefined) {
market = this.market (symbol);
request['market_id'] = market['id'];
}
if (since !== undefined) {
request['start_time'] = this.iso8601 (since);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.privateGetTradeHistory (this.extend (request, params));
//
// {
// data: [
// {
// "id":"BTC-USDT:183566",
// "order_id":"17209376",
// "side":"sell",
// "fee_amount":"0.657396569175",
// "fee_currency_id":"USDT",
// "status":"settled",
// "price":"6573.96569175",
// "quantity":"0.1",
// "cost":"657.396569175",
// "time":"2018-08-10T06:06:46.000Z",
// "market_id":"BTC-USDT"
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseTrades (data, market, since, limit);
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market_id': market['id'],
'limit': 100,
'start_time': '1970-01-01T00:00:00.000Z',
'end_time': this.iso8601 (this.milliseconds ()),
};
if (since !== undefined) {
request['start_time'] = this.iso8601 (since);
}
if (limit !== undefined) {
request['limit'] = limit;
}
const response = await this.publicGetTrade (this.extend (request, params));
//
// {
// "data":[
// {
// "id":"ETH-BTC:3331886",
// "price":"0.022981",
// "quantity":"12.337",
// "time":"2020-04-12T20:55:42.371Z",
// "side":"sell",
// "tick_direction":"down"
// },
// {
// "id":"ETH-BTC:3331885",
// "price":"0.022982",
// "quantity":"6.472",
// "time":"2020-04-12T20:55:39.652Z",
// "side":"sell",
// "tick_direction":"down"
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseTrades (data, market, since, limit);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades (public)
//
// {
// "id":"ETH-BTC:3331886",
// "price":"0.022981",
// "quantity":"12.337",
// "time":"2020-04-12T20:55:42.371Z",
// "side":"sell",
// "tick_direction":"down"
// }
//
// fetchMyTrades (private)
//
// {
// "id":"BTC-USDT:183566",
// "order_id":"17209376",
// "side":"sell",
// "fee_amount":"0.657396569175",
// "fee_currency_id":"USDT",
// "status":"settled",
// "price":"6573.96569175",
// "quantity":"0.1",
// "cost":"657.396569175",
// "time":"2018-08-10T06:06:46.000Z",
// "market_id":"BTC-USDT"
// }
//
const timestamp = this.parse8601 (this.safeString (trade, 'time'));
const id = this.safeString (trade, 'id');
let marketId = undefined;
if (id !== undefined) {
const parts = id.split (':');
marketId = this.safeString (parts, 0);
}
marketId = this.safeString (trade, 'market_id', marketId);
const symbol = this.safeSymbol (marketId, market, '-');
const side = this.safeString (trade, 'side');
const priceString = this.safeString (trade, 'price');
const amountString = this.safeString (trade, 'quantity');
const price = this.parseNumber (priceString);
const amount = this.parseNumber (amountString);
const cost = this.parseNumber (Precise.stringMul (priceString, amountString));
const orderId = this.safeString (trade, 'order_id');
const feeCost = this.safeNumber (trade, 'fee_amount');
let fee = undefined;
if (feeCost !== undefined) {
const feeCurrencyId = this.safeString (trade, 'fee_currency_id');
const feeCurrencyCode = this.safeCurrencyCode (feeCurrencyId);
fee = {
'cost': feeCost,
'currency': feeCurrencyCode,
};
}
return {
'id': id,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'order': orderId,
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
};
}
async fetchTime (params = {}) {
const response = await this.publicGetTime (params);
//
// { "data":"2020-04-12T18:54:25.390Z" }
//
const timestamp = this.parse8601 (this.safeString (response, 'data'));
return timestamp;
}
normalizeOHLCVTimestamp (timestamp, timeframe, after = false) {
const duration = this.parseTimeframe (timeframe);
if (timeframe === '1M') {
const iso8601 = this.iso8601 (timestamp);
const parts = iso8601.split ('-');
const year = this.safeString (parts, 0);
let month = this.safeInteger (parts, 1);
if (after) {
month = this.sum (month, 1);
}
if (month < 10) {
month = '0' + month.toString ();
} else {
month = month.toString ();
}
return year + '-' + month + '-01T00:00:00.000Z';
} else if (timeframe === '1w') {
timestamp = parseInt (timestamp / 1000);
const firstSunday = 259200; // 1970-01-04T00:00:00.000Z
const difference = timestamp - firstSunday;
const numWeeks = this.integerDivide (difference, duration);
let previousSunday = this.sum (firstSunday, numWeeks * duration);
if (after) {
previousSunday = this.sum (previousSunday, duration);
}
return this.iso8601 (previousSunday * 1000);
} else {
timestamp = parseInt (timestamp / 1000);
timestamp = duration * parseInt (timestamp / duration);
if (after) {
timestamp = this.sum (timestamp, duration);
}
return this.iso8601 (timestamp * 1000);
}
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const interval = this.timeframes[timeframe];
limit = (limit === undefined) ? 100 : limit;
let requestLimit = this.sum (limit, 1);
requestLimit = Math.min (1000, requestLimit); // max 1000
const request = {
'market_ids': market['id'],
'interval': interval,
'sort': 'asc', // 'asc' will always include the start_time, 'desc' will always include end_time
'limit': requestLimit, // max 1000
};
const now = this.milliseconds ();
const duration = this.parseTimeframe (timeframe);
let startTime = since;
let endTime = now;
if (since === undefined) {
if (limit === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOHLCV() requires either a since argument or a limit argument');
} else {
startTime = now - limit * duration * 1000;
}
} else {
if (limit === undefined) {
endTime = now;
} else {
endTime = this.sum (since, this.sum (limit, 1) * duration * 1000);
}
}
const startTimeNormalized = this.normalizeOHLCVTimestamp (startTime, timeframe);
const endTimeNormalized = this.normalizeOHLCVTimestamp (endTime, timeframe, true);
request['start_time'] = startTimeNormalized;
request['end_time'] = endTimeNormalized;
const response = await this.publicGetCandle (this.extend (request, params));
//
// {
// "data":[
// {
// "market_id":"ETH-BTC",
// "open":"0.02811",
// "close":"0.02811",
// "low":"0.02811",
// "high":"0.02811",
// "base_volume":"0.0005",
// "quote_volume":"0.000014055",
// "start_time":"2018-11-30T18:19:00.000Z",
// "end_time":"2018-11-30T18:20:00.000Z"
// },
// ]
// }
//
const data = this.safeValue (response, 'data', []);
return this.parseOHLCVs (data, market, timeframe, since, limit);
}
parseOHLCV (ohlcv, market = undefined) {
//
// {
// "market_id":"ETH-BTC",
// "open":"0.02811",
// "close":"0.02811",
// "low":"0.02811",
// "high":"0.02811",
// "base_volume":"0.0005",
// "quote_volume":"0.000014055",
// "start_time":"2018-11-30T18:19:00.000Z",
// "end_time":"2018-11-30T18:20:00.000Z"
// }
//
return [
this.parse8601 (this.safeString (ohlcv, 'start_time')),
this.safeNumber (ohlcv, 'open'),
this.safeNumber (ohlcv, 'high'),
this.safeNumber (ohlcv, 'low'),
this.safeNumber (ohlcv, 'close'),
this.safeNumber (ohlcv, 'base_volume'),
];
}
async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
since = this.parse8601 (since);
const request = {};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market_id'] = market['id'];
}
const response = await this.privateGetOpenOrder (this.extend (request, params));
const data = this.safeValue (response, 'data');
return this.parseOrders (data, market, since, limit);
}
async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'start_time': this.iso8601 (0),
'end_time': this.iso8601 (this.milliseconds ()),
'limit': 100,
};
let market = undefined;
if (symbol !== undefined) {
market = this.market (symbol);
request['market_id'] = market['id'];
}
if (since) {
request['start_time'] = this.iso8601 (since);
}
if (limit) {
request['limit'] = limit;
}
const response = await this.privateGetOrderHistory (this.extend (request, params));
const data = this.safeValue (response, 'data');
return this.parseOrders (data, market, since, limit);
}
async fetchOrder (id, symbol = undefined, params = {}) {
if (symbol === undefined) {
throw new ArgumentsRequired (this.id + ' fetchOrder() requires a symbol argument');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market_id': market['id'],
};
const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_order_id');
if (clientOrderId !== undefined) {
request['client_order_id'] = clientOrderId;
} else {
request['order_id'] = id;
}
const query = this.omit (params, [ 'clientOrderId', 'client_order_id' ]);
const response = await this.privateGetOrder (this.extend (request, query));
const data = this.safeValue (response, 'data', []);
const order = this.safeValue (data, 0);
return this.parseOrder (order, market);
}
parseOrderStatus (status) {
const statuses = {
'open': 'open',
'cancelled': 'canceled',
'filled': 'closed',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// {
// id: string,
// user_id: string,
// market_id: string,
// type: 'orderType',
// side: 'side',
// quantity: string,
// limit_price: string,
// time_in_force: 'timeInForce',
// filled_cost: string,
// filled_quantity: string,
// open_quantity: string,
// cancelled_quantity: string,
// status: 'orderStatus',
// time: 'date',
// client_order_id: string,
// }
//
const status = this.parseOrderStatus (this.safeString (order, 'status'));
const id = this.safeString (order, 'id');
const type = this.safeString (order, 'type');
const side = this.safeString (order, 'side');
const marketId = this.safeString (order, 'market_id');
const symbol = this.safeSymbol (marketId, market, '-');
const timestamp = this.parse8601 (this.safeString (order, 'time'));
let price = this.safeNumber (order, 'limit_price');
const filled = this.safeNumber (order, 'filled_quantity');
let remaining = this.safeNumber (order, 'open_quantity');
const canceledAmount = this.safeNumber (order, 'cancelled_quantity');
if (canceledAmount !== undefined) {
remaining = this.sum (remaining, canceledAmount);
}
const amount = this.safeNumber (order, 'quantity', this.sum (filled, remaining));
const cost = this.safeNumber2 (order, 'filled_cost', 'cost');
if (type === 'market') {
price = undefined;
}
let clientOrderId = this.safeString (order, 'client_order_id');
if (clientOrderId === '') {
clientOrderId = undefined;
}
const timeInForce = this.safeStringUpper (order, 'time_in_force');
return this.safeOrder ({
'id': id,
'info': order,
'clientOrderId': clientOrderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'lastTradeTimestamp': undefined,
'symbol': symbol,
'type': type,
'timeInForce': timeInForce,
'side': side,
'status': status,
'price': price,
'stopPrice': undefined,
'amount': amount,
'filled': filled,
'remaining': remaining,
'average': undefined,
'cost': cost,
'fee': undefined,
'trades': undefined,
});
}
costToPrecision (symbol, cost) {
return this.decimalToPrecision (cost, TRUNCATE, this.markets[symbol]['precision']['cost'], this.precisionMode);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const options = this.safeValue (this.options, 'timeInForce');
const defaultTimeInForce = this.safeValue (options, type);
const timeInForce = this.safeString2 (params, 'timeInForce', 'time_in_force', defaultTimeInForce);
const request = {
'market_id': market['id'],
'type': type,
'side': side,
'time_in_force': timeInForce,
};
const clientOrderId = this.safeString2 (params, 'clientOrderId', 'client_order_id');
if (clientOrderId !== undefined) {
request['client_order_id'] = clientOrderId;
}
let costToPrecision = undefined;
if (type === 'limit') {
request['limit_price'] = this.priceToPrecision (symbol, price);
request['quantity'] = this.amountToPrecision (symbol, amount);
} else if (type === 'market') {
// for market buy it requires the amount of quote currency to spend
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;
}
costToPrecision = this.costToPrecision (symbol, cost);
request['cost'] = costToPrecision;
} else {
request['quantity'] = this.amountToPrecision (symbol, amount);
}
}
const query = this.omit (params, [ 'timeInForce', 'time_in_force', 'clientOrderId', 'client_order_id' ]);
const response = await this.privatePostNewOrder (this.extend (request, query));
//
// {
// data: {
// id: string,
// user_id: string,
// market_id: string,
// type: 'orderType',
// side: 'side',
// quantity: string,
// limit_price: string,
// time_in_force: 'timeInForce',
// filled_cost: string,
// filled_quantity: string,
// open_quantity: string,
// cancelled_quantity: string,
// status: 'orderStatus',
// time: 'date',
// client_order_id: string,
// }
// }
//
const data = this.safeValue (response, 'data');
const order = this.parseOrder (data, market);
// a workaround for incorrect huge amounts
// returned by the exchange on market buys
if ((type === 'market') && (side === 'buy')) {
order['amount'] = undefined;
order['cost'] = parseFloat (costToPrecision);
order['remaining'] = undefined;
}
return order;
}
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_id': market['id'],
'order_id': id,
};
const response = await this.privatePostCancelOrder (this.extend (request, params));
const data = this.safeValue (response, 'data');
return this.parseOrder (data);
}
parseDepositAddress (depositAddress, currency = undefined) {
const address = this.safeString (depositAddress, 'address');
const tag = this.safeString (depositAddress, 'destination_tag');
const currencyId = this.safeString (depositAddress, 'currency_id');
const code = this.safeCurrencyCode (currencyId);
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': tag,
'info': depositAddress,
};
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'currency_id': currency['id'],
};
const response = await this.privateGetDepositAddress (this.extend (request, params));
//
// {
// "data":[
// {
// "currency_id":"ETH",
// "address":"0x12e2caf3c4051ba1146e612f532901a423a9898a",
// "destination_tag":null
// }
// ]
// }
//
const data = this.safeValue (response, 'data', []);
const firstAddress = this.safeValue (data, 0);
if (firstAddress === undefined) {
throw new InvalidAddress (this.id + ' fetchDepositAddress returned an empty response');
}
return this.parseDepositAddress (firstAddress, currency);
}
async fetchDepositAddresses (codes = undefined, params = {}) {
await this.loadMarkets ();
const request = {};
if (codes) {
const currencyIds = [];
for (let i = 0; i < codes.length; i++) {
const currency = this.currency (codes[i]);
currencyIds.push (currency['id']);
}
request['currency_id'] = codes.join (',');
}
const response = await this.privateGetDepositAddress (this.extend (request, params));
const data = this.safeValue (response, 'data', []);
return this.parseDepositAddresses (data);
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
// In order to use this method
// you need to allow API withdrawal from the API Settings Page, and
// and register the list of withdrawal addresses and destination tags on the API Settings page
// you can only withdraw to the registered addresses using the API
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
if (tag === undefined) {
tag = '';
}
const request = {
'currency_id': currency['id'],
// 'platform_id': 'ETH', // if omitted it will use the default platform for the currency
'address': address,
'destination_tag': tag,
'amount': this.currencyToPrecision (code, amount),
// which currency to pay the withdrawal fees
// only applicable for currencies that accepts multiple withdrawal fee options
// 'fee_currency_id': 'ETH', // if omitted it will use the default fee policy for each currency
// whether the amount field includes fees
// 'include_fee': false, // makes sense only when fee_currency_id is equal to currency_id
};
const response = await this.privatePostWithdrawal (this.extend (request, params));
const data = this.safeValue (response, 'data');
return this.parseTransaction (data, currency);
}
parseTransaction (transaction, currency = undefined) {
const id = this.safeString (transaction, 'id');
const amount = this.safeNumber (transaction, 'amount');
const address = this.safeString (transaction, 'address');
const tag = this.safeString (transaction, 'destination_tag');
const txid = this.safeString (transaction, 'hash');
const timestamp = this.parse8601 (this.safeString (transaction, 'time'));
const type = this.safeString (transaction, 'type');
const currencyId = this.safeString (transaction, 'currency_id');
const code = this.safeCurrencyCode (currencyId);
const status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
const feeCost = this.safeNumber (transaction, 'fee');
let fee = undefined;
if (feeCost !== undefined && feeCost !== 0) {
fee = {
'currency': code,
'cost': feeCost,
};
}
return {
'id': id,
'currency': code,
'amount': amount,
'addressFrom': undefined,
'address': address,
'addressTo': address,
'tagFrom': undefined,
'tag': tag,
'tagTo': tag,
'status': status,
'type': type,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'fee': fee,
'info': transaction,
};
}
parseTransactionStatus (status) {
const statuses = {
'requested': 'pending',
'pending': 'pending',
'confirming': 'pending',
'confirmed': 'pending',
'applying': 'pending',
'done': 'ok',
'cancelled': 'canceled',
'cancelling': 'canceled',
};
return this.safeString (statuses, status, status);
}
nonce () {
return this.milliseconds ();
}
sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
let url = this.urls['api'][api] + '/';
const query = this.omit (params, this.extractParams (path));
if (api === 'accounts') {
this.checkRequiredCredentials ();
url += this.implodeParams (path, params);
const auth = this.apiKey + ':' + this.secret;
const auth64 = this.stringToBase64 (auth);
headers = {
'Authorization': 'Basic ' + this.decode (auth64),
'Content-Type': 'application/json',
};
if (Object.keys (query).length) {
body = this.json (query);
}
} else {
url += this.version + '/';
if (api === 'public') {
url += this.implodeParams (path, params);
if (Object.keys (query).length) {
url += '?' + this.urlencode (query);
}
} else if (api === 'private') {
const now = this.milliseconds ();
this.checkRequiredCredentials ();
const expires = this.safeInteger (this.options, 'expires');
if ((expires === undefined) || (expires < now)) {
throw new AuthenticationError (this.id + ' access token expired, call signIn() method');
}
const accessToken = this.safeString (this.options, 'accessToken');