gekko
Version:
A bitcoin trading bot for auto trading at various exchanges
168 lines (135 loc) • 4.79 kB
JavaScript
/*
This method uses `Exponential Moving Average crossovers` to determine the current trend the
market is in. Using this information it will suggest to ride the trend. Note that this is
not MACD because it just checks whether the longEMA and shortEMA are [threshold]% removed
from eachother.
@link http://en.wikipedia.org/wiki/Exponential_moving_average#Exponential_moving_average
This method is fairly popular in bitcoin trading due to Bitcointalk user Goomboo.
@link https://bitcointalk.org/index.php?topic=60501.0
*/
// helpers
var moment = require('moment');
var _ = require('underscore');
var util = require('../util.js');
var Util = require('util');
var log = require('../log.js');
var config = util.getConfig();
var settings = config.EMA;
if(config.backtest.enabled)
settings = _.extend(settings, config.backtest);
var TradingMethod = function(watcher) {
this.watcher = watcher;
this.currentTrend;
this.amount = settings.ticks + 1;
// store EMAsettings
this.settings = settings;
// store whole config
this.config = config;
_.bindAll(this);
this.on('prepare', this.prepare);
this.on('prepared', this.start);
this.on('calculated candle', this.calculateEMAs);
}
if(config.backtest.enabled)
var CandleMethod = require('./historical-candle-fetcher.js');
else
var CandleMethod = require('./realtime-candle-fetcher.js');
Util.inherits(TradingMethod, CandleMethod);
// first prepare this trading method, then
// prepare the candleMethod this trade method
// is extending.
TradingMethod.prototype.prepare = function() {
log.info('Calculating EMA on historical data...');
// setup method specific parameters
this.ema = {
short: [],
long: [],
diff: []
};
this.set();
}
TradingMethod.prototype.start = function() {
if(config.backtest.enabled) {
this.on('calculated new candle', this.getNewCandle);
this.getHistoricalCandles();
} else {
this.getHistoricalCandles();
setInterval(this.getNewCandle, util.minToMs( settings.interval ) );
}
}
// add a price and calculate the EMAs and
// the diff for that price
TradingMethod.prototype.calculateEMAs = function() {
if(!this.fetchingHistorical) {
// we need to remove the oldest EMAs
this.ema.short.shift();
this.ema.long.shift();
this.ema.diff.shift();
}
this.calculateEMA('short');
this.calculateEMA('long');
this.calculateEMAdiff();
log.debug('calced EMA properties for new candle:');
_.each(['short', 'long', 'diff'], function(e) {
log.debug('\t', e, 'ema', _.last(this.ema[e]).toFixed(3));
}, this);
if(!this.fetchingHistorical)
this.advice();
}
// calculation (based on tick/day):
// EMA = Price(t) * k + EMA(y) * (1 – k)
// t = today, y = yesterday, N = number of days in EMA, k = 2 / (N+1)
TradingMethod.prototype.calculateEMA = function(type) {
var price = _.last(this.candles.close);
var k = 2 / (settings[type] + 1);
var ema, y;
var current = _.size(this.candles.close);
if(current === 1)
// we don't have any 'yesterday'
y = price;
else
y = this.ema[type][current - 2];
ema = price * k + y * (1 - k);
this.ema[type].push(ema);
}
// @link https://github.com/virtimus/GoxTradingBot/blob/85a67d27b856949cf27440ae77a56d4a83e0bfbe/background.js#L145
TradingMethod.prototype.calculateEMAdiff = function() {
var shortEMA = _.last(this.ema.short);
var longEMA = _.last(this.ema.long);
var diff = 100 * (shortEMA - longEMA) / ((shortEMA + longEMA) / 2);
this.ema.diff.push(diff);
}
TradingMethod.prototype.advice = function() {
var diff = _.last(this.ema.diff).toFixed(3);
var price = _.last(this.candles.close).toFixed(3);
var message = '@ ' + price + ' (' + diff + ')';
if(config.backtest.enabled)
message += '\tat \t' + moment.unix(this.currentTimestamp).format('YYYY-MM-DD HH:mm:ss');
if(diff > settings.buyTreshold) {
log.debug('we are currently in uptrend (' + diff + ')');
if(this.currentTrend !== 'up') {
this.currentTrend = 'up';
this.emit('advice', 'BUY', price, message);
} else
this.emit('advice', 'HOLD', price, message);
} else if(diff < settings.sellTreshold) {
log.debug('we are currently in a downtrend', message);
if(this.currentTrend !== 'down') {
this.currentTrend = 'down';
this.emit('advice', 'SELL', price, message);
} else
this.emit('advice', 'HOLD', price, message);
} else {
log.debug('we are currently not in an up or down trend', message);
this.emit('advice', 'HOLD', price, message);
}
}
TradingMethod.prototype.refresh = function() {
log.debug('refreshing');
// remove the oldest tick
this.ticks.splice(0, 1);
// get new tick
this.callback = this.advice;
this.getTicks();
}
module.exports = TradingMethod;