Skip to content

Commit

Permalink
Merge https://github.com/askmike/gekko into anti-conflicts
Browse files Browse the repository at this point in the history
Note:

Does not use the original README.md

Primarily because askmike's version is MIT licensed

but the enhancements are AGPLv3

(copyright Sarah White AKA kuzetsa, 2013-2014)
  • Loading branch information
Sarah White committed Nov 19, 2014
2 parents b31123a + 50ed5e8 commit fa9fb05
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 0 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,32 @@ Gekko also has a plugin system that can do certain things whenever something hap
- Profit Simulator (paper trader): Hold a fake portfolio and simulate trades based on advice.
- Redis Beacon: Broadcast events propagating through Gekko on [Redis pub/sub](http://redis.io/topics/pubsub).

<<<<<<< HEAD
### Installing Gekko
||||||| merged common ancestors
## Supported exchanges

Gekko works on the following exchanges:

- Mt. Gox
- Bitstamp
- CEX.io
- Kraken
- BTC-e
- ~~Cryptsy~~ (In the [pipeline](https://github.com/askmike/gekko/pull/200))
=======
## Supported exchanges

Gekko works on the following exchanges:

- Mt. Gox
- Bitstamp
- CEX.io
- Kraken
- BTC-e
- Bitfinex
- ~~Cryptsy~~ (In the [pipeline](https://github.com/askmike/gekko/pull/200))
>>>>>>> 50ed5e840d7a355932bd8ed01410605c35d682ee
Windows user? Here is a [step-by-step guide](https://github.com/kuzetsa/gekko/blob/master/docs/installing_gekko_on_windows.md) on how to get Gekko running on Windows.

Expand Down
142 changes: 142 additions & 0 deletions exchanges/bitfinex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@

var Bitfinex = require("bitfinex");
var util = require('../core/util.js');
var _ = require('lodash');
var moment = require('moment');
var log = require('../core/log');

// Module-wide constants
var exchangeName = 'bitfinex';
// Bitfinex supports Litecoin, but this module currently only supports Bitcoin
var defaultAsset = 'btcusd';

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.bitfinex = new Bitfinex(this.key, this.secret);
}

// 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) {
this.bitfinex.wallet_balances(function (err, data, body) {
var portfolio = _.map(body, function (asset) {
return {
name: asset.currency.toUpperCase(),
// TODO: use .amount instead of .available?
amount: +asset.available
}
});
callback(err, portfolio);
});
}

Trader.prototype.getTicker = function(callback) {
this.bitfinex.ticker(defaultAsset, function (err, data, body) {
var tick = JSON.parse(body);
callback(err, { bid: +tick.bid, ask: +tick.ask })
});
}

// This assumes that only limit orders are being placed, so fees are the
// "maker fee" of 0.1%. It does not take into account volume discounts.
Trader.prototype.getFee = function(callback) {
var makerFee = 0.1;
callback(false, makerFee / 100);
}

function submit_order(bfx, type, amount, price, callback) {
// TODO: Bitstamp module included the following - is it necessary?
// amount *= 0.995; // remove fees
amount = Math.floor(amount*100000000)/100000000;
bfx.new_order(defaultAsset, amount, price, exchangeName,
type,
'exchange limit',
function (err, data, body) {
if (err)
return log.error('unable to ' + type, err, body);

var order = JSON.parse(body);
callback(err, order.order_id);
});
}

Trader.prototype.buy = function(amount, price, callback) {
submit_order(this.bitfinex, 'buy', amount, price, callback);

}

Trader.prototype.sell = function(amount, price, callback) {
submit_order(this.bitfinex, 'sell', amount, price, callback);
}

Trader.prototype.checkOrder = function(order_id, callback) {
this.bitfinex.order_status(order_id, function (err, data, body) {
var result = JSON.parse(body);
callback(err, result.is_live);
});
}

Trader.prototype.cancelOrder = function(order_id, callback) {
this.bitfinex.cancel_order(order_id, function (err, data, body) {
var result = JSON.parse(body);
if (err || !result || !result.is_cancelled)
log.error('unable to cancel order', order, '(', err, result, ')');
});
}

Trader.prototype.getTrades = function(since, callback, descending) {
var args = _.toArray(arguments);
var self = this;

// Bitfinex API module does not support start date, but Bitfinex API does.
// Could implement here as in following comments:
// var start = since ? since.unix() : null;
this.bitfinex.trades(defaultAsset, /* start, */ function (err, data, body) {
if (err)
return self.retry(self.getTrades, args);

var result = JSON.parse(body);

var trades = _.map(result, function (trade) {
return {
"date": trade.timestamp,
"price": +trade.price,
"amount": +trade.amount // not mentioned in gekko exchange docs
}
});

callback(null, descending ? trades : trades.reverse());
});
}

module.exports = Trader;

134 changes: 134 additions & 0 deletions plugins/mandrillMailer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
var _ = require('lodash');
var mandrill = require('mandrill-api/mandrill');
var log = require('../core/log.js');
var util = require('../core/util.js');
var config = util.getConfig();
var mailConfig = config.mandrillMailer;

var MandrillMailer = function(done) {
_.bindAll(this);

this.price = 'N/A';
this.client;
this.done = done;
this.setup();
}

MandrillMailer.prototype.setup = function(done) {
var errors = [];
if(_.isEmpty(mailConfig.to))
errors.push("No destination address configured for Mandrill Mail Config");
if(_.isEmpty(mailConfig.from))
errors.push("No sending address configured for Mandrill Mail Config");
if(_.isEmpty(mailConfig.apiKey))
errors.push("No API Key configured for Mandrill Mail Config");

// init the client...
var mandrill_client = new mandrill.Mandrill(mailConfig.apiKey);
this.client = mandrill_client;

debugger;
if(mailConfig.sendMailOnStart && errors.length === 0) {
var subject = "Gekko has started";
var messageText = [
"I've just started watching ",
config.watch.exchange,
' ',
config.watch.currency,
'/',
config.watch.asset,
". I'll let you know when I got some advice"
].join('');

this.mail(
subject,
messageText,
_.bind(function(err) {
this.checkResults(err);
this.done();
}, this)
);

} else if(errors.length !== 0){
throw new Error(errors);
} else {
this.done();
}

if(mailConfig.testProcessAdvice){
this.testProcessAdvice();
}

log.debug('Setup email (Mandrill) adviser successfully.');
}

MandrillMailer.prototype.mail = function(subject, content, done) {
var self = this;
var message = {
"text": content,
"to": [{
"email": mailConfig.to,
"name": mailConfig.toName,
"type": "to"
}],
"from_name": mailConfig.fromName,
"from_email": mailConfig.from,
"subject": mailConfig.tag + subject
};

var ip_pool = "Main Pool";
self.client.messages.send({
"message": message,
"async": false,
"ip_pool": ip_pool
},
function(result){
log.debug("Mail sent successfully via Mandrill: ", result);
if(done){
done();
}
}, function(error){
self.checkResults(error);
if(done){
done();
}
});
}

MandrillMailer.prototype.processTrade = function(trade) {
this.price = trade.price;
}

MandrillMailer.prototype.processAdvice = function(advice) {
var text = [
'Gekko is watching ',
config.watch.exchange,
' and has detected a new trend, advice is to go ',
advice.recommandation,
'.\n\nThe current ',
config.watch.asset,
' price is ',
this.price
].join('');

var subject = 'New advice: go ' + advice.recommandation;

this.mail(subject, text);
}

MandrillMailer.prototype.testProcessAdvice = function(){
var advice = {
recommandation: "short"
};
this.price = 0;
this.processAdvice(advice);
}

MandrillMailer.prototype.checkResults = function(err) {
if(err)
log.warn('error sending email', err);
else
log.info('Send advice via email.');
}

module.exports = MandrillMailer;
Loading

0 comments on commit fa9fb05

Please sign in to comment.