diff --git a/commands/sim.js b/commands/sim.js index a7e799dbea..4a2379e3ec 100644 --- a/commands/sim.js +++ b/commands/sim.js @@ -84,7 +84,7 @@ module.exports = function container (get, set, clear) { console.log('buy hold', buy_hold.format('0.00000000').yellow + ' (' + n(buy_hold_profit).format('0.00%') + ')') console.log('vs. buy hold', n(s.balance.currency).subtract(buy_hold).divide(buy_hold).format('0.00%').yellow) console.log(s.my_trades.length + ' trades over ' + s.day_count + ' days (avg ' + n(s.my_trades.length / s.day_count).format('0.00') + ' trades/day)') - var data = s.lookback.map(function (period) { + var data = s.lookback.slice(0, s.lookback.length - so.min_periods).map(function (period) { return { time: period.time, open: period.open, diff --git a/extensions/trend_ema/strategy.js b/extensions/trend_ema/strategy.js index 91b3450fb7..6dbd2554c2 100644 --- a/extensions/trend_ema/strategy.js +++ b/extensions/trend_ema/strategy.js @@ -10,10 +10,11 @@ module.exports = function container (get, set, clear) { this.option('period', 'period length', String, '1h') this.option('min_periods', 'min. number of history periods', Number, 36) this.option('trend_ema', 'number of periods for trend EMA', Number, 34) - this.option('buy_rate', 'buy if trend ema rate between 0 and this positive float', Number, 0) - this.option('sell_rate', 'sell if trend ema rate between 0 and this negative float', Number, 0) - this.option('max_buy_duration', 'avoid buy if trend duration over this number', Number, 1) - this.option('max_sell_duration', 'avoid sell if trend duration over this number', Number, 1) + this.option('buy_rate', 'buy if trend EMA rate between neutral_rate and this positive float', Number, 0) + this.option('sell_rate', 'sell if trend EMA rate between neutral_rate * -1 and this negative float', Number, 0) + this.option('neutral_rate', 'avoid signals when trend EMA rate is under this absolute value', Number, 'auto') + this.option('max_buy_duration', 'avoid buy if trend duration over this number', Number, 0) + this.option('max_sell_duration', 'avoid sell if trend duration over this number', Number, 0) this.option('oversold_rsi_periods', 'number of periods for oversold RSI', Number, 14) this.option('oversold_rsi', 'buy when RSI reaches this value', Number, 0) }, @@ -29,8 +30,15 @@ module.exports = function container (get, set, clear) { }, onPeriod: function (s, cb) { + if (s.options.neutral_rate === 'auto' || s.options.neutral_rate_auto) { + s.options.neutral_rate_auto = true + get('lib.stddev')(s, 'trend_ema_stddev', Math.floor(s.options.trend_ema / 2), 'trend_ema_rate') + if (typeof s.period.trend_ema_stddev === 'number') { + s.options.neutral_rate = s.period.trend_ema_stddev + } + } if (typeof s.period.oversold_rsi === 'number') { - if (s.period.oversold_rsi <= s.options.oversold_rsi) { + if (s.period.oversold_rsi <= s.options.oversold_rsi && !s.oversold) { s.oversold = true console.log(('\noversold at ' + s.period.oversold_rsi + ' RSI, preparing to buy\n').cyan) } @@ -43,24 +51,24 @@ module.exports = function container (get, set, clear) { } } if (typeof s.period.trend_ema_rate === 'number') { - if (s.period.trend_ema_rate >= 0) { + if (s.period.trend_ema_rate > s.options.neutral_rate) { if (s.trend !== 'up') { s.acted_on_trend = false s.trend_duration = 0 } s.trend_duration++ s.trend = 'up' - s.signal = (!s.options.buy_rate || s.period.trend_ema_rate <= s.options.buy_rate) && (!s.options.max_buy_duration || s.trend_duration <= s.options.max_buy_duration) && !s.acted_on_trend ? 'buy' : null + s.signal = (!s.options.buy_rate || s.period.trend_ema_rate <= s.options.buy_rate) && (!s.options.max_buy_duration || !s.trend || s.trend_duration <= s.options.max_buy_duration) && !s.acted_on_trend ? 'buy' : null s.cancel_down = false } - else if (!s.cancel_down) { + else if (!s.cancel_down && s.period.trend_ema_rate < (s.options.neutral_rate * -1)) { if (s.trend !== 'down') { s.acted_on_trend = false s.trend_duration = 0 } s.trend_duration++ s.trend = 'down' - s.signal = (!s.options.sell_rate || s.period.trend_ema_rate >= s.options.sell_rate) && (!s.options.max_sell_duration || s.trend_duration <= s.options.max_sell_duration) && !s.acted_on_trend ? 'sell' : null + s.signal = (!s.options.sell_rate || s.period.trend_ema_rate >= s.options.sell_rate) && (!s.options.max_sell_duration || !s.trend || s.trend_duration <= s.options.max_sell_duration) && !s.acted_on_trend ? 'sell' : null } } cb() @@ -69,11 +77,21 @@ module.exports = function container (get, set, clear) { onReport: function (s) { var cols = [] if (typeof s.period.trend_ema_rate === 'number') { - cols.push(z(8, n(s.period.trend_ema_rate).format('0.0000'), ' ')[s.period.trend_ema_rate >= 0 ? 'green' : 'red']) + var color = 'grey' + if (s.period.trend_ema_rate > s.options.neutral_rate) { + color = 'green' + } + else if (s.period.trend_ema_rate < (s.options.neutral_rate * -1)) { + color = 'red' + } + cols.push(z(8, n(s.period.trend_ema_rate).format('0.0000'), ' ')[color]) } else { cols.push(' ') } + if (typeof s.period.trend_ema_stddev === 'number') { + cols.push(z(8, n(s.period.trend_ema_stddev).format('0.0000'), ' ').grey) + } return cols } } diff --git a/lib/_codemap.js b/lib/_codemap.js index a616806490..41c441f659 100644 --- a/lib/_codemap.js +++ b/lib/_codemap.js @@ -5,5 +5,6 @@ module.exports = { 'ema': require('./ema'), 'engine': require('./engine'), 'normalize-selector': require('./normalize-selector'), - 'rsi': require('./rsi') + 'rsi': require('./rsi'), + 'stddev': require('./stddev') } \ No newline at end of file diff --git a/lib/stddev.js b/lib/stddev.js new file mode 100644 index 0000000000..5a934dbd04 --- /dev/null +++ b/lib/stddev.js @@ -0,0 +1,24 @@ +module.exports = function container (get, set, clear) { + return function stddev (s, key, length, source_key) { + if (typeof s.period[source_key] === 'number') { + var sum = s.period[source_key] + var sum_len = 1 + for (var idx = 0; idx < length; idx++) { + if (typeof s.lookback[idx][source_key] === 'number') { + sum += s.lookback[idx][source_key] + sum_len++ + } + else { + break; + } + } + var avg = sum / sum_len + var var_sum = 0 + for (var idx = 0; idx < sum_len - 1; idx++) { + var_sum += Math.pow(s.lookback[idx][source_key] - avg, 2) + } + var variance = var_sum / sum_len + s.period[key] = Math.sqrt(variance) + } + } +} \ No newline at end of file