kamiswiss-ccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
283 lines (272 loc) • 10.8 kB
JavaScript
'use strict';
// ---------------------------------------------------------------------------
const liqui = require ('./liqui.js');
const { ExchangeError, InsufficientFunds, AuthenticationError } = require ('./base/errors');
// ---------------------------------------------------------------------------
module.exports = class yobit extends liqui {
describe () {
return this.deepExtend (super.describe (), {
'id': 'yobit',
'name': 'YoBit',
'countries': [ 'RU' ],
'rateLimit': 3000, // responses are cached every 2 seconds
'version': '3',
'has': {
'createDepositAddress': true,
'fetchDepositAddress': true,
'fetchDeposits': false,
'fetchWithdrawals': false,
'fetchTransactions': false,
'fetchTickers': false,
'CORS': false,
'withdraw': true,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/27766910-cdcbfdae-5eea-11e7-9859-03fea873272d.jpg',
'api': {
'public': 'https://yobit.net/api',
'private': 'https://yobit.net/tapi',
},
'www': 'https://www.yobit.net',
'doc': 'https://www.yobit.net/en/api/',
'fees': 'https://www.yobit.net/en/fees/',
},
'api': {
'public': {
'get': [
'depth/{pair}',
'info',
'ticker/{pair}',
'trades/{pair}',
],
},
'private': {
'post': [
'ActiveOrders',
'CancelOrder',
'GetDepositAddress',
'getInfo',
'OrderInfo',
'Trade',
'TradeHistory',
'WithdrawCoinsToAddress',
],
},
},
'fees': {
'trading': {
'maker': 0.002,
'taker': 0.002,
},
'funding': {
'withdraw': {},
},
},
'commonCurrencies': {
'AIR': 'AirCoin',
'ANI': 'ANICoin',
'ANT': 'AntsCoin', // what is this, a coin for ants?
'ATMCHA': 'ATM',
'ASN': 'Ascension',
'AST': 'Astral',
'ATM': 'Autumncoin',
'BCC': 'BCH',
'BCS': 'BitcoinStake',
'BLN': 'Bulleon',
'BOT': 'BOTcoin',
'BON': 'BONES',
'BPC': 'BitcoinPremium',
'BTS': 'Bitshares2',
'CAT': 'BitClave',
'CMT': 'CometCoin',
'COV': 'Coven Coin',
'COVX': 'COV',
'CPC': 'Capricoin',
'CS': 'CryptoSpots',
'DCT': 'Discount',
'DGD': 'DarkGoldCoin',
'DIRT': 'DIRTY',
'DROP': 'FaucetCoin',
'EKO': 'EkoCoin',
'ENTER': 'ENTRC',
'EPC': 'ExperienceCoin',
'ERT': 'Eristica Token',
'ESC': 'EdwardSnowden',
'EUROPE': 'EUROP',
'EXT': 'LifeExtension',
'FUNK': 'FUNKCoin',
'GCC': 'GlobalCryptocurrency',
'GEN': 'Genstake',
'GENE': 'Genesiscoin',
'GOLD': 'GoldMint',
'GOT': 'Giotto Coin',
'HTML5': 'HTML',
'HYPERX': 'HYPER',
'ICN': 'iCoin',
'INSANE': 'INSN',
'JNT': 'JointCoin',
'JPC': 'JupiterCoin',
'KNC': 'KingN Coin',
'LBTCX': 'LiteBitcoin',
'LIZI': 'LiZi',
'LOC': 'LocoCoin',
'LOCX': 'LOC',
'LUNYR': 'LUN',
'LUN': 'LunarCoin', // they just change the ticker if it is already taken
'MDT': 'Midnight',
'NAV': 'NavajoCoin',
'NBT': 'NiceBytes',
'OMG': 'OMGame',
'PAC': '$PAC',
'PLAY': 'PlayCoin',
'PIVX': 'Darknet',
'PRS': 'PRE',
'PUTIN': 'PUT',
'STK': 'StakeCoin',
'SUB': 'Subscriptio',
'PAY': 'EPAY',
'PLC': 'Platin Coin',
'RCN': 'RCoin',
'REP': 'Republicoin',
'RUR': 'RUB',
'XIN': 'XINCoin',
},
'options': {
'fetchOrdersRequiresSymbol': true,
'fetchTickersMaxLength': 512,
},
'exceptions': {
'broad': {
'Total transaction amount': ExchangeError, // { "success": 0, "error": "Total transaction amount is less than minimal total: 0.00010000"}
'Insufficient funds': InsufficientFunds,
'invalid key': AuthenticationError,
},
},
});
}
parseOrderStatus (status) {
const statuses = {
'0': 'open',
'1': 'closed',
'2': 'canceled',
'3': 'open', // or partially-filled and closed? https://github.com/ccxt/ccxt/issues/1594
};
return this.safeString (statuses, status, status);
}
async fetchBalance (params = {}) {
await this.loadMarkets ();
const response = await this.privatePostGetInfo (params);
const balances = response['return'];
const result = { 'info': balances };
const sides = { 'free': 'funds', 'total': 'funds_incl_orders' };
const keys = Object.keys (sides);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const side = sides[key];
if (side in balances) {
const currencies = Object.keys (balances[side]);
for (let j = 0; j < currencies.length; j++) {
const lowercase = currencies[j];
const uppercase = lowercase.toUpperCase ();
const currency = this.commonCurrencyCode (uppercase);
let account = undefined;
if (currency in result) {
account = result[currency];
} else {
account = this.account ();
}
account[key] = balances[side][lowercase];
if ((account['total'] !== undefined) && (account['free'] !== undefined)) {
account['used'] = account['total'] - account['free'];
}
result[currency] = account;
}
}
}
return this.parseBalance (result);
}
async createDepositAddress (code, params = {}) {
const request = {
'need_new': 1,
};
const response = await this.fetchDepositAddress (code, this.extend (request, params));
const address = this.safeString (response, 'address');
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': undefined,
'info': response['info'],
};
}
async fetchDepositAddress (code, params = {}) {
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coinName': currency['id'],
'need_new': 0,
};
const response = await this.privatePostGetDepositAddress (this.extend (request, params));
const address = this.safeString (response['return'], 'address');
this.checkAddress (address);
return {
'currency': code,
'address': address,
'tag': undefined,
'info': response,
};
}
async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets ();
let market = undefined;
// some derived classes use camelcase notation for request fields
const request = {
// 'from': 123456789, // trade ID, from which the display starts numerical 0 (test result: liqui ignores this field)
// 'count': 1000, // the number of trades for display numerical, default = 1000
// 'from_id': trade ID, from which the display starts numerical 0
// 'end_id': trade ID on which the display ends numerical ∞
// 'order': 'ASC', // sorting, default = DESC (test result: liqui ignores this field, most recent trade always goes last)
// 'since': 1234567890, // UTC start time, default = 0 (test result: liqui ignores this field)
// 'end': 1234567890, // UTC end time, default = ∞ (test result: liqui ignores this field)
// 'pair': 'eth_btc', // default = all markets
};
if (symbol !== undefined) {
market = this.market (symbol);
request['pair'] = market['id'];
}
if (limit !== undefined) {
request['count'] = parseInt (limit);
}
if (since !== undefined) {
request['since'] = parseInt (since / 1000);
}
const method = this.options['fetchMyTradesMethod'];
const response = await this[method] (this.extend (request, params));
const trades = this.safeValue (response, 'return', {});
const ids = Object.keys (trades);
const result = [];
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
const trade = this.parseTrade (this.extend (trades[id], {
'trade_id': id,
}), market);
result.push (trade);
}
return this.filterBySymbolSinceLimit (result, symbol, since, limit);
}
async withdraw (code, amount, address, tag = undefined, params = {}) {
this.checkAddress (address);
await this.loadMarkets ();
const currency = this.currency (code);
const request = {
'coinName': currency['id'],
'amount': amount,
'address': address,
};
const response = await this.privatePostWithdrawCoinsToAddress (this.extend (request, params));
return {
'info': response,
'id': undefined,
};
}
};