本人非原创,原文链接https://justhodl.blogspot.com/2018/02/gekko-rsibullbear-tradingview.html
RSI_BULL_BEAR是由@ tommiehansen所撰写的Gekko交易策略,除了最原版的之外,现在最被讨论的大概就是加上ADX指标调整的RSI_BULL_BEAR_ADX了,ADX可以确认市场是否存在趋势以及衡量其强度,只要搭配好的参数很容易能超出原版的表现,在对不同币种的适应性下也更好,但现在又有由 @ Kohette 再加入了Ping Pong trading的概念,简单的说就是在买入后会马上挂一个高于原价%数的卖单,赚取短线的反弹,卖出时反之亦然。
每个策略配置两个文件 *.js 和 *.toml。 配置路径分别为 gekko/strategies 和 gekko/config/strategies
RSI_BULL_BEAR
RSI_BULL_BEAR.js
/*
RSI Bull and Bear
Use different RSI-strategies depending on a longer trend
3 feb 2017
(CC-BY-SA 4.0) Tommie Hansen
https://creativecommons.org/licenses/by-sa/4.0/
*/
// req's
var log = require ('../core/log.js');
var config = require ('../core/util.js').getConfig();
// strategy
var strat = {
/* INIT */
init: function()
{
this.name = 'RSI Bull and Bear';
this.requiredHistory = config.tradingAdvisor.historySize;
this.resetTrend();
// debug? set to flase to disable all logging/messages (improves performance)
this.debug = false;
// performance
config.backtest.batchSize = 1000; // increase performance
config.silent = true;
config.debug = false;
// add indicators
this.addTulipIndicator('maSlow', 'sma', { optInTimePeriod: this.settings.SMA_long });
this.addTulipIndicator('maFast', 'sma', { optInTimePeriod: this.settings.SMA_short });
this.addTulipIndicator('BULL_RSI', 'rsi', { optInTimePeriod: this.settings.BULL_RSI });
this.addTulipIndicator('BEAR_RSI', 'rsi', { optInTimePeriod: this.settings.BEAR_RSI });
// debug stuff
this.startTime = new Date();
this.stat = {
bear: { min: 100, max: 0 },
bull: { min: 100, max: 0 }
};
}, // init()
/* RESET TREND */
resetTrend: function()
{
var trend = {
duration: 0,
direction: 'none',
longPos: false,
};
this.trend = trend;
},
/* get lowest/highest for backtest-period */
lowHigh: function( rsi, type )
{
let cur;
if( type == 'bear' ) {
cur = this.stat.bear;
if( rsi < cur.min ) this.stat.bear.min = rsi; // set new
if( rsi > cur.max ) this.stat.bear.max = rsi;
}
else {
cur = this.stat.bull;
if( rsi < cur.min ) this.stat.bull.min = rsi; // set new
if( rsi > cur.max ) this.stat.bull.max = rsi;
}
},
/* CHECK */
check: function()
{
// get all indicators
let ind = this.tulipIndicators,
maSlow = ind.maSlow.result.result,
maFast = ind.maFast.result.result,
rsi;
// BEAR TREND
if( maFast < maSlow )
{
rsi = ind.BEAR_RSI.result.result;
if( rsi > this.settings.BEAR_RSI_high ) this.short();
else if( rsi < this.settings.BEAR_RSI_low ) this.long();
if(this.debug) this.lowHigh( rsi, 'bear' );
//log.debug('BEAR-trend');
}
// BULL TREND
else
{
rsi = ind.BULL_RSI.result.result;
if( rsi > this.settings.BULL_RSI_high ) this.short();
else if( rsi < this.settings.BULL_RSI_low ) this.long();
if(this.debug) this.lowHigh( rsi, 'bull' );
//log.debug('BULL-trend');
}
}, // 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');
//log.debug('go long');
}
if(this.debug)
{
this.trend.duration++;
log.debug ('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)
{
this.trend.duration++;
log.debug ('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 + ' seconds' : str = minutes + ' minutes';
log.debug('====================================');
log.debug('Finished in ' + str);
log.debug('====================================');
if(this.debug)
{
let stat = this.stat;
log.debug('RSI low/high for period');
log.debug('BEAR low/high: ' + stat.bear.min + ' / ' + stat.bear.max);
log.debug('BULL low/high: ' + stat.bull.min + ' / ' + stat.bull.max);
}
}
};
module.exports = strat;
RSI_BULL_BEAR.toml
# SMA Trends
SMA_long = 1000
SMA_short = 50
# BULL
BULL_RSI = 10
BULL_RSI_high = 80
BULL_RSI_low = 60
# BEAR
BEAR_RSI = 15
BEAR_RSI_high = 50
BEAR_RSI_low = 20
# BULL/BEAR is defined by the longer SMA trends
# if SHORT over LONG = BULL
# if SHORT under LONG = BEAR
RSI_BULL_BEAR_ADX
RSI_BULL_BEAR_ADX.js
/*
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/
*/
// req's
var log = require('../core/log.js');
var config = require('../core/util.js').getConfig();
// strategy
var strat = {
/* INIT */
init: function()
{
// core
this.name = 'RSI Bull and Bear + ADX';
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;
config.debug = false;
// SMA
this.addTulipIndicator('maSlow', 'sma', { optInTimePeriod: this.settings.SMA_long });
this.addTulipIndicator('maFast', 'sma', { optInTimePeriod: this.settings.SMA_short });
// RSI
this.addTulipIndicator('BULL_RSI', 'rsi', { optInTimePeriod: this.settings.BULL_RSI });
this.addTulipIndicator('BEAR_RSI', 'rsi', { optInTimePeriod: this.settings.BEAR_RSI });
// ADX
this.addTulipIndicator('ADX', 'adx', { optInTimePeriod: 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;
}
},
/* CHECK */
check: function()
{
// get all indicators
let ind = this.tulipIndicators,
maSlow = ind.maSlow.result.result,
maFast = ind.maFast.result.result,
rsi,
adx = ind.ADX.result.result;
// BEAR TREND
if( maFast < maSlow )
{
rsi = ind.BEAR_RSI.result.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 = ind.BULL_RSI.result.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;
RSI_BULL_BEAR_ADX.toml
# SMA INDICATOR
SMA_long = 1000
SMA_short = 50
# RSI BULL / BEAR
BULL_RSI = 10
BULL_RSI_high = 80
BULL_RSI_low = 60
BEAR_RSI = 15
BEAR_RSI_high = 50
BEAR_RSI_low = 20
# 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
RSI_BULL_BEAR_PINGPONG
RSI_BULL_BEAR_PINGPONG.js
/*
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/
UPDATE:
3. Add pingPong for sideways market
Rafael Martín.
*/
// req's
var log = require('../core/log.js');
var config = require('../core/util.js').getConfig();
// strategy
var strat = {
/* INIT */
init: function()
{
// core
this.name = 'RSI Bull and Bear + ADX + PingPong';
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;
config.debug = false;
// SMA
this.addTulipIndicator('maSlow', 'sma', { optInTimePeriod: this.settings.SMA_long });
this.addTulipIndicator('maFast', 'sma', { optInTimePeriod: this.settings.SMA_short });
// RSI
this.addTulipIndicator('BULL_RSI', 'rsi', { optInTimePeriod: this.settings.BULL_RSI });
this.addTulipIndicator('BEAR_RSI', 'rsi', { optInTimePeriod: this.settings.BEAR_RSI });
// ADX
this.addTulipIndicator('ADX', 'adx', { optInTimePeriod: 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 will be false or a price if we already have a long position
pingPong : {
gainsPercentage: this.settings.PINGPONG_GAINS_PERCENTAGE // when we want to close the long position?
}
};
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;
}
},
/* CHECK */
check: function()
{
// get all indicators
let ind = this.tulipIndicators,
maSlow = ind.maSlow.result.result,
maFast = ind.maFast.result.result,
rsi,
adx = ind.ADX.result.result;
// BEAR TREND
if( maFast < maSlow )
{
rsi = ind.BEAR_RSI.result.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();
//else this.pingPong();
if(this.debug) this.lowHigh( rsi, 'bear' );
}
// BULL TREND
else
{
rsi = ind.BULL_RSI.result.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();
else this.pingPong();
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.trend.longPos = this.candle.close;
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.trend.longPos = false;
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)');
}
},
pingPong: function() {
/**
* Si actualmente tenemos una posicion long abierta vamos a comprobar si el precio
* actual del asset es un <gainsPercentage> más alto (trend.long + %gainsPercentage >= currentPrice)
* y si es así cerramos la posición.
*/
if (this.trend.longPos) {
/**
* Si tenemos una posicion long abierta pero la tendencia actual es bullish entonces
* no hacemos nada y dejamos que siga subiendo
*/
//if (this.trend.direction == 'up') return;
if (this.candle.close < (this.trend.longPos - (this.trend.longPos * (this.trend.pingPong.gainsPercentage / 3) / 100))) this.trend.longPos = this.candle.close;
/**
* Si no tenemos un porcentage de ganancias salimos de aqui
*/
if (this.candle.close < (this.trend.longPos + (this.trend.longPos * this.trend.pingPong.gainsPercentage / 100) )) return;
/**
* Si hemos llegado hasta aqui significa que tenemos un long abierto, la tendencia actual es
* bajista y tenemos un <gainsPercentage> de ganancias, por lo tanto cerramos la posicion
* para recoger ganancias y ponemos el longPos en false.
*/
this.trend.longPos = false;
this.advice('short');
/**
* Si hemos llegado hasta aqui significa que no tenemos ninguna posicion long abierta, por lo tanto
* podemos aprovechar para abrir una nueva posicion cuando sea el momento propicio.
*/
} else {
/**
* Si estamos en tendencia bajista salimos de aqui sin hacer nada, asi dejamos que siga
* bajando y solo actuamos cuando la tendencia cambie a alcista (bullish).
*/
if (this.trend.direction == 'down') return;
/**
* Si ha bajado al menos un <gains_percentage> abrimos un nuevo long
*/
//if (this.candle.close < (this.trend.longPos - (this.trend.longPos * this.trend.pingPong.gainsPercentage / 100) )) return;
/**
* Si hemos llegado hasta aqui significa que se cumple los requisitos necesarios para volver a
* abrir una posicion long, por lo tanto ejecutamos un long y ademas guardamos el precio de la
* candle actual para saber a que precio hemos iniciado el long.
*/
this.trend.longPos = this.candle.close;
this.advice('long');
}
},
/* 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;
RSI_BULL_BEAR_PINGPONG.toml
# SMA INDICATOR
SMA_long = 1000
SMA_short = 50
# RSI BULL
BULL_RSI = 10
BULL_RSI_high = 80
BULL_RSI_low = 60
# RSI BEAR
BEAR_RSI = 15
BEAR_RSI_high = 50
BEAR_RSI_low = 20
# 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
# PING PONG
PINGPONG_GAINS_PERCENTAGE = 2
大家可以选择策略进行backtest,选择适合自己的参数。
欢迎大家加入gekko 知识星球, 在这里你可以得到,关于gekko环境搭建问题的解答/查看群主分享的交易机器人策略/加入gekko策略交流群组,以及获取更多的关于gekko使用方面的信息。
没有任何基础的小白也可以搭建自己的交易机器人。