[Share] RSI-BB-ADX with candlebatcher
#1
This is more a share of the method I'm using for candle batching than of the strategy, but as Tommies RSI strat is rather excellent and so widely known it seemed a good demonstrator.

This strategy runs on 1 minute candles and batches them in the update function. 
The less obvious bit is that instead of keeping one copy of each indicator, it keeps an array of each indicator as long as the candle size for that indicator. Each new minute candle, the next indicator in the array is updated and read. 
The upshot of this is that each minute there is an up to date result of the longer term indicator, allowing far more accurate entry and exit points checked every minute, instead of every 5, 10, 30, 60 etc minutes. This also makes a strategy less sensitive to start time - although it still makes more of a difference than I'd expect!

The timeframe for each indicator is also independent so different candle sizes can be used for bear and bull market indicators - however the indicator candle size and indicator period do become a little interchangeable as they're both changing the amount of time that the indicator is watching the market over.

Other benefits of running at 1 minute is that any stop losses or take profits that you might add can be checked that much more frequently, so are that much more useful at catching quick market movements.

Downsides:
> As with any strategy running on very short timeframes trying to tune it with GA style optimisers can results in huge profits with hundreds of trades per day that will perform absolutely miserably if ran live as the orders simply can't be filled. Tuning requires a little more understanding of what the strategy is doing and inputting sensible parameters.
> Gekko currently has a limitation on the number of candles it can pre-seed a strategy with of around 4000. When running at 1 minute, this is 2.7 days of history so you need to shrink those long SMAs


So - here's a quick example backtest from 6 months history on an alt, with Tommie's original strategy first, candlebatched second. Both have exactly the same parameters, as in the TOML file below.

Original:
[Image: DJkIwSX.png]

Candlebatched:
[Image: fBuUMVB.png]
Paper trading settings are at their defaults. These two are purely intended as a relative comparison. I obviously can't promise any results but hope it shows that there is potential!


I'm sharing this under the same CC-BY-SA 4.0 license that Tommie shared his under - I learnt a lot from that bit of code, so hopefully giving a little back.
Run the strategy in 1 minute candles in gekko and specify the candle sizes you want each indicator to run at with the timeframe parameter in the TOML.

TOML: Github
Code:
# SMA INDICATOR
SMA_long = 1000
SMA_short = 50
SMA_Timeframe = 10

# RSI BULL / BEAR
BULL_RSI = 10
BULL_RSI_high = 80
BULL_RSI_low = 45
BULL_RSI_Timeframe = 10

BEAR_RSI = 15
BEAR_RSI_high = 50
BEAR_RSI_low = 20
BEAR_RSI_Timeframe = 10

# MODIFY RSI (depending on ADX)
BULL_MOD_high = 5
BULL_MOD_low = -5
BEAR_MOD_high = 15
BEAR_MOD_low = -5

# ADX
ADX = 3
ADX_high = 70
ADX_low = 50
ADX_Timeframe = 10

Strategy: Github
Code:
/*
    Adapted to run on 1 Minute candles with candle batching
    by Gryphon/RJPGriffin Nov'18

    RSI Bull and Bear + ADX modifier
    1. Use different RSI-strategies depending on a longer trend
    2. But modify this slighly if shorter BULL/BEAR is detected
    -
    (CC-BY-SA 4.0) Tommie Hansen
    https://creativecommons.org/licenses/by-sa/4.0/
    -
    NOTE: Requires custom indicators found here:
    https://github.com/Gab0/Gekko-extra-indicators
    (c) Gabriel Araujo
    Howto: Download + add to gekko/strategies/indicators
*/

// req's
var log = require('../core/log.js');
var config = require('../core/util.js').getConfig();
var RSI = require('./indicators/RSI.js')
var ADX = require('./indicators/ADX.js')
var SMA = require('./indicators/SMA.js')


// strategy
var strat = {

 /* INIT */
 init: function() {
   // core
   this.name = 'RSI Bull and Bear + ADX M1';
   this.requiredHistory = config.tradingAdvisor.historySize;
   this.resetTrend();

   // debug? set to false to disable all logging/messages/stats (improves performance in backtests)
   this.debug = false;

   // performance
   config.backtest.batchSize = 1000; // increase performance
   config.silent = true; // NOTE: You may want to set this to 'false' @ live
   config.debug = false;

   //Add Custom Timeframe indicators
   //SMA
   this.maSlow = new SMA(this.settings.SMA_long);
   this.maFast = new SMA(this.settings.SMA_short)

   // RSI
   this.BULL_RSI = [];
   for (let i = 0; i < this.settings.BULL_RSI_Timeframe; i++) {
     this.BULL_RSI[i] = new RSI({
       interval: this.settings.BULL_RSI
     });
   }

   this.BEAR_RSI = [];
   for (let i = 0; i < this.settings.BEAR_RSI_Timeframe; i++) {
     this.BEAR_RSI[i] = new RSI({
       interval: this.settings.BEAR_RSI
     });
   }

   // ADX
   this.ADX = new ADX(this.settings.ADX);

   this.timeframes = {
     SMA: this.settings.SMA_Timeframe,
     SMA_Count: 0,
     BULL_RSI: this.settings.BULL_RSI_Timeframe,
     BULL_RSI_Count: 0,
     BEAR_RSI: this.settings.BEAR_RSI_Timeframe,
     BEAR_RSI_Count: 0,
     ADX: this.settings.ADX_Timeframe,
     ADX_Count: 0
   };

   // ADX
   this.addIndicator('ADX', 'ADX', this.settings.ADX);

   // MOD (RSI modifiers)
   this.BULL_MOD_high = this.settings.BULL_MOD_high;
   this.BULL_MOD_low = this.settings.BULL_MOD_low;
   this.BEAR_MOD_high = this.settings.BEAR_MOD_high;
   this.BEAR_MOD_low = this.settings.BEAR_MOD_low;


   // debug stuff
   this.startTime = new Date();

   // add min/max if debug
   if (this.debug) {
     this.stat = {
       adx: {
         min: 1000,
         max: 0
       },
       bear: {
         min: 1000,
         max: 0
       },
       bull: {
         min: 1000,
         max: 0
       }
     };
   }

   /* MESSAGES */

   // message the user about required history
   log.info("====================================");
   log.info('Running', this.name);
   log.info('====================================');
   log.info("Make sure your warmup period matches SMA_long and that Gekko downloads data if needed");

   // warn users
   if (this.requiredHistory < this.settings.SMA_long) {
     log.warn("*** WARNING *** Your Warmup period is lower then SMA_long. If Gekko does not download data automatically when running LIVE the strategy will default to BEAR-mode until it has enough data.");
   }

 }, // init()


 /* RESET TREND */
 resetTrend: function() {
   var trend = {
     duration: 0,
     direction: 'none',
     longPos: false,
   };

   this.trend = trend;
 },


 /* get low/high for backtest-period */
 lowHigh: function(val, type) {
   let cur;
   if (type == 'bear') {
     cur = this.stat.bear;
     if (val < cur.min) this.stat.bear.min = val; // set new
     else if (val > cur.max) this.stat.bear.max = val;
   } else if (type == 'bull') {
     cur = this.stat.bull;
     if (val < cur.min) this.stat.bull.min = val; // set new
     else if (val > cur.max) this.stat.bull.max = val;
   } else {
     cur = this.stat.adx;
     if (val < cur.min) this.stat.adx.min = val; // set new
     else if (val > cur.max) this.stat.adx.max = val;
   }
 },

 //Update all of the non gekko managed indicators here
 update: function(candle) {
   tf = this.timeframes;
   if (tf.SMA_Count >= tf.SMA) {
     this.maSlow.update(candle.close);
     this.maFast.update(candle.close);
     tf.SMA_Count = 0;
   } else {
     tf.SMA_Count++;
   }

   tf.BULL_RSI_Count = (tf.BULL_RSI_Count + 1) % (tf.BULL_RSI - 1);
   this.BULL_RSI[tf.BULL_RSI_Count].update(candle);


   tf.BEAR_RSI_Count = (tf.BEAR_RSI_Count + 1) % (tf.BEAR_RSI - 1);
   this.BEAR_RSI[tf.BEAR_RSI_Count].update(candle);

   if (tf.ADX_Count >= tf.ADX) {
     this.ADX.update(candle);
     tf.ADX_Count = 0;
   } else {
     tf.ADX_Count++;
   }
 },


 /* CHECK */
 check: function(candle) {
   // get all indicators

   var maSlow = this.maSlow.result,
     maFast = this.maFast.result,
     rsi,
     adx = this.ADX.result;

   // BEAR TREND
   // NOTE: maFast will always be under maSlow if maSlow can't be calculated
   if (maFast < maSlow) {
     rsi = this.BEAR_RSI[this.timeframes.BEAR_RSI_Count].result;
     let rsi_hi = this.settings.BEAR_RSI_high,
       rsi_low = this.settings.BEAR_RSI_low;

     // ADX trend strength?
     if (adx > this.settings.ADX_high) rsi_hi = rsi_hi + this.BEAR_MOD_high;
     else if (adx < this.settings.ADX_low) rsi_low = rsi_low + this.BEAR_MOD_low;

     if (rsi > rsi_hi) this.short();
     else if (rsi < rsi_low) this.long();

     if (this.debug) this.lowHigh(rsi, 'bear');
   }

   // BULL TREND
   else {
     rsi = this.BULL_RSI[this.timeframes.BULL_RSI_Count].result;
     let rsi_hi = this.settings.BULL_RSI_high,
       rsi_low = this.settings.BULL_RSI_low;

     // ADX trend strength?
     if (adx > this.settings.ADX_high) rsi_hi = rsi_hi + this.BULL_MOD_high;
     else if (adx < this.settings.ADX_low) rsi_low = rsi_low + this.BULL_MOD_low;

     if (rsi > rsi_hi) this.short();
     else if (rsi < rsi_low) this.long();
     if (this.debug) this.lowHigh(rsi, 'bull');
   }

   // add adx low/high if debug
   if (this.debug) this.lowHigh(adx, 'adx');

 }, // check()


 /* LONG */
 long: function() {
   if (this.trend.direction !== 'up') // new trend? (only act on new trends)
   {
     this.resetTrend();
     this.trend.direction = 'up';
     this.advice('long');
     if (this.debug) log.info('Going long');
   }

   if (this.debug) {
     this.trend.duration++;
     log.info('Long since', this.trend.duration, 'candle(s)');
   }
 },


 /* SHORT */
 short: function() {
   // new trend? (else do things)
   if (this.trend.direction !== 'down') {
     this.resetTrend();
     this.trend.direction = 'down';
     this.advice('short');
     if (this.debug) log.info('Going short');
   }

   if (this.debug) {
     this.trend.duration++;
     log.info('Short since', this.trend.duration, 'candle(s)');
   }
 },


 /* END backtest */
 end: function() {
   let seconds = ((new Date() - this.startTime) / 1000),
     minutes = seconds / 60,
     str;

   minutes < 1 ? str = seconds.toFixed(2) + ' seconds' : str = minutes.toFixed(2) + ' minutes';

   log.info('====================================');
   log.info('Finished in ' + str);
   log.info('====================================');

   // print stats and messages if debug
   if (this.debug) {
     let stat = this.stat;
     log.info('BEAR RSI low/high: ' + stat.bear.min + ' / ' + stat.bear.max);
     log.info('BULL RSI low/high: ' + stat.bull.min + ' / ' + stat.bull.max);
     log.info('ADX min/max: ' + stat.adx.min + ' / ' + stat.adx.max);
   }

 }

};

module.exports = strat;
  Reply
#2
Great stuff Gryphon! Definite going to implement this in my setup. Thanks for the share.
  Reply
#3
Heart 
Dear Gryphon, thank you for your efforts and time you put into this strategy and especially for sharing this well-thought trough strategy with us.

From what I've seen and understood, this strategy has some advantages:
  • Performing in bull and bear markets and performing not too bad sideways
  • Optimized the use of 1 min candles and making these most useful
  • Built to perfom live and not just on paper.

Settings:
I've made some simulations with some 20-50 alts.
These are my current "best" performing settings usually better performing than market with 0.3% fees set.
Making huge gains in bull market and not too hard losses in bearmarkets. Need some improvements siedways, any ideas Idea ?

Please let me know your thoughts and post or PM your settings if you find these useful or have made improvements based on mine.

Quote:# SMA INDICATOR
SMA_long = 180
SMA_short = 55
SMA_Timeframe = 10

# RSI BULL / BEAR
BULL_RSI = 9
BULL_RSI_high = 85
BULL_RSI_low = 60
BULL_RSI_Timeframe = 10

BEAR_RSI = 12.5
BEAR_RSI_high = 60
BEAR_RSI_low = 25
BEAR_RSI_Timeframe = 10

# MODIFY RSI (depending on ADX)
BULL_MOD_high = 5
BULL_MOD_low = -5
BEAR_MOD_high = 5
BEAR_MOD_low = -5

# ADX
ADX = 10
ADX_high = 50
ADX_low = 40
ADX_Timeframe = 10
  Reply
#4
Dear Gryphon, thank you for your efforts and time you put into this strategy and especially for sharing this well-thought trough strategy with us.

From what I've seen and understood, this strategy has some advantages:
 
  • Performing in bull and bear markets and performing not too bad sideways
  • Optimized the use of 1 min candles and making these most useful
  • Built to perfom live and not just on paper.
 
Settings:
I've made some simulations with some 20-50 alts.
The one below is my current "best" performing setting, usually better performing than market with 0.3% fees set.
Sometimes making huge losses like 10-15% on a trade and I can't figure how to smooth this out.

Please let me know your thoughts and post or PM your settings if you find these useful or have made improvements based on mine.

Quote:# SMA INDICATOR
SMA_long = 180
SMA_short = 55
SMA_Timeframe = 10

# RSI BULL / BEAR
BULL_RSI = 9
BULL_RSI_high = 85
BULL_RSI_low = 60
BULL_RSI_Timeframe = 10

BEAR_RSI = 12.5
BEAR_RSI_high = 60
BEAR_RSI_low = 25
BEAR_RSI_Timeframe = 10

# MODIFY RSI (depending on ADX)
BULL_MOD_high = 5
BULL_MOD_low = -5
BEAR_MOD_high = 5
BEAR_MOD_low = -5

# ADX
ADX = 10
ADX_high = 50
ADX_low = 40
ADX_Timeframe = 10
  Reply
#5
Thank you
  Reply
#6
Gryphon, great work as always, i'm gonna throw this one on paper trader and see what happens throughout December i think. Are you looking to add the trailing stop into this? Based on the strat logic, backtests never showed any benefit on adding the SL, but on live market i can only see this being a benefit.
  Reply
#7
So I took Gryphons work, grabbed kris191 idea, got into coding this piece and :

  Added STOP-LOSS (You really would want to get out at a certain % loss )
  Added a minimum short spread (Think of all the fees, you might not want to trade .0003698 against .0003699)

https://github.com/R4nd0/Gekko-Strategies
  Reply
#8
Nice to see people experimenting with it already Smile

The strategy itself and the trading logic is exactly the same as Tommie's original strategy - I'd say that this has the same strengths and weaknesses, just that it checks the market that much more regularly that entry and exit points are caught sooner, which generally results in it making more trades.

The large losses that it can make on trades when the market dips sharply is a result of trading with the RSI indicator crossing thresholds. It could help to take a buy signal as the RSI value passing back up through the RSI_Low threshold, which is more likely to signal the market beginning to rise. When I tried this with the original strategy it didn't work well as due to the infrequent checks, the good buy in point was often long gone by the time the strategy had realised the RSI was above the threshold again. With this checking it every minute it proved far more successful.

I like stop losses in a strategy. In my view, if adding a stop loss kills a strategy in back testing, then the strategy is picking the wrong time to buy in. Of course this is much easier said than done, but I've had more success with this candle batching than other strategies. I've yet to try it, but Gekko's new inbuilt trailing stop loss should be the way to go for stop losses as it acts on tick data rather than candle.
  Reply
#9
Guys;

I am experimenting with this - and would really love to fire it up on live trade (for testing) - As I have "0" experience coding Sad

Unfortunately I am failing to create the config file (for running on CLI).

Could someone help ?

I was getting cannot set property 'batchsize' of undefined; and when I removed the backtesting part (lines 31); I get another error of cannot find module of undefined errors ...
  Reply
#10
(12-05-2018, 10:40 AM)xelixes Wrote: Guys;

I am experimenting with this - and would really love to fire it up on live trade (for testing) - As I have "0" experience coding Sad  

Unfortunately I am failing to create the config file (for running on CLI).

Could someone help ?

I was getting cannot set property 'batchsize' of undefined; and when I removed the backtesting part (lines 31); I get another error of cannot find module of undefined errors ...

this the error I am getting  (note I cant start live trading on GUI either!):
======
/xx/pi/gekko/strategies/RSI_BULL_BEAR_ADX_CB_SGLGL.js:43
    config.backtest.batchSize = 10000; // increase performance
                              ^

TypeError: Cannot set property 'batchSize' of undefined
    at Base.init (/xx/pi/gekko/strategies/RSI_BULL_BEAR_ADX_CB_SGLGL.js:43:31)
    at Base.bound [as init] (/xx/pi/gekko/node_modules/lodash/dist/lodash.js:729:21)
    at new Base (/xx/pi/gekko/plugins/tradingAdvisor/baseTradingMethod.js:70:8)
    at Actor.setupStrategy (/xx/pi/gekko/plugins/tradingAdvisor/tradingAdvisor.js:61:19)
    at Actor.bound [as setupStrategy] (/xx/pi/gekko/node_modules/lodash/dist/lodash.js:729:21)
    at new Actor (/xx/pi/gekko/plugins/tradingAdvisor/tradingAdvisor.js:23:8)
    at load (/xx/pi/gekko/core/pluginUtil.js:98:22)
    at /xx/pi/gekko/node_modules/async/dist/async.js:1156:9
    at replenish (/xx/pi/gekko/node_modules/async/dist/async.js:1030:17)
    at iterateeCallback (/xx/pi/gekko/node_modules/async/dist/async.js:1015:17)

=======



after removing batchsize thing from js file, I am getting:

2018-12-05 17:26:18 (INFO): Make sure your warmup period matches SMA_long and that Gekko downloads data if needed
module.js:550
    throw err;
    ^

Error: Cannot find module '/xx/pi/gekko/core/../plugins/undefined/reader'
    at Function.Module._resolveFilename (module.js:548:15)
    at Function.Module._load (module.js:475:25)
    at Module.require (module.js:597:17)
    at require (internal/module.js:11:18)
    at Stitcher.prepareHistoricalData (/xx/pi/gekko/core/tools/dataStitcher.js:50:16)
    at new Actor (/xx/pi/gekko/plugins/tradingAdvisor/tradingAdvisor.js:34:14)
    at load (/xx/pi/gekko/core/pluginUtil.js:98:22)
    at /xx/pi/gekko/node_modules/async/dist/async.js:1156:9
    at replenish (/xx/pi/gekko/node_modules/async/dist/async.js:1030:17)
    at iterateeCallback (/xx/pi/gekko/node_modules/async/dist/async.js:1015:17)
  Reply


Forum Jump:


Users browsing this thread: