Skip to content

Instantly share code, notes, and snippets.

@LuisAlejandro
Last active October 29, 2023 00:48
Show Gist options
  • Save LuisAlejandro/b63a19e17a45267dc15e88e133ee6bc5 to your computer and use it in GitHub Desktop.
Save LuisAlejandro/b63a19e17a45267dc15e88e133ee6bc5 to your computer and use it in GitHub Desktop.
//
// This file is part of ZenProfits.
// Copyright (C) 2021, Luis Martínez.
//
// Please refer to AUTHORS.rst for a complete list of Copyright holders.
//
// ZenProfits is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ZenProfits is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses.
//
const z = require('zero-fill')
const n = require('numbro')
const debug = require('../../../lib/debug')
const zp_stddev = require('../../../lib/zp_stddev')
const zp_trema = require('../../../lib/zp_trema')
const zp_adx = require('../../../lib/zp_adx')
const zp_stochastic = require('../../../lib/zp_stochastic')
const Phenotypes = require('../../../lib/phenotype')
module.exports = {
name: 'zp_ema_dema',
description: 'Trades with trend_ema behavior when in strong trend bullish market, ' +
'switches to stochastic when in ranging bullish market. Does nothing when in bearish market.',
getOptions: function () {
this.option('period', 'period length, same as --period_length', String, '15m')
this.option('period_length', 'period length, same as --period', String, '15m')
this.option('min_periods', 'min. number of history periods', Number, 52)
this.option('ema_trend_period', 'number of periods for trend EMA', Number, 5)
this.option('ema_short_period', 'number of periods for the shorter EMA', Number, 13)
this.option('ema_long_period', 'number of periods for the longer EMA', Number, 89)
this.option('adx_periods', 'number of periods for ADX', Number, 14)
this.option('adx_threshold', 'ADX threshold for trending market', Number, 20)
this.option('fast_k_periods', 'periods for building the Fast-K line', Number, 14)
this.option('slow_k_periods', 'periods for building the Slow-K line', Number, 3)
this.option('slow_k_ma_type', 'type of Moving Average for Slow-K: SMA,EMA,WMA,DEMA,TEMA,TRIMA,KAMA,MAMA,T3', String, 'SMA')
this.option('slow_d_periods', 'periods for building the Slow-D line', Number, 3)
this.option('slow_d_ma_type', 'type of Moving Average for Slow-D: SMA,EMA,WMA,DEMA,TEMA,TRIMA,KAMA,MAMA,T3', String, 'SMA')
},
calculate: function(s) {
if (s.in_preroll) return
},
onPeriod: function (s, cb) {
function pushMessage(title, message) {
if (s.options.mode === 'live' || s.options.mode === 'paper') {
s.notifier.pushMessage(title, message)
debug.msg(title + ': ' + message)
}
}
if (s.in_preroll) return cb()
if (s.options.mode === 'live' || s.options.mode === 'paper') {
process.stdout.write('\n' + [
z(19, 'DATE', ' ').grey,
z(18, 'PRICE', ' ').grey,
z(9, 'DIFF', ' ').grey,
z(11, 'DEMA', ' ').grey,
z(9, 'ADX', ' ').grey,
z(9, 'STOCH', ' ').grey,
z(11, 'EMA', ' ').grey,
].join('') + '\n')
}
zp_trema(s, s.options.ema_trend_period,
s.options.ema_short_period, s.options.ema_long_period).then((trema) => {
zp_adx(s, s.options.adx_periods).then((adx) => {
zp_stochastic(s, s.options.fast_k_periods,
s.options.slow_k_periods, s.options.slow_k_ma_type,
s.options.slow_d_periods, s.options.slow_d_ma_type).then((stoch) => {
s.period.K = stoch.K
s.period.D = stoch.D
s.period.ema_trend = trema[0]
s.period.ema_short = trema[1]
s.period.ema_long = trema[2]
s.period.adx = adx[0]
if (typeof s.period.ema_short === 'number' && !isNaN(s.period.ema_short) &&
typeof s.period.ema_long === 'number' && !isNaN(s.period.ema_long)) {
s.period.dema_histogram = s.period.ema_short - s.period.ema_long
}
if (typeof s.period.K === 'number' && !isNaN(s.period.K) &&
typeof s.period.D === 'number' && !isNaN(s.period.D)) {
s.period.stoch_histogram = s.period.K - s.period.D
}
if (typeof s.period.ema_trend === 'number' && !isNaN(s.period.ema_trend) &&
typeof s.lookback[0].ema_trend === 'number' && !isNaN(s.lookback[0].ema_trend)) {
s.period.ema_trend_rate = s.lookback[0].ema_trend ?
((s.period.ema_trend - s.lookback[0].ema_trend) / s.lookback[0].ema_trend) * 100 : 0
} else {
s.period.ema_trend_rate = 0
}
zp_stddev(s, s.options.ema_trend_period).then((stddev) => {
s.period.ema_trend_stddev = stddev
// if indicators are not ready, we do nothing
if (typeof s.period.K !== 'number' || isNaN(s.period.K) ||
typeof s.period.D !== 'number' || isNaN(s.period.D) ||
typeof s.period.ema_trend !== 'number' || isNaN(s.period.ema_trend) ||
typeof s.period.ema_short !== 'number' || isNaN(s.period.ema_short) ||
typeof s.period.ema_long !== 'number' || isNaN(s.period.ema_long) ||
typeof s.period.adx !== 'number' || isNaN(s.period.adx) ||
typeof s.period.ema_trend_stddev !== 'number' || isNaN(s.period.ema_trend_stddev) ||
typeof s.period.dema_histogram !== 'number' || isNaN(s.period.dema_histogram) ||
typeof s.lookback[0].dema_histogram !== 'number' || isNaN(s.lookback[0].dema_histogram) ||
typeof s.period.stoch_histogram !== 'number' || isNaN(s.period.stoch_histogram) ||
typeof s.lookback[0].stoch_histogram !== 'number' || isNaN(s.lookback[0].stoch_histogram) ||
typeof s.period.ema_trend_rate !== 'number' || isNaN(s.period.ema_trend_rate) ||
typeof s.lookback[0].ema_trend_rate !== 'number' || isNaN(s.lookback[0].ema_trend_rate)) {
s.signal = null
return cb()
}
// bullish market, we buy
if (s.period.dema_histogram > 0) {
if (s.lookback[0].dema_histogram <= 0) {
pushMessage(`[${s.exchange.name}.${s.asset}-${s.currency}]`,
'intel: 😍 entering bullish market')
s.signal = 'buy'
return cb()
}
// solid trend market, we enter ema_trend behavior
if (s.period.adx > s.options.adx_threshold) {
if (s.lookback[0].adx <= s.options.adx_threshold) {
if (s.period.ema_trend_rate > 0) {
pushMessage(`[${s.exchange.name}.${s.asset}-${s.currency}]`,
'intel: ↗ the market started a strong bullish trend')
s.signal = 'buy'
return cb()
} else {
pushMessage(`[${s.exchange.name}.${s.asset}-${s.currency}]`,
'intel: ↘ the market started a strong bearish trend')
s.signal = 'sell'
return cb()
}
}
// ema_trend slope is upward beyond the standard deviation, we buy
// negative to positive
if ((s.period.ema_trend_rate - s.period.ema_trend_stddev) > 0) {
if ((s.lookback[0].ema_trend_rate - s.lookback[0].ema_trend_stddev) <= 0) {
s.signal = 'buy'
return cb()
}
// ema_trend slope is downward beyond the standard deviation, we sell
// positive to negative
} else if ((s.period.ema_trend_rate + s.period.ema_trend_stddev) <= 0) {
if ((s.lookback[0].ema_trend_rate + s.lookback[0].ema_trend_stddev) > 0) {
s.signal = 'sell'
return cb()
}
}
// sideways market, we use stochastic lines crossover for signals
} else if (s.period.adx <= s.options.adx_threshold) {
if (s.lookback[0].adx > s.options.adx_threshold) {
if (s.period.stoch_histogram > 0) {
pushMessage(`[${s.exchange.name}.${s.asset}-${s.currency}]`,
'intel: ↕ the market started moving sideways on a bullish trend')
s.signal = 'buy'
return cb()
} else {
pushMessage(`[${s.exchange.name}.${s.asset}-${s.currency}]`,
'intel: ↕ the market started moving sideways on a bearish trend')
s.signal = 'sell'
return cb()
}
}
// negative to positive %K - %D
if (s.period.stoch_histogram > 0) {
if (s.lookback[0].stoch_histogram <= 0) {
if (s.period.K <= 20 || s.lookback[0].K <= 20 || s.lookback[1].K <= 20) {
s.signal = 'buy'
return cb()
}
}
// positive to negative %K - %D
} else if (s.period.stoch_histogram <= 0) {
if (s.lookback[0].stoch_histogram > 0) {
if (s.period.K >= 80 || s.lookback[0].K >= 80 || s.lookback[1].K >= 80) {
s.signal = 'sell'
return cb()
}
}
}
}
// bearish market, we sell and do nothing, wait for bullish market
} else if (s.period.dema_histogram <= 0) {
if (s.lookback[0].dema_histogram > 0) {
pushMessage(`[${s.exchange.name}.${s.asset}-${s.currency}]`,
'intel: 😅 entering bearish market')
s.signal = 'sell'
return cb()
}
}
s.signal = null
return cb()
}).catch((error) => {
console.log(error)
return cb()
})
}).catch((error) => {
console.log(error)
return cb()
})
}).catch((error) => {
console.log(error)
return cb()
})
}).catch((error) => {
console.log(error)
return cb()
})
},
onReport: function(s) {
var cols = []
var emacolor = 'grey'
var demacolor = 'grey'
var stochcolor = 'grey'
var adxcolor = 'grey'
if (typeof s.period.dema_histogram !== 'number' || isNaN(s.period.dema_histogram) ||
typeof s.period.stoch_histogram !== 'number' || isNaN(s.period.stoch_histogram) ||
typeof s.period.adx !== 'number' || isNaN(s.period.adx) ||
typeof s.period.ema_trend_rate !== 'number' || isNaN(s.period.ema_trend_rate) ||
typeof s.period.ema_trend_stddev !== 'number' || isNaN(s.period.ema_trend_stddev)) {
cols.push(' ')
return cols
}
if (s.period.dema_histogram > 0) {
demacolor = 'green'
} else if (s.period.dema_histogram <= 0) {
demacolor = 'red'
}
if (s.period.stoch_histogram > 0) {
stochcolor = 'green'
} else if (s.period.stoch_histogram <= 0) {
stochcolor = 'red'
}
if (s.period.adx > s.options.adx_threshold) {
adxcolor = 'green'
} else if (s.period.adx <= s.options.adx_threshold) {
adxcolor = 'red'
}
if ((s.period.ema_trend_rate - s.period.ema_trend_stddev) > 0) {
emacolor = 'green'
} else if ((s.period.ema_trend_rate + s.period.ema_trend_stddev) <= 0) {
emacolor = 'red'
}
cols.push(z(10, n(s.period.dema_histogram).format('+0.000000'), ' ')[demacolor])
cols.push(z(8, n(s.period.stoch_histogram).format('+0.00'), ' ')[stochcolor])
cols.push(z(8, n(s.period.adx).format('+0.00'), ' ')[adxcolor])
cols.push(z(10, n(s.period.ema_trend_rate).format('+0.000000'), ' ')[emacolor])
return cols
},
phenotypes: {
// -- common
period_length: Phenotypes.ListOption([15, 30, 45, 60]),
profit_stop_enable_pct: Phenotypes.ListOption([6, 8, 10, 12]),
profit_stop_pct: Phenotypes.ListOption([0.5, 1, 2, 3]),
max_buy_loss_pct: Phenotypes.ListOption([0.1, 0.2, 0.5, 1]),
max_sell_loss_pct: Phenotypes.ListOption([0.1, 0.2, 0.5, 1]),
sell_stop_pct: Phenotypes.ListOption([1, 2, 3, 4]),
buy_stop_pct: Phenotypes.ListOption([1, 2, 3, 4]),
// -- strategy
ema_trend_period: Phenotypes.ListOption([5, 8, 13, 21]),
ema_short_period: Phenotypes.ListOption([21, 34, 55, 89]),
ema_long_period: Phenotypes.ListOption([55, 89, 144, 233]),
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment