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

Commit

Permalink
normalized backtest market stream behaviour over CP & standalone
Browse files Browse the repository at this point in the history
  • Loading branch information
askmike committed Nov 4, 2016
1 parent 59f7e36 commit 65f372d
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 41 deletions.
40 changes: 19 additions & 21 deletions core/markets/backtest.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var Market = function() {
_.bindAll(this);
this.pushing = false;
this.ended = false;
this.closed = false;

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

Expand All @@ -41,12 +42,9 @@ Market.prototype = Object.create(Readable.prototype, {
constructor: { value: Market }
});

Market.prototype._read = function() {
if(this.pushing)
return;

Market.prototype._read = _.once(function() {
this.get();
}
});

Market.prototype.get = function() {
if(this.iterator.to >= to) {
Expand All @@ -66,8 +64,17 @@ Market.prototype.processCandles = function(err, candles) {
this.pushing = true;
var amount = _.size(candles);

if(amount === 0)
util.die('Query returned no candles (do you have local data for the specified range?)');
if(amount === 0) {
if(this.ended) {
// _.defer(function() {
this.closed = true;
this.reader.close();
this.emit('end');
// }.bind(this));
} else {
util.die('Query returned no candles (do you have local data for the specified range?)');
}
}

if(!this.ended && amount < this.batchSize) {
var d = function(ts) {
Expand All @@ -80,27 +87,18 @@ Market.prototype.processCandles = function(err, candles) {

_.each(candles, function(c, i) {
c.start = moment.unix(c.start);

if(++i === amount) {
// last one candle from batch
if(!this.ended)
this.pushing = false;
else {
_.defer(function() {
this.reader.close();
this.emit('end');
}.bind(this));
}
}

this.push(c);

}, this);

this.pushing = false;

this.iterator = {
from: this.iterator.from.clone().add(this.batchSize, 'm'),
to: this.iterator.from.clone().add(this.batchSize * 2, 'm').subtract(1, 's')
}

if(!this.closed)
this.get();
}

module.exports = Market;
3 changes: 3 additions & 0 deletions core/workers/pipeline/backtestHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ module.exports = done => {

else if(message.type === 'report')
report = message.report;

else if(message.type === 'log')
console.log(message.log);
},
exit: status => {
if(status !== 0)
Expand Down
2 changes: 1 addition & 1 deletion methods/MACD.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ method.log = function() {
log.debug('\t', 'long:', macd.long.result.toFixed(digits));
log.debug('\t', 'macd:', diff.toFixed(digits));
log.debug('\t', 'signal:', signal.toFixed(digits));
log.debug('\t', 'macdiff:', macd.result.toFixed(digits));
log.debug('\t', 'macdiff:', macd.result.toFixed(digits));
}

method.check = function() {
Expand Down
17 changes: 6 additions & 11 deletions plugins/profitSimulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,20 @@ Logger.prototype.calculateStartBalance = function() {
// with more BTC than we started with, this function
// calculates Gekko's profit in %.
Logger.prototype.updatePosition = function(advice) {
var what = advice.recommendation;
let what = advice.recommendation;
let price = advice.candle.close;
let at = advice.candle.start.clone().utc().format();

// virtually trade all {currency} to {asset} at the current price
if(what === 'long') {
this.current.asset += this.extractFee(this.current.currency / this.price);
this.current.asset += this.extractFee(this.current.currency / price);
this.current.currency = 0;
this.trades++;
}

// virtually trade all {currency} to {asset} at the current price
if(what === 'short') {
this.current.currency += this.extractFee(this.current.asset * this.price);
this.current.currency += this.extractFee(this.current.asset * price);
this.current.asset = 0;
this.trades++;
}
Expand Down Expand Up @@ -113,13 +115,6 @@ Logger.prototype.processCandle = function(candle, done) {
if(!this.start.balance)
this.calculateStartBalance();

if(!calcConfig.verbose)
return done();

// skip on history
if(++this.historyReceived < this.historySize)
return done();

done();
}

Expand All @@ -146,7 +141,7 @@ Logger.prototype.jsonReport = function(advice) {
action: 'buy',
price: price,
date: at,
balance: this.current.asset * this.price
balance: this.current.asset * price
}
});

Expand Down
8 changes: 4 additions & 4 deletions plugins/tradingAdvisor/baseTradingMethod.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Base.prototype.tick = function(candle) {
i.update(price);
if(i.input === 'candle')
i.update(candle);
});
},this);

// update the trading method
if(!this.asyncTick || this.requiredHistory > this.age) {
Expand Down Expand Up @@ -252,18 +252,18 @@ Base.prototype.addIndicator = function(name, type, parameters) {
}

Base.prototype.advice = function(newPosition) {
// Possible values are long and short. Long will trigger a buy method
// while short will trigger a sell method
var advice = 'soft';
if(newPosition) {
advice = newPosition;
}

let candle = this.candle;
candle.start = candle.start.clone();
_.defer(function() {
this.emit('advice', {
recommendation: advice,
portfolio: 1,
candle: this.candle
candle
});
}.bind(this));
}
Expand Down
6 changes: 2 additions & 4 deletions web/routes/baseBacktestConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var config = {};
// GENERAL SETTINGS
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

config.silent = true; // for additional logging / debugging
config.silent = true;
config.debug = false;

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -26,12 +26,10 @@ config.tradingAdvisor = {
// do you want Gekko to calculate the profit of its own advice?
config.profitSimulator = {
enabled: true,
// report the profit in the currency or the asset?
reportInCurrency: true,
// start balance, on what the current balance is compared with
simulationBalance: {
// these are in the unit types configured in the watcher.
asset: 1,
asset: 0,
currency: 100,
},
// how much fee in % does each trade cost?
Expand Down

4 comments on commit 65f372d

@thegamecat
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, running a side by side backtest of the same custom method on development branch vs main branch gives a very different outcome - development: 32005% profit over 5 years vs main: 51852%.

I am now re-testing the custom method with different thresholds etc to see if there is a new "sweet spot".

Is there any obvious reason as to why the development branch would be so different?

@askmike
Copy link
Owner Author

@askmike askmike commented on 65f372d Mar 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woa that is an insanely big difference, what candleSize did you use & did you use a method that has a TAlib indicator?

Yes there was a race condition bug in the stable version: the method would broadcast advices later (after the next tick), by this time the tradingSimulator would act on a different candle. This was only a problem with backtesting.

@thegamecat
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah interesting.

Candlesize = 60.

Now interestingly, I improved performance to 60k+% by changing a couple of thresholds. Something that actually made perfect sense because I thought they were too low as to be logical on standard, so now I know you've fixed something I have more confidence!

@askmike
Copy link
Owner Author

@askmike askmike commented on 65f372d Mar 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say the backtester still needs a lot more testing and verification, there are a lot of moving pieces involved in these simulations. I hope that the UI will lower the bar for people to actually run backtests and study the results.

See #489 for more details.

Please sign in to comment.