// NOT USED, see: https://github.com/bitfinexcom/bitfinex-api-node/issues/321 const Bitfinex = require("bitfinex-api-node"); const _ = require('lodash'); const moment = require('moment'); const Errors = require('../exchangeErrors'); const retry = require('../exchangeUtils').retry; const marketData = require('./bitfinex-markets.json'); var Trader = function(config) { _.bindAll(this); if(_.isObject(config)) { this.key = config.key; this.secret = config.secret; } this.name = 'Bitfinex'; this.balance; this.price; this.asset = config.asset; this.currency = config.currency; this.pair = 't' + this.asset + this.currency; this.bitfinex = new Bitfinex.RESTv2({apiKey: this.key, apiSecret: this.secret, transform: true}); } const includes = (str, list) => { if(!_.isString(str)) return false; return _.some(list, item => str.includes(item)); } // Probably we need to update these string const recoverableErrors = [ 'SOCKETTIMEDOUT', 'TIMEDOUT', 'CONNRESET', 'CONNREFUSED', 'NOTFOUND', '429', '443', '504', '503', '500', '502', 'Empty response' ]; Trader.prototype.handleResponse = function(funcName, callback) { return (error, data) => { if(!error && _.isEmpty(data)) { error = new Error('Empty response'); } if(error) { const message = error.message || error; if(!includes(message, recoverableErrors)) { const error = new Error(message); error.notFatal = true; return callback(error); } } return callback(error, data); } }; Trader.prototype.getPortfolio = function(callback) { const processResponse = (err, data) => { console.log('processResponse', {err, data}); if (err) return callback(err); // We are only interested in funds in the "exchange" wallet data = data.filter(c => c.type === 'exchange'); const asset = _.find(data, c => c.currency.toUpperCase() === this.asset); const currency = _.find(data, c => c.currency.toUpperCase() === this.currency); console.log(currency.balance); let assetAmount, currencyAmount; if(_.isObject(asset) && _.isNumber(+asset.balanceAvailable) && !_.isNaN(+asset.balanceAvailable)) assetAmount = +asset.balanceAvailable; else { assetAmount = 0; } if(_.isObject(currency) && _.isNumber(+currency.balanceAvailable) && !_.isNaN(+currency.balanceAvailable)) currencyAmount = +currency.balanceAvailable; else { currencyAmount = 0; } const portfolio = [ { name: this.asset, amount: assetAmount }, { name: this.currency, amount: currencyAmount }, ]; callback(undefined, portfolio); }; const fetch = cb => this.bitfinex.wallets(this.handleResponse('getPortfolio', cb)); retry(null, fetch, processResponse); } Trader.prototype.getTicker = function(callback) { const processResponse = (err, data) => { if (err) return callback(err); callback(undefined, {bid: +data.bid, ask: +data.ask}); }; const fetch = cb => this.bitfinex.ticker(this.pair, this.handleResponse('getTicker', cb)); retry(null, fetch, processResponse); } Trader.prototype.getFee = function(callback) { const makerFee = 0.1; // const takerFee = 0.2; callback(undefined, makerFee / 100); } Trader.prototype.roundAmount = function(amount) { return Math.floor(amount*100000000)/100000000; } Trader.prototype.roundPrice = function(price) { // todo: calc significant digits return price; } Trader.prototype.submit_order = function(type, amount, price, callback) { const processResponse = (err, data) => { if (err) return callback(err); callback(err, data.order_id); } const fetcher = cb => this.bitfinex.new_order(this.pair, amount + '', price + '', this.name.toLowerCase(), type, 'exchange limit', this.handleResponse('submitOrder', cb) ); retry(retryCritical, fetcher, processResponse); } Trader.prototype.buy = function(amount, price, callback) { this.submit_order('buy', amount, price, callback); } Trader.prototype.sell = function(amount, price, callback) { this.submit_order('sell', amount, price, callback); } Trader.prototype.checkOrder = function(order_id, callback) { const processResponse = (err, data) => { if (err) return callback(err); return callback(undefined, { open: data.is_live, executed: data.original_amount === data.executed_amount, filled: +data.executed_amount }); } const fetcher = cb => this.bitfinex.order_status(order_id, this.handleResponse('checkOrder', cb)); retry(retryCritical, fetcher, processResponse); } Trader.prototype.getOrder = function(order_id, callback) { const processResponse = (err, data) => { if (err) return callback(err); var price = parseFloat(data.avg_execution_price); var amount = parseFloat(data.executed_amount); var date = moment.unix(data.timestamp); callback(undefined, {price, amount, date}); }; const fetcher = cb => this.bitfinex.order_status(order_id, this.handleResponse('getOrder', cb)); retry(retryCritical, fetcher, processResponse); } Trader.prototype.cancelOrder = function(order_id, callback) { const processResponse = (err, data) => { if (err) return callback(err); return callback(undefined); } const handler = cb => this.bitfinex.cancel_order(order_id, this.handleResponse('cancelOrder', cb)); retry(retryForever, handler, processResponse); } Trader.prototype.getTrades = function(since, callback, descending) { const processResponse = (err, data) => { if (err) return callback(err); var trades = _.map(data, function(trade) { return { tid: trade.tid, date: trade.timestamp, price: +trade.price, amount: +trade.amount } }); callback(undefined, descending ? trades : trades.reverse()); }; var path = this.pair; if(since) path += '?limit_trades=2000'; const handler = cb => this.bitfinex.trades(path, this.handleResponse('getTrades', cb)); retry(retryForever, handler, processResponse); } Trader.getCapabilities = function () { return { name: 'Bitfinex', slug: 'bitfinex', currencies: marketData.currencies, assets: marketData.assets, markets: marketData.markets, requires: ['key', 'secret'], tid: 'tid', providesFullHistory: true, providesHistory: 'date', tradable: true, forceReorderDelay: true }; } module.exports = Trader;