Last active
October 29, 2023 00:48
-
-
Save LuisAlejandro/b63a19e17a45267dc15e88e133ee6bc5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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