ccxt
Version:
1,144 lines (1,142 loc) • 106 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/upbit.js';
import { ExchangeError, BadRequest, AuthenticationError, InvalidOrder, InsufficientFunds, OrderNotFound, PermissionDenied, AddressPending, ArgumentsRequired } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha512 } from './static_dependencies/noble-hashes/sha512.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
import { jwt } from './base/functions/rsa.js';
// ---------------------------------------------------------------------------
/**
* @class upbit
* @augments Exchange
*/
export default class upbit extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'upbit',
'name': 'Upbit',
'countries': ['KR'],
'version': 'v1',
'rateLimit': 50,
'pro': true,
// new metainfo interface
'has': {
'CORS': true,
'spot': true,
'margin': undefined,
'swap': false,
'future': false,
'option': false,
'cancelOrder': true,
'createDepositAddress': true,
'createMarketBuyOrderWithCost': true,
'createMarketOrder': true,
'createMarketOrderWithCost': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'editOrder': true,
'fetchBalance': true,
'fetchCanceledOrders': true,
'fetchClosedOrders': true,
'fetchDeposit': true,
'fetchDepositAddress': true,
'fetchDepositAddresses': true,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchMarginMode': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': false,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': true,
'fetchOrders': false,
'fetchPositionMode': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': true,
'fetchTransactions': false,
'fetchWithdrawal': true,
'fetchWithdrawals': true,
'transfer': false,
'withdraw': true,
},
'timeframes': {
'1s': 'seconds',
'1m': 'minutes',
'3m': 'minutes',
'5m': 'minutes',
'10m': 'minutes',
'15m': 'minutes',
'30m': 'minutes',
'1h': 'minutes',
'4h': 'minutes',
'1d': 'days',
'1w': 'weeks',
'1M': 'months',
'1y': 'years',
},
'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': {
// 'endpoint','API Cost'
// cost = 1000 / (rateLimit * RPS)
'public': {
'get': {
'market/all': 2,
'candles/{timeframe}': 2,
'candles/{timeframe}/{unit}': 2,
'candles/seconds': 2,
'candles/minutes/{unit}': 2,
'candles/minutes/1': 2,
'candles/minutes/3': 2,
'candles/minutes/5': 2,
'candles/minutes/10': 2,
'candles/minutes/15': 2,
'candles/minutes/30': 2,
'candles/minutes/60': 2,
'candles/minutes/240': 2,
'candles/days': 2,
'candles/weeks': 2,
'candles/months': 2,
'candles/years': 2,
'trades/ticks': 2,
'ticker': 2,
'ticker/all': 2,
'orderbook': 2,
'orderbook/supported_levels': 2, // Upbit KR only
},
},
'private': {
'get': {
'accounts': 0.67,
'orders/chance': 0.67,
'order': 0.67,
'orders/closed': 0.67,
'orders/open': 0.67,
'orders/uuids': 0.67,
'withdraws': 0.67,
'withdraw': 0.67,
'withdraws/chance': 0.67,
'withdraws/coin_addresses': 0.67,
'deposits': 0.67,
'deposits/chance/coin': 0.67,
'deposit': 0.67,
'deposits/coin_addresses': 0.67,
'deposits/coin_address': 0.67,
'travel_rule/vasps': 0.67,
'status/wallet': 0.67,
'api_keys': 0.67, // Upbit KR only
},
'post': {
'orders': 2.5,
'orders/cancel_and_new': 2.5,
'withdraws/coin': 0.67,
'withdraws/krw': 0.67,
'deposits/krw': 0.67,
'deposits/generate_coin_address': 0.67,
'travel_rule/deposit/uuid': 0.67,
'travel_rule/deposit/txid': 0.67, // RPS: 30, but each deposit can only be queried once every 10 minutes
},
'delete': {
'order': 0.67,
'orders/open': 40,
'orders/uuids': 0.67,
},
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
'maker': this.parseNumber('0.0025'),
'taker': this.parseNumber('0.0025'),
},
'funding': {
'tierBased': false,
'percentage': false,
'withdraw': {},
'deposit': {},
},
},
'features': {
'spot': {
'sandbox': false,
'createOrder': {
'marginMode': false,
'triggerPrice': false,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': false,
'GTD': false,
},
'hedged': false,
'leverage': false,
'marketBuyByCost': false,
'marketBuyRequiresPrice': false,
'selfTradePrevention': false,
'trailing': false,
'iceberg': false,
},
'createOrders': undefined,
'fetchMyTrades': undefined,
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': true,
'limit': 100,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': undefined,
'fetchClosedOrders': {
'marginMode': false,
'limit': 1000,
'daysBack': 100000,
'daysBackCanceled': 1,
'untilDays': 7,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 200,
},
},
'swap': {
'linear': undefined,
'inverse': undefined,
},
'future': {
'linear': undefined,
'inverse': undefined,
},
},
'precisionMode': TICK_SIZE,
'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,
'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.safeString(withdrawLimits, 'onetime');
const maxDailyWithdrawal = this.safeString(withdrawLimits, 'daily', maxOnetimeWithdrawal);
const remainingDailyWithdrawal = this.safeString(withdrawLimits, 'remaining_daily', maxDailyWithdrawal);
let maxWithdrawLimit = undefined;
if (Precise.stringGt(remainingDailyWithdrawal, '0')) {
maxWithdrawLimit = remainingDailyWithdrawal;
}
else {
maxWithdrawLimit = maxDailyWithdrawal;
}
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': undefined,
'limits': {
'withdraw': {
'min': this.safeNumber(withdrawLimits, 'minimum'),
'max': this.parseNumber(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.0015",
// "ask_fee": "0.0015",
// "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": "100000000.0",
// "state": "active",
// },
// "bid_account": {
// "currency": "KRW",
// "balance": "0.0",
// "locked": "0.0",
// "avg_buy_price": "0",
// "avg_buy_price_modified": false,
// "unit_currency": "KRW",
// },
// "ask_account": {
// "currency": "BTC",
// "balance": "10.0",
// "locked": "0.0",
// "avg_buy_price": "8042000",
// "avg_buy_price_modified": false,
// "unit_currency": "KRW",
// }
// }
//
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 state = this.safeString(marketInfo, 'state');
const bidFee = this.safeString(response, 'bid_fee');
const askFee = this.safeString(response, 'ask_fee');
const fee = this.parseNumber(Precise.stringMax(bidFee, askFee));
return this.safeMarketStructure({
'id': marketId,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'active': (state === 'active'),
'contract': false,
'linear': undefined,
'inverse': undefined,
'taker': fee,
'maker': fee,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber('1e-8'),
'price': this.parseNumber('1e-8'),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber(ask, 'min_total'),
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': this.safeNumber(bid, 'min_total'),
'max': this.safeNumber(marketInfo, 'max_total'),
},
'info': response,
},
});
}
/**
* @method
* @name upbit#fetchMarkets
* @see https://docs.upbit.com/kr/reference/마켓-코드-조회
* @see https://global-docs.upbit.com/reference/listing-market-list
* @description retrieves data on all markets for upbit
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const response = await this.publicGetMarketAll(params);
//
// [
// {
// "market": "KRW-BTC",
// "korean_name": "비트코인",
// "english_name": "Bitcoin"
// },
// ...,
// ]
//
return this.parseMarkets(response);
}
parseMarket(market) {
const id = this.safeString(market, 'market');
const [quoteId, baseId] = id.split('-');
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
return this.safeMarketStructure({
'id': id,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'active': true,
'contract': false,
'linear': undefined,
'inverse': undefined,
'taker': this.safeNumber(this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['taker']),
'maker': this.safeNumber(this.options['tradingFeesByQuoteCurrency'], quote, this.fees['trading']['maker']),
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'price': this.parseNumber('1e-8'),
'amount': this.parseNumber('1e-8'),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'created': undefined,
'info': market,
});
}
parseBalance(response) {
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.safeBalance(result);
}
/**
* @method
* @name upbit#fetchBalance
* @see https://docs.upbit.com/kr/reference/전체-계좌-조회
* @see https://global-docs.upbit.com/reference/overall-account-inquiry
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
*/
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 } ]
//
return this.parseBalance(response);
}
/**
* @method
* @name upbit#fetchOrderBooks
* @see https://docs.upbit.com/kr/reference/호가-정보-조회
* @see https://global-docs.upbit.com/reference/order-book-list
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data for multiple markets
* @param {string[]|undefined} symbols list of unified market symbols, all symbols fetched if undefined, default is undefined
* @param {int} [limit] not used by upbit fetchOrderBooks ()
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbol
*/
async fetchOrderBooks(symbols = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let ids = undefined;
if (symbols === undefined) {
ids = this.ids.join(',');
}
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;
}
/**
* @method
* @name upbit#fetchOrderBook
* @see https://docs.upbit.com/kr/reference/호가-정보-조회
* @see https://global-docs.upbit.com/reference/order-book-list
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
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": 1542883543096,
// "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');
market = this.safeMarket(marketId, market, '-');
const last = this.safeString(ticker, 'trade_price');
return this.safeTicker({
'symbol': market['symbol'],
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'high': this.safeString(ticker, 'high_price'),
'low': this.safeString(ticker, 'low_price'),
'bid': undefined,
'bidVolume': undefined,
'ask': undefined,
'askVolume': undefined,
'vwap': undefined,
'open': this.safeString(ticker, 'opening_price'),
'close': last,
'last': last,
'previousClose': this.safeString(ticker, 'prev_closing_price'),
'change': this.safeString(ticker, 'signed_change_price'),
'percentage': this.safeString(ticker, 'signed_change_rate'),
'average': undefined,
'baseVolume': this.safeString(ticker, 'acc_trade_volume_24h'),
'quoteVolume': this.safeString(ticker, 'acc_trade_price_24h'),
'info': ticker,
}, market);
}
/**
* @method
* @name upbit#fetchTickers
* @see https://docs.upbit.com/kr/reference/ticker현재가-정보
* @see https://global-docs.upbit.com/reference/tickers
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
* @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTickers(symbols = undefined, params = {}) {
await this.loadMarkets();
symbols = this.marketSymbols(symbols);
let ids = undefined;
if (symbols === undefined) {
ids = this.ids.join(',');
}
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.filterByArrayTickers(result, 'symbol', symbols);
}
/**
* @method
* @name upbit#fetchTicker
* @see https://docs.upbit.com/kr/reference/ticker현재가-정보
* @see https://global-docs.upbit.com/reference/tickers
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
* @param {string} symbol unified symbol of the market to fetch the ticker for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
*/
async fetchTicker(symbol, params = {}) {
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';
}
const cost = this.safeString(trade, 'funds');
const price = this.safeString2(trade, 'trade_price', 'price');
const amount = this.safeString2(trade, 'trade_volume', 'volume');
const marketId = this.safeString2(trade, 'market', 'code');
market = this.safeMarket(marketId, market, '-');
let fee = undefined;
const feeCost = this.safeString(trade, askOrBid + '_fee');
if (feeCost !== undefined) {
fee = {
'currency': market['quote'],
'cost': feeCost,
};
}
return this.safeTrade({
'id': id,
'info': trade,
'order': orderId,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': market['symbol'],
'type': undefined,
'side': side,
'takerOrMaker': undefined,
'price': price,
'amount': amount,
'cost': cost,
'fee': fee,
}, market);
}
/**
* @method
* @name upbit#fetchTrades
* @see https://docs.upbit.com/kr/reference/최근-체결-내역
* @see https://global-docs.upbit.com/reference/today-trades-history
* @description get the list of most recent trades for a particular symbol
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
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);
}
/**
* @method
* @name upbit#fetchTradingFee
* @see https://docs.upbit.com/kr/reference/주문-가능-정보
* @see https://global-docs.upbit.com/reference/available-order-information
* @description fetch the trading fees for a market
* @param {string} symbol unified market symbol
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [fee structure]{@link https://docs.ccxt.com/#/?id=fee-structure}
*/
async fetchTradingFee(symbol, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'market': market['id'],
};
const response = await this.privateGetOrdersChance(this.extend(request, params));
//
// {
// "bid_fee": "0.0005",
// "ask_fee": "0.0005",
// "maker_bid_fee": "0.0005",
// "maker_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": 5000 },
// "ask": { "currency": "BTC", "price_unit": null, "min_total": 5000 },
// "max_total": "1000000000.0",
// "state": "active"
// },
// "bid_account": {
// "currency": "KRW",
// "balance": "0.34202415",
// "locked": "4999.99999922",
// "avg_buy_price": "0",
// "avg_buy_price_modified": true,
// "unit_currency": "KRW"
// },
// "ask_account": {
// "currency": "BTC",
// "balance": "0.00048",
// "locked": "0.0",
// "avg_buy_price": "20870000",
// "avg_buy_price_modified": false,
// "unit_currency": "KRW"
// }
// }
//
const askFee = this.safeString(response, 'ask_fee');
const bidFee = this.safeString(response, 'bid_fee');
const taker = Precise.stringMax(askFee, bidFee);
const makerAskFee = this.safeString(response, 'maker_ask_fee');
const makerBidFee = this.safeString(response, 'maker_bid_fee');
const maker = Precise.stringMax(makerAskFee, makerBidFee);
return {
'info': response,
'symbol': symbol,
'maker': this.parseNumber(maker),
'taker': this.parseNumber(taker),
'percentage': true,
'tierBased': false,
};
}
/**
* @method
* @name upbit#fetchTradingFees
* @description fetch the trading fees for markets
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} a [trading fee structure]{@link https://docs.ccxt.com/#/?id=trading-fee-structure}
*/
async fetchTradingFees(params = {}) {
await this.loadMarkets();
const fetchMarketResponse = await this.fetchMarkets(params);
const response = {};
for (let i = 0; i < fetchMarketResponse.length; i++) {
const element = {};
element['maker'] = this.safeNumber(fetchMarketResponse[i], 'maker');
element['taker'] = this.safeNumber(fetchMarketResponse[i], 'taker');
element['symbol'] = this.safeString(fetchMarketResponse[i], 'symbol');
element['percentage'] = true;
element['tierBased'] = false;
element['info'] = fetchMarketResponse[i];
response[this.safeString(fetchMarketResponse[i], 'symbol')] = element;
}
return response;
}
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
];
}
/**
* @method
* @name upbit#fetchOHLCV
* @see https://docs.upbit.com/kr/reference/분minute-캔들-1
* @see https://global-docs.upbit.com/reference/minutes
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, 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.safeString(this.timeframes, timeframe, timeframe);
if (limit === undefined) {
limit = 200;
}
const request = {
'market': market['id'],
'timeframe': timeframeValue,
'count': limit,
};
let response = undefined;
if (since !== undefined) {
// convert `since` to `to` value
request['to'] = this.iso8601(this.sum(since, timeframePeriod * limit * 1000));
}
if (timeframeValue === 'minutes') {
const numMinutes = Math.round(timeframePeriod / 60);
request['unit'] = numMinutes;
response = await this.publicGetCandlesTimeframeUnit(this.extend(request, params));
}
else {
response = await this.publicGetCandlesTimeframe(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);
}
calcOrderPrice(symbol, amount, price = undefined, params = {}) {
let quoteAmount = undefined;
const createMarketBuyOrderRequiresPrice = this.safeValue(this.options, 'createMarketBuyOrderRequiresPrice');
const cost = this.safeString(params, 'cost');
if (cost !== undefined) {
quoteAmount = this.costToPrecision(symbol, cost);
}
else if (createMarketBuyOrderRequiresPrice) {
if (price === undefined || amount === undefined