Skip to content
This repository has been archived by the owner on Feb 16, 2020. It is now read-only.

[WIP] 0.6 new gekko events #1850

Merged
merged 58 commits into from
May 28, 2018
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
dbb292a
add dedicated events page
askmike Feb 1, 2018
95c8ffb
add events page
askmike Feb 1, 2018
16d9673
streamline events intro
askmike Feb 1, 2018
fc4ac03
typo
askmike Feb 1, 2018
7f41958
catch unsuccessful broadcast
askmike Feb 1, 2018
28326bf
use gekko events for market start and market update
askmike Feb 1, 2018
4c647e2
hook up plugins to market events
askmike Feb 1, 2018
c5dbb78
add eventLogger plugin
askmike Feb 1, 2018
5e459c3
add additional strat events "stratStart" and "stratUpdate"
askmike Feb 2, 2018
c7aedf2
make sure to properly enclose broadcast catch wrap
askmike Feb 5, 2018
875a3b1
rm stratStart event
askmike Feb 6, 2018
7caf0d3
rm all stratEvent docs
askmike Feb 6, 2018
9a4b2b1
remove stratStart subscription
askmike Feb 9, 2018
0c35666
introduce portfolioChange & portfolioValueChange events
askmike Feb 11, 2018
1af48aa
introduce events to describe trades async
askmike Feb 12, 2018
08beb2b
add stratWarmupCompleted event
askmike Feb 12, 2018
9ca7caf
implement stratWarmupCompleted
askmike Feb 13, 2018
d2a2146
implement stratUpdate event
askmike Feb 13, 2018
e78cfbb
error when plugins consume candles too slow
askmike Feb 21, 2018
0d97570
make sure to callback after consuming candle
askmike Feb 21, 2018
d29f785
var cleanup
askmike Feb 22, 2018
3993638
Fix issue with trade events being deferred too long
cmroche Feb 13, 2018
b722b17
force order of market events
askmike Mar 3, 2018
410dff5
remove cpRelay out of performance analyzer
askmike Mar 5, 2018
0eabf08
make sure we dont report on no trades
askmike Mar 5, 2018
decddd8
rm mentions of simulated
askmike Mar 5, 2018
eeec247
defer processCandle until the strat is completely done processing the…
askmike Mar 16, 2018
505ed01
implement tradeInitialized & tradeCompleted events
askmike Mar 19, 2018
4d52ca9
use a LIFO stack based event emittor
askmike Mar 19, 2018
d05b4fe
make all plugins fifo event emitters
askmike Mar 20, 2018
350efd3
refer to blogpost with background information
askmike Mar 20, 2018
e7d7e56
add native gekko indicator results to stratUpdate event
askmike Mar 20, 2018
59b5c70
implement roundtrip, roundtripUpdate & performanceUpdate events
askmike Mar 23, 2018
587ddcb
properly catch no trade scenario
askmike Mar 24, 2018
4df7af2
pass all plugins to gekkoStream
askmike Mar 24, 2018
4995f22
pass all plugins into gekkostream
askmike Mar 24, 2018
479d321
create plugin to handle backtest results
askmike Mar 24, 2018
3847362
only wait for actual candle consumers to handle candles
askmike Mar 25, 2018
cea17ca
only flush events from plugins that actually emit
askmike Mar 25, 2018
a42e5a0
rm the id of the small candle
askmike Mar 25, 2018
5e4d4d8
add stratCandle event
askmike Mar 25, 2018
5e9c670
properly handle stratUpdates
askmike Mar 25, 2018
674af1d
clarify strat events during warmup
askmike Mar 25, 2018
0d6ce81
allow the exporting of raw trades
askmike Mar 25, 2018
7bbb59c
hookup backtest UI to new event flow
askmike Mar 25, 2018
6ae42a2
make sure to print cp final words
askmike Mar 25, 2018
a694b09
upgrade backtest API call to use backtestResultExporter plugin
askmike Mar 25, 2018
2041cd2
update to new backtest api call
askmike Mar 26, 2018
8e2760a
allow for specifying what candle props to return
askmike Mar 26, 2018
a639317
make sure we output the binance error, fix #2037
askmike Apr 1, 2018
c6faee1
Performance Analyzer fixes and features (#2178)
stereohelix May 17, 2018
f948a2d
define relativeYearlyProfit, fix #2190
askmike May 18, 2018
b080ff7
update required node version to 8.11.2
askmike May 24, 2018
7358e55
run appveyor tests using node v9
askmike May 24, 2018
52f6e5d
pull async indicator wrap code out of base strat
askmike May 28, 2018
658bf76
remove cp.js
askmike May 28, 2018
e703963
Merge branch 'pre-v0.6' into feature/sync-gekko-events
askmike May 28, 2018
ea6df42
remove tulind & talib from default deps
askmike May 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 12 additions & 27 deletions core/budfox/budfox.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,25 @@ var BudFox = function(config) {
this.candleManager.processTrades
);

// relay a marketUpdate event
this.marketDataProvider.on(
'marketUpdate',
e => this.emit('marketUpdate', e)
);

// relay a marketStart event
this.marketDataProvider.on(
'marketStart',
e => this.emit('marketStart', e)
);

// 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;
Expand All @@ -76,18 +75,4 @@ 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;
10 changes: 0 additions & 10 deletions core/budfox/candleManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ var Manager = function() {

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

this.messageFirstCandle = _.once(candle => {
cp.firstCandle(candle);
})
};

util.makeEventEmitter(Manager);
Expand All @@ -34,12 +30,6 @@ Manager.prototype.processTrades = function(tradeBatch) {

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

if(!_.size(candles))
return;

this.messageFirstCandle(_.first(candles));
cp.lastCandle(_.last(candles));
}

module.exports = Manager;
8 changes: 4 additions & 4 deletions core/budfox/marketDataProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ Manager.prototype.retrieve = function() {
Manager.prototype.relayTrades = function(batch) {
this.emit('trades', batch);

this.sendStartAt(batch);
cp.update(batch.last.date.format());
this.sendMarketStart(batch);
this.emit('marketUpdate', batch.last.date);
}

Manager.prototype.sendStartAt = _.once(function(batch) {
cp.startAt(batch.first.date.format())
Manager.prototype.sendMarketStart = _.once(function(batch) {
this.emit('marketStart', batch.first.date);
});

module.exports = Manager;
18 changes: 0 additions & 18 deletions core/cp.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,6 @@ var message = (type, payload) => {
}

var cp = {
// string like: '2016-12-03T22:23:00.000Z'
update: latest => message('update', { latest }),
startAt: startAt => message('startAt', { startAt }),

// object like:
//
// {
// start: '2016-12-03T22:23:00.000Z',
// open: 765,
// high: 765,
// low: 765,
// close: 765,
// vwp: 765,
// volume: 0,
// trades: 0
// }
lastCandle: lastCandle => message('lastCandle', { lastCandle }),
firstCandle: firstCandle => message('firstCandle', { firstCandle }),

// object like:
//
Expand Down
2 changes: 1 addition & 1 deletion core/gekkoStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Gekko.prototype.shutdown = function() {
if (c.finalize) c.finalize(callback);
else callback();
},
function() {
() => {
// 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');
Expand Down
77 changes: 55 additions & 22 deletions core/pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ var pipeline = (settings) => {
// and how they should hooked up to consumers.
var subscriptions = require(dirs.gekko + 'subscriptions');

var market;

// Instantiate each enabled plugin
var loadPlugins = function(next) {
// load all plugins
Expand Down Expand Up @@ -74,7 +76,6 @@ var pipeline = (settings) => {

// Subscribe all plugins to other emitting plugins
var subscribePlugins = function(next) {

// events broadcasted by plugins
var pluginSubscriptions = _.filter(
subscriptions,
Expand All @@ -88,8 +89,9 @@ var pipeline = (settings) => {
pluginSubscriptions.filter(s => _.isArray(s.emitter)),
subscription => {
var singleEventEmitters = subscription.emitter
.filter(s => _.size(plugins.filter(p => p.meta.slug === s)
));
.filter(
s => _.size(plugins.filter(p => p.meta.slug === s))
);

if(_.size(singleEventEmitters) > 1) {
var error = `Multiple plugins are broadcasting`;
Expand All @@ -107,7 +109,7 @@ var pipeline = (settings) => {
_.each(plugins, function(plugin) {
_.each(pluginSubscriptions, function(sub) {

if(_.has(plugin, sub.handler)) {
if(plugin[sub.handler]) {

// if a plugin wants to listen
// to something disabled
Expand Down Expand Up @@ -143,27 +145,65 @@ var pipeline = (settings) => {
_.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);
if(plugin[sub.handler]) {
if(sub.event === 'candle')
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();
}

var setupMarket = function(next) {
// 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);

market = new Market(config);

next();
}

var subscribePluginsToMarket = function(next) {

// events broadcasted by the market
var marketSubscriptions = _.filter(
subscriptions,
{emitter: 'market'}
);

_.each(plugins, function(plugin) {
_.each(marketSubscriptions, function(sub) {

if(sub.event === 'candle')
// these are handled via the market stream
return;

if(plugin[sub.handler]) {
market.on(sub.event, plugin[sub.handler]);
}

});
});

next();

}

log.info('Setting up Gekko in', mode, 'mode');
log.info('');

Expand All @@ -172,19 +212,12 @@ var pipeline = (settings) => {
loadPlugins,
referenceEmitters,
subscribePlugins,
prepareMarket
prepareMarket,
setupMarket,
subscribePluginsToMarket
],
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 gekko = new GekkoStream(candleConsumers);

market
Expand Down
57 changes: 1 addition & 56 deletions docs/extending/add_a_plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,66 +21,11 @@ Note that in order to use custom plugins, you have to run Gekko over [the comman

*And more! Take a look in the `gekko/plugins folder.`*

## What kind of events can I listen to?

Note that these are low level internal events to the plugin system, they have overlap with the websocket events being streamed to the UI but are not the same.

- `candle`: Every time Gekko calculated a new 1 minute candle from the market.
- `advice`: Every time the trading strategy has new advice.
- `trade`: Every time a trading plugin (either the live trader or the paper trader) has completed a trade.
- `portfolioUpdate`: Is broadcasted once a trading plugin has an updated portflio.

Each of these events contains a javascript object describing the latest data.

## Implementing a new plugin

If you want to add your own plugin you need to expose a constructor function inside
`plugins/[slugname of plugin].js`. The object needs methods based on which event you want
to listen to:

- market feed / candle: `processCandle(candle, callback)`.
This method will be fed a minute candles like:

{
start: [moment object of the start time of the candle],
open: [number, open of candle],
high: [number, high of candle],
low: [number, low of candle],
close: [number, close of candle],
vwp: [number, average weighted price of candle],
volume: [number, total volume volume],
trades: [number, amount of trades]
}

As well as a callback method. You are required to call this method
once you are done processing the candle.

- advice feed / advice `processAdvice(advice)`:
This method will be fed an advice like:

{
recommendation: [position to take, either long or short],
portfolio: [amount of portfolio you should move to position] **DECREPATED**
}

- trading feed / trade `processTrade(trade)`:
This method will be fed a trade like:

{
action: [either "buy" or "sell"],
price: [number, price that was sold at],
date: [moment object, exchange time trade completed at],
portfolio: [object containing amount in currency and asset],
balance: [number, total worth of portfolio]
}

- trading feed / portfolioUpdate `portfolioUpdate(portfolio)`:
This method will be fed an portfolioUpdate like:

{
currency: [number, portfolio amount of currency],
asset: [number, portfolio amount of asset]
}
to listen to. All events can be found in [the events page](../architecture/events.md).

You also need to add an entry for your plugin inside `plugins.js` which registers your plugin for use with Gekko. Finally you need to add a configuration object to `sample-config.js` with at least:

Expand Down
6 changes: 3 additions & 3 deletions docs/internals/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Communicaton between those two components is handled by Node.JS' [Stream API](ht

## A Market

All markets in Gekko eventually output `candle` data. Where these candles come from and how old they are does not matter to the GekkoStream they get piped into. On default Gekko looks for markets in the [`core/markets/` directory](https://github.com/askmike/gekko/tree/stable/core/markets) (but changing that is [not too hard](https://github.com/askmike/gekko/blob/72a858339afb5a856179c716ec4ea13070a6c87c/gekko.js#L48-L49)). The top orange block in the picture is a BudFox market (the default).
All markets in Gekko eventually output `candle` data. Where these candles come from and how old they are does not matter to the GekkoStream they get piped into. On default Gekko looks for markets in the [`core/markets/` directory](https://github.com/askmike/gekko/tree/stable/core/markets). The top orange block in the picture is a BudFox market (the default semi-realtime market that gets live data from exchanges).

Example Markets that come included with Gekko are:

Expand All @@ -23,7 +23,7 @@ Example Markets that come included with Gekko are:

A GekkoStream is nothing more than a collection of [plugins](../commandline/plugins.md). Plugins are simple modules that can subscribe to events, and do something based on event data. The most basic event every GekkoStream has is the "candle" event, this event comes from the market.

However **plugins are allowed to broadcast their own events, which other plugins can subscribe to**. An example of this is the `tradingAdvisor` plugin. This plugin will implement a [trading method](https://github.com/askmike/gekko/blob/stable/docs/Trading_methods.md) that will be fed candle data. As soon as the trading method suggests to take a certain position in the market ("I detect an uptrend, I advice to go **long**") it will broadcast an `advice` event. The `paperTrader` is a plugin that simulates trading using these advices, the `trader` is a plugin that creates real market orders based on these advices. You can decide to only turn the `paperTrader` on or to turn the `trader` on (you now have a live trading bot).
However **plugins are allowed to broadcast their own events, which other plugins can subscribe to**. An example of this is the `tradingAdvisor` plugin. This plugin will implement a [strategy](../strategies/example_strategies) that will be fed candle data. As soon as the strategy suggests to take a certain position in the market ("I detect an uptrend, I advice to go **long**") it will broadcast an `advice` event. The `paperTrader` is a plugin that simulates trading using these advices, the `trader` is a plugin that creates real market orders based on these advices. You can decide to only turn the `paperTrader` on or to turn the `trader` on (you now have a live trading bot).

When you run a backtest using Gekko the following things happen:

Expand All @@ -35,7 +35,7 @@ When you run a backtest using Gekko the following things happen:

## Plugins & Adapters

Those two core components describe the majority of Gekko's flow. A lot "core functionality" like saving candles to disk are simply plugins that push all candles to a database adapter.
Those two core components describe the majority of Gekko's flow. A lot "core functionality" like saving candles to disk are simply plugins that push all candles to a database.

## Seperated architecture

Expand Down
Loading