bluebot
Version:
A bitcoin trading bot for auto trading at various exchanges
203 lines (159 loc) • 5.12 kB
JavaScript
/*
A pipeline implements a full BlueBot Flow based on a config and
a mode. The mode is an abstraction that tells BlueBot what market
to load (realtime, backtesting or importing) while making sure
all enabled plugins are actually supported by that market.
*/
var util = require('./util');
var dirs = util.dirs();
var _ = require('lodash');
var async = require('async');
var log = require(dirs.core + 'log');
var pipeline = (settings) => {
var mode = settings.mode;
var config = settings.config;
// prepare a BlueBotStream
var BlueBotStream = require(dirs.core + 'bluebotStream');
// all plugins
var plugins = [];
// all emitting plugins
var emitters = {};
// all plugins interested in candles
var candleConsumers = [];
// utility to check and load plugins.
var pluginHelper = require(dirs.core + 'pluginUtil');
// meta information about every plugin that tells BlueBot
// something about every available plugin
var pluginParameters = require(dirs.bluebot + 'plugins');
// meta information about the events plugins can broadcast
// and how they should hooked up to consumers.
var subscriptions = require(dirs.bluebot + 'subscriptions');
// Instantiate each enabled plugin
var loadPlugins = function(next) {
// load all plugins
async.mapSeries(
pluginParameters,
pluginHelper.load,
function(error, _plugins) {
if(error)
return util.die(error, true);
plugins = _.compact(_plugins);
next();
}
);
};
// Some plugins emit their own events, store
// a reference to those plugins.
var referenceEmitters = function(next) {
_.each(plugins, function(plugin) {
if(plugin.meta.emits)
emitters[plugin.meta.slug] = plugin;
});
next();
}
// Subscribe all plugins to other emitting plugins
var subscribePlugins = function(next) {
// events broadcasted by plugins
var pluginSubscriptions = _.filter(
subscriptions,
sub => sub.emitter !== 'market'
);
// some events can be broadcasted by different
// plugins, however the pipeline only allows a single
// emitting plugin for each event to be enabled.
_.each(
pluginSubscriptions.filter(s => _.isArray(s.emitter)),
subscription => {
var singleEventEmitters = subscription.emitter
.filter(s => _.size(plugins.filter(p => p.meta.slug === s)
));
if(_.size(singleEventEmitters) > 1) {
var error = `Multiple plugins are broadcasting`;
error += ` the event "${subscription.event}" (${singleEventEmitters.join(',')}).`;
error += 'This is unsupported.'
util.die(error);
} else {
subscription.emitter = _.first(singleEventEmitters);
}
}
);
// subscribe interested plugins to
// emitting plugins
_.each(plugins, function(plugin) {
_.each(pluginSubscriptions, function(sub) {
if(_.has(plugin, sub.handler)) {
// if a plugin wants to listen
// to something disabled
if(!emitters[sub.emitter]) {
return log.warn([
plugin.meta.name,
'wanted to listen to the',
sub.emitter + ',',
'however the',
sub.emitter,
'is disabled.'
].join(' '));
}
// attach handler
emitters[sub.emitter]
.on(sub.event,
plugin[
sub.handler
])
}
});
});
// events broadcasted by the market
var marketSubscriptions = _.filter(
subscriptions,
{emitter: 'market'}
);
// subscribe plugins to the market
_.each(plugins, function(plugin) {
_.each(marketSubscriptions, function(sub) {
// for now, only subscribe to candles
if(sub.event !== 'candle')
return;
if(_.has(plugin, sub.handler))
candleConsumers.push(plugin);
});
});
next();
}
// TODO: move this somewhere where it makes more sense
var prepareMarket = function(next) {
if(mode === 'backtest' && config.backtest.daterange === 'scan')
require(dirs.core + 'prepareDateRange')(next);
else
next();
}
log.info('Setting up BlueBot in', mode, 'mode');
log.info('');
async.series(
[
loadPlugins,
referenceEmitters,
subscribePlugins,
prepareMarket
],
function() {
// load a market based on the config (or fallback to mode)
let marketType;
if(config.market)
marketType = config.market.type;
else
marketType = mode;
var Market = require(dirs.markets + marketType);
var market = new Market(config);
var bluebot = new BlueBotStream(candleConsumers);
market
.pipe(bluebot)
// convert JS objects to JSON string
// .pipe(new require('stringify-stream')())
// output to standard out
// .pipe(process.stdout);
market.on('end', bluebot.finalize);
}
);
}
module.exports = pipeline;