UNPKG

kamiswiss-ccxt

Version:

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

596 lines (571 loc) 19.4 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require('./base/Exchange'); // --------------------------------------------------------------------------- module.exports = class nokuexchange extends Exchange { describe () { return this.deepExtend(super.describe(), { 'id': 'nokuexchange', 'name': 'NokuExchange', 'countries': 'CH', 'enableRateLimit': true, 'rateLimit': 2000, // milliseconds = seconds * 1000 'certified': false, 'has': { 'cancelAllOrders': false, 'cancelOrder': true, 'cancelOrders': false, 'CORS': false, 'createDepositAddress': true, 'createLimitOrder': false, 'createMarketOrder': false, 'createOrder': true, 'deposit': false, 'editOrder': false, 'fetchBalance': true, 'fetchBidsAsks': false, 'fetchClosedOrders': false, 'fetchCurrencies': false, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchFundingFees': false, 'fetchL2OrderBook': false, 'fetchLedger': false, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': false, 'fetchOpenOrders': false, 'fetchOrder': false, 'fetchOrderBook': true, 'fetchOrderBooks': false, 'fetchOrders': true, 'fetchTicker': false, 'fetchTickers': false, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': false, 'fetchTradingLimits': false, 'fetchTransactions': false, 'fetchWithdrawals': true, 'privateAPI': true, 'publicAPI': true, 'withdraw': true, }, 'urls': { 'api': 'https://api.noku.exchange', 'www': 'https://noku.exchange', 'doc': 'https://docapi.noku.exchange', }, 'api': { 'public': { 'get': [ 'data/instruments', 'data/assets', 'data/rates', ], 'post': [ 'data/depth', 'data/history', 'data/candles', ] }, 'private': { 'get': [ 'user/limits', 'user/type', 'user/balance', ], 'post': [ 'exchange/order', 'exchange/transactions', 'wallet/depositAddress', 'wallet/ccxtdata', ], 'put': [ 'exchange/order', 'wallet/withdrawal', ], 'delete': [ 'exchange/order/{market}/{id}', ] } }, 'aliases': { 'eur': 'EUR', 'bitcoin': 'BTC', 'ethereum': 'ETH', 'zcash': 'ZEC', 'dash': 'DASH', 'litecoin': 'LTC', 'bitcoincash': 'BCH', 'pippofranchi': 'PHF', 'dogecoin': 'DOGE', 'eosio.token': 'EOS' }, 'assets': { 'EUR': 'eur', 'BTC': 'bitcoin', 'ETH': 'ethereum', 'ZEC': 'zcash', 'DASH': 'dash', 'LTC': 'litecoin', 'BCH': 'bitcoincash', 'PHF': 'pippofranchi', 'DOGE': 'dogecoin', 'EOS': 'eosio.token' }, 'markets': { 'BTCEUR': { 'id': 'btceur', 'symbol': 'BTC/EUR', 'base': 'BTC', 'quote': 'EUR' }, 'ETHEUR': { 'id': 'etheur', 'symbol': 'ETH/EUR', 'base': 'ETH', 'quote': 'EUR' }, 'ETHBTC': { 'id': 'ethbtc', 'symbol': 'ETH/BTC', 'base': 'ETH', 'quote': 'BTC' }, 'LTCBTC': { 'id': 'ltcbtc', 'symbol': 'LTC/BTC', 'base': 'LTC', 'quote': 'BTC' }, 'DASHBTC': { 'id': 'dashbtc', 'symbol': 'DASH/BTC', 'base': 'DASH', 'quote': 'BTC' }, 'ZECBTC': { 'id': 'zecbtc', 'symbol': 'ZEC/BTC', 'base': 'ZEC', 'quote': 'BTC' }, 'PHFETH': { 'id': 'phfeth', 'symbol': 'PHF/ETH', 'base': 'PHF', 'quote': 'ETH' } }, 'timeframes': { '1m': '1', '5m': '5', '10m': '10', '15m': '15', '30m': '30', '1h': '60', '2h': '120', '4h': '240', '1d': '1440', '2d': '2880' }, 'version': 'v1', 'requiredCredentials': { 'apiKey': false, 'secret': false, 'uid': false, 'login': false, 'password': false, 'twofa': false, // 2-factor authentication (one-time password key) 'privateKey': false, // a "0x"-prefixed hexstring private key for a wallet 'walletAddress': false, // the wallet address "0x"-prefixed hexstring 'token': true, // reserved for HTTP auth in some cases }, }) // return } // describe () async fetchMarkets () { const response = await this.publicGetDataInstruments(); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const markets = result.map(market => { const { ticker, stock, money, options } = market; return { info: market, id: ticker.toLowerCase(), symbol: this.markets[ticker].symbol, base: stock.alias, quote: money.alias, baseId: stock.alias.toLowerCase(), quoteId: money.alias.toLowerCase(), active: true, precision: { price: options.decimal_precision, amount: options.stock_precision, cost: 8 }, limits: { amount: { min: Math.pow(10, - options.min_amount_pow), max: Math.pow(10, options.max_amount_pow) }, price: { min: Math.pow(10, -8), max: Math.pow(10, 8) }, cost: { min: Math.pow(10, -8), max: Math.pow(10, 8) } } }; }); return markets; } async fetchDataAssets () { const response = await this.publicGetDataAssets(); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return result; } async fetchDataRates () { const response = await this.publicGetDataRates(); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return result; } async fetchOHLCV (symbol, resolution, since, limit, params = {}) { const request = this.extend({ symbol: symbol.replace('/', ''), resolution, since: Math.floor(since / 1000), limit }, params); const response = await this.publicPostDataCandles(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } let klines = result; if (!(klines instanceof Array)) { throw new Error('Invalid klines'); } return klines.map(kline => { if (!(kline instanceof Array)) { throw new Error('Invalid kline'); } else { kline[0] *= 1000 return kline.slice(0, 6).map(v => Number(v)); } }); } async fetchOrderBook (market, params = {}) { const request = this.extend({ market: market.replace('/', '') }, params); const response = await this.publicPostDataDepth(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return result; } async fetchTrades (market, params = {}) { const request = this.extend({ market: market.replace('/', '') }, params); const response = await this.publicPostDataHistory(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const trades = result.map(trade => { const { id, time, market, side, price, amount } = trade; return { info: trade, id, timestamp: Math.floor(time * 1000), datetime: new Date(time * 1000), symbol: this.markets[market].symbol, order: null, type: null, side, price: Number(price), amount: Number(amount) } }) return trades; } async fetchMyTrades (market, params = {}) { const request = this.extend({ market: market.replace('/', '') }, params); const response = await this.privatePostExchangeTransactions(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const trades = result.map(trade => { const { id, time, market, side, price, amount, role, fee, feeAsset } = trade; return { info: trade, id, timestamp: Math.floor(time * 1000), datetime: new Date(time * 1000), symbol: this.markets[market].symbol, order: null, type: null, side, takerOrMaker: role, price: Number(price), amount: Number(amount), cost: Number(price) * Number(amount), fee: { currency: feeAsset, cost: fee } }; }); return trades; } async fetchOrders (market, offset, limit, params = {}) { const request = this.extend({ market: market.replace('/', ''), offset, limit }, params); const response = await this.privatePostExchangeOrder(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const orders = result.map(order => { const { id, time, market, type, side, price, amount, left, dealFee, feeAsset:fAsset, dealFeeTh, feeAssetTh, feeAssetPrice } = order; const fee = dealFeeTh && dealFeeTh !== '0' ? dealFeeTh : dealFee, feeAsset = dealFeeTh && dealFeeTh !== '0' ? feeAssetTh : fAsset; const filled = Number(amount) - Number(left); return { info: order, id, timestamp: Math.floor(time * 1000), datetime: new Date(time * 1000), lastTradeTimestamp: null, status: 'open', symbol: this.markets[market].symbol, type, side, price: Number(price), amount: Number(amount), filled, remaining: Number(left), cost: Number(price) * Number(filled), trades: [], fee: { currency: feeAsset, cost: fee } }; }); return orders; } async createOrder (market, type, side, amount, price, params = {}) { const request = this.extend({ market: market.replace('/', ''), amount, price, side, type }, params); for (const param in request) { if (request[param] === null) { delete request[param]; } } const response = await this.privatePutExchangeOrder(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return { info: result, id: result.id }; } async cancelOrder (id, market) { const response = await this.privateDeleteExchangeOrderMarketId({ id, market: market.replace('/', '') }); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return { info: result, id: result.id }; } async fetchUserLimits () { const response = await this.privateGetUserLimits(); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return result; } async fetchUserType () { const response = await this.privateGetUserType(); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } return result; } async fetchBalance () { const response = await this.privateGetUserBalance(); const { error, refCode } = response; if (error) { return { info: response, id: refCode }; } const result = response.result.default.balance; const balance = { info: result, free: {}, used: {}, total: {} }; for (const asset in result) { const available = Number(result[asset].available); const freeze = Number(result[asset].freeze); const total = available + freeze; balance.free[asset] = available; balance.used[asset] = freeze; balance.total[asset] = total; balance[asset] = { free: available, used: freeze, total }; } return balance; } async createDepositAddress (asset, params = {}) { const request = this.extend({ asset: this.assets[asset] || asset }, params); const response = await this.privatePostWalletDepositAddress(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const { asset: currency, address } = result; return { currency, address, tag: null, info: result }; } fetchDepositAddress (asset, params = {}) { return this.createDepositAddress(asset, params); } async withdraw (asset, amount, address, params = {}) { const request = this.extend({ asset: this.assets[asset] || asset, amount, address }, params); const response = await this.privatePutWalletWithdrawal(request); const { refCode } = response; return { info: response, id: refCode }; } async fetchDeposits (action, params = {}) { const request = this.extend({ action, type: 'deposit' }, params); const response = await this.privatePostWalletCcxtdata(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const deposits = result.map(deposit => { const { txid, created, type, amount, asset } = deposit; return { info: deposit, id: null, txid, timestamp: created * 1000, datetime: null, address: null, type, amount: Number(amount), currency: this.aliases[asset] || asset, status: 'ok' }; }); return deposits; } async fetchWithdrawals (action, params = {}) { const request = this.extend({ action, type: 'withdrawal' }, params); const response = await this.privatePostWalletCcxtdata(request); const { error, refCode, result } = response; if (error) { return { info: response, id: refCode }; } const withdrawals = result.map(withdrawal => { const { txid, created, address, type, amount, asset } = withdrawal; return { info: withdrawal, id: null, txid, timestamp: created * 1000, datetime: null, address, type, amount: Number(amount), currency: this.aliases[asset] || asset, status: 'ok' }; }); return withdrawals; } sign (path, api = 'public', method = 'GET', params = {}, headers = {}, body) { let url = `${this.urls.api}/${path}`; if (/{.*}/.test(url)) { url = `${this.urls.api}/${this.url(path, params)}`; } if (['POST', 'PUT'].includes(method)) { if (Object.keys(params).length) { body = this.json(params); headers['Content-Type'] = 'application/json'; } } if (this.token) { headers['Authorization'] = `Bearer ${this.token}`; } return { url, method, body, headers }; } };