Skip to content

Commit

Permalink
Merge 'pascal66/elliot_waves' [1st cleanup pass]
Browse files Browse the repository at this point in the history
  • Loading branch information
Sarah White committed Aug 26, 2016
2 parents 8159c38 + 4b24e63 commit 1752a58
Show file tree
Hide file tree
Showing 86 changed files with 6,683 additions and 1,301 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# OS or Editor folders
.DS_Store
Thumbs.db
config.js
.cache
.project
.settings
Expand All @@ -39,3 +40,4 @@ node_modules
candles.csv
cexio.db
history
TMP_*
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
language: node_js
node_js:
- "0.10"
- "4.2.1"
before_install:
- cp sample-config.js config.js
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Contributing

Thanks for (thinking about) contributing to Gekko, all help is wanted!

- If you want to add an exchange to Gekko, see [this doc](https://github.com/askmike/gekko/blob/develop/docs/internals/exchanges.md) for all the information you need.
- If you want to Gekko react to anything from the market, you can most likely put this functionality into a plugin. See [this document](https://github.com/askmike/gekko/blob/develop/docs/internals/plugins.md) for details.
- If you want to add trading strategies / indicators, please see [this document](https://github.com/askmike/gekko/blob/develop/docs/internals/trading_methods.md).
- If you just want to work on Gekko, you can use the open issues with the tag `open-for-pulls` for inspiration.

Things to take into consideration when submitting a pull request:

- Please submit all pull requests (except for hotfixes) to the [develop branch](https://github.com/askmike/gekko/tree/develop).
- Please keep current code styling in mind.
93 changes: 93 additions & 0 deletions core/budfox/budfox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Budfox is the realtime market for Gekko!
//
// Read more here:
// @link https://github.com/askmike/gekko/blob/stable/docs/internals/budfox.md
//
// > [getting up] I don't know. I guess I realized that I'm just Bud Fox.
// > As much as I wanted to be Gordon Gekko, I'll *always* be Bud Fox.
// > [tosses back the handkerchief and walks away]

var _ = require('lodash');
var async = require('async');

var util = require(__dirname + '/../util');
var dirs = util.dirs();

var Heart = require(dirs.budfox + 'heart');
var MarketDataProvider = require(dirs.budfox + 'marketDataProvider');
var CandleManager = require(dirs.budfox + 'candleManager');

var BudFox = function(config) {
_.bindAll(this);

Readable.call(this, {objectMode: true});

// BudFox internal modules:

this.heart = new Heart;
this.marketDataProvider = new MarketDataProvider(config);
this.candleManager = new CandleManager;

// BudFox data flow:

// on every `tick` retrieve trade data
this.heart.on(
'tick',
this.marketDataProvider.retrieve
);

// on new trade data create candles
this.marketDataProvider.on(
'trades',
this.candleManager.processTrades
);

// Output the candles
this.candleManager.on(
'candles',
this.pushCandles
);

this.heart.pump();

// Budfox also reports:

// Trades & last trade
//
// this.marketDataProvider.on(
// 'trades',
// this.broadcast('trades')
// );
// this.marketDataProvider.on(
// 'trades',
// this.broadcastTrade
// );
}

var Readable = require('stream').Readable;

BudFox.prototype = Object.create(Readable.prototype, {
constructor: { value: BudFox }
});

BudFox.prototype._read = function noop() {}

BudFox.prototype.pushCandles = function(candles) {
_.each(candles, this.push);
}

// BudFox.prototype.broadcastTrade = function(trades) {
// _.defer(function() {
// this.emit('trade', trades.last);
// }.bind(this));
// }

// BudFox.prototype.broadcast = function(message) {
// return function(payload) {
// _.defer(function() {
// this.emit(message, payload);
// }.bind(this));
// }.bind(this);
// }

module.exports = BudFox;
197 changes: 197 additions & 0 deletions core/budfox/candleCreator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// The CandleCreator creates one minute candles based on trade batches. Note
// that it also adds empty candles to fill gaps with no volume.
//
// Expects trade batches to be written like:
//
// {
// amount: x,
// start: (moment),
// end: (moment),
// first: (trade),
// last: (trade),
// timespan: x,
// all: [
// // batch of new trades with
// // moments instead of timestamps
// ]
// }
//
// Emits 'new candles' event with:
//
// [
// {
// start: (moment),
// end: (moment),
// high: (float),
// open: (float),
// low: (float),
// close: (float)
// volume: (float)
// vwp: (float) // volume weighted price
// },
// {
// start: (moment), // + 1
// end: (moment),
// high: (float),
// open: (float),
// low: (float),
// close: (float)
// volume: (float)
// vwp: (float) // volume weighted price
// }
// // etc.
// ]
//

var _ = require('lodash');
var moment = require('moment');

var util = require(__dirname + '/../util');

var CandleCreator = function() {
_.bindAll(this);

// TODO: remove fixed date
this.threshold = moment("1970-01-01", "YYYY-MM-DD");

// This also holds the leftover between fetches
this.buckets = {};
}

util.makeEventEmitter(CandleCreator);

CandleCreator.prototype.write = function(batch) {
var trades = batch.data;

if(_.isEmpty(trades))
return;

trades = this.filter(trades);
this.fillBuckets(trades);
var candles = this.calculateCandles();

candles = this.addEmptyCandles(candles);

// the last candle is not complete
this.threshold = candles.pop().start;

this.emit('candles', candles);
}

CandleCreator.prototype.filter = function(trades) {
// make sure we only include trades more recent
// than the previous emitted candle
return _.filter(trades, function(trade) {
return trade.date > this.threshold;
}, this);
}

// put each trade in a per minute bucket
CandleCreator.prototype.fillBuckets = function(trades) {
_.each(trades, function(trade) {
var minute = trade.date.format('YYYY-MM-DD HH:mm');

if(!(minute in this.buckets))
this.buckets[minute] = [];

this.buckets[minute].push(trade);
}, this);

this.lastTrade = _.last(trades);
}

// convert each bucket into a candle
CandleCreator.prototype.calculateCandles = function() {
var minutes = _.size(this.buckets);

// catch error from high volume getTrades
if (this.lastTrade !== undefined)
// create a string referencing to minute this trade happened in
var lastMinute = this.lastTrade.date.format('YYYY-MM-DD HH:mm');

var candles = _.map(this.buckets, function(bucket, name) {
var candle = this.calculateCandle(bucket);

// clean all buckets, except the last one:
// this candle is not complete
if(name !== lastMinute)
delete this.buckets[name];

return candle;
}, this);

return candles;
}

CandleCreator.prototype.calculateCandle = function(trades) {
var first = _.first(trades);

var f = parseFloat;

var candle = {
start: first.date.clone().startOf('minute'),
open: f(first.price),
high: f(first.price),
low: f(first.price),
close: f(_.last(trades).price),
vwp: 0,
volume: 0,
trades: _.size(trades)
};

_.each(trades, function(trade) {
candle.high = _.max([candle.high, f(trade.price)]);
candle.low = _.min([candle.low, f(trade.price)]);
candle.volume += f(trade.amount);
candle.vwp += f(trade.price) * f(trade.amount);
});

candle.vwp /= candle.volume;

return candle;
}

// Gekko expects a candle every minute, if nothing happened
// during a particilar minute Gekko will add empty candles with:
//
// - open, high, close, low, vwp are the same as the close of the previous candle.
// - trades, volume are 0
CandleCreator.prototype.addEmptyCandles = function(candles) {
var amount = _.size(candles);
if(!amount)
return candles;

// iterator
var start = _.first(candles).start.clone();
var end = _.last(candles).start;
var i, j = -1;

var minutes = _.map(candles, function(candle) {
return +candle.start;
});

while(start < end) {
start.add('minute', 1);
i = +start;
j++;

if(_.contains(minutes, i))
continue; // we have a candle for this minute

var lastPrice = candles[j].close;

candles.splice(j + 1, 0, {
start: start.clone(),
open: lastPrice,
high: lastPrice,
low: lastPrice,
close: lastPrice,
vwp: lastPrice,
volume: 0,
trades: 0
});
}
return candles;
}

module.exports = CandleCreator;
34 changes: 34 additions & 0 deletions core/budfox/candleManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// The candleManager consumes trades and emits:
// - `candles`: array of minutly candles.
// - `candle`: the most recent candle after a fetch Gekko.

var _ = require('lodash');
var moment = require('moment');
var fs = require('fs');

var util = require(__dirname + '/../util');
var dirs = util.dirs();
var config = util.getConfig();
var log = require(dirs.core + 'log');

var CandleCreator = require(dirs.budfox + 'candleCreator');

var Manager = function() {
_.bindAll(this);

this.candleCreator = new CandleCreator;

this.candleCreator
.on('candles', this.relayCandles);
};

util.makeEventEmitter(Manager);
Manager.prototype.processTrades = function(tradeBatch) {
this.candleCreator.write(tradeBatch);
}

Manager.prototype.relayCandles = function(candles) {
this.emit('candles', candles);
}

module.exports = Manager;
Loading

0 comments on commit 1752a58

Please sign in to comment.