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)
}
}