sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
783 lines (775 loc) • 121 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired, ExchangeNotAvailable, InsufficientFunds, OrderNotFound, InvalidOrder, DDoSProtection, InvalidNonce, AuthenticationError, RateLimitExceeded, PermissionDenied, NotSupported, BadRequest, BadSymbol, AccountSuspended, OrderImmediatelyFillable, OnMaintenance, BadResponse, RequestTimeout, OrderNotFillable, MarginModeAlreadySet } = require ('./base/errors');
const { TRUNCATE, DECIMAL_PLACES } = require ('./base/functions/number');
const Precise = require ('./base/Precise');
// ---------------------------------------------------------------------------
module.exports = class tokocrypto extends Exchange {
describe () {
return this.deepExtend (super.describe (), {
'id': 'tokocrypto',
'name': 'Tokocrypto',
'countries': [ 'ID' ], // Indonesia
'certified': false,
'pro': false,
'version': 'v1',
// new metainfo interface
'has': {
'CORS': undefined,
'spot': true,
'margin': true,
'swap': false,
'future': false,
'option': false,
'addMargin': undefined,
'borrowMargin': undefined,
'cancelAllOrders': false,
'cancelOrder': true,
'cancelOrders': undefined,
'createDepositAddress': false,
'createOrder': true,
'createReduceOnlyOrder': undefined,
'createStopLimitOrder': false,
'createStopMarketOrder': false,
'createStopOrder': true,
'fetchAccounts': false,
'fetchBalance': true,
'fetchBidsAsks': true,
'fetchBorrowInterest': undefined,
'fetchBorrowRate': undefined,
'fetchBorrowRateHistories': undefined,
'fetchBorrowRateHistory': undefined,
'fetchBorrowRates': undefined,
'fetchBorrowRatesPerSymbol': undefined,
'fetchCanceledOrders': false,
'fetchClosedOrder': false,
'fetchClosedOrders': 'emulated',
'fetchCurrencies': false,
'fetchDeposit': false,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchL3OrderBook': false,
'fetchLedger': undefined,
'fetchLeverage': false,
'fetchLeverageTiers': false,
'fetchMarketLeverageTiers': 'emulated',
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrder': false,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': false,
'fetchOrders': true,
'fetchOrderTrades': false,
'fetchPosition': false,
'fetchPositions': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchStatus': false,
'fetchTicker': false,
'fetchTickers': false,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchTradingLimits': false,
'fetchTransactionFee': false,
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfers': false,
'fetchWithdrawal': false,
'fetchWithdrawals': true,
'fetchWithdrawalWhitelist': false,
'reduceMargin': false,
'repayMargin': false,
'setLeverage': false,
'setMargin': false,
'setMarginMode': false,
'setPositionMode': false,
'signIn': false,
'transfer': false,
'withdraw': false,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/183870484-d3398d0c-f6a1-4cce-91b8-d58792308716.jpg',
'api': {
'rest': {
'public': 'https://www.tokocrypto.com',
'binance': 'https://api.binance.com/api/v3',
'private': 'https://www.tokocrypto.com',
},
},
'www': 'https://tokocrypto.com',
// 'referral': 'https://www.binance.us/?ref=35005074',
'doc': 'https://www.tokocrypto.com/apidocs/',
'fees': 'https://www.tokocrypto.com/fees/newschedule',
},
'api': {
'binance': {
'get': {
'ping': 1,
'time': 1,
'depth': { 'cost': 1, 'byLimit': [ [ 100, 1 ], [ 500, 5 ], [ 1000, 10 ], [ 5000, 50 ] ] },
'trades': 1,
'aggTrades': 1,
'historicalTrades': 5,
'klines': 1,
'ticker/24hr': { 'cost': 1, 'noSymbol': 40 },
'ticker/price': { 'cost': 1, 'noSymbol': 2 },
'ticker/bookTicker': { 'cost': 1, 'noSymbol': 2 },
'exchangeInfo': 10,
},
'put': {
'userDataStream': 1,
},
'post': {
'userDataStream': 1,
},
'delete': {
'userDataStream': 1,
},
},
'public': {
'get': {
'open/v1/common/time': 1,
'open/v1/common/symbols': 1,
// all the actual symbols are type 1
'open/v1/market/depth': 1, // when symbol type is not 1
'open/v1/market/trades': 1, // when symbol type is not 1
'open/v1/market/agg-trades': 1, // when symbol type is not 1
'open/v1/market/klines': 1, // when symbol type is not 1
},
},
'private': {
'get': {
'open/v1/orders/detail': 1,
'open/v1/orders': 1,
'open/v1/account/spot': 1,
'open/v1/account/spot/asset': 1,
'open/v1/orders/trades': 1,
'open/v1/withdraws': 1,
'open/v1/deposits': 1,
'open/v1/deposits/address': 1,
},
'post': {
'open/v1/orders': 1,
'open/v1/orders/cancel': 1,
'open/v1/orders/oco': 1,
'open/v1/withdraws': 1,
'open/v1/user-data-stream': 1,
},
},
},
'fees': {
'trading': {
'tierBased': true,
'percentage': true,
'taker': this.parseNumber ('0.0075'), // 0.1% trading fee, zero fees for all trading pairs before November 1
'maker': this.parseNumber ('0.0075'), // 0.1% trading fee, zero fees for all trading pairs before November 1
},
},
'precisionMode': DECIMAL_PLACES,
'options': {
// 'fetchTradesMethod': 'binanceGetTrades', // binanceGetTrades, binanceGetAggTrades
'defaultTimeInForce': 'GTC', // 'GTC' = Good To Cancel (default), 'IOC' = Immediate Or Cancel
// 'defaultType': 'spot', // 'spot', 'future', 'margin', 'delivery'
'hasAlreadyAuthenticatedSuccessfully': false,
'warnOnFetchOpenOrdersWithoutSymbol': true,
// 'fetchPositions': 'positionRisk', // or 'account'
'recvWindow': 5 * 1000, // 5 sec, binance default
'timeDifference': 0, // the difference between system clock and Binance clock
'adjustForTimeDifference': false, // controls the adjustment logic upon instantiation
'newOrderRespType': {
'market': 'FULL', // 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills
'limit': 'FULL', // we change it from 'ACK' by default to 'FULL' (returns immediately if limit is not hit)
},
'quoteOrderQty': false, // whether market orders support amounts in quote currency
'networks': {
'ERC20': 'ETH',
'TRC20': 'TRX',
'BEP2': 'BNB',
'BEP20': 'BSC',
'OMNI': 'OMNI',
'EOS': 'EOS',
'SPL': 'SOL',
},
'reverseNetworks': {
'tronscan.org': 'TRC20',
'etherscan.io': 'ERC20',
'bscscan.com': 'BSC',
'explorer.binance.org': 'BEP2',
'bithomp.com': 'XRP',
'bloks.io': 'EOS',
'stellar.expert': 'XLM',
'blockchair.com/bitcoin': 'BTC',
'blockchair.com/bitcoin-cash': 'BCH',
'blockchair.com/ecash': 'XEC',
'explorer.litecoin.net': 'LTC',
'explorer.avax.network': 'AVAX',
'solscan.io': 'SOL',
'polkadot.subscan.io': 'DOT',
'dashboard.internetcomputer.org': 'ICP',
'explorer.chiliz.com': 'CHZ',
'cardanoscan.io': 'ADA',
'mainnet.theoan.com': 'AION',
'algoexplorer.io': 'ALGO',
'explorer.ambrosus.com': 'AMB',
'viewblock.io/zilliqa': 'ZIL',
'viewblock.io/arweave': 'AR',
'explorer.ark.io': 'ARK',
'atomscan.com': 'ATOM',
'www.mintscan.io': 'CTK',
'explorer.bitcoindiamond.org': 'BCD',
'btgexplorer.com': 'BTG',
'bts.ai': 'BTS',
'explorer.celo.org': 'CELO',
'explorer.nervos.org': 'CKB',
'cerebro.cortexlabs.ai': 'CTXC',
'chainz.cryptoid.info': 'VIA',
'explorer.dcrdata.org': 'DCR',
'digiexplorer.info': 'DGB',
'dock.subscan.io': 'DOCK',
'dogechain.info': 'DOGE',
'explorer.elrond.com': 'EGLD',
'blockscout.com': 'ETC',
'explore-fetchhub.fetch.ai': 'FET',
'filfox.info': 'FIL',
'fio.bloks.io': 'FIO',
'explorer.firo.org': 'FIRO',
'neoscan.io': 'NEO',
'ftmscan.com': 'FTM',
'explorer.gochain.io': 'GO',
'block.gxb.io': 'GXS',
'hash-hash.info': 'HBAR',
'www.hiveblockexplorer.com': 'HIVE',
'explorer.helium.com': 'HNT',
'tracker.icon.foundation': 'ICX',
'www.iostabc.com': 'IOST',
'explorer.iota.org': 'IOTA',
'iotexscan.io': 'IOTX',
'irishub.iobscan.io': 'IRIS',
'kava.mintscan.io': 'KAVA',
'scope.klaytn.com': 'KLAY',
'kmdexplorer.io': 'KMD',
'kusama.subscan.io': 'KSM',
'explorer.lto.network': 'LTO',
'polygonscan.com': 'POLYGON',
'explorer.ont.io': 'ONT',
'minaexplorer.com': 'MINA',
'nanolooker.com': 'NANO',
'explorer.nebulas.io': 'NAS',
'explorer.nbs.plus': 'NBS',
'explorer.nebl.io': 'NEBL',
'nulscan.io': 'NULS',
'nxscan.com': 'NXS',
'explorer.harmony.one': 'ONE',
'explorer.poa.network': 'POA',
'qtum.info': 'QTUM',
'explorer.rsk.co': 'RSK',
'www.oasisscan.com': 'ROSE',
'ravencoin.network': 'RVN',
'sc.tokenview.com': 'SC',
'secretnodes.com': 'SCRT',
'explorer.skycoin.com': 'SKY',
'steemscan.com': 'STEEM',
'explorer.stacks.co': 'STX',
'www.thetascan.io': 'THETA',
'scan.tomochain.com': 'TOMO',
'explore.vechain.org': 'VET',
'explorer.vite.net': 'VITE',
'www.wanscan.org': 'WAN',
'wavesexplorer.com': 'WAVES',
'wax.eosx.io': 'WAXP',
'waltonchain.pro': 'WTC',
'chain.nem.ninja': 'XEM',
'verge-blockchain.info': 'XVG',
'explorer.yoyow.org': 'YOYOW',
'explorer.zcha.in': 'ZEC',
'explorer.zensystem.io': 'ZEN',
},
'impliedNetworks': {
'ETH': { 'ERC20': 'ETH' },
'TRX': { 'TRC20': 'TRX' },
},
'legalMoney': {
'MXN': true,
'UGX': true,
'SEK': true,
'CHF': true,
'VND': true,
'AED': true,
'DKK': true,
'KZT': true,
'HUF': true,
'PEN': true,
'PHP': true,
'USD': true,
'TRY': true,
'EUR': true,
'NGN': true,
'PLN': true,
'BRL': true,
'ZAR': true,
'KES': true,
'ARS': true,
'RUB': true,
'AUD': true,
'NOK': true,
'CZK': true,
'GBP': true,
'UAH': true,
'GHS': true,
'HKD': true,
'CAD': true,
'INR': true,
'JPY': true,
'NZD': true,
},
},
// https://binance-docs.github.io/apidocs/spot/en/#error-codes-2
'exceptions': {
'exact': {
'System is under maintenance.': OnMaintenance, // {"code":1,"msg":"System is under maintenance."}
'System abnormality': ExchangeError, // {"code":-1000,"msg":"System abnormality"}
'You are not authorized to execute this request.': PermissionDenied, // {"msg":"You are not authorized to execute this request."}
'API key does not exist': AuthenticationError,
'Order would trigger immediately.': OrderImmediatelyFillable,
'Stop price would trigger immediately.': OrderImmediatelyFillable, // {"code":-2010,"msg":"Stop price would trigger immediately."}
'Order would immediately match and take.': OrderImmediatelyFillable, // {"code":-2010,"msg":"Order would immediately match and take."}
'Account has insufficient balance for requested action.': InsufficientFunds,
'Rest API trading is not enabled.': ExchangeNotAvailable,
"You don't have permission.": PermissionDenied, // {"msg":"You don't have permission.","success":false}
'Market is closed.': ExchangeNotAvailable, // {"code":-1013,"msg":"Market is closed."}
'Too many requests. Please try again later.': DDoSProtection, // {"msg":"Too many requests. Please try again later.","success":false}
'This action disabled is on this account.': AccountSuspended, // {"code":-2010,"msg":"This action disabled is on this account."}
'-1000': ExchangeNotAvailable, // {"code":-1000,"msg":"An unknown error occured while processing the request."}
'-1001': ExchangeNotAvailable, // {"code":-1001,"msg":"'Internal error; unable to process your request. Please try again.'"}
'-1002': AuthenticationError, // {"code":-1002,"msg":"'You are not authorized to execute this request.'"}
'-1003': RateLimitExceeded, // {"code":-1003,"msg":"Too much request weight used, current limit is 1200 request weight per 1 MINUTE. Please use the websocket for live updates to avoid polling the API."}
'-1004': DDoSProtection, // {"code":-1004,"msg":"Server is busy, please wait and try again"}
'-1005': PermissionDenied, // {"code":-1005,"msg":"No such IP has been white listed"}
'-1006': BadResponse, // {"code":-1006,"msg":"An unexpected response was received from the message bus. Execution status unknown."}
'-1007': RequestTimeout, // {"code":-1007,"msg":"Timeout waiting for response from backend server. Send status unknown; execution status unknown."}
'-1010': BadResponse, // {"code":-1010,"msg":"ERROR_MSG_RECEIVED."}
'-1011': PermissionDenied, // {"code":-1011,"msg":"This IP cannot access this route."}
'-1013': InvalidOrder, // {"code":-1013,"msg":"createOrder -> 'invalid quantity'/'invalid price'/MIN_NOTIONAL"}
'-1014': InvalidOrder, // {"code":-1014,"msg":"Unsupported order combination."}
'-1015': RateLimitExceeded, // {"code":-1015,"msg":"'Too many new orders; current limit is %s orders per %s.'"}
'-1016': ExchangeNotAvailable, // {"code":-1016,"msg":"'This service is no longer available.',"}
'-1020': BadRequest, // {"code":-1020,"msg":"'This operation is not supported.'"}
'-1021': InvalidNonce, // {"code":-1021,"msg":"'your time is ahead of server'"}
'-1022': AuthenticationError, // {"code":-1022,"msg":"Signature for this request is not valid."}
'-1023': BadRequest, // {"code":-1023,"msg":"Start time is greater than end time."}
'-1099': AuthenticationError, // {"code":-1099,"msg":"Not found, authenticated, or authorized"}
'-1100': BadRequest, // {"code":-1100,"msg":"createOrder(symbol, 1, asdf) -> 'Illegal characters found in parameter 'price'"}
'-1101': BadRequest, // {"code":-1101,"msg":"Too many parameters; expected %s and received %s."}
'-1102': BadRequest, // {"code":-1102,"msg":"Param %s or %s must be sent, but both were empty"}
'-1103': BadRequest, // {"code":-1103,"msg":"An unknown parameter was sent."}
'-1104': BadRequest, // {"code":-1104,"msg":"Not all sent parameters were read, read 8 parameters but was sent 9"}
'-1105': BadRequest, // {"code":-1105,"msg":"Parameter %s was empty."}
'-1106': BadRequest, // {"code":-1106,"msg":"Parameter %s sent when not required."}
'-1108': BadRequest, // {"code":-1108,"msg":"Invalid asset."}
'-1109': AuthenticationError, // {"code":-1109,"msg":"Invalid account."}
'-1110': BadRequest, // {"code":-1110,"msg":"Invalid symbolType."}
'-1111': BadRequest, // {"code":-1111,"msg":"Precision is over the maximum defined for this asset."}
'-1112': InvalidOrder, // {"code":-1112,"msg":"No orders on book for symbol."}
'-1113': BadRequest, // {"code":-1113,"msg":"Withdrawal amount must be negative."}
'-1114': BadRequest, // {"code":-1114,"msg":"TimeInForce parameter sent when not required."}
'-1115': BadRequest, // {"code":-1115,"msg":"Invalid timeInForce."}
'-1116': BadRequest, // {"code":-1116,"msg":"Invalid orderType."}
'-1117': BadRequest, // {"code":-1117,"msg":"Invalid side."}
'-1118': BadRequest, // {"code":-1118,"msg":"New client order ID was empty."}
'-1119': BadRequest, // {"code":-1119,"msg":"Original client order ID was empty."}
'-1120': BadRequest, // {"code":-1120,"msg":"Invalid interval."}
'-1121': BadSymbol, // {"code":-1121,"msg":"Invalid symbol."}
'-1125': AuthenticationError, // {"code":-1125,"msg":"This listenKey does not exist."}
'-1127': BadRequest, // {"code":-1127,"msg":"More than %s hours between startTime and endTime."}
'-1128': BadRequest, // {"code":-1128,"msg":"{"code":-1128,"msg":"Combination of optional parameters invalid."}"}
'-1130': BadRequest, // {"code":-1130,"msg":"Data sent for paramter %s is not valid."}
'-1131': BadRequest, // {"code":-1131,"msg":"recvWindow must be less than 60000"}
'-1136': BadRequest, // {"code":-1136,"msg":"Invalid newOrderRespType"}
'-2008': AuthenticationError, // {"code":-2008,"msg":"Invalid Api-Key ID."}
'-2010': ExchangeError, // {"code":-2010,"msg":"generic error code for createOrder -> 'Account has insufficient balance for requested action.', {"code":-2010,"msg":"Rest API trading is not enabled."}, etc..."}
'-2011': OrderNotFound, // {"code":-2011,"msg":"cancelOrder(1, 'BTC/USDT') -> 'UNKNOWN_ORDER'"}
'-2013': OrderNotFound, // {"code":-2013,"msg":"fetchOrder (1, 'BTC/USDT') -> 'Order does not exist'"}
'-2014': AuthenticationError, // {"code":-2014,"msg":"API-key format invalid."}
'-2015': AuthenticationError, // {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."}
'-2016': BadRequest, // {"code":-2016,"msg":"No trading window could be found for the symbol. Try ticker/24hrs instead."}
'-2018': InsufficientFunds, // {"code":-2018,"msg":"Balance is insufficient"}
'-2019': InsufficientFunds, // {"code":-2019,"msg":"Margin is insufficient."}
'-2020': OrderNotFillable, // {"code":-2020,"msg":"Unable to fill."}
'-2021': OrderImmediatelyFillable, // {"code":-2021,"msg":"Order would immediately trigger."}
'-2022': InvalidOrder, // {"code":-2022,"msg":"ReduceOnly Order is rejected."}
'-2023': InsufficientFunds, // {"code":-2023,"msg":"User in liquidation mode now."}
'-2024': InsufficientFunds, // {"code":-2024,"msg":"Position is not sufficient."}
'-2025': InvalidOrder, // {"code":-2025,"msg":"Reach max open order limit."}
'-2026': InvalidOrder, // {"code":-2026,"msg":"This OrderType is not supported when reduceOnly."}
'-2027': InvalidOrder, // {"code":-2027,"msg":"Exceeded the maximum allowable position at current leverage."}
'-2028': InsufficientFunds, // {"code":-2028,"msg":"Leverage is smaller than permitted: insufficient margin balance"}
'-3000': ExchangeError, // {"code":-3000,"msg":"Internal server error."}
'-3001': AuthenticationError, // {"code":-3001,"msg":"Please enable 2FA first."}
'-3002': BadSymbol, // {"code":-3002,"msg":"We don't have this asset."}
'-3003': BadRequest, // {"code":-3003,"msg":"Margin account does not exist."}
'-3004': ExchangeError, // {"code":-3004,"msg":"Trade not allowed."}
'-3005': InsufficientFunds, // {"code":-3005,"msg":"Transferring out not allowed. Transfer out amount exceeds max amount."}
'-3006': InsufficientFunds, // {"code":-3006,"msg":"Your borrow amount has exceed maximum borrow amount."}
'-3007': ExchangeError, // {"code":-3007,"msg":"You have pending transaction, please try again later.."}
'-3008': InsufficientFunds, // {"code":-3008,"msg":"Borrow not allowed. Your borrow amount has exceed maximum borrow amount."}
'-3009': BadRequest, // {"code":-3009,"msg":"This asset are not allowed to transfer into margin account currently."}
'-3010': ExchangeError, // {"code":-3010,"msg":"Repay not allowed. Repay amount exceeds borrow amount."}
'-3011': BadRequest, // {"code":-3011,"msg":"Your input date is invalid."}
'-3012': ExchangeError, // {"code":-3012,"msg":"Borrow is banned for this asset."}
'-3013': BadRequest, // {"code":-3013,"msg":"Borrow amount less than minimum borrow amount."}
'-3014': AccountSuspended, // {"code":-3014,"msg":"Borrow is banned for this account."}
'-3015': ExchangeError, // {"code":-3015,"msg":"Repay amount exceeds borrow amount."}
'-3016': BadRequest, // {"code":-3016,"msg":"Repay amount less than minimum repay amount."}
'-3017': ExchangeError, // {"code":-3017,"msg":"This asset are not allowed to transfer into margin account currently."}
'-3018': AccountSuspended, // {"code":-3018,"msg":"Transferring in has been banned for this account."}
'-3019': AccountSuspended, // {"code":-3019,"msg":"Transferring out has been banned for this account."}
'-3020': InsufficientFunds, // {"code":-3020,"msg":"Transfer out amount exceeds max amount."}
'-3021': BadRequest, // {"code":-3021,"msg":"Margin account are not allowed to trade this trading pair."}
'-3022': AccountSuspended, // {"code":-3022,"msg":"You account's trading is banned."}
'-3023': BadRequest, // {"code":-3023,"msg":"You can't transfer out/place order under current margin level."}
'-3024': ExchangeError, // {"code":-3024,"msg":"The unpaid debt is too small after this repayment."}
'-3025': BadRequest, // {"code":-3025,"msg":"Your input date is invalid."}
'-3026': BadRequest, // {"code":-3026,"msg":"Your input param is invalid."}
'-3027': BadSymbol, // {"code":-3027,"msg":"Not a valid margin asset."}
'-3028': BadSymbol, // {"code":-3028,"msg":"Not a valid margin pair."}
'-3029': ExchangeError, // {"code":-3029,"msg":"Transfer failed."}
'-3036': AccountSuspended, // {"code":-3036,"msg":"This account is not allowed to repay."}
'-3037': ExchangeError, // {"code":-3037,"msg":"PNL is clearing. Wait a second."}
'-3038': BadRequest, // {"code":-3038,"msg":"Listen key not found."}
'-3041': InsufficientFunds, // {"code":-3041,"msg":"Balance is not enough"}
'-3042': BadRequest, // {"code":-3042,"msg":"PriceIndex not available for this margin pair."}
'-3043': BadRequest, // {"code":-3043,"msg":"Transferring in not allowed."}
'-3044': DDoSProtection, // {"code":-3044,"msg":"System busy."}
'-3045': ExchangeError, // {"code":-3045,"msg":"The system doesn't have enough asset now."}
'-3999': ExchangeError, // {"code":-3999,"msg":"This function is only available for invited users."}
'-4001': BadRequest, // {"code":-4001 ,"msg":"Invalid operation."}
'-4002': BadRequest, // {"code":-4002 ,"msg":"Invalid get."}
'-4003': BadRequest, // {"code":-4003 ,"msg":"Your input email is invalid."}
'-4004': AuthenticationError, // {"code":-4004,"msg":"You don't login or auth."}
'-4005': RateLimitExceeded, // {"code":-4005 ,"msg":"Too many new requests."}
'-4006': BadRequest, // {"code":-4006 ,"msg":"Support main account only."}
'-4007': BadRequest, // {"code":-4007 ,"msg":"Address validation is not passed."}
'-4008': BadRequest, // {"code":-4008 ,"msg":"Address tag validation is not passed."}
'-4010': BadRequest, // {"code":-4010 ,"msg":"White list mail has been confirmed."} // [TODO] possible bug: it should probably be "has not been confirmed"
'-4011': BadRequest, // {"code":-4011 ,"msg":"White list mail is invalid."}
'-4012': BadRequest, // {"code":-4012 ,"msg":"White list is not opened."}
'-4013': AuthenticationError, // {"code":-4013 ,"msg":"2FA is not opened."}
'-4014': PermissionDenied, // {"code":-4014 ,"msg":"Withdraw is not allowed within 2 min login."}
'-4015': ExchangeError, // {"code":-4015 ,"msg":"Withdraw is limited."}
'-4016': PermissionDenied, // {"code":-4016 ,"msg":"Within 24 hours after password modification, withdrawal is prohibited."}
'-4017': PermissionDenied, // {"code":-4017 ,"msg":"Within 24 hours after the release of 2FA, withdrawal is prohibited."}
'-4018': BadSymbol, // {"code":-4018,"msg":"We don't have this asset."}
'-4019': BadSymbol, // {"code":-4019,"msg":"Current asset is not open for withdrawal."}
'-4021': BadRequest, // {"code":-4021,"msg":"Asset withdrawal must be an %s multiple of %s."}
'-4022': BadRequest, // {"code":-4022,"msg":"Not less than the minimum pick-up quantity %s."}
'-4023': ExchangeError, // {"code":-4023,"msg":"Within 24 hours, the withdrawal exceeds the maximum amount."}
'-4024': InsufficientFunds, // {"code":-4024,"msg":"You don't have this asset."}
'-4025': InsufficientFunds, // {"code":-4025,"msg":"The number of hold asset is less than zero."}
'-4026': InsufficientFunds, // {"code":-4026,"msg":"You have insufficient balance."}
'-4027': ExchangeError, // {"code":-4027,"msg":"Failed to obtain tranId."}
'-4028': BadRequest, // {"code":-4028,"msg":"The amount of withdrawal must be greater than the Commission."}
'-4029': BadRequest, // {"code":-4029,"msg":"The withdrawal record does not exist."}
'-4030': ExchangeError, // {"code":-4030,"msg":"Confirmation of successful asset withdrawal. [TODO] possible bug in docs"}
'-4031': ExchangeError, // {"code":-4031,"msg":"Cancellation failed."}
'-4032': ExchangeError, // {"code":-4032,"msg":"Withdraw verification exception."}
'-4033': BadRequest, // {"code":-4033,"msg":"Illegal address."}
'-4034': ExchangeError, // {"code":-4034,"msg":"The address is suspected of fake."}
'-4035': PermissionDenied, // {"code":-4035,"msg":"This address is not on the whitelist. Please join and try again."}
'-4036': BadRequest, // {"code":-4036,"msg":"The new address needs to be withdrawn in {0} hours."}
'-4037': ExchangeError, // {"code":-4037,"msg":"Re-sending Mail failed."}
'-4038': ExchangeError, // {"code":-4038,"msg":"Please try again in 5 minutes."}
'-4039': BadRequest, // {"code":-4039,"msg":"The user does not exist."}
'-4040': BadRequest, // {"code":-4040,"msg":"This address not charged."}
'-4041': ExchangeError, // {"code":-4041,"msg":"Please try again in one minute."}
'-4042': ExchangeError, // {"code":-4042,"msg":"This asset cannot get deposit address again."}
'-4043': BadRequest, // {"code":-4043,"msg":"More than 100 recharge addresses were used in 24 hours."}
'-4044': BadRequest, // {"code":-4044,"msg":"This is a blacklist country."}
'-4045': ExchangeError, // {"code":-4045,"msg":"Failure to acquire assets."}
'-4046': AuthenticationError, // {"code":-4046,"msg":"Agreement not confirmed."}
'-4047': BadRequest, // {"code":-4047,"msg":"Time interval must be within 0-90 days"}
'-5001': BadRequest, // {"code":-5001,"msg":"Don't allow transfer to micro assets."}
'-5002': InsufficientFunds, // {"code":-5002,"msg":"You have insufficient balance."}
'-5003': InsufficientFunds, // {"code":-5003,"msg":"You don't have this asset."}
'-5004': BadRequest, // {"code":-5004,"msg":"The residual balances of %s have exceeded 0.001BTC, Please re-choose."}
'-5005': InsufficientFunds, // {"code":-5005,"msg":"The residual balances of %s is too low, Please re-choose."}
'-5006': BadRequest, // {"code":-5006,"msg":"Only transfer once in 24 hours."}
'-5007': BadRequest, // {"code":-5007,"msg":"Quantity must be greater than zero."}
'-5008': InsufficientFunds, // {"code":-5008,"msg":"Insufficient amount of returnable assets."}
'-5009': BadRequest, // {"code":-5009,"msg":"Product does not exist."}
'-5010': ExchangeError, // {"code":-5010,"msg":"Asset transfer fail."}
'-5011': BadRequest, // {"code":-5011,"msg":"future account not exists."}
'-5012': ExchangeError, // {"code":-5012,"msg":"Asset transfer is in pending."}
'-5013': InsufficientFunds, // {"code":-5013,"msg":"Asset transfer failed: insufficient balance""} // undocumented
'-5021': BadRequest, // {"code":-5021,"msg":"This parent sub have no relation"}
'-6001': BadRequest, // {"code":-6001,"msg":"Daily product not exists."}
'-6003': BadRequest, // {"code":-6003,"msg":"Product not exist or you don't have permission"}
'-6004': ExchangeError, // {"code":-6004,"msg":"Product not in purchase status"}
'-6005': InvalidOrder, // {"code":-6005,"msg":"Smaller than min purchase limit"}
'-6006': BadRequest, // {"code":-6006,"msg":"Redeem amount error"}
'-6007': BadRequest, // {"code":-6007,"msg":"Not in redeem time"}
'-6008': BadRequest, // {"code":-6008,"msg":"Product not in redeem status"}
'-6009': RateLimitExceeded, // {"code":-6009,"msg":"Request frequency too high"}
'-6011': BadRequest, // {"code":-6011,"msg":"Exceeding the maximum num allowed to purchase per user"}
'-6012': InsufficientFunds, // {"code":-6012,"msg":"Balance not enough"}
'-6013': ExchangeError, // {"code":-6013,"msg":"Purchasing failed"}
'-6014': BadRequest, // {"code":-6014,"msg":"Exceed up-limit allowed to purchased"}
'-6015': BadRequest, // {"code":-6015,"msg":"Empty request body"}
'-6016': BadRequest, // {"code":-6016,"msg":"Parameter err"}
'-6017': BadRequest, // {"code":-6017,"msg":"Not in whitelist"}
'-6018': BadRequest, // {"code":-6018,"msg":"Asset not enough"}
'-6019': AuthenticationError, // {"code":-6019,"msg":"Need confirm"}
'-6020': BadRequest, // {"code":-6020,"msg":"Project not exists"}
'-7001': BadRequest, // {"code":-7001,"msg":"Date range is not supported."}
'-7002': BadRequest, // {"code":-7002,"msg":"Data request type is not supported."}
'-9000': InsufficientFunds, // {"code":-9000,"msg":"user have no avaliable amount"}"
'-10017': BadRequest, // {"code":-10017,"msg":"Repay amount should not be larger than liability."}
'-11008': InsufficientFunds, // {"code":-11008,"msg":"Exceeding the account's maximum borrowable limit."} // undocumented
'-12014': RateLimitExceeded, // {"code":-12014,"msg":"More than 1 request in 3 seconds"}
'-13000': BadRequest, // {"code":-13000,"msg":"Redeption of the token is forbiden now"}
'-13001': BadRequest, // {"code":-13001,"msg":"Exceeds individual 24h redemption limit of the token"}
'-13002': BadRequest, // {"code":-13002,"msg":"Exceeds total 24h redemption limit of the token"}
'-13003': BadRequest, // {"code":-13003,"msg":"Subscription of the token is forbiden now"}
'-13004': BadRequest, // {"code":-13004,"msg":"Exceeds individual 24h subscription limit of the token"}
'-13005': BadRequest, // {"code":-13005,"msg":"Exceeds total 24h subscription limit of the token"}
'-13006': InvalidOrder, // {"code":-13006,"msg":"Subscription amount is too small"}
'-13007': AuthenticationError, // {"code":-13007,"msg":"The Agreement is not signed"}
'-21001': BadRequest, // {"code":-21001,"msg":"USER_IS_NOT_UNIACCOUNT"}
'-21002': BadRequest, // {"code":-21002,"msg":"UNI_ACCOUNT_CANT_TRANSFER_FUTURE"}
'-21003': BadRequest, // {"code":-21003,"msg":"NET_ASSET_MUST_LTE_RATIO"}
'100001003': BadRequest, // {"code":100001003,"msg":"Verification failed"} // undocumented
'2202': InsufficientFunds, // {"code":2202,"msg":"Insufficient balance","data":{"code":-2010,"msg":"Account has insufficient balance for requested action."},"timestamp":1662733681161}
'3210': InvalidOrder, // {"code":3210,"msg":"The total volume is too low","data":{"code":-1013,"msg":"Filter failure: MIN_NOTIONAL"},"timestamp":1662734704462}
'3203': InvalidOrder, // {"code":3203,"msg":"Incorrect Order Quantity","timestamp":1662734809758}
'3211': InvalidOrder, // {"code":3211,"msg":"The total volume must be greater than 10","timestamp":1662739358179}
'3207': InvalidOrder, // {"code":3207,"msg":"The price cannot be lower than 12.18","timestamp":1662739502856}
'3218': OrderNotFound, // {"code":3218,"msg":"Order does not exist","timestamp":1662739749275}
},
'broad': {
'has no operation privilege': PermissionDenied,
'MAX_POSITION': InvalidOrder, // {"code":-2010,"msg":"Filter failure: MAX_POSITION"}
},
},
});
}
nonce () {
return this.milliseconds () - this.options['timeDifference'];
}
async fetchTime (params = {}) {
/**
* @method
* @name tokocrypto#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {object} params extra parameters specific to the tokocrypto api endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
const response = await this.publicGetTime (params);
//
//
//
return this.safeInteger (response, 'serverTime');
}
async fetchMarkets (params = {}) {
/**
* @method
* @name tokocrypto#fetchMarkets
* @description retrieves data on all markets for tokocrypto
* @param {object} params extra parameters specific to the exchange api endpoint
* @returns {[object]} an array of objects representing market data
*/
const response = await this.publicGetOpenV1CommonSymbols (params);
//
// {
// "code":0,
// "msg":"Success",
// "data":{
// "list":[
// {
// "type":1,
// "symbol":"1INCH_BTC",
// "baseAsset":"1INCH",
// "basePrecision":8,
// "quoteAsset":"BTC",
// "quotePrecision":8,
// "filters":[
// {"filterType":"PRICE_FILTER","minPrice":"0.00000001","maxPrice":"1000.00000000","tickSize":"0.00000001","applyToMarket":false},
// {"filterType":"PERCENT_PRICE","multiplierUp":5,"multiplierDown":0.2,"avgPriceMins":"5","applyToMarket":false},
// {"filterType":"LOT_SIZE","minQty":"0.10000000","maxQty":"90000000.00000000","stepSize":"0.10000000","applyToMarket":false},
// {"filterType":"MIN_NOTIONAL","avgPriceMins":"5","minNotional":"0.00010000","applyToMarket":true},
// {"filterType":"ICEBERG_PARTS","applyToMarket":false,"limit":"10"},
// {"filterType":"MARKET_LOT_SIZE","minQty":"0.00000000","maxQty":"79460.14117231","stepSize":"0.00000000","applyToMarket":false},
// {"filterType":"TRAILING_DELTA","applyToMarket":false},
// {"filterType":"MAX_NUM_ORDERS","applyToMarket":false},
// {"filterType":"MAX_NUM_ALGO_ORDERS","applyToMarket":false,"maxNumAlgoOrders":"5"}
// ],
// "orderTypes":["LIMIT","LIMIT_MAKER","MARKET","STOP_LOSS_LIMIT","TAKE_PROFIT_LIMIT"],
// "icebergEnable":1,
// "ocoEnable":1,
// "spotTradingEnable":1,
// "marginTradingEnable":1,
// "permissions":["SPOT","MARGIN"]
// },
// ]
// },
// "timestamp":1659492212507
// }
//
if (this.options['adjustForTimeDifference']) {
await this.loadTimeDifference ();
}
const data = this.safeValue (response, 'data', {});
const list = this.safeValue (data, 'list', []);
const result = [];
for (let i = 0; i < list.length; i++) {
const market = list[i];
const baseId = this.safeString (market, 'baseAsset');
const quoteId = this.safeString (market, 'quoteAsset');
const id = this.safeString (market, 'symbol');
const lowercaseId = this.safeStringLower (market, 'symbol');
const settleId = this.safeString (market, 'marginAsset');
const base = this.safeCurrencyCode (baseId);
const quote = this.safeCurrencyCode (quoteId);
const settle = this.safeCurrencyCode (settleId);
const symbol = base + '/' + quote;
const filters = this.safeValue (market, 'filters', []);
const filtersByType = this.indexBy (filters, 'filterType');
const status = this.safeString2 (market, 'status', 'contractStatus');
let active = (status === 'TRADING');
const permissions = this.safeValue (market, 'permissions', []);
for (let j = 0; j < permissions.length; j++) {
if (permissions[j] === 'TRD_GRP_003') {
active = false;
break;
}
}
const isMarginTradingAllowed = this.safeValue (market, 'isMarginTradingAllowed', false);
const entry = {
'id': id,
'lowercaseId': lowercaseId,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': 'spot',
'spot': true,
'margin': isMarginTradingAllowed,
'swap': false,
'future': false,
'delivery': false,
'option': false,
'active': active,
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.safeInteger (market, 'quantityPrecision'),
'price': this.safeInteger (market, 'pricePrecision'),
'base': this.safeInteger (market, 'baseAssetPrecision'),
'quote': this.safeInteger (market, 'quotePrecision'),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'info': market,
};
if ('PRICE_FILTER' in filtersByType) {
const filter = this.safeValue (filtersByType, 'PRICE_FILTER', {});
const tickSize = this.safeString (filter, 'tickSize');
entry['precision']['price'] = this.precisionFromString (tickSize);
// PRICE_FILTER reports zero values for maxPrice
// since they updated filter types in November 2018
// https://github.com/ccxt/ccxt/issues/4286
// therefore limits['price']['max'] doesn't have any meaningful value except undefined
entry['limits']['price'] = {
'min': this.safeNumber (filter, 'minPrice'),
'max': this.safeNumber (filter, 'maxPrice'),
};
entry['precision']['price'] = this.precisionFromString (filter['tickSize']);
}
if ('LOT_SIZE' in filtersByType) {
const filter = this.safeValue (filtersByType, 'LOT_SIZE', {});
const stepSize = this.safeString (filter, 'stepSize');
entry['precision']['amount'] = this.precisionFromString (stepSize);
entry['limits']['amount'] = {
'min': this.safeNumber (filter, 'minQty'),
'max': this.safeNumber (filter, 'maxQty'),
};
}
if ('MARKET_LOT_SIZE' in filtersByType) {
const filter = this.safeValue (filtersByType, 'MARKET_LOT_SIZE', {});
entry['limits']['market'] = {
'min': this.safeNumber (filter, 'minQty'),
'max': this.safeNumber (filter, 'maxQty'),
};
}
if ('MIN_NOTIONAL' in filtersByType) {
const filter = this.safeValue (filtersByType, 'MIN_NOTIONAL', {});
entry['limits']['cost']['min'] = this.safeNumber2 (filter, 'minNotional', 'notional');
}
result.push (entry);
}
return result;
}
async fetchOrderBook (symbol, limit = undefined, params = {}) {
/**
* @method
* @name tokocrypto#fetchOrderBook
* @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|undefined} limit the maximum amount of order book entries to return
* @param {object} params extra parameters specific to the tokocrypto api endpoint
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/en/latest/manual.html#order-book-structure} indexed by market symb