121 lines
3.0 KiB
JavaScript
121 lines
3.0 KiB
JavaScript
// Small writable stream wrapper that
|
|
// passes data to all `candleConsumers`.
|
|
|
|
const Writable = require('stream').Writable;
|
|
const _ = require('lodash');
|
|
const async = require('async');
|
|
const moment = require('moment');
|
|
|
|
const util = require('./util');
|
|
const env = util.gekkoEnv();
|
|
const mode = util.gekkoMode();
|
|
const config = util.getConfig();
|
|
const log = require(util.dirs().core + 'log');
|
|
|
|
var Gekko = function(plugins) {
|
|
this.plugins = plugins;
|
|
this.candleConsumers = plugins
|
|
.filter(plugin => plugin.processCandle);
|
|
Writable.call(this, {objectMode: true});
|
|
|
|
this.producers = this.plugins
|
|
.filter(p => p.meta.emits);
|
|
|
|
this.finalize = _.bind(this.finalize, this);
|
|
}
|
|
|
|
Gekko.prototype = Object.create(Writable.prototype, {
|
|
constructor: { value: Gekko }
|
|
});
|
|
|
|
if(config.debug && mode !== 'importer') {
|
|
// decorate with more debug information
|
|
Gekko.prototype._write = function(chunk, encoding, _done) {
|
|
|
|
if(chunk.isFinished) {
|
|
return this.finalize();
|
|
}
|
|
|
|
const start = moment();
|
|
var relayed = false;
|
|
var at = null;
|
|
|
|
const timer = setTimeout(() => {
|
|
if(!relayed)
|
|
log.error([
|
|
`The plugin "${at}" has not processed a candle for 1 second.`,
|
|
`This will cause Gekko to slow down or stop working completely.`
|
|
].join(' '));
|
|
}, 1000);
|
|
|
|
const flushEvents = _.after(this.candleConsumers.length, () => {
|
|
relayed = true;
|
|
clearInterval(timer);
|
|
this.flushDefferedEvents();
|
|
_done();
|
|
});
|
|
_.each(this.candleConsumers, function(c) {
|
|
at = c.meta.name;
|
|
c.processCandle(chunk, flushEvents);
|
|
}, this);
|
|
}
|
|
} else {
|
|
// skip decoration
|
|
Gekko.prototype._write = function(chunk, encoding, _done) {
|
|
if(chunk.isFinished) {
|
|
return this.finalize();
|
|
}
|
|
|
|
const flushEvents = _.after(this.candleConsumers.length, () => {
|
|
this.flushDefferedEvents();
|
|
_done();
|
|
});
|
|
_.each(this.candleConsumers, function(c) {
|
|
c.processCandle(chunk, flushEvents);
|
|
}, this);
|
|
}
|
|
}
|
|
|
|
Gekko.prototype.flushDefferedEvents = function() {
|
|
const broadcasted = _.find(
|
|
this.producers,
|
|
producer => producer.broadcastDeferredEmit()
|
|
);
|
|
|
|
// If we braodcasted anything we might have
|
|
// triggered more events, recurse until we
|
|
// have fully broadcasted everything.
|
|
if(broadcasted)
|
|
this.flushDefferedEvents();
|
|
}
|
|
|
|
Gekko.prototype.finalize = function() {
|
|
var tradingMethod = _.find(
|
|
this.candleConsumers,
|
|
c => c.meta.name === 'Trading Advisor'
|
|
);
|
|
|
|
if(!tradingMethod)
|
|
return this.shutdown();
|
|
|
|
tradingMethod.finish(this.shutdown.bind(this));
|
|
}
|
|
|
|
Gekko.prototype.shutdown = function() {
|
|
this.end();
|
|
async.eachSeries(
|
|
this.plugins,
|
|
function(c, callback) {
|
|
if (c.finalize) c.finalize(callback);
|
|
else callback();
|
|
},
|
|
() => {
|
|
// If we are a child process, we signal to the parent to kill the child once it is done
|
|
// so that is has time to process all remaining events (and send report data)
|
|
if (env === 'child-process') process.send('done');
|
|
else process.exit(0);
|
|
}
|
|
);
|
|
};
|
|
|
|
module.exports = Gekko; |