kamiswiss-ccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
596 lines (571 loc) • 19.4 kB
JavaScript
'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 };
}
};