var z = require('zero-fill') , n = require('numbro') , bollinger = require('../../../lib/bollinger') , Phenotypes = require('../../../lib/phenotype') module.exports = { name: 'bollinger', description: 'Buy when (Signal ≤ Lower Bollinger Band) and sell when (Signal ≥ Upper Bollinger Band).', getOptions: function () { this.option('period', 'period length, same as --period_length', String, '1h') this.option('period_length', 'period length, same as --period', String, '1h') this.option('bollinger_size', 'period size', Number, 20) this.option('bollinger_time', 'times of standard deviation between the upper band and the moving averages', Number, 2) this.option('bollinger_upper_bound_pct', 'pct the current price should be near the bollinger upper bound before we sell', Number, 0) this.option('bollinger_lower_bound_pct', 'pct the current price should be near the bollinger lower bound before we buy', Number, 0) }, calculate: function (s) { // calculate Bollinger Bands bollinger(s, 'bollinger', s.options.bollinger_size) }, onPeriod: function (s, cb) { if (s.period.bollinger) { if (s.period.bollinger.upperBound && s.period.bollinger.lowerBound) { let upperBound = s.period.bollinger.upperBound let lowerBound = s.period.bollinger.lowerBound if (s.period.close > (upperBound / 100) * (100 - s.options.bollinger_upper_bound_pct)) { s.signal = 'sell' } else if (s.period.close < (lowerBound / 100) * (100 + s.options.bollinger_lower_bound_pct)) { s.signal = 'buy' } else { s.signal = null // hold } } } cb() }, onReport: function (s) { var cols = [] if (s.period.bollinger) { if (s.period.bollinger.upperBound && s.period.bollinger.lowerBound) { let upperBound = s.period.bollinger.upperBound let lowerBound = s.period.bollinger.lowerBound var color = 'grey' if (s.period.close > (upperBound / 100) * (100 - s.options.bollinger_upper_bound_pct)) { color = 'green' } else if (s.period.close < (lowerBound / 100) * (100 + s.options.bollinger_lower_bound_pct)) { color = 'red' } cols.push(z(8, n(s.period.close).format('+00.0000'), ' ')[color]) cols.push(z(8, n(lowerBound).format('0.000000').substring(0,7), ' ').cyan) cols.push(z(8, n(upperBound).format('0.000000').substring(0,7), ' ').cyan) } } else { cols.push(' ') } return cols }, phenotypes: { // -- common period_length: Phenotypes.RangePeriod(1, 120, 'm'), markdown_buy_pct: Phenotypes.RangeFloat(-1, 5), markup_sell_pct: Phenotypes.RangeFloat(-1, 5), order_type: Phenotypes.ListOption(['maker', 'taker']), sell_stop_pct: Phenotypes.Range0(1, 50), buy_stop_pct: Phenotypes.Range0(1, 50), profit_stop_enable_pct: Phenotypes.Range0(1, 20), profit_stop_pct: Phenotypes.Range(1,20), // -- strategy bollinger_size: Phenotypes.Range(1, 40), bollinger_time: Phenotypes.RangeFloat(1,6), bollinger_upper_bound_pct: Phenotypes.RangeFloat(-1, 30), bollinger_lower_bound_pct: Phenotypes.RangeFloat(-1, 30) } }