UNPKG

bluebot

Version:

A bitcoin trading bot for auto trading at various exchanges

262 lines (209 loc) 7.21 kB
var Bitstamp = require("bitstamp"); var _ = require('lodash'); var moment = require('moment'); var log = require('../core/log'); var util = require('../core/util'); var Trader = function(config) { _.bindAll(this); if(_.isObject(config)) { this.key = config.key; this.secret = config.secret; this.clientID = config.username; this.asset = config.asset.toLowerCase(); this.currency = config.currency.toLowerCase(); this.market = this.asset + this.currency; } this.name = 'Bitstamp'; this.bitstamp = new Bitstamp(this.key, this.secret, this.clientID); } // if the exchange errors we try the same call again after // waiting 10 seconds Trader.prototype.retry = function(method, args) { var wait = +moment.duration(10, 'seconds'); log.debug(this.name, 'returned an error, retrying..'); var self = this; // make sure the callback (and any other fn) // is bound to Trader _.each(args, function(arg, i) { if(_.isFunction(arg)) args[i] = _.bind(arg, self); }); // run the failed method again with the same // arguments after wait setTimeout( function() { method.apply(self, args) }, wait ); } Trader.prototype.getPortfolio = function(callback) { var args = _.toArray(arguments); var set = function(err, data) { if(data && data.error) { err = data.error; } if(err) { if(err.meta && err.meta.reason === 'API key not found') util.die('Bitstamp says this API keys is invalid..'); log.error('BITSTAMP API ERROR:', err); return this.retry(this.getPortfolio, args); } var portfolio = []; _.each(data, function(amount, asset) { if(asset.indexOf('available') !== -1) { asset = asset.substr(0, 3).toUpperCase(); portfolio.push({name: asset, amount: parseFloat(amount)}); } }); callback(err, portfolio); }.bind(this); this.bitstamp.balance(this.market, set); } Trader.prototype.getTicker = function(callback) { this.bitstamp.ticker(this.market, callback); } Trader.prototype.getFee = function(callback) { var set = function(err, data) { if(err) callback(err); callback(false, data.fee / 100); }.bind(this); this.bitstamp.balance(this.market, set); } Trader.prototype.buy = function(amount, price, callback) { var args = _.toArray(arguments); var set = function(err, result) { if(err || result.status === "error") { log.error('unable to buy:', err, result.reason, 'retrying...'); return this.retry(this.buy, args); } callback(null, result.id); }.bind(this); //Decrease amount by 1% to avoid trying to buy more than balance allows. amount -= amount / 100; amount *= 100000000; amount = Math.floor(amount); amount /= 100000000; // prevent: // 'Ensure that there are no more than 2 decimal places.' price *= 100; price = Math.floor(price); price /= 100; this.bitstamp.buy(this.market, amount, price, undefined, set); } Trader.prototype.sell = function(amount, price, callback) { var args = _.toArray(arguments); var set = function(err, result) { if(err || result.status === "error") { log.error('unable to sell:', err, result.reason, 'retrying...'); return this.retry(this.sell, args); } callback(null, result.id); }.bind(this); // prevent: // 'Ensure that there are no more than 8 decimal places.' amount *= 100000000; amount = Math.floor(amount); amount /= 100000000; // prevent: // 'Ensure that there are no more than 2 decimal places.' price *= 100; price = Math.ceil(price); price /= 100; this.bitstamp.sell(this.market, amount, price, undefined, set); } Trader.prototype.getOrder = function(id, callback) { var args = _.toArray(arguments); var get = function(err, data) { if(!err && _.isEmpty(data) && _.isEmpty(data.result)) err = 'no data'; else if(!err && !_.isEmpty(data.error)) err = data.error; if(err) { log.error('Unable to get order', order, JSON.stringify(err)); return this.retry(this.getOrder, args); } var order = _.find(data, o => o.order_id === +id); if(!order) { // if the order was cancelled we are unable // to retrieve it, assume that this is what // is happening. return callback(err, { price: 0, amount: 0, date: moment(0) }); } var price = parseFloat( order[this.market] ); var amount = Math.abs(parseFloat( order[this.asset] )); var date = moment( order.datetime ); callback(err, {price, amount, date}); }.bind(this); this.bitstamp.user_transactions(this.market, {}, get); } Trader.prototype.checkOrder = function(order, callback) { var check = function(err, result) { var stillThere = _.find(result, function(o) { return o.id === order }); callback(err, !stillThere); }.bind(this); this.bitstamp.open_orders(this.market, check); } Trader.prototype.cancelOrder = function(order, callback) { var args = _.toArray(arguments); var cancel = function(err, result) { if(err || !result) { log.error('unable to cancel order', order, '(', err, result, ')'); return this.retry(this.cancelOrder, args); } callback(); }.bind(this); this.bitstamp.cancel_order(order, cancel); } Trader.prototype.getTrades = function(since, callback, descending) { var args = _.toArray(arguments); var process = function(err, trades) { if(err) return this.retry(this.getTrades, args); var result = _.map(trades, t => { return { date: t.date, tid: +t.tid, price: +t.price, amount: +t.amount } }) callback(null, result.reverse()); }.bind(this); // if(since) // this.bitstamp.transactions(this.market, {time: 'day'}, process); // else this.bitstamp.transactions(this.market, process); } Trader.getCapabilities = function () { return { name: 'Bitstamp', slug: 'bitstamp', currencies: ['USD', 'EUR', 'BTC'], assets: ['BTC', 'EUR', 'LTC', 'ETH', 'XRP'], maxTradesAge: 60, maxHistoryFetch: null, markets: [ { pair: ['USD', 'EUR'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['USD', 'BTC'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['EUR', 'BTC'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['USD', 'XRP'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['EUR', 'XRP'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['BTC', 'XRP'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['USD', 'LTC'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['EUR', 'LTC'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['BTC', 'LTC'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['USD', 'ETH'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['EUR', 'ETH'], minimalOrder: { amount: 5, unit: 'currency' } }, { pair: ['BTC', 'ETH'], minimalOrder: { amount: 5, unit: 'currency' } }, ], requires: ['key', 'secret', 'username'], fetchTimespan: 60, tid: 'tid', tradable: true }; } module.exports = Trader;