forked from askmike/gekko
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge 'pascal66/elliot_waves' [1st cleanup pass]
- Loading branch information
Showing
86 changed files
with
6,683 additions
and
1,301 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.