UNPKG

consequunturatque

Version:

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

1,202 lines (1,178 loc) 53 kB
'use strict'; // --------------------------------------------------------------------------- const Exchange = require ('./base/Exchange'); const { InvalidNonce, InsufficientFunds, AuthenticationError, InvalidOrder, ExchangeError, OrderNotFound, AccountSuspended, BadSymbol, OrderImmediatelyFillable, RateLimitExceeded, OnMaintenance, PermissionDenied } = require ('./base/errors'); const Precise = require ('./base/Precise'); // --------------------------------------------------------------------------- module.exports = class bitbay extends Exchange { describe () { return this.deepExtend (super.describe (), { 'id': 'bitbay', 'name': 'BitBay', 'countries': [ 'MT', 'EU' ], // Malta 'rateLimit': 1000, 'has': { 'cancelOrder': true, 'CORS': true, 'createOrder': true, 'fetchBalance': true, 'fetchLedger': true, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrderBook': true, 'fetchTicker': true, 'fetchTrades': true, 'withdraw': true, }, 'timeframes': { '1m': '60', '3m': '180', '5m': '300', '15m': '900', '30m': '1800', '1h': '3600', '2h': '7200', '4h': '14400', '6h': '21600', '12h': '43200', '1d': '86400', '3d': '259200', '1w': '604800', }, 'hostname': 'bitbay.net', 'urls': { 'referral': 'https://auth.bitbay.net/ref/jHlbB4mIkdS1', 'logo': 'https://user-images.githubusercontent.com/1294454/27766132-978a7bd8-5ece-11e7-9540-bc96d1e9bbb8.jpg', 'www': 'https://bitbay.net', 'api': { 'public': 'https://{hostname}/API/Public', 'private': 'https://{hostname}/API/Trading/tradingApi.php', 'v1_01Public': 'https://api.{hostname}/rest', 'v1_01Private': 'https://api.{hostname}/rest', }, 'doc': [ 'https://bitbay.net/public-api', 'https://bitbay.net/en/private-api', 'https://bitbay.net/account/tab-api', 'https://github.com/BitBayNet/API', 'https://docs.bitbay.net/v1.0.1-en/reference', ], 'support': 'https://support.bitbay.net', 'fees': 'https://bitbay.net/en/fees', }, 'api': { 'public': { 'get': [ '{id}/all', '{id}/market', '{id}/orderbook', '{id}/ticker', '{id}/trades', ], }, 'private': { 'post': [ 'info', 'trade', 'cancel', 'orderbook', 'orders', 'transfer', 'withdraw', 'history', 'transactions', ], }, 'v1_01Public': { 'get': [ 'trading/ticker', 'trading/ticker/{symbol}', 'trading/stats', 'trading/orderbook/{symbol}', 'trading/transactions/{symbol}', 'trading/candle/history/{symbol}/{resolution}', ], }, 'v1_01Private': { 'get': [ 'payments/withdrawal/{detailId}', 'payments/deposit/{detailId}', 'trading/offer', 'trading/config/{symbol}', 'trading/history/transactions', 'balances/BITBAY/history', 'balances/BITBAY/balance', 'fiat_cantor/rate/{baseId}/{quoteId}', 'fiat_cantor/history', ], 'post': [ 'trading/offer/{symbol}', 'trading/config/{symbol}', 'balances/BITBAY/balance', 'balances/BITBAY/balance/transfer/{source}/{destination}', 'fiat_cantor/exchange', ], 'delete': [ 'trading/offer/{symbol}/{id}/{side}/{price}', ], 'put': [ 'balances/BITBAY/balance/{id}', ], }, }, 'fees': { 'trading': { 'maker': 0.0, 'taker': 0.1 / 100, 'percentage': true, 'tierBased': false, }, 'fiat': { 'maker': 0.30 / 100, 'taker': 0.43 / 100, 'percentage': true, 'tierBased': true, 'tiers': { 'taker': [ [ 0.0043, 0 ], [ 0.0042, 1250 ], [ 0.0041, 3750 ], [ 0.0040, 7500 ], [ 0.0039, 10000 ], [ 0.0038, 15000 ], [ 0.0037, 20000 ], [ 0.0036, 25000 ], [ 0.0035, 37500 ], [ 0.0034, 50000 ], [ 0.0033, 75000 ], [ 0.0032, 100000 ], [ 0.0031, 150000 ], [ 0.0030, 200000 ], [ 0.0029, 250000 ], [ 0.0028, 375000 ], [ 0.0027, 500000 ], [ 0.0026, 625000 ], [ 0.0025, 875000 ], ], 'maker': [ [ 0.0030, 0 ], [ 0.0029, 1250 ], [ 0.0028, 3750 ], [ 0.0028, 7500 ], [ 0.0027, 10000 ], [ 0.0026, 15000 ], [ 0.0025, 20000 ], [ 0.0025, 25000 ], [ 0.0024, 37500 ], [ 0.0023, 50000 ], [ 0.0023, 75000 ], [ 0.0022, 100000 ], [ 0.0021, 150000 ], [ 0.0021, 200000 ], [ 0.0020, 250000 ], [ 0.0019, 375000 ], [ 0.0018, 500000 ], [ 0.0018, 625000 ], [ 0.0017, 875000 ], ], }, }, 'funding': { 'withdraw': { 'BTC': 0.0009, 'LTC': 0.005, 'ETH': 0.00126, 'LSK': 0.2, 'BCH': 0.0006, 'GAME': 0.005, 'DASH': 0.001, 'BTG': 0.0008, 'PLN': 4, 'EUR': 1.5, }, }, }, 'options': { 'fiatCurrencies': [ 'EUR', 'USD', 'GBP', 'PLN' ], }, 'exceptions': { '400': ExchangeError, // At least one parameter wasn't set '401': InvalidOrder, // Invalid order type '402': InvalidOrder, // No orders with specified currencies '403': InvalidOrder, // Invalid payment currency name '404': InvalidOrder, // Error. Wrong transaction type '405': InvalidOrder, // Order with this id doesn't exist '406': InsufficientFunds, // No enough money or crypto // code 407 not specified are not specified in their docs '408': InvalidOrder, // Invalid currency name '501': AuthenticationError, // Invalid public key '502': AuthenticationError, // Invalid sign '503': InvalidNonce, // Invalid moment parameter. Request time doesn't match current server time '504': ExchangeError, // Invalid method '505': AuthenticationError, // Key has no permission for this action '506': AccountSuspended, // Account locked. Please contact with customer service // codes 507 and 508 are not specified in their docs '509': ExchangeError, // The BIC/SWIFT is required for this currency '510': BadSymbol, // Invalid market name 'FUNDS_NOT_SUFFICIENT': InsufficientFunds, 'OFFER_FUNDS_NOT_EXCEEDING_MINIMUMS': InvalidOrder, 'OFFER_NOT_FOUND': OrderNotFound, 'OFFER_WOULD_HAVE_BEEN_PARTIALLY_FILLED': OrderImmediatelyFillable, 'ACTION_LIMIT_EXCEEDED': RateLimitExceeded, 'UNDER_MAINTENANCE': OnMaintenance, 'REQUEST_TIMESTAMP_TOO_OLD': InvalidNonce, 'PERMISSIONS_NOT_SUFFICIENT': PermissionDenied, }, 'commonCurrencies': { 'GGC': 'Global Game Coin', }, }); } async fetchMarkets (params = {}) { const response = await this.v1_01PublicGetTradingTicker (params); const fiatCurrencies = this.safeValue (this.options, 'fiatCurrencies', []); // // { // status: 'Ok', // items: { // 'BSV-USD': { // market: { // code: 'BSV-USD', // first: { currency: 'BSV', minOffer: '0.00035', scale: 8 }, // second: { currency: 'USD', minOffer: '5', scale: 2 } // }, // time: '1557569762154', // highestBid: '52.31', // lowestAsk: '62.99', // rate: '63', // previousRate: '51.21', // }, // }, // } // const result = []; const items = this.safeValue (response, 'items'); const keys = Object.keys (items); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const item = items[key]; const market = this.safeValue (item, 'market', {}); const first = this.safeValue (market, 'first', {}); const second = this.safeValue (market, 'second', {}); const baseId = this.safeString (first, 'currency'); const quoteId = this.safeString (second, 'currency'); const id = baseId + quoteId; const base = this.safeCurrencyCode (baseId); const quote = this.safeCurrencyCode (quoteId); const symbol = base + '/' + quote; const precision = { 'amount': this.safeInteger (first, 'scale'), 'price': this.safeInteger (second, 'scale'), }; let fees = this.safeValue (this.fees, 'trading', {}); if (this.inArray (base, fiatCurrencies) || this.inArray (quote, fiatCurrencies)) { fees = this.safeValue (this.fees, 'fiat', {}); } const maker = this.safeNumber (fees, 'maker'); const taker = this.safeNumber (fees, 'taker'); // todo: check that the limits have ben interpreted correctly // todo: parse the fees page result.push ({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'baseId': baseId, 'quoteId': quoteId, 'precision': precision, 'active': undefined, 'maker': maker, 'taker': taker, 'limits': { 'amount': { 'min': this.safeNumber (first, 'minOffer'), 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber (second, 'minOffer'), 'max': undefined, }, }, 'info': item, }); } return result; } async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; const response = await this.v1_01PrivateGetTradingOffer (this.extend (request, params)); const items = this.safeValue (response, 'items', []); return this.parseOrders (items, undefined, since, limit, { 'status': 'open' }); } parseOrder (order, market = undefined) { // // { // market: 'ETH-EUR', // offerType: 'Sell', // id: '93d3657b-d616-11e9-9248-0242ac110005', // currentAmount: '0.04', // lockedAmount: '0.04', // rate: '280', // startAmount: '0.04', // time: '1568372806924', // postOnly: false, // hidden: false, // mode: 'limit', // receivedAmount: '0.0', // firstBalanceId: '5b816c3e-437c-4e43-9bef-47814ae7ebfc', // secondBalanceId: 'ab43023b-4079-414c-b340-056e3430a3af' // } // const marketId = this.safeString (order, 'market'); const symbol = this.safeSymbol (marketId, market, '-'); const timestamp = this.safeInteger (order, 'time'); const amount = this.safeNumber (order, 'startAmount'); const remaining = this.safeNumber (order, 'currentAmount'); const postOnly = this.safeValue (order, 'postOnly'); return this.safeOrder ({ 'id': this.safeString (order, 'id'), 'clientOrderId': undefined, 'info': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': undefined, 'symbol': symbol, 'type': this.safeString (order, 'mode'), 'timeInForce': undefined, 'postOnly': postOnly, 'side': this.safeStringLower (order, 'offerType'), 'price': this.safeNumber (order, 'rate'), 'stopPrice': undefined, 'amount': amount, 'cost': undefined, 'filled': undefined, 'remaining': remaining, 'average': undefined, 'fee': undefined, 'trades': undefined, }); } async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const request = {}; if (symbol) { const markets = [ this.marketId (symbol) ]; request['markets'] = markets; } const query = { 'query': this.json (this.extend (request, params)) }; const response = await this.v1_01PrivateGetTradingHistoryTransactions (query); // // { // status: 'Ok', // totalRows: '67', // items: [ // { // id: 'b54659a0-51b5-42a0-80eb-2ac5357ccee2', // market: 'BTC-EUR', // time: '1541697096247', // amount: '0.00003', // rate: '4341.44', // initializedBy: 'Sell', // wasTaker: false, // userAction: 'Buy', // offerId: 'bd19804a-6f89-4a69-adb8-eb078900d006', // commissionValue: null // }, // ] // } // const items = this.safeValue (response, 'items'); const result = this.parseTrades (items, undefined, since, limit); if (symbol === undefined) { return result; } return this.filterBySymbol (result, symbol); } async fetchBalance (params = {}) { await this.loadMarkets (); const response = await this.v1_01PrivateGetBalancesBITBAYBalance (params); const balances = this.safeValue (response, 'balances'); if (balances === undefined) { throw new ExchangeError (this.id + ' empty balance response ' + this.json (response)); } const result = { 'info': response }; for (let i = 0; i < balances.length; i++) { const balance = balances[i]; const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const account = this.account (); account['used'] = this.safeString (balance, 'lockedFunds'); account['free'] = this.safeString (balance, 'availableFunds'); result[code] = account; } return this.parseBalance (result, false); } async fetchOrderBook (symbol, limit = undefined, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const orderbook = await this.publicGetIdOrderbook (this.extend (request, params)); return this.parseOrderBook (orderbook, symbol); } async fetchTicker (symbol, params = {}) { await this.loadMarkets (); const request = { 'id': this.marketId (symbol), }; const ticker = await this.publicGetIdTicker (this.extend (request, params)); const timestamp = this.milliseconds (); const baseVolume = this.safeNumber (ticker, 'volume'); const vwap = this.safeNumber (ticker, 'vwap'); let quoteVolume = undefined; if (baseVolume !== undefined && vwap !== undefined) { quoteVolume = baseVolume * vwap; } const last = this.safeNumber (ticker, 'last'); return { 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'high': this.safeNumber (ticker, 'max'), 'low': this.safeNumber (ticker, 'min'), 'bid': this.safeNumber (ticker, 'bid'), 'bidVolume': undefined, 'ask': this.safeNumber (ticker, 'ask'), 'askVolume': undefined, 'vwap': vwap, 'open': undefined, 'close': last, 'last': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': this.safeNumber (ticker, 'average'), 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }; } async fetchLedger (code = undefined, since = undefined, limit = undefined, params = {}) { const balanceCurrencies = []; if (code !== undefined) { const currency = this.currency (code); balanceCurrencies.push (currency['id']); } let request = { 'balanceCurrencies': balanceCurrencies, }; if (since !== undefined) { request['fromTime'] = since; } if (limit !== undefined) { request['limit'] = limit; } request = this.extend (request, params); const response = await this.v1_01PrivateGetBalancesBITBAYHistory ({ 'query': this.json (request) }); const items = response['items']; return this.parseLedger (items, undefined, since, limit); } parseLedgerEntry (item, currency = undefined) { // // FUNDS_MIGRATION // { // "historyId": "84ea7a29-7da5-4de5-b0c0-871e83cad765", // "balance": { // "id": "821ec166-cb88-4521-916c-f4eb44db98df", // "currency": "LTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "LTC" // }, // "detailId": null, // "time": 1506128252968, // "type": "FUNDS_MIGRATION", // "value": 0.0009957, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 0.0009957, "available": 0.0009957, "locked": 0 }, // "change": { "total": 0.0009957, "available": 0.0009957, "locked": 0 } // } // // CREATE_BALANCE // { // "historyId": "d0fabd8d-9107-4b5e-b9a6-3cab8af70d49", // "balance": { // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", // "currency": "BTG", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTG" // }, // "detailId": null, // "time": 1508895244751, // "type": "CREATE_BALANCE", // "value": 0, // "fundsBefore": { "total": null, "available": null, "locked": null }, // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, // "change": { "total": 0, "available": 0, "locked": 0 } // } // // BITCOIN_GOLD_FORK // { // "historyId": "2b4d52d3-611c-473d-b92c-8a8d87a24e41", // "balance": { // "id": "653ffcf2-3037-4ebe-8e13-d5ea1a01d60d", // "currency": "BTG", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTG" // }, // "detailId": null, // "time": 1508895244778, // "type": "BITCOIN_GOLD_FORK", // "value": 0.00453512, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, // "change": { "total": 0.00453512, "available": 0.00453512, "locked": 0 } // } // // ADD_FUNDS // { // "historyId": "3158236d-dae5-4a5d-81af-c1fa4af340fb", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "8e83a960-e737-4380-b8bb-259d6e236faa", // "time": 1520631178816, // "type": "ADD_FUNDS", // "value": 0.628405, // "fundsBefore": { "total": 0.00453512, "available": 0.00453512, "locked": 0 }, // "fundsAfter": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, // "change": { "total": 0.628405, "available": 0.628405, "locked": 0 } // } // // TRANSACTION_PRE_LOCKING // { // "historyId": "e7d19e0f-03b3-46a8-bc72-dde72cc24ead", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": null, // "time": 1520706403868, // "type": "TRANSACTION_PRE_LOCKING", // "value": -0.1, // "fundsBefore": { "total": 0.63294012, "available": 0.63294012, "locked": 0 }, // "fundsAfter": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, // "change": { "total": 0, "available": -0.1, "locked": 0.1 } // } // // TRANSACTION_POST_OUTCOME // { // "historyId": "c4010825-231d-4a9c-8e46-37cde1f7b63c", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "bf2876bc-b545-4503-96c8-ef4de8233876", // "time": 1520706404032, // "type": "TRANSACTION_POST_OUTCOME", // "value": -0.01771415, // "fundsBefore": { "total": 0.63294012, "available": 0.53294012, "locked": 0.1 }, // "fundsAfter": { "total": 0.61522597, "available": 0.53294012, "locked": 0.08228585 }, // "change": { "total": -0.01771415, "available": 0, "locked": -0.01771415 } // } // // TRANSACTION_POST_INCOME // { // "historyId": "7f18b7af-b676-4125-84fd-042e683046f6", // "balance": { // "id": "ab43023b-4079-414c-b340-056e3430a3af", // "currency": "EUR", // "type": "FIAT", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "EUR" // }, // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", // "time": 1520706404035, // "type": "TRANSACTION_POST_INCOME", // "value": 628.78, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 628.78, "available": 628.78, "locked": 0 }, // "change": { "total": 628.78, "available": 628.78, "locked": 0 } // } // // TRANSACTION_COMMISSION_OUTCOME // { // "historyId": "843177fa-61bc-4cbf-8be5-b029d856c93b", // "balance": { // "id": "ab43023b-4079-414c-b340-056e3430a3af", // "currency": "EUR", // "type": "FIAT", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "EUR" // }, // "detailId": "f5fcb274-0cc7-4385-b2d3-bae2756e701f", // "time": 1520706404050, // "type": "TRANSACTION_COMMISSION_OUTCOME", // "value": -2.71, // "fundsBefore": { "total": 766.06, "available": 766.06, "locked": 0 }, // "fundsAfter": { "total": 763.35,"available": 763.35, "locked": 0 }, // "change": { "total": -2.71, "available": -2.71, "locked": 0 } // } // // TRANSACTION_OFFER_COMPLETED_RETURN // { // "historyId": "cac69b04-c518-4dc5-9d86-e76e91f2e1d2", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": null, // "time": 1520714886425, // "type": "TRANSACTION_OFFER_COMPLETED_RETURN", // "value": 0.00000196, // "fundsBefore": { "total": 0.00941208, "available": 0.00941012, "locked": 0.00000196 }, // "fundsAfter": { "total": 0.00941208, "available": 0.00941208, "locked": 0 }, // "change": { "total": 0, "available": 0.00000196, "locked": -0.00000196 } // } // // WITHDRAWAL_LOCK_FUNDS // { // "historyId": "03de2271-66ab-4960-a786-87ab9551fc14", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", // "time": 1522245654481, // "type": "WITHDRAWAL_LOCK_FUNDS", // "value": -0.8, // "fundsBefore": { "total": 0.8, "available": 0.8, "locked": 0 }, // "fundsAfter": { "total": 0.8, "available": 0, "locked": 0.8 }, // "change": { "total": 0, "available": -0.8, "locked": 0.8 } // } // // WITHDRAWAL_SUBTRACT_FUNDS // { // "historyId": "b0308c89-5288-438d-a306-c6448b1a266d", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "6ad3dc72-1d6d-4ec2-8436-ca43f85a38a6", // "time": 1522246526186, // "type": "WITHDRAWAL_SUBTRACT_FUNDS", // "value": -0.8, // "fundsBefore": { "total": 0.8, "available": 0, "locked": 0.8 }, // "fundsAfter": { "total": 0, "available": 0, "locked": 0 }, // "change": { "total": -0.8, "available": 0, "locked": -0.8 } // } // // TRANSACTION_OFFER_ABORTED_RETURN // { // "historyId": "b1a3c075-d403-4e05-8f32-40512cdd88c0", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": null, // "time": 1522512298662, // "type": "TRANSACTION_OFFER_ABORTED_RETURN", // "value": 0.0564931, // "fundsBefore": { "total": 0.44951311, "available": 0.39302001, "locked": 0.0564931 }, // "fundsAfter": { "total": 0.44951311, "available": 0.44951311, "locked": 0 }, // "change": { "total": 0, "available": 0.0564931, "locked": -0.0564931 } // } // // WITHDRAWAL_UNLOCK_FUNDS // { // "historyId": "0ed569a2-c330-482e-bb89-4cb553fb5b11", // "balance": { // "id": "3a7e7a1e-0324-49d5-8f59-298505ebd6c7", // "currency": "BTC", // "type": "CRYPTO", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "BTC" // }, // "detailId": "0c7be256-c336-4111-bee7-4eb22e339700", // "time": 1527866360785, // "type": "WITHDRAWAL_UNLOCK_FUNDS", // "value": 0.05045, // "fundsBefore": { "total": 0.86001578, "available": 0.80956578, "locked": 0.05045 }, // "fundsAfter": { "total": 0.86001578, "available": 0.86001578, "locked": 0 }, // "change": { "total": 0, "available": 0.05045, "locked": -0.05045 } // } // // TRANSACTION_COMMISSION_RETURN // { // "historyId": "07c89c27-46f1-4d7a-8518-b73798bf168a", // "balance": { // "id": "ab43023b-4079-414c-b340-056e3430a3af", // "currency": "EUR", // "type": "FIAT", // "userId": "a34d361d-7bad-49c1-888e-62473b75d877", // "name": "EUR" // }, // "detailId": null, // "time": 1528304043063, // "type": "TRANSACTION_COMMISSION_RETURN", // "value": 0.6, // "fundsBefore": { "total": 0, "available": 0, "locked": 0 }, // "fundsAfter": { "total": 0.6, "available": 0.6, "locked": 0 }, // "change": { "total": 0.6, "available": 0.6, "locked": 0 } // } // const timestamp = this.safeInteger (item, 'time'); const balance = this.safeValue (item, 'balance', {}); const currencyId = this.safeString (balance, 'currency'); const code = this.safeCurrencyCode (currencyId); const change = this.safeValue (item, 'change', {}); let amount = this.safeNumber (change, 'total'); let direction = 'in'; if (amount < 0) { direction = 'out'; amount = -amount; } const id = this.safeString (item, 'historyId'); // there are 2 undocumented api calls: (v1_01PrivateGetPaymentsDepositDetailId and v1_01PrivateGetPaymentsWithdrawalDetailId) // that can be used to enrich the transfers with txid, address etc (you need to use info.detailId as a parameter) const referenceId = this.safeString (item, 'detailId'); const type = this.parseLedgerEntryType (this.safeString (item, 'type')); const fundsBefore = this.safeValue (item, 'fundsBefore', {}); const before = this.safeNumber (fundsBefore, 'total'); const fundsAfter = this.safeValue (item, 'fundsAfter', {}); const after = this.safeNumber (fundsAfter, 'total'); return { 'info': item, 'id': id, 'direction': direction, 'account': undefined, 'referenceId': referenceId, 'referenceAccount': undefined, 'type': type, 'currency': code, 'amount': amount, 'before': before, 'after': after, 'status': 'ok', 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'fee': undefined, }; } parseLedgerEntryType (type) { const types = { 'ADD_FUNDS': 'transaction', 'BITCOIN_GOLD_FORK': 'transaction', 'CREATE_BALANCE': 'transaction', 'FUNDS_MIGRATION': 'transaction', 'WITHDRAWAL_LOCK_FUNDS': 'transaction', 'WITHDRAWAL_SUBTRACT_FUNDS': 'transaction', 'WITHDRAWAL_UNLOCK_FUNDS': 'transaction', 'TRANSACTION_COMMISSION_OUTCOME': 'fee', 'TRANSACTION_COMMISSION_RETURN': 'fee', 'TRANSACTION_OFFER_ABORTED_RETURN': 'trade', 'TRANSACTION_OFFER_COMPLETED_RETURN': 'trade', 'TRANSACTION_POST_INCOME': 'trade', 'TRANSACTION_POST_OUTCOME': 'trade', 'TRANSACTION_PRE_LOCKING': 'trade', }; return this.safeString (types, type, type); } parseOHLCV (ohlcv, market = undefined) { // // [ // '1582399800000', // { // o: '0.0001428', // c: '0.0001428', // h: '0.0001428', // l: '0.0001428', // v: '4', // co: '1' // } // ] // const first = this.safeValue (ohlcv, 1, {}); return [ this.safeInteger (ohlcv, 0), this.safeNumber (first, 'o'), this.safeNumber (first, 'h'), this.safeNumber (first, 'l'), this.safeNumber (first, 'c'), this.safeNumber (first, 'v'), ]; } async fetchOHLCV (symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, 'resolution': this.timeframes[timeframe], // 'from': 1574709092000, // unix timestamp in milliseconds, required // 'to': 1574709092000, // unix timestamp in milliseconds, required }; if (limit === undefined) { limit = 100; } const duration = this.parseTimeframe (timeframe); const timerange = limit * duration * 1000; if (since === undefined) { request['to'] = this.milliseconds (); request['from'] = request['to'] - timerange; } else { request['from'] = parseInt (since); request['to'] = this.sum (request['from'], timerange); } const response = await this.v1_01PublicGetTradingCandleHistorySymbolResolution (this.extend (request, params)); // // { // "status":"Ok", // "items":[ // ["1591503060000",{"o":"0.02509572","c":"0.02509438","h":"0.02509664","l":"0.02509438","v":"0.02082165","co":"17"}], // ["1591503120000",{"o":"0.02509606","c":"0.02509515","h":"0.02509606","l":"0.02509487","v":"0.04971703","co":"13"}], // ["1591503180000",{"o":"0.02509532","c":"0.02509589","h":"0.02509589","l":"0.02509454","v":"0.01332236","co":"7"}], // ] // } // const items = this.safeValue (response, 'items', []); return this.parseOHLCVs (items, market, timeframe, since, limit); } parseTrade (trade, market = undefined) { // // createOrder trades // // { // "rate": "0.02195928", // "amount": "0.00167952" // } // // fetchMyTrades (private) // // { // amount: "0.29285199", // commissionValue: "0.00125927", // id: "11c8203a-a267-11e9-b698-0242ac110007", // initializedBy: "Buy", // market: "ETH-EUR", // offerId: "11c82038-a267-11e9-b698-0242ac110007", // rate: "277", // time: "1562689917517", // userAction: "Buy", // wasTaker: true, // } // // fetchTrades (public) // // { // id: 'df00b0da-e5e0-11e9-8c19-0242ac11000a', // t: '1570108958831', // a: '0.04776653', // r: '0.02145854', // ty: 'Sell' // } // const timestamp = this.safeInteger2 (trade, 'time', 't'); const userAction = this.safeString (trade, 'userAction'); const side = (userAction === 'Buy') ? 'buy' : 'sell'; const wasTaker = this.safeValue (trade, 'wasTaker'); let takerOrMaker = undefined; if (wasTaker !== undefined) { takerOrMaker = wasTaker ? 'taker' : 'maker'; } const priceString = this.safeString2 (trade, 'rate', 'r'); const amountString = this.safeString2 (trade, 'amount', 'a'); const price = this.parseNumber (priceString); const amount = this.parseNumber (amountString); const cost = this.parseNumber (Precise.stringMul (priceString, amountString)); const feeCost = this.safeNumber (trade, 'commissionValue'); const marketId = this.safeString (trade, 'market'); market = this.safeMarket (marketId, market, '-'); const symbol = market['symbol']; let fee = undefined; if (feeCost !== undefined) { const feeCcy = (side === 'buy') ? market['base'] : market['quote']; fee = { 'currency': feeCcy, 'cost': feeCost, }; } const order = this.safeString (trade, 'offerId'); // todo: check this logic let type = undefined; if (order !== undefined) { type = order ? 'limit' : 'market'; } return { 'id': this.safeString (trade, 'id'), 'order': order, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'takerOrMaker': takerOrMaker, 'fee': fee, 'info': trade, }; } async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, }; if (since !== undefined) { request['fromTime'] = since - 1; // result does not include exactly `since` time therefore decrease by 1 } if (limit !== undefined) { request['limit'] = limit; // default - 10, max - 300 } const response = await this.v1_01PublicGetTradingTransactionsSymbol (this.extend (request, params)); const items = this.safeValue (response, 'items'); return this.parseTrades (items, market, since, limit); } async createOrder (symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, 'offerType': side, 'amount': amount, 'mode': type, }; if (type === 'limit') { request['rate'] = price; price = parseFloat (price); } amount = parseFloat (amount); const response = await this.v1_01PrivatePostTradingOfferSymbol (this.extend (request, params)); // // unfilled (open order) // // { // status: 'Ok', // completed: false, // can deduce status from here // offerId: 'ce9cc72e-d61c-11e9-9248-0242ac110005', // transactions: [], // can deduce order info from here // } // // filled (closed order) // // { // "status": "Ok", // "offerId": "942a4a3e-e922-11e9-8c19-0242ac11000a", // "completed": true, // "transactions": [ // { // "rate": "0.02195928", // "amount": "0.00167952" // }, // { // "rate": "0.02195928", // "amount": "0.00167952" // }, // { // "rate": "0.02196207", // "amount": "0.27704177" // } // ] // } // // partially-filled (open order) // // { // "status": "Ok", // "offerId": "d0ebefab-f4d7-11e9-8c19-0242ac11000a", // "completed": false, // "transactions": [ // { // "rate": "0.02106404", // "amount": "0.0019625" // }, // { // "rate": "0.02106404", // "amount": "0.0019625" // }, // { // "rate": "0.02105901", // "amount": "0.00975256" // } // ] // } // const timestamp = this.milliseconds (); // the real timestamp is missing in the response const id = this.safeString (response, 'offerId'); const completed = this.safeValue (response, 'completed', false); const status = completed ? 'closed' : 'open'; let filled = 0; let cost = undefined; const transactions = this.safeValue (response, 'transactions'); let trades = undefined; if (transactions !== undefined) { trades = this.parseTrades (transactions, market, undefined, undefined, { 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'symbol': symbol, 'side': side, 'type': type, 'orderId': id, }); cost = 0; for (let i = 0; i < trades.length; i++) { filled = this.sum (filled, trades[i]['amount']); cost = this.sum (cost, trades[i]['cost']); } } const remaining = amount - filled; return { 'id': id, 'info': response, 'timestamp': timestamp, 'datetime': this.iso8601 (timestamp), 'lastTradeTimestamp': undefined, 'status': status, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'filled': filled, 'remaining': remaining, 'average': undefined, 'fee': undefined, 'trades': trades, 'clientOrderId': undefined, }; } async cancelOrder (id, symbol = undefined, params = {}) { const side = this.safeString (params, 'side'); if (side === undefined) { throw new ExchangeError (this.id + ' cancelOrder() requires a `side` parameter ("buy" or "sell")'); } const price = this.safeValue (params, 'price'); if (price === undefined) { throw new ExchangeError (this.id + ' cancelOrder() requires a `price` parameter (float or string)'); } await this.loadMarkets (); const market = this.market (symbol); const tradingSymbol = market['baseId'] + '-' + market['quoteId']; const request = { 'symbol': tradingSymbol, 'id': id, 'side': side, 'price': price, }; // { status: 'Fail', errors: [ 'NOT_RECOGNIZED_OFFER_TYPE' ] } -- if required params are missing // { status: 'Ok', errors: [] } return this.v1_01PrivateDeleteTradingOfferSymbolIdSidePrice (this.extend (request, params)); } isFiat (currency) { const fiatCurrencies = { 'USD': true, 'EUR': true, 'PLN': true, }; return this.safeValue (fiatCurrencies, currency, false); } async withdraw (code, amount, address, tag = undefined, params = {}) { this.checkAddress (address); await this.loadMarkets (); let method = undefined; const currency = this.currency (code); const request = { 'currency': currency['id'], 'quantity': amount, }; if (this.isFiat (code)) { method = 'privatePostWithdraw'; // request['account'] = params['account']; // they demand an account number // request['express'] = params['express']; // whatever it means, they don't explain // request['bic'] = ''; } else { method = 'privatePostTransfer'; if (tag !== undefined) { address += '?dt=' + tag.toString (); } request['address'] = address; } const response = await this[method] (this.extend (request, params)); return { 'info': response, 'id': undefined, }; } sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) { let url = this.implodeParams (this.urls['api'][api], { 'hostname': this.hostname }); if (api === 'public') { const query = this.omit (params, this.extractParams (path)); url += '/' + this.implodeParams (path, params) + '.json'; if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'v1_01Public') { const query = this.omit (params, this.extractParams (path)); url += '/' + this.implodeParams (path, params); if (Object.keys (query).length) { url += '?' + this.urlencode (query); } } else if (api === 'v1_01Private') { this.checkRequiredCredentials (); const query = this.omit (params, this.extractParams (path)); url += '/' + this.implodeParams (path, params); const nonce = this.milliseconds ().toString (); let payload = undefined; if (method !== 'POST') { if (Object.keys (query).length) { url += '?' + this.urlencode (query); } payload = this.apiKey + nonce; } else if (body === undefined) { body = this.json (query); payload = this.apiKey + nonce + body; } headers = { 'Request-Timestamp': nonce, 'Operation-Id': this.uuid (), 'API-Key': this.apiKey, 'API-Hash': this.hmac (this.encode (payload), this.encode (this.secret), 'sha512'), 'Content-Type': 'application/json', };