consequunturatque
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
1,188 lines (1,160 loc) • 63.6 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied, AddressPending } = require ('./base/errors');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class upbit extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'upbit',
'name': 'Upbit',
'countries': [ 'KR' ],
'version': 'v1',
'rateLimit': 1000,
'pro': true,
// new metainfo interface
'has': {
'cancelOrder': true,
'CORS': true,
'createDepositAddress': true,
'createMarketOrder': true,
'createOrder': true,
'fetchBalance': true,
'fetchClosedOrders': true,
'fetchDepositAddress': true,
'fetchDeposits': true,
'fetchMarkets': true,
'fetchMyTrades': false,
'fetchOHLCV': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': true,
'fetchOrders': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTrades': true,
'fetchTransactions': false,
'fetchWithdrawals': true,
'withdraw': true,
},
'timeframes': {
'1m': 'minutes',
'3m': 'minutes',
'5m': 'minutes',
'15m': 'minutes',
'30m': 'minutes',
'1h': 'minutes',
'4h': 'minutes',
'1d': 'days',
'1w': 'weeks',
'1M': 'months',
},
'hostname': 'api.upbit.com',
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/49245610-eeaabe00-f423-11e8-9cba-4b0aed794799.jpg',
'api': {
'public': 'https://{hostname}',
'private': 'https://{hostname}',
},
'www': 'https://upbit.com',
'doc': 'https://docs.upbit.com/docs/%EC%9A%94%EC%B2%AD-%EC%88%98-%EC%A0%9C%ED%95%9C',
'fees': 'https://upbit.com/service_center/guide',
},
'api': {
'public': {
'get': [
'market/all',
'candles/{timeframe}',
'candles/{timeframe}/{unit}',
'candles/minutes/{unit}',
'candles/minutes/1',
'candles/minutes/3',
'candles/minutes/5',
'candles/minutes/15',
'candles/minutes/30',
'candles/minutes/60',
'candles/minutes/240',
'candles/days',
'candles/weeks',
'candles/months',
'trades/ticks',
'ticker',
'orderbook',
],
},
'private': {
'get': [
'accounts',
'orders/chance',
'order',
'orders',
'withdraws',
'withdraw',
'withdraws/chance',
'deposits',
'deposit',
'deposits/coin_addresses',
'deposits/coin_address',
],
'post': [
'orders',
'withdraws/coin',
'withdraws/krw',
'deposits/generate_coin_address',
],
'delete': [
'order',
],
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': 0.0025,
'taker': 0.0025,
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {},
'deposit': {},
},
},
'exceptions': {
'exact': {
'This key has expired.': AuthenticationError,
'Missing request parameter error. Check the required parameters!': BadRequest,
'side is missing, side does not have a valid value': InvalidOrder,
},
'broad': {
'thirdparty_agreement_required': PermissionDenied,
'out_of_scope': PermissionDenied,
'order_not_found': OrderNotFound,
'insufficient_funds': InsufficientFunds,
'invalid_access_key': AuthenticationError,
'jwt_verification': AuthenticationError,
'create_ask_error': ExchangeError,
'create_bid_error': ExchangeError,
'volume_too_large': InvalidOrder,
'invalid_funds': InvalidOrder,
},
},
'options': {
'createMarketBuyOrderRequiresPrice': true,
'fetchTickersMaxLength': 4096, // 2048,
'fetchOrderBooksMaxLength': 4096, // 2048,
'tradingFeesByQuoteCurrency': {
'KRW': 0.0005,
},
},
'commonCurrencies': {
'TON': 'Tokamak Network',
},
});
}
async fetchCurrency (code, params = {}) {
// this method is for retrieving funding fees and limits per currency
// it requires private access and API keys properly set up
await this.loadMarkets ();
const currency = this.currency (code);
return await this.fetchCurrencyById (currency['id'], params);
}
async fetchCurrencyById (id, params = {}) {
// this method is for retrieving funding fees and limits per currency
// it requires private access and API keys properly set up
const request = {
'currency': id,
};
const response = await this.privateGetWithdrawsChance (this.extend (request, params));
//
// {
// "member_level": {
// "security_level": 3,
// "fee_level": 0,
// "email_verified": true,
// "identity_auth_verified": true,
// "bank_account_verified": true,
// "kakao_pay_auth_verified": false,
// "locked": false,
// "wallet_locked": false
// },
// "currency": {
// "code": "BTC",
// "withdraw_fee": "0.0005",
// "is_coin": true,
// "wallet_state": "working",
// "wallet_support": [ "deposit", "withdraw" ]
// },
// "account": {
// "currency": "BTC",
// "balance": "10.0",
// "locked": "0.0",
// "avg_krw_buy_price": "8042000",
// "modified": false
// },
// "withdraw_limit": {
// "currency": "BTC",
// "minimum": null,
// "onetime": null,
// "daily": "10.0",
// "remaining_daily": "10.0",
// "remaining_daily_krw": "0.0",
// "fixed": null,
// "can_withdraw": true
// }
// }
//
const memberInfo = this.safeValue (response, 'member_level', {});
const currencyInfo = this.safeValue (response, 'currency', {});
const withdrawLimits = this.safeValue (response, 'withdraw_limit', {});
const canWithdraw = this.safeValue (withdrawLimits, 'can_withdraw');
const walletState = this.safeString (currencyInfo, 'wallet_state');
const walletLocked = this.safeValue (memberInfo, 'wallet_locked');
const locked = this.safeValue (memberInfo, 'locked');
let active = true;
if ((canWithdraw !== undefined) && !canWithdraw) {
active = false;
} else if (walletState !== 'working') {
active = false;
} else if ((walletLocked !== undefined) && walletLocked) {
active = false;
} else if ((locked !== undefined) && locked) {
active = false;
}
const maxOnetimeWithdrawal = this.safeNumber (withdrawLimits, 'onetime');
const maxDailyWithdrawal = this.safeNumber (withdrawLimits, 'daily', maxOnetimeWithdrawal);
const remainingDailyWithdrawal = this.safeNumber (withdrawLimits, 'remaining_daily', maxDailyWithdrawal);
let maxWithdrawLimit = undefined;
if (remainingDailyWithdrawal > 0) {
maxWithdrawLimit = remainingDailyWithdrawal;
} else {
maxWithdrawLimit = maxDailyWithdrawal;
}
const precision = undefined;
const currencyId = this.safeString (currencyInfo, 'code');
const code = this.safeCurrencyCode (currencyId);
return {
'info': response,
'id': currencyId,
'code': code,
'name': code,
'active': active,
'fee': this.safeNumber (currencyInfo, 'withdraw_fee'),
'precision': precision,
'limits': {
'withdraw': {
'min': this.safeNumber (withdrawLimits, 'minimum'),
'max': maxWithdrawLimit,
},
},
};
}
async fetchMarket (symbol, params = {}) {
// this method is for retrieving trading fees and limits per market
// it requires private access and API keys properly set up
await this.loadMarkets ();
const market = this.market (symbol);
return await this.fetchMarketById (market['id'], params);
}
async fetchMarketById (id, params = {}) {
// this method is for retrieving trading fees and limits per market
// it requires private access and API keys properly set up
const request = {
'market': id,
};
const response = await this.privateGetOrdersChance (this.extend (request, params));
//
// { bid_fee: "0.0005",
// ask_fee: "0.0005",
// market: { id: "KRW-BTC",
// name: "BTC/KRW",
// order_types: ["limit"],
// order_sides: ["ask", "bid"],
// bid: { currency: "KRW",
// price_unit: null,
// min_total: 1000 },
// ask: { currency: "BTC",
// price_unit: null,
// min_total: 1000 },
// max_total: "1000000000.0",
// state: "active" },
// bid_account: { currency: "KRW",
// balance: "0.0",
// locked: "0.0",
// avg_krw_buy_price: "0",
// modified: false },
// ask_account: { currency: "BTC",
// balance: "0.00780836",
// locked: "0.0",
// avg_krw_buy_price: "6465564.67",
// modified: false } }
//
const marketInfo = this.safeValue (response, 'market');
const bid = this.safeValue (marketInfo, 'bid');
const ask = this.safeValue (marketInfo, 'ask');
const marketId = this.safeString (marketInfo, 'id');
const baseId = this.safeString (ask, 'currency');
const quoteId = this.safeString (bid, 'currency');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const precision = {
'amount': 8,
'price': 8,
};
const state = this.safeString (marketInfo, 'state');
const active = (state === 'active');
const bidFee = this.safeNumber (response, 'bid_fee');
const askFee = this.safeNumber (response, 'ask_fee');
const fee = Math.max (bidFee, askFee);
return {
'info': response,
'id': marketId,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'precision': precision,
'maker': fee,
'taker': fee,
'limits': {
'amount': {
'min': this.safeNumber (ask, 'min_total'),
'max': undefined,
},
'price': {
'min': Math.pow (10, -precision['price']),
'max': undefined,
},
'cost': {
'min': this.safeNumber (bid, 'min_total'),
'max': this.safeNumber (marketInfo, 'max_total'),
},
},
};
}
async fetchMarkets (params = {}) {
const response = await this.publicGetMarketAll (params);
//
// [ { market: "KRW-BTC",
// korean_name: "비트코인",
// english_name: "Bitcoin" },
// { market: "KRW-DASH",
// korean_name: "대시",
// english_name: "Dash" },
// { market: "KRW-ETH",
// korean_name: "이더리움",
// english_name: "Ethereum" },
// { market: "BTC-ETH",
// korean_name: "이더리움",
// english_name: "Ethereum" },
// ...,
// { market: "BTC-BSV",
// korean_name: "비트코인에스브이",
// english_name: "Bitcoin SV" } ]
//
const result = [];
for (let i = 0; i < response.length; i++) {
const market = response[i];
const id = this.safeString (market, 'market');
const [ quoteId, baseId ] = id.split ('-');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const symbol = base + '/' + quote;
const precision = {
'amount': 8,
'price': 8,
};
const active = true;
const makerFee = this.safeNumber (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['maker']);
const takerFee = this.safeNumber (this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['taker']);
result.push ({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'baseId': baseId,
'quoteId': quoteId,
'active': active,
'info': market,
'precision': precision,
'maker': makerFee,
'taker': takerFee,
'limits': {
'amount': {
'min': Math.pow (10, -precision['amount']),
'max': undefined,
},
'price': {
'min': Math.pow (10, -precision['price']),
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
});
}
return result;
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privateGetAccounts (params);
//
// [ { currency: "BTC",
// balance: "0.005",
// locked: "0.0",
// avg_krw_buy_price: "7446000",
// modified: false },
// { currency: "ETH",
// balance: "0.1",
// locked: "0.0",
// avg_krw_buy_price: "250000",
// modified: false } ]
//
const result = {
'info': response,
'timestamp': undefined,
'datetime': undefined,
};
for (let i = 0; i < response.length; i++) {
const balance = response[i];
const currencyId = this.safeString (balance, 'currency');
const code = this.safeCurrencyCode (currencyId);
const account = this.account ();
account['free'] = this.safeString (balance, 'balance');
account['used'] = this.safeString (balance, 'locked');
result[code] = account;
}
return this.parseBalance (result, false);
}
async fetchOrderBooks (symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
ids = this.ids.join (',');
// max URL length is 2083 symbols, including http schema, hostname, tld, etc...
if (ids.length > this.options['fetchOrderBooksMaxLength']) {
const numIds = this.ids.length;
throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols (' + ids.length.toString () + ' characters) exceeding max URL length (' + this.options['fetchOrderBooksMaxLength'].toString () + ' characters), you are required to specify a list of symbols in the first argument to fetchOrderBooks');
}
} else {
ids = this.marketIds (symbols);
ids = ids.join (',');
}
const request = {
'markets': ids,
};
const response = await this.publicGetOrderbook (this.extend (request, params));
//
// [ { market: "BTC-ETH",
// timestamp: 1542899030043,
// total_ask_size: 109.57065201,
// total_bid_size: 125.74430631,
// orderbook_units: [ { ask_price: 0.02926679,
// bid_price: 0.02919904,
// ask_size: 4.20293961,
// bid_size: 11.65043576 },
// ...,
// { ask_price: 0.02938209,
// bid_price: 0.0291231,
// ask_size: 0.05135782,
// bid_size: 13.5595 } ] },
// { market: "KRW-BTC",
// timestamp: 1542899034662,
// total_ask_size: 12.89790974,
// total_bid_size: 4.88395783,
// orderbook_units: [ { ask_price: 5164000,
// bid_price: 5162000,
// ask_size: 2.57606495,
// bid_size: 0.214 },
// ...,
// { ask_price: 5176000,
// bid_price: 5152000,
// ask_size: 2.752,
// bid_size: 0.4650305 } ] } ]
//
const result = {};
for (let i = 0; i < response.length; i++) {
const orderbook = response[i];
const marketId = this.safeString (orderbook, 'market');
const symbol = this.safeSymbol (marketId, undefined, '-');
const timestamp = this.safeInteger (orderbook, 'timestamp');
result[symbol] = {
'symbol': symbol,
'bids': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'bid_price', 'bid_size'), 0, true),
'asks': this.sortBy (this.parseBidsAsks (orderbook['orderbook_units'], 'ask_price', 'ask_size'), 0),
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'nonce': undefined,
};
}
return result;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
const orderbooks = await this.fetchOrderBooks ([ symbol ], limit, params);
return this.safeValue (orderbooks, symbol);
}
parseTicker (ticker, market = undefined) {
//
// { market: "BTC-ETH",
// trade_date: "20181122",
// trade_time: "104543",
// trade_date_kst: "20181122",
// trade_time_kst: "194543",
// trade_timestamp: 1542883543097,
// opening_price: 0.02976455,
// high_price: 0.02992577,
// low_price: 0.02934283,
// trade_price: 0.02947773,
// prev_closing_price: 0.02966,
// change: "FALL",
// change_price: 0.00018227,
// change_rate: 0.0061453136,
// signed_change_price: -0.00018227,
// signed_change_rate: -0.0061453136,
// trade_volume: 1.00000005,
// acc_trade_price: 100.95825586,
// acc_trade_price_24h: 289.58650166,
// acc_trade_volume: 3409.85311036,
// acc_trade_volume_24h: 9754.40510513,
// highest_52_week_price: 0.12345678,
// highest_52_week_date: "2018-02-01",
// lowest_52_week_price: 0.023936,
// lowest_52_week_date: "2017-12-08",
// timestamp: 1542883543813 }
//
const timestamp = this.safeInteger (ticker, 'trade_timestamp');
const marketId = this.safeString2 (ticker, 'market', 'code');
const symbol = this.safeSymbol (marketId, market, '-');
const previous = this.safeNumber (ticker, 'prev_closing_price');
const last = this.safeNumber (ticker, 'trade_price');
const change = this.safeNumber (ticker, 'signed_change_price');
const percentage = this.safeNumber (ticker, 'signed_change_rate');
return {
'symbol': symbol,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'high': this.safeNumber (ticker, 'high_price'),
'low': this.safeNumber (ticker, 'low_price'),
'bid': undefined,
'bidVolume': undefined,
'ask': undefined,
'askVolume': undefined,
'vwap': undefined,
'open': this.safeNumber (ticker, 'opening_price'),
'close': last,
'last': last,
'previousClose': previous,
'change': change,
'percentage': percentage,
'average': undefined,
'baseVolume': this.safeNumber (ticker, 'acc_trade_volume_24h'),
'quoteVolume': this.safeNumber (ticker, 'acc_trade_price_24h'),
'info': ticker,
};
}
async fetchTickers (symbols = undefined, params = {}) {
await this.loadMarkets ();
let ids = undefined;
if (symbols === undefined) {
ids = this.ids.join (',');
// max URL length is 2083 symbols, including http schema, hostname, tld, etc...
if (ids.length > this.options['fetchTickersMaxLength']) {
const numIds = this.ids.length;
throw new ExchangeError (this.id + ' has ' + numIds.toString () + ' symbols exceeding max URL length, you are required to specify a list of symbols in the first argument to fetchTickers');
}
} else {
ids = this.marketIds (symbols);
ids = ids.join (',');
}
const request = {
'markets': ids,
};
const response = await this.publicGetTicker (this.extend (request, params));
//
// [ { market: "BTC-ETH",
// trade_date: "20181122",
// trade_time: "104543",
// trade_date_kst: "20181122",
// trade_time_kst: "194543",
// trade_timestamp: 1542883543097,
// opening_price: 0.02976455,
// high_price: 0.02992577,
// low_price: 0.02934283,
// trade_price: 0.02947773,
// prev_closing_price: 0.02966,
// change: "FALL",
// change_price: 0.00018227,
// change_rate: 0.0061453136,
// signed_change_price: -0.00018227,
// signed_change_rate: -0.0061453136,
// trade_volume: 1.00000005,
// acc_trade_price: 100.95825586,
// acc_trade_price_24h: 289.58650166,
// acc_trade_volume: 3409.85311036,
// acc_trade_volume_24h: 9754.40510513,
// highest_52_week_price: 0.12345678,
// highest_52_week_date: "2018-02-01",
// lowest_52_week_price: 0.023936,
// lowest_52_week_date: "2017-12-08",
// timestamp: 1542883543813 } ]
//
const result = {};
for (let t = 0; t < response.length; t++) {
const ticker = this.parseTicker (response[t]);
const symbol = ticker['symbol'];
result[symbol] = ticker;
}
return this.filterByArray (result, 'symbol', symbols);
}
async fetchTicker (symbol, params = {}) {
const tickers = await this.fetchTickers ([ symbol ], params);
return this.safeValue (tickers, symbol);
}
parseTrade (trade, market = undefined) {
//
// fetchTrades
//
// { market: "BTC-ETH",
// trade_date_utc: "2018-11-22",
// trade_time_utc: "13:55:24",
// timestamp: 1542894924397,
// trade_price: 0.02914289,
// trade_volume: 0.20074397,
// prev_closing_price: 0.02966,
// change_price: -0.00051711,
// ask_bid: "ASK",
// sequential_id: 15428949259430000 }
//
// fetchOrder trades
//
// {
// "market": "KRW-BTC",
// "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3",
// "price": "101000.0",
// "volume": "0.77368323",
// "funds": "78142.00623",
// "ask_fee": "117.213009345",
// "bid_fee": "117.213009345",
// "created_at": "2018-04-05T14:09:15+09:00",
// "side": "bid",
// }
//
const id = this.safeString2 (trade, 'sequential_id', 'uuid');
const orderId = undefined;
let timestamp = this.safeInteger (trade, 'timestamp');
if (timestamp === undefined) {
timestamp = this.parse8601 (this.safeString (trade, 'created_at'));
}
let side = undefined;
const askOrBid = this.safeStringLower2 (trade, 'ask_bid', 'side');
if (askOrBid === 'ask') {
side = 'sell';
} else if (askOrBid === 'bid') {
side = 'buy';
}
let cost = this.safeNumber (trade, 'funds');
const priceString = this.safeString2 (trade, 'trade_price', 'price');
const amountString = this.safeString2 (trade, 'trade_volume', 'volume');
const price = this.parseNumber (priceString);
const amount = this.parseNumber (amountString);
if (cost === undefined) {
cost = this.parseNumber (Precise.stringMul (priceString, amountString));
}
const marketId = this.safeString2 (trade, 'market', 'code');
market = this.safeMarket (marketId, market);
let fee = undefined;
let feeCurrency = undefined;
let symbol = undefined;
if (market !== undefined) {
symbol = market['symbol'];
feeCurrency = market['quote'];
} else {
const [ baseId, quoteId ] = marketId.split ('-');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
symbol = base + '/' + quote;
feeCurrency = quote;
}
const feeCost = this.safeString (trade, askOrBid + '_fee');
if (feeCost !== undefined) {
fee = {
'currency': feeCurrency,
'cost': feeCost,
};
}
return {
'id': id,
'info': trade,
'order': orderId,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'symbol': symbol,
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
};
}
async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
if (limit === undefined) {
limit = 200;
}
const request = {
'market': market['id'],
'count': limit,
};
const response = await this.publicGetTradesTicks (this.extend (request, params));
//
// [ { market: "BTC-ETH",
// trade_date_utc: "2018-11-22",
// trade_time_utc: "13:55:24",
// timestamp: 1542894924397,
// trade_price: 0.02914289,
// trade_volume: 0.20074397,
// prev_closing_price: 0.02966,
// change_price: -0.00051711,
// ask_bid: "ASK",
// sequential_id: 15428949259430000 },
// { market: "BTC-ETH",
// trade_date_utc: "2018-11-22",
// trade_time_utc: "13:03:10",
// timestamp: 1542891790123,
// trade_price: 0.02917,
// trade_volume: 7.392,
// prev_closing_price: 0.02966,
// change_price: -0.00049,
// ask_bid: "ASK",
// sequential_id: 15428917910540000 } ]
//
return this.parseTrades (response, market, since, limit);
}
parseOHLCV (ohlcv, market = undefined) {
//
// {
// market: "BTC-ETH",
// candle_date_time_utc: "2018-11-22T13:47:00",
// candle_date_time_kst: "2018-11-22T22:47:00",
// opening_price: 0.02915963,
// high_price: 0.02915963,
// low_price: 0.02915448,
// trade_price: 0.02915448,
// timestamp: 1542894473674,
// candle_acc_trade_price: 0.0981629437535248,
// candle_acc_trade_volume: 3.36693173,
// unit: 1
// }
//
return [
this.parse8601 (this.safeString (ohlcv, 'candle_date_time_utc')),
this.safeNumber (ohlcv, 'opening_price'),
this.safeNumber (ohlcv, 'high_price'),
this.safeNumber (ohlcv, 'low_price'),
this.safeNumber (ohlcv, 'trade_price'),
this.safeNumber (ohlcv, 'candle_acc_trade_volume'), // base volume
];
}
async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const market = this.market (symbol);
const timeframePeriod = this.parseTimeframe (timeframe);
const timeframeValue = this.timeframes[timeframe];
if (limit === undefined) {
limit = 200;
}
const request = {
'market': market['id'],
'timeframe': timeframeValue,
'count': limit,
};
let method = 'publicGetCandlesTimeframe';
if (timeframeValue === 'minutes') {
const numMinutes = Math.round (timeframePeriod / 60);
request['unit'] = numMinutes;
method += 'Unit';
}
if (since !== undefined) {
// convert `since` to `to` value
request['to'] = this.iso8601 (this.sum (since, timeframePeriod * limit * 1000));
}
const response = await this[method] (this.extend (request, params));
//
// [
// {
// market: "BTC-ETH",
// candle_date_time_utc: "2018-11-22T13:47:00",
// candle_date_time_kst: "2018-11-22T22:47:00",
// opening_price: 0.02915963,
// high_price: 0.02915963,
// low_price: 0.02915448,
// trade_price: 0.02915448,
// timestamp: 1542894473674,
// candle_acc_trade_price: 0.0981629437535248,
// candle_acc_trade_volume: 3.36693173,
// unit: 1
// },
// {
// market: "BTC-ETH",
// candle_date_time_utc: "2018-11-22T10:06:00",
// candle_date_time_kst: "2018-11-22T19:06:00",
// opening_price: 0.0294,
// high_price: 0.02940882,
// low_price: 0.02934283,
// trade_price: 0.02937354,
// timestamp: 1542881219276,
// candle_acc_trade_price: 0.0762597110943884,
// candle_acc_trade_volume: 2.5949617,
// unit: 1
// }
// ]
//
return this.parseOHLCVs (response, market, timeframe, since, limit);
}
async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
if (type === 'market') {
// for market buy it requires the amount of quote currency to spend
if (side === 'buy') {
if (this.options['createMarketBuyOrderRequiresPrice']) {
if (price === undefined) {
throw new InvalidOrder (this.id + " createOrder() requires the price argument with 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 to supply the cost in the amount argument (the exchange-specific behaviour)");
} else {
amount = amount * price;
}
}
}
}
let orderSide = undefined;
if (side === 'buy') {
orderSide = 'bid';
} else if (side === 'sell') {
orderSide = 'ask';
} else {
throw new InvalidOrder (this.id + ' createOrder allows buy or sell side only!');
}
await this.loadMarkets ();
const market = this.market (symbol);
const request = {
'market': market['id'],
'side': orderSide,
};
if (type === 'limit') {
request['volume'] = this.amountToPrecision (symbol, amount);
request['price'] = this.priceToPrecision (symbol, price);
request['ord_type'] = type;
} else if (type === 'market') {
if (side === 'buy') {
request['ord_type'] = 'price';
request['price'] = this.priceToPrecision (symbol, amount);
} else if (side === 'sell') {
request['ord_type'] = type;
request['volume'] = this.amountToPrecision (symbol, amount);
}
}
const response = await this.privatePostOrders (this.extend (request, params));
//
// {
// 'uuid': 'cdd92199-2897-4e14-9448-f923320408ad',
// 'side': 'bid',
// 'ord_type': 'limit',
// 'price': '100.0',
// 'avg_price': '0.0',
// 'state': 'wait',
// 'market': 'KRW-BTC',
// 'created_at': '2018-04-10T15:42:23+09:00',
// 'volume': '0.01',
// 'remaining_volume': '0.01',
// 'reserved_fee': '0.0015',
// 'remaining_fee': '0.0015',
// 'paid_fee': '0.0',
// 'locked': '1.0015',
// 'executed_volume': '0.0',
// 'trades_count': 0
// }
//
return this.parseOrder (response);
}
async cancelOrder (id, symbol = undefined, params = {}) {
await this.loadMarkets ();
const request = {
'uuid': id,
};
const response = await this.privateDeleteOrder (this.extend (request, params));
//
// {
// "uuid": "cdd92199-2897-4e14-9448-f923320408ad",
// "side": "bid",
// "ord_type": "limit",
// "price": "100.0",
// "state": "wait",
// "market": "KRW-BTC",
// "created_at": "2018-04-10T15:42:23+09:00",
// "volume": "0.01",
// "remaining_volume": "0.01",
// "reserved_fee": "0.0015",
// "remaining_fee": "0.0015",
// "paid_fee": "0.0",
// "locked": "1.0015",
// "executed_volume": "0.0",
// "trades_count": 0
// }
//
return this.parseOrder (response);
}
async fetchDeposits (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
// 'page': 1,
// 'order_by': 'asc', // 'desc'
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
request['currency'] = currency['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default is 100
}
const response = await this.privateGetDeposits (this.extend (request, params));
//
// [
// {
// "type": "deposit",
// "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830",
// "currency": "KRW",
// "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a",
// "state": "ACCEPTED",
// "created_at": "2017-12-08T15:38:02+09:00",
// "done_at": "2017-12-08T15:38:02+09:00",
// "amount": "100000.0",
// "fee": "0.0"
// },
// ...,
// ]
//
return this.parseTransactions (response, currency, since, limit);
}
async fetchWithdrawals (code = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
const request = {
// 'state': 'submitting', // 'submitted', 'almost_accepted', 'rejected', 'accepted', 'processing', 'done', 'canceled'
};
let currency = undefined;
if (code !== undefined) {
currency = this.currency (code);
request['currency'] = currency['id'];
}
if (limit !== undefined) {
request['limit'] = limit; // default is 100
}
const response = await this.privateGetWithdraws (this.extend (request, params));
//
// [
// {
// "type": "withdraw",
// "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79",
// "currency": "BTC",
// "txid": null,
// "state": "processing",
// "created_at": "2018-04-13T11:24:01+09:00",
// "done_at": null,
// "amount": "0.01",
// "fee": "0.0",
// "krw_amount": "80420.0"
// },
// ...,
// ]
//
return this.parseTransactions (response, currency, since, limit);
}
parseTransactionStatus (status) {
const statuses = {
'submitting': 'pending', // 처리 중
'submitted': 'pending', // 처리 완료
'almost_accepted': 'pending', // 출금대기중
'rejected': 'failed', // 거부
'accepted': 'pending', // 승인됨
'processing': 'pending', // 처리 중
'done': 'ok', // 완료
'canceled': 'canceled', // 취소됨
};
return this.safeString (statuses, status, status);
}
parseTransaction (transaction, currency = undefined) {
//
// fetchDeposits
//
// {
// "type": "deposit",
// "uuid": "94332e99-3a87-4a35-ad98-28b0c969f830",
// "currency": "KRW",
// "txid": "9e37c537-6849-4c8b-a134-57313f5dfc5a",
// "state": "ACCEPTED",
// "created_at": "2017-12-08T15:38:02+09:00",
// "done_at": "2017-12-08T15:38:02+09:00",
// "amount": "100000.0",
// "fee": "0.0"
// }
//
// fetchWithdrawals
//
// {
// "type": "withdraw",
// "uuid": "9f432943-54e0-40b7-825f-b6fec8b42b79",
// "currency": "BTC",
// "txid": "cd81e9b45df8da29f936836e58c907a106057e454a45767a7b06fcb19b966bba",
// "state": "processing",
// "created_at": "2018-04-13T11:24:01+09:00",
// "done_at": null,
// "amount": "0.01",
// "fee": "0.0",
// "krw_amount": "80420.0"
// }
//
const id = this.safeString (transaction, 'uuid');
const amount = this.safeNumber (transaction, 'amount');
const address = undefined; // not present in the data structure received from the exchange
const tag = undefined; // not present in the data structure received from the exchange
const txid = this.safeString (transaction, 'txid');
const updated = this.parse8601 (this.safeString (transaction, 'done_at'));
const timestamp = this.parse8601 (this.safeString (transaction, 'created_at', updated));
let type = this.safeString (transaction, 'type');
if (type === 'withdraw') {
type = 'withdrawal';
}
const currencyId = this.safeString (transaction, 'currency');
const code = this.safeCurrencyCode (currencyId);
const status = this.parseTransactionStatus (this.safeStringLower (transaction, 'state'));
const feeCost = this.safeNumber (transaction, 'fee');
return {
'info': transaction,
'id': id,
'currency': code,
'amount': amount,
'address': address,
'tag': tag,
'status': status,
'type': type,
'updated': updated,
'txid': txid,
'timestamp': timestamp,
'datetime': this.iso8601 (timestamp),
'fee': {
'currency': code,
'cost': feeCost,
},
};
}
parseOrderStatus (status) {
const statuses = {
'wait': 'open',
'done': 'closed',
'cancel': 'canceled',
};
return this.safeString (statuses, status, status);
}
parseOrder (order, market = undefined) {
//
// {
// "uuid": "a08f09b1-1718-42e2-9358-f0e5e083d3ee",
// "side": "bid",
// "ord_type": "limit",
// "price": "17417000.0",
// "state": "done",
// "market": "KRW-BTC",
// "created_at": "2018-04-05T14:09:14+09:00",
// "volume": "1.0",
// "remaining_volume": "0.0",
// "reserved_fee": "26125.5",
// "remaining_fee": "25974.0",
// "paid_fee": "151.5",
// "locked": "17341974.0",
// "executed_volume": "1.0",
// "trades_count": 2,
// "trades": [
// {
// "market": "KRW-BTC",
// "uuid": "78162304-1a4d-4524-b9e6-c9a9e14d76c3",
// "price": "101000.0",
// "volume": "0.77368323",
// "funds": "78142.00623",
// "ask_fee": "117.213009345",
// "bid_fee": "117.213009345",
// "created_at": "2018-04-05T14:09:15+09:00",
// "side": "bid",
// },
// {
// "market": "KRW-BTC",
// "uuid": "f73da467-c42f-407d-92fa-e10d86450a20",
// "price": "101000.0",
// "volume": "0.22631677",
// "funds": "22857.99377",
// "ask_fee": "34.286990655", // missing in market orders
// "bid_fee": "34.286990655", // missing in market orders
// "created_at": "2018-04-05T14:09:15+09:00", // missing in market orders
// "side": "bid",
// },
// ],
// }
//
const id = this.safeString (order, 'uuid');
let side = this.safeString (order, 'side');
if (side === 'bid') {
side = 'buy';
} else {
side = 'sell';
}
let type = this.safeString (order, 'ord_type');
const timestamp = this.parse8601 (this.safeString (order, 'created_at'));
const status = this.parseOrderStatus (this.safeString (order, 'state'));
let lastTradeTimestamp = undefined;
let price = this.safeNumber (order, 'price');
const amount = this.safeNumber (order, 'volume');
const remaining = this.safeNumber (order, 'remaining_volume');
const filled = this.safeNumber (order, 'executed_volume');
let cost = undefined;
if (type === 'price') {
type = 'market';
cost = price;
price = undefined;
}
let average = undefined;
let fee = undefined;
let feeCost = this.safeNumber (order, 'paid_fee');
const marketId = this.safeString (order, 'market');
market = this.safeMarket (marketId, market);
let trades = this.safeValue (order, 'trades', []);
trades = this.parseTrades (trades, market, undefined, undefined, {
'order': id,
'type': type,
});
const numTrades = trades.length;
if (numTrades > 0) {
// the timestamp in fetchOrder trades is missing
lastTradeTimestamp = trades[numTrades - 1]['timestamp'];
let getFeesFromTrades = false;
if (feeCost === undefined) {
getFeesFromTrades = true;
feeCost = 0;
}
cost = 0;
for (let i = 0; i < numTrades; i++) {
const trade = trades[i];
cost = this.sum (cost, trade['cost']);
if (getFeesFromTrades) {
const tradeFee = this.safeValue (trades[i], 'fee', {});
const tradeFeeCost = this.safeNumber (tradeFee, 'cost');
if (tradeFeeCost !== undefined) {
feeCost = this.sum (feeCost, tradeFeeCost);
}