bluebot
Version:
A bitcoin trading bot for auto trading at various exchanges
295 lines (252 loc) • 8.96 kB
JavaScript
var Bitcoincoid = require('bitcoin-co-id'),
_ = require('lodash'),
moment = require('moment'),
log = require('../core/log');
var Trader = function(config) {
_.bindAll(this);
if(_.isObject(config)) {
this.key = config.key;
this.secret = config.secret;
this.clientID = config.username;
this.currency = config.currency.toLowerCase();
this.asset = config.asset.toLowerCase();
this.pair = this.asset + '_' + this.currency;
}
this.name = 'Bitcoin-co-id';
this.bitcoincoid = new Bitcoincoid(this.key, this.secret);
}
Trader.prototype.roundAmount = function(amount) {
var priceDivider = 100000000; // one hundred million;
amount *= priceDivider;
amount = Math.floor(amount);
amount /= priceDivider;
return amount;
};
// 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.getTicker = function(callback) {
var args = _.toArray(arguments),
set = function(err, data) {
if(err)
return this.retry(this.getTicker, args);
var ticker = {
ask: this.roundAmount(data.ticker.buy),
bid: this.roundAmount(data.ticker.sell),
};
callback(err, ticker);
}.bind(this);
this.bitcoincoid.getTicker(this.pair, set);
}
Trader.prototype.getPortfolio = function(callback) {
var functionName = 'Trader.getPortfolio()',
args = _.toArray(arguments);
set = function(err, data) {
if(err)
return this.retry(this.getPortfolio, args);
var assetAmount = this.roundAmount( data.return.balance[this.asset] ),
currencyAmount = this.roundAmount( data.return.balance[this.currency] ),
assetHold = this.roundAmount( data.return.balance_hold[this.asset] ),
currencyHold = this.roundAmount( data.return.balance_hold[this.currency] );
if(
!_.isNumber(assetAmount) || _.isNaN(assetAmount) ||
!_.isNumber(currencyAmount) || _.isNaN(currencyAmount)
) {
return log.error('account balance error: BlueBot is unable to trade with ',this.currency.toUpperCase(),':',currencyAmount,' or ',this.asset.toUpperCase(),':',assetAmount);
}
var portfolio = [
{ name: this.currency.toUpperCase(), amount: currencyAmount - currencyHold},
{ name: this.asset.toUpperCase(), amount: assetAmount - assetHold }
];
callback(err, portfolio);
}.bind(this);
this.bitcoincoid.getAccountBalances(set);
}
Trader.prototype.getTrades = function(since, callback, descending) {
var args = _.toArray(arguments);
if(since)
since = 150 //???;
var process = function(err, data) {
if(err)
return this.retry(this.getTrades, args);
var trades = _.map(data, function(trade) {
return {
price: +trade.price,
amount: +trade.amount,
tid: +trade.tid,
date: trade.date
}
});
callback(null, data.reverse());
}.bind(this);
this.bitcoincoid.getTrades(this.pair, process);
}
// bitcoin.co.id: Maker 0% - Taker 0.3%
// Note: trading fee: 0.3% for IDR pairs and 0% for BTC pairs
Trader.prototype.getFee = function(callback) {
if (this.currency.toUpperCase() === 'IDR')
{
var fee = 0.003;
}
else if (this.currency.toUpperCase() === 'BTC')
{
var fee = 0.000;;
}
callback(false, fee);
};
Trader.prototype.buy = function(amount, price, callback) {
this.type = 'buy';
// decrease purchase by 1% to avoid trying to buy more than balance
amount -= amount / 100;
amount = this.roundAmount(amount);
// decrease purchase price by 1% less than asking price
//price -= price / 100;
amount *= price;
var set = function(err, data) {
if(!err && _.isEmpty(data))
err = 'no data';
else if(!err && !_.isEmpty(data.errorMessage))
err = data.errorMessage;
if(err)
return log.error('unable to buy', err);
callback(null, data.return.order_id);
}.bind(this);
this.bitcoincoid.createOrders(
this.pair + '',
this.type,
price,
amount,
set
);
}
Trader.prototype.sell = function(amount, price, callback) {
this.type = 'sell';
// increase purchase price by 1% more than bidding price
//price += price / 100;
amount = this.roundAmount(amount);
var set = function(err, data) {
if(!err && _.isEmpty(data))
err = 'no data';
else if(!err && !_.isEmpty(data.errorMessage))
err = data.errorMessage;
if(err)
return log.error('unable to sell', err)
callback(null, data.return.order_id);
}.bind(this);
this.bitcoincoid.createOrders(
this.pair + '',
this.type,
price,
amount,
set
);
}
Trader.prototype.checkOrder = function(order, callback) {
var args = _.toArray(arguments);
if (order == null) {
return callback('no order_id', false);
}
var check = function(err, data) {
if(err){
return this.retry(this.checkOrder, arguments);
}
var status = data.return.order.status;
if (status === 'filled') {
return callback(err, true);
} else if (status === 'open') {
return callback(err, false);
}
callback(err, false);
}.bind(this);
this.bitcoincoid.getOrderDetails(this.pair, order, check);
}
Trader.prototype.getOrder = function(order, callback) {
var args = _.toArray(arguments);
if (order == null) {
return callback('no order_id', false);
}
var get = function(data, err) {
if(err)
return callback(err);
var price = 0;
var amount = 0;
var date = moment(0);
if(!data.success)
return callback(null, {price, amount, date});
var result = data.return.order;
var orderAmount = result.order+'_'+this.asset;
price = result.Price;
amount = result.orderAmount;
if(result.status === 'open') {
date = moment(result.submit_time);
} else {
date = moment(result.finish_time);
}
callback(err, {price, amount, date});
}.bind(this);
this.bitcoincoid.getOrderDetails(this.pair, order, get);
}
Trader.prototype.cancelOrder = function(order, callback) {
var args = _.toArray(arguments);
var cancel = function(err, data) {
if(err){
return log.error('unable to cancel order: ',order, '(', err, '), retrying...');
this.retry(this.cancelOrder, args);
}
callback();
};
this.bitcoincoid.cancelOrder(this.pair, order, this.type, cancel);
}
Trader.getCapabilities = function () {
return {
name: 'Bitcoin.co.id',
slug: 'bitcoin-co-id',
currencies: ['IDR', 'BTC'],
assets: [
'BTC', 'BCH', 'BTS', 'ETH', 'ETC', 'LTC', 'WAVE', 'XRP', 'XZR', 'BTC', 'DASH', 'DOGE', 'NXT', 'XLM', 'XEM'
],
markets: [
// IDR <-> XXXX
{ pair: ['IDR', 'BTC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
{ pair: ['IDR', 'BCH'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
{ pair: ['IDR', 'ETH'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
{ pair: ['IDR', 'ETC'], minimalOrder: { amount: 0.0001, unit: 'asset' } },
{ pair: ['IDR', 'LTC'], minimalOrder: { amount: 0.001, unit: 'asset' } },
{ pair: ['IDR', 'WAVE'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['IDR', 'XRP'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['IDR', 'XZR'], minimalOrder: { amount: 0.01, unit: 'asset' } },
// BTC <-> XXXX
{ pair: ['BTC', 'BTS'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'DASH'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'DOGE'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'ETH'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'LTC'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'NXT'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'XLM'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'XEM'], minimalOrder: { amount: 0.01, unit: 'asset' } },
{ pair: ['BTC', 'XRP'], minimalOrder: { amount: 0.01, unit: 'asset' } }
],
requires: ['key', 'secret'],
tid: 'tid',
providesHistory: true,
providesFullHistory: false,
tradable: true
};
}
module.exports = Trader;