first commit
This commit is contained in:
commit
aa7869eb33
386 files changed
+69375
No files matched your search
@@ -0,0 +1,125 @@
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.currentTrend;
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
this.age = 0;
|
||||
this.trend = {
|
||||
direction: 'undefined',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
this.historySize = this.settings.history;
|
||||
this.ppoadv = 'none';
|
||||
this.uplevel = this.settings.thresholds.up;
|
||||
this.downlevel = this.settings.thresholds.down;
|
||||
this.persisted = this.settings.thresholds.persistence;
|
||||
|
||||
// log.debug("CCI started with:\nup:\t", this.uplevel, "\ndown:\t", this.downlevel, "\npersistence:\t", this.persisted);
|
||||
// define the indicators we need
|
||||
this.addIndicator('cci', 'CCI', this.settings);
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
}
|
||||
|
||||
// for debugging purposes: log the last calculated
|
||||
// EMAs and diff.
|
||||
method.log = function(candle) {
|
||||
var cci = this.indicators.cci;
|
||||
if (typeof(cci.result) == 'boolean') {
|
||||
log.debug('Insufficient data available. Age: ', cci.size, ' of ', cci.maxSize);
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug('calculated CCI properties for candle:');
|
||||
log.debug('\t', 'Price:\t\t', candle.close.toFixed(8));
|
||||
log.debug('\t', 'CCI tp:\t', cci.tp.toFixed(8));
|
||||
log.debug('\t', 'CCI tp/n:\t', cci.avgtp.toFixed(8));
|
||||
log.debug('\t', 'CCI md:\t', cci.mean.toFixed(8));
|
||||
if (typeof(cci.result) == 'boolean' )
|
||||
log.debug('\t In sufficient data available.');
|
||||
else
|
||||
log.debug('\t', 'CCI:\t\t', cci.result.toFixed(2));
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
method.check = function(candle) {
|
||||
|
||||
var lastPrice = candle.close;
|
||||
|
||||
this.age++;
|
||||
var cci = this.indicators.cci;
|
||||
|
||||
if (typeof(cci.result) == 'number') {
|
||||
|
||||
// overbought?
|
||||
if (cci.result >= this.uplevel && (this.trend.persisted || this.persisted == 0) && !this.trend.adviced && this.trend.direction == 'overbought' ) {
|
||||
this.trend.adviced = true;
|
||||
this.trend.duration++;
|
||||
this.advice('short');
|
||||
} else if (cci.result >= this.uplevel && this.trend.direction != 'overbought') {
|
||||
this.trend.duration = 1;
|
||||
this.trend.direction = 'overbought';
|
||||
this.trend.persisted = false;
|
||||
this.trend.adviced = false;
|
||||
if (this.persisted == 0) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
}
|
||||
} else if (cci.result >= this.uplevel) {
|
||||
this.trend.duration++;
|
||||
if (this.trend.duration >= this.persisted) {
|
||||
this.trend.persisted = true;
|
||||
}
|
||||
} else if (cci.result <= this.downlevel && (this.trend.persisted || this.persisted == 0) && !this.trend.adviced && this.trend.direction == 'oversold') {
|
||||
this.trend.adviced = true;
|
||||
this.trend.duration++;
|
||||
this.advice('long');
|
||||
} else if (cci.result <= this.downlevel && this.trend.direction != 'oversold') {
|
||||
this.trend.duration = 1;
|
||||
this.trend.direction = 'oversold';
|
||||
this.trend.persisted = false;
|
||||
this.trend.adviced = false;
|
||||
if (this.persisted == 0) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
}
|
||||
} else if (cci.result <= this.downlevel) {
|
||||
this.trend.duration++;
|
||||
if (this.trend.duration >= this.persisted) {
|
||||
this.trend.persisted = true;
|
||||
}
|
||||
} else {
|
||||
if( this.trend.direction != 'nodirection') {
|
||||
this.trend = {
|
||||
direction: 'nodirection',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
} else {
|
||||
this.trend.duration++;
|
||||
}
|
||||
this.advice();
|
||||
}
|
||||
|
||||
} else {
|
||||
this.advice();
|
||||
}
|
||||
|
||||
log.debug("Trend: ", this.trend.direction, " for ", this.trend.duration);
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,37 @@
|
||||
var settings = {
|
||||
wait: 0,
|
||||
// advice: 'short'
|
||||
advice: 'long'
|
||||
};
|
||||
|
||||
// -------
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var i = 0;
|
||||
|
||||
var method = {
|
||||
init: _.noop,
|
||||
update: _.noop,
|
||||
log: _.noop,
|
||||
check: function() {
|
||||
|
||||
// log.info('iteration:', i);
|
||||
if(settings.wait === i) {
|
||||
console.log('trigger advice!');
|
||||
this.advice({
|
||||
direction: settings.advice,
|
||||
trigger: {
|
||||
type: 'trailingStop',
|
||||
trailPercentage: 0.5
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
i++
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,45 @@
|
||||
var settings = {
|
||||
wait: 0,
|
||||
each: 6
|
||||
};
|
||||
|
||||
// -------
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var i = 0;
|
||||
|
||||
var method = {
|
||||
init: _.noop,
|
||||
update: _.noop,
|
||||
log: _.noop,
|
||||
processTrade: function(trade) {
|
||||
log.debug('TRADE RECEIVED BY processTrade:', trade);
|
||||
},
|
||||
check: function(candle) {
|
||||
|
||||
if(settings.wait > i)
|
||||
return;
|
||||
|
||||
log.info('iteration:', i);
|
||||
|
||||
if(i % settings.each === 0) {
|
||||
log.debug('trigger SHORT');
|
||||
this.advice('short');
|
||||
} else if(i % settings.each === settings.each / 2) {
|
||||
log.debug('trigger LONG');
|
||||
this.advice('long');
|
||||
}
|
||||
|
||||
// if(i % 2 === 0)
|
||||
// this.advice('long');
|
||||
// else if(i % 2 === 1)
|
||||
// this.advice('short');
|
||||
|
||||
i++;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,73 @@
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'DEMA';
|
||||
|
||||
this.currentTrend;
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('dema', 'DEMA', this.settings);
|
||||
this.addIndicator('sma', 'SMA', this.settings.weight);
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
// for debugging purposes: log the last calculated
|
||||
// EMAs and diff.
|
||||
method.log = function() {
|
||||
let dema = this.indicators.dema;
|
||||
let sma = this.indicators.sma;
|
||||
|
||||
log.debug('Calculated DEMA and SMA properties for candle:');
|
||||
log.debug('\t Inner EMA:', dema.inner.result.toFixed(8));
|
||||
log.debug('\t Outer EMA:', dema.outer.result.toFixed(8));
|
||||
log.debug('\t DEMA:', dema.result.toFixed(5));
|
||||
log.debug('\t SMA:', sma.result.toFixed(5));
|
||||
log.debug('\t DEMA age:', dema.inner.age, 'candles');
|
||||
}
|
||||
|
||||
method.check = function(candle) {
|
||||
let dema = this.indicators.dema;
|
||||
let sma = this.indicators.sma;
|
||||
let resDEMA = dema.result;
|
||||
let resSMA = sma.result;
|
||||
let price = candle.close;
|
||||
let diff = resSMA - resDEMA;
|
||||
|
||||
let message = '@ ' + price.toFixed(8) + ' (' + resDEMA.toFixed(5) + '/' + diff.toFixed(5) + ')';
|
||||
|
||||
if(diff > this.settings.thresholds.up) {
|
||||
log.debug('we are currently in uptrend', message);
|
||||
|
||||
if(this.currentTrend !== 'up') {
|
||||
this.currentTrend = 'up';
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(diff < this.settings.thresholds.down) {
|
||||
log.debug('we are currently in a downtrend', message);
|
||||
|
||||
if(this.currentTrend !== 'down') {
|
||||
this.currentTrend = 'down';
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else {
|
||||
log.debug('we are currently not in an up or down trend', message);
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
|
||||
MACD - DJM 31/12/2013
|
||||
|
||||
(updated a couple of times since, check git history)
|
||||
|
||||
*/
|
||||
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
// keep state about the current trend
|
||||
// here, on every new candle we use this
|
||||
// state object to check if we need to
|
||||
// report it.
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
// how many candles do we need as a base
|
||||
// before we can start giving advice?
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('macd', 'MACD', this.settings);
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
// for debugging purposes: log the last calculated
|
||||
// EMAs and diff.
|
||||
method.log = function() {
|
||||
var digits = 8;
|
||||
var macd = this.indicators.macd;
|
||||
|
||||
var diff = macd.diff;
|
||||
var signal = macd.signal.result;
|
||||
|
||||
log.debug('calculated MACD properties for candle:');
|
||||
log.debug('\t', 'short:', macd.short.result.toFixed(digits));
|
||||
log.debug('\t', 'long:', macd.long.result.toFixed(digits));
|
||||
log.debug('\t', 'macd:', diff.toFixed(digits));
|
||||
log.debug('\t', 'signal:', signal.toFixed(digits));
|
||||
log.debug('\t', 'macdiff:', macd.result.toFixed(digits));
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
var macddiff = this.indicators.macd.result;
|
||||
|
||||
if(macddiff > this.settings.thresholds.up) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'up')
|
||||
// reset the state for the new trend
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'up',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In uptrend since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(macddiff < this.settings.thresholds.down) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'down')
|
||||
// reset the state for the new trend
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'down',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In downtrend since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else {
|
||||
|
||||
log.debug('In no trend');
|
||||
|
||||
// we're not in an up nor in a downtrend
|
||||
// but for now we ignore sideways trends
|
||||
//
|
||||
// read more @link:
|
||||
//
|
||||
// https://github.com/askmike/gekko/issues/171
|
||||
|
||||
// this.trend = {
|
||||
// direction: 'none',
|
||||
// duration: 0,
|
||||
// persisted: false,
|
||||
// adviced: false
|
||||
// };
|
||||
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
|
||||
PPO - cykedev 15/01/2014
|
||||
|
||||
(updated a couple of times since, check git history)
|
||||
|
||||
*/
|
||||
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'PPO';
|
||||
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('ppo', 'PPO', this.settings);
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function() {
|
||||
var digits = 8;
|
||||
var ppo = this.indicators.ppo;
|
||||
var long = ppo.result.longEMA;
|
||||
var short = ppo.result.shortEMA;
|
||||
var macd = ppo.result.macd;
|
||||
var result = ppo.result.ppo;
|
||||
var macdSignal = ppo.result.MACDsignal;
|
||||
var ppoSignal = ppo.result.PPOsignal;
|
||||
|
||||
log.debug('calculated MACD properties for candle:');
|
||||
log.debug('\t', 'short:', short.toFixed(digits));
|
||||
log.debug('\t', 'long:', long.toFixed(digits));
|
||||
log.debug('\t', 'macd:', macd.toFixed(digits));
|
||||
log.debug('\t', 'macdsignal:', macdSignal.toFixed(digits));
|
||||
log.debug('\t', 'machist:', (macd - macdSignal).toFixed(digits));
|
||||
log.debug('\t', 'ppo:', result.toFixed(digits));
|
||||
log.debug('\t', 'pposignal:', ppoSignal.toFixed(digits));
|
||||
log.debug('\t', 'ppohist:', (result - ppoSignal).toFixed(digits));
|
||||
}
|
||||
|
||||
method.check = function(candle) {
|
||||
var price = candle.close;
|
||||
|
||||
var ppo = this.indicators.ppo;
|
||||
var long = ppo.result.longEMA;
|
||||
var short = ppo.result.shortEMA;
|
||||
var macd = ppo.result.macd;
|
||||
var result = ppo.result.ppo;
|
||||
var macdSignal = ppo.result.MACDsignal;
|
||||
var ppoSignal = ppo.result.PPOsignal;
|
||||
|
||||
// TODO: is this part of the indicator or not?
|
||||
// if it is it should move there
|
||||
var ppoHist = result - ppoSignal;
|
||||
|
||||
if(ppoHist > this.settings.thresholds.up) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'up')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'up',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In uptrend since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(ppoHist < this.settings.thresholds.down) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'down')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'down',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In downtrend since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
log.debug('In no trend');
|
||||
|
||||
// we're not in an up nor in a downtrend
|
||||
// but for now we ignore sideways trends
|
||||
//
|
||||
// read more @link:
|
||||
//
|
||||
// https://github.com/askmike/gekko/issues/171
|
||||
|
||||
// this.trend = {
|
||||
// direction: 'none',
|
||||
// duration: 0,
|
||||
// persisted: false,
|
||||
// adviced: false
|
||||
// };
|
||||
|
||||
this.advice();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
|
||||
RSI - cykedev 14/02/2014
|
||||
|
||||
(updated a couple of times since, check git history)
|
||||
|
||||
*/
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var RSI = require('./indicators/RSI.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'RSI';
|
||||
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('rsi', 'RSI', this.settings);
|
||||
}
|
||||
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function(candle) {
|
||||
var digits = 8;
|
||||
var rsi = this.indicators.rsi;
|
||||
|
||||
log.debug('calculated RSI properties for candle:');
|
||||
log.debug('\t', 'rsi:', rsi.result.toFixed(digits));
|
||||
log.debug('\t', 'price:', candle.close.toFixed(digits));
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
var rsi = this.indicators.rsi;
|
||||
var rsiVal = rsi.result;
|
||||
|
||||
if(rsiVal > this.settings.thresholds.high) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'high')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'high',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In high since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(rsiVal < this.settings.thresholds.low) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'low')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'low',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In low since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else {
|
||||
|
||||
log.debug('In no trend');
|
||||
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
|
||||
StochRSI - SamThomp 11/06/2014
|
||||
|
||||
(updated by askmike) @ 30/07/2016
|
||||
|
||||
*/
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var RSI = require('./indicators/RSI.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.interval = this.settings.interval;
|
||||
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('rsi', 'RSI', { interval: this.interval });
|
||||
|
||||
this.RSIhistory = [];
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
this.rsi = this.indicators.rsi.result;
|
||||
|
||||
this.RSIhistory.push(this.rsi);
|
||||
|
||||
if(_.size(this.RSIhistory) > this.interval)
|
||||
// remove oldest RSI value
|
||||
this.RSIhistory.shift();
|
||||
|
||||
this.lowestRSI = _.min(this.RSIhistory);
|
||||
this.highestRSI = _.max(this.RSIhistory);
|
||||
this.stochRSI = ((this.rsi - this.lowestRSI) / (this.highestRSI - this.lowestRSI)) * 100;
|
||||
}
|
||||
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function() {
|
||||
var digits = 8;
|
||||
|
||||
log.debug('calculated StochRSI properties for candle:');
|
||||
log.debug('\t', 'rsi:', this.rsi.toFixed(digits));
|
||||
log.debug("StochRSI min:\t\t" + this.lowestRSI.toFixed(digits));
|
||||
log.debug("StochRSI max:\t\t" + this.highestRSI.toFixed(digits));
|
||||
log.debug("StochRSI Value:\t\t" + this.stochRSI.toFixed(2));
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
if(this.stochRSI > this.settings.thresholds.high) {
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'high')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'high',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In high since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(this.stochRSI < this.settings.thresholds.low) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'low')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'low',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In low since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else {
|
||||
// trends must be on consecutive candles
|
||||
this.trend.duration = 0;
|
||||
log.debug('In no trend');
|
||||
|
||||
this.advice();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,34 @@
|
||||
var method = {};
|
||||
|
||||
method.init = function() {
|
||||
this.name = 'Triple Moving Average';
|
||||
this.requiredHistory = this.settings.long;
|
||||
|
||||
this.addIndicator('short', 'SMA', this.settings.short)
|
||||
this.addIndicator('medium', 'SMA', this.settings.medium)
|
||||
this.addIndicator('long', 'SMA', this.settings.long)
|
||||
}
|
||||
|
||||
method.update = function(candle) {
|
||||
this.indicators.short.update(candle.close)
|
||||
this.indicators.medium.update(candle.close)
|
||||
this.indicators.long.update(candle.close)
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
const short = this.indicators.short.result;
|
||||
const medium = this.indicators.medium.result;
|
||||
const long = this.indicators.long.result;
|
||||
|
||||
if((short > medium) && (medium > long)) {
|
||||
this.advice('long')
|
||||
} else if((short < medium) && (medium > long)) {
|
||||
this.advice('short')
|
||||
} else if(((short > medium) && (medium < long))) {
|
||||
this.advice('short')
|
||||
} else {
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,98 @@
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var TSI = require('./indicators/TSI.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'TSI';
|
||||
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('tsi', 'TSI', this.settings);
|
||||
}
|
||||
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function(candle) {
|
||||
var digits = 8;
|
||||
var tsi = this.indicators.tsi;
|
||||
|
||||
log.debug('calculated TSI properties for candle:');
|
||||
log.debug('\t', 'tsi:', tsi.tsi.toFixed(digits));
|
||||
log.debug('\t', 'price:', candle.close.toFixed(digits));
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
var tsi = this.indicators.tsi;
|
||||
var tsiVal = tsi.tsi;
|
||||
|
||||
if(tsiVal > this.settings.thresholds.high) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'high')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'high',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In high since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(tsiVal < this.settings.thresholds.low) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'low')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'low',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In low since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else {
|
||||
|
||||
log.debug('In no trend');
|
||||
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,98 @@
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var UO = require('./indicators/UO.js');
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'UO';
|
||||
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('uo', 'UO', this.settings);
|
||||
}
|
||||
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function(candle) {
|
||||
var digits = 8;
|
||||
var uo = this.indicators.uo;
|
||||
|
||||
log.debug('calculated Ultimate Oscillator properties for candle:');
|
||||
log.debug('\t', 'UO:', uo.uo.toFixed(digits));
|
||||
log.debug('\t', 'price:', candle.close.toFixed(digits));
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
var uo = this.indicators.uo;
|
||||
var uoVal = uo.uo;
|
||||
|
||||
if(uoVal > this.settings.thresholds.high) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'high')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'high',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In high since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(uoVal < this.settings.thresholds.low) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'low')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'low',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In low since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= this.settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else {
|
||||
|
||||
log.debug('In no trend');
|
||||
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,63 @@
|
||||
// This is a basic example strategy for Gekko.
|
||||
// For more information on everything please refer
|
||||
// to this document:
|
||||
//
|
||||
// https://gekko.wizb.it/docs/strategies/creating_a_strategy.html
|
||||
//
|
||||
// The example below is pretty bad investment advice: on every new candle there is
|
||||
// a 10% chance it will recommend to change your position (to either
|
||||
// long or short).
|
||||
|
||||
var log = require('../core/log');
|
||||
|
||||
// Let's create our own strat
|
||||
var strat = {};
|
||||
|
||||
// Prepare everything our method needs
|
||||
strat.init = function() {
|
||||
this.input = 'candle';
|
||||
this.currentTrend = 'long';
|
||||
this.requiredHistory = 0;
|
||||
}
|
||||
|
||||
// What happens on every new candle?
|
||||
strat.update = function(candle) {
|
||||
|
||||
// Get a random number between 0 and 1.
|
||||
this.randomNumber = Math.random();
|
||||
|
||||
// There is a 10% chance it is smaller than 0.1
|
||||
this.toUpdate = this.randomNumber < 0.1;
|
||||
}
|
||||
|
||||
// For debugging purposes.
|
||||
strat.log = function() {
|
||||
log.debug('calculated random number:');
|
||||
log.debug('\t', this.randomNumber.toFixed(3));
|
||||
}
|
||||
|
||||
// Based on the newly calculated
|
||||
// information, check if we should
|
||||
// update or not.
|
||||
strat.check = function() {
|
||||
|
||||
// Only continue if we have a new update.
|
||||
if(!this.toUpdate)
|
||||
return;
|
||||
|
||||
if(this.currentTrend === 'long') {
|
||||
|
||||
// If it was long, set it to short
|
||||
this.currentTrend = 'short';
|
||||
this.advice('short');
|
||||
|
||||
} else {
|
||||
|
||||
// If it was short, set it to long
|
||||
this.currentTrend = 'long';
|
||||
this.advice('long');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = strat;
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* CCI
|
||||
*/
|
||||
var log = require('../../core/log');
|
||||
|
||||
var Indicator = function(settings) {
|
||||
this.input = 'candle';
|
||||
this.tp = 0.0;
|
||||
this.result = false;
|
||||
this.hist = []; // needed for mean?
|
||||
this.mean = 0.0;
|
||||
this.size = 0;
|
||||
this.constant = settings.constant;
|
||||
this.maxSize = settings.history;
|
||||
for (var i = 0; i < this.maxSize; i++)
|
||||
this.hist.push(0.0);
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(candle) {
|
||||
|
||||
// We need sufficient history to get the right result.
|
||||
|
||||
var tp = (candle.high + candle.close + candle.low) / 3;
|
||||
if (this.size < this.maxSize) {
|
||||
this.hist[this.size] = tp;
|
||||
this.size++;
|
||||
} else {
|
||||
for (var i = 0; i < this.maxSize-1; i++) {
|
||||
this.hist[i] = this.hist[i+1];
|
||||
}
|
||||
this.hist[this.maxSize-1] = tp;
|
||||
}
|
||||
|
||||
if (this.size < this.maxSize) {
|
||||
this.result = false;
|
||||
} else {
|
||||
this.calculate(tp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle calculations
|
||||
*/
|
||||
Indicator.prototype.calculate = function(tp) {
|
||||
|
||||
var sumtp = 0.0
|
||||
|
||||
for (var i = 0; i < this.size; i++) {
|
||||
sumtp = sumtp + this.hist[i];
|
||||
}
|
||||
|
||||
this.avgtp = sumtp / this.size;
|
||||
|
||||
this.tp = tp;
|
||||
|
||||
var sum = 0.0;
|
||||
// calculate tps
|
||||
for (var i = 0; i < this.size; i++) {
|
||||
|
||||
var z = (this.hist[i] - this.avgtp);
|
||||
if (z < 0) z = z * -1.0;
|
||||
sum = sum + z;
|
||||
|
||||
}
|
||||
|
||||
this.mean = (sum / this.size);
|
||||
|
||||
|
||||
|
||||
this.result = (this.tp - this.avgtp) / (this.constant * this.mean);
|
||||
|
||||
// log.debug("===\t", this.mean, "\t", this.tp, '\t', this.TP.result, "\t", sum, "\t", avgtp, '\t', this.result.toFixed(2));
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,19 @@
|
||||
// required indicators
|
||||
var EMA = require('./EMA.js');
|
||||
|
||||
var Indicator = function(config) {
|
||||
this.input = 'price';
|
||||
this.result = false;
|
||||
this.inner = new EMA(config.weight);
|
||||
this.outer = new EMA(config.weight);
|
||||
}
|
||||
|
||||
// add a price and calculate the EMAs and
|
||||
// the result
|
||||
Indicator.prototype.update = function(price) {
|
||||
this.inner.update(price);
|
||||
this.outer.update(this.inner.result);
|
||||
this.result = 2 * this.inner.result - this.outer.result;
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,36 @@
|
||||
// @link http://en.wikipedia.org/wiki/Exponential_moving_average#Exponential_moving_average
|
||||
|
||||
var Indicator = function(weight) {
|
||||
this.input = 'price';
|
||||
this.weight = weight;
|
||||
this.result = false;
|
||||
this.age = 0;
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(price) {
|
||||
// The first time we can't calculate based on previous
|
||||
// ema, because we haven't calculated any yet.
|
||||
if(this.result === false)
|
||||
this.result = price;
|
||||
|
||||
this.age++;
|
||||
this.calculate(price);
|
||||
|
||||
return this.result;
|
||||
}
|
||||
|
||||
// calculation (based on tick/day):
|
||||
// EMA = Price(t) * k + EMA(y) * (1 – k)
|
||||
// t = today, y = yesterday, N = number of days in EMA, k = 2 / (N+1)
|
||||
Indicator.prototype.calculate = function(price) {
|
||||
// weight factor
|
||||
var k = 2 / (this.weight + 1);
|
||||
|
||||
// yesterday
|
||||
var y = this.result;
|
||||
|
||||
// calculation
|
||||
this.result = price * k + y * (1 - k);
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Linear regression curve
|
||||
*/
|
||||
var log = require('../../core/log');
|
||||
|
||||
var Indicator = function(settings) {
|
||||
this.input = 'price';
|
||||
this.depth = settings;
|
||||
this.result = false;
|
||||
this.age = 0;
|
||||
this.history = [];
|
||||
this.x = [];
|
||||
/*
|
||||
* Do not use array(depth) as it might not be implemented
|
||||
*/
|
||||
for (var i = 0; i < this.depth; i++) {
|
||||
this.history.push(0.0);
|
||||
this.x.push(i);
|
||||
}
|
||||
|
||||
// log.debug("Created LRC indicator with h: ", this.depth);
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(price) {
|
||||
|
||||
// We need sufficient history to get the right result.
|
||||
if(this.result === false && this.age < this.depth) {
|
||||
|
||||
this.history[this.age] = price;
|
||||
this.age++;
|
||||
this.result = false;
|
||||
// log.debug("Waiting for sufficient age: ", this.age, " out of ", this.depth);
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
this.age++;
|
||||
// shift history
|
||||
for (var i = 0; i < (this.depth - 1); i++) {
|
||||
this.history[i] = this.history[i+1];
|
||||
}
|
||||
this.history[this.depth-1] = price;
|
||||
|
||||
this.calculate(price);
|
||||
|
||||
|
||||
// log.debug("Checking LRC: ", this.result.toFixed(8), "\tH: ", this.age);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Least squares linear regression fitting.
|
||||
*/
|
||||
function linreg(values_x, values_y) {
|
||||
var sum_x = 0;
|
||||
var sum_y = 0;
|
||||
var sum_xy = 0;
|
||||
var sum_xx = 0;
|
||||
var count = 0;
|
||||
|
||||
/*
|
||||
* We'll use those variables for faster read/write access.
|
||||
*/
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var values_length = values_x.length;
|
||||
|
||||
if (values_length != values_y.length) {
|
||||
throw new Error('The parameters values_x and values_y need to have same size!');
|
||||
}
|
||||
|
||||
/*
|
||||
* Nothing to do.
|
||||
*/
|
||||
if (values_length === 0) {
|
||||
return [ [], [] ];
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the sum for each of the parts necessary.
|
||||
*/
|
||||
for (var v = 0; v < values_length; v++) {
|
||||
x = values_x[v];
|
||||
y = values_y[v];
|
||||
sum_x += x;
|
||||
sum_y += y;
|
||||
sum_xx += x*x;
|
||||
sum_xy += x*y;
|
||||
count++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate m and b for the formular:
|
||||
* y = x * m + b
|
||||
*/
|
||||
var m = (count*sum_xy - sum_x*sum_y) / (count*sum_xx - sum_x*sum_x);
|
||||
var b = (sum_y/count) - (m*sum_x)/count;
|
||||
|
||||
return [m, b];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle calculations
|
||||
*/
|
||||
Indicator.prototype.calculate = function(price) {
|
||||
|
||||
// get the reg
|
||||
var reg = linreg(this.x, this.history);
|
||||
|
||||
// y = a * x + b
|
||||
this.result = ((this.depth-1) * reg[0]) + reg[1];
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,27 @@
|
||||
// required indicators
|
||||
var EMA = require('./EMA.js');
|
||||
|
||||
var Indicator = function(config) {
|
||||
this.input = 'price';
|
||||
this.diff = false;
|
||||
this.short = new EMA(config.short);
|
||||
this.long = new EMA(config.long);
|
||||
this.signal = new EMA(config.signal);
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(price) {
|
||||
this.short.update(price);
|
||||
this.long.update(price);
|
||||
this.calculateEMAdiff();
|
||||
this.signal.update(this.diff);
|
||||
this.result = this.diff - this.signal.result;
|
||||
}
|
||||
|
||||
Indicator.prototype.calculateEMAdiff = function() {
|
||||
var shortEMA = this.short.result;
|
||||
var longEMA = this.long.result;
|
||||
|
||||
this.diff = shortEMA - longEMA;
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,37 @@
|
||||
// required indicators
|
||||
var EMA = require('./EMA.js');
|
||||
|
||||
var Indicator = function(config) {
|
||||
this.result = {};
|
||||
this.input = 'price';
|
||||
this.macd = 0;
|
||||
this.ppo = 0;
|
||||
this.short = new EMA(config.short);
|
||||
this.long = new EMA(config.long);
|
||||
this.MACDsignal = new EMA(config.signal);
|
||||
this.PPOsignal = new EMA(config.signal);
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(price) {
|
||||
this.short.update(price);
|
||||
this.long.update(price);
|
||||
this.calculatePPO();
|
||||
this.MACDsignal.update(this.result.macd);
|
||||
this.MACDhist = this.result.macd - this.MACDsignal.result;
|
||||
this.PPOsignal.update(this.result.ppo);
|
||||
this.PPOhist = this.result.ppo - this.PPOsignal.result;
|
||||
|
||||
this.result.MACDsignal = this.MACDsignal.result;
|
||||
this.result.MACDhist = this.MACDhist;
|
||||
this.result.PPOsignal = this.PPOsignal.result;
|
||||
this.result.PPOhist = this.PPOhist;
|
||||
}
|
||||
|
||||
Indicator.prototype.calculatePPO = function() {
|
||||
this.result.shortEMA = this.short.result;
|
||||
this.result.longEMA = this.long.result;
|
||||
this.result.macd = this.result.shortEMA - this.result.longEMA;
|
||||
this.result.ppo = 100 * (this.result.macd / this.result.longEMA);
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,53 @@
|
||||
// required indicators
|
||||
var SMMA = require('./SMMA.js');
|
||||
|
||||
var Indicator = function (settings) {
|
||||
this.input = 'candle';
|
||||
this.lastClose = null;
|
||||
this.weight = settings.interval;
|
||||
this.avgU = new SMMA(this.weight);
|
||||
this.avgD = new SMMA(this.weight);
|
||||
this.u = 0;
|
||||
this.d = 0;
|
||||
this.rs = 0;
|
||||
this.result = 0;
|
||||
this.age = 0;
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function (candle) {
|
||||
var currentClose = candle.close;
|
||||
|
||||
if (this.lastClose === null) {
|
||||
// Set initial price to prevent invalid change calculation
|
||||
this.lastClose = currentClose;
|
||||
|
||||
// Do not calculate RSI for this reason - there's no change!
|
||||
this.age++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentClose > this.lastClose) {
|
||||
this.u = currentClose - this.lastClose;
|
||||
this.d = 0;
|
||||
} else {
|
||||
this.u = 0;
|
||||
this.d = this.lastClose - currentClose;
|
||||
}
|
||||
|
||||
this.avgU.update(this.u);
|
||||
this.avgD.update(this.d);
|
||||
|
||||
this.rs = this.avgU.result / this.avgD.result;
|
||||
this.result = 100 - (100 / (1 + this.rs));
|
||||
|
||||
if (this.avgD.result === 0 && this.avgU.result !== 0) {
|
||||
this.result = 100;
|
||||
} else if (this.avgD.result === 0) {
|
||||
this.result = 0;
|
||||
}
|
||||
|
||||
this.lastClose = currentClose;
|
||||
this.age++;
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,21 @@
|
||||
// required indicators
|
||||
// Simple Moving Average - O(1) implementation
|
||||
|
||||
var Indicator = function(windowLength) {
|
||||
this.input = 'price';
|
||||
this.windowLength = windowLength;
|
||||
this.prices = [];
|
||||
this.result = 0;
|
||||
this.age = 0;
|
||||
this.sum = 0;
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(price) {
|
||||
var tail = this.prices[this.age] || 0; // oldest price in window
|
||||
this.prices[this.age] = price;
|
||||
this.sum += price - tail;
|
||||
this.result = this.sum / this.prices.length;
|
||||
this.age = (this.age + 1) % this.windowLength
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,28 @@
|
||||
// required indicators
|
||||
var SMA = require('./SMA');
|
||||
|
||||
var Indicator = function (weight) {
|
||||
this.input = 'price';
|
||||
this.sma = new SMA(weight);
|
||||
this.weight = weight;
|
||||
this.prices = [];
|
||||
this.result = 0;
|
||||
this.age = 0;
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function (price) {
|
||||
this.prices[this.age] = price;
|
||||
|
||||
if(this.prices.length < this.weight) {
|
||||
this.sma.update(price);
|
||||
} else if(this.prices.length === this.weight) {
|
||||
this.sma.update(price);
|
||||
this.result = this.sma.result;
|
||||
} else {
|
||||
this.result = (this.result * (this.weight - 1) + price) / this.weight;
|
||||
}
|
||||
|
||||
this.age++;
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,38 @@
|
||||
// required indicators
|
||||
var EMA = require('./EMA.js');
|
||||
|
||||
var Indicator = function(settings) {
|
||||
this.input = 'candle';
|
||||
this.lastClose = null;
|
||||
this.tsi = 0;
|
||||
this.inner = new EMA(settings.long);
|
||||
this.outer = new EMA(settings.short);
|
||||
this.absoluteInner = new EMA(settings.long);
|
||||
this.absoluteOuter = new EMA(settings.short);
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(candle) {
|
||||
var close = candle.close;
|
||||
var prevClose = this.lastClose;
|
||||
|
||||
if (prevClose === null) {
|
||||
// Set initial price to prevent invalid change calculation
|
||||
this.lastClose = close;
|
||||
// Do not calculate TSI on first close
|
||||
return;
|
||||
}
|
||||
|
||||
var momentum = close - prevClose;
|
||||
|
||||
this.inner.update(momentum);
|
||||
this.outer.update(this.inner.result);
|
||||
|
||||
this.absoluteInner.update(Math.abs(momentum));
|
||||
this.absoluteOuter.update(this.absoluteInner.result);
|
||||
|
||||
this.tsi = 100 * this.outer.result / this.absoluteOuter.result;
|
||||
|
||||
this.lastClose = close;
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,45 @@
|
||||
// required indicators
|
||||
var SMA = require('./SMA.js');
|
||||
|
||||
var Indicator = function(settings) {
|
||||
this.input = 'candle';
|
||||
this.lastClose = 0;
|
||||
this.uo = 0;
|
||||
this.firstWeight = settings.first.weight;
|
||||
this.secondWeight = settings.second.weight;
|
||||
this.thirdWeight = settings.third.weight;
|
||||
this.firstLow = new SMA(settings.first.period);
|
||||
this.firstHigh = new SMA(settings.first.period);
|
||||
this.secondLow = new SMA(settings.second.period);
|
||||
this.secondHigh = new SMA(settings.second.period);
|
||||
this.thirdLow = new SMA(settings.third.period);
|
||||
this.thirdHigh = new SMA(settings.third.period);
|
||||
}
|
||||
|
||||
Indicator.prototype.update = function(candle) {
|
||||
var close = candle.close;
|
||||
var prevClose = this.lastClose;
|
||||
var low = candle.low;
|
||||
var high = candle.high;
|
||||
|
||||
var bp = close - Math.min(low, prevClose);
|
||||
var tr = Math.max(high, prevClose) - Math.min(low, prevClose);
|
||||
|
||||
this.firstLow.update(tr);
|
||||
this.secondLow.update(tr);
|
||||
this.thirdLow.update(tr);
|
||||
|
||||
this.firstHigh.update(bp);
|
||||
this.secondHigh.update(bp);
|
||||
this.thirdHigh.update(bp);
|
||||
|
||||
var first = this.firstHigh.result / this.firstLow.result;
|
||||
var second = this.secondHigh.result / this.secondLow.result;
|
||||
var third = this.thirdHigh.result / this.thirdLow.result;
|
||||
|
||||
this.uo = 100 * (this.firstWeight * first + this.secondWeight * second + this.thirdWeight * third) / (this.firstWeight + this.secondWeight + this.thirdWeight);
|
||||
|
||||
this.lastClose = close;
|
||||
}
|
||||
|
||||
module.exports = Indicator;
|
||||
@@ -0,0 +1,13 @@
|
||||
// This method is a noop (it doesn't do anything)
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
// Let's create our own method
|
||||
var method = {};
|
||||
|
||||
method.init = _.noop;
|
||||
method.update = _.noop;
|
||||
method.log = _.noop;
|
||||
method.check = _.noop;
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,59 @@
|
||||
// If you want to use your own trading methods you can
|
||||
// write them here. For more information on everything you
|
||||
// can use please refer to this document:
|
||||
//
|
||||
// https://github.com/askmike/gekko/blob/stable/docs/trading_methods.md
|
||||
|
||||
// Let's create our own method
|
||||
var method = {};
|
||||
|
||||
// Prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'talib-macd'
|
||||
this.input = 'candle';
|
||||
// keep state about the current trend
|
||||
// here, on every new candle we use this
|
||||
// state object to check if we need to
|
||||
// report it.
|
||||
this.trend = 'none';
|
||||
|
||||
// how many candles do we need as a base
|
||||
// before we can start giving advice?
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
var customMACDSettings = this.settings.parameters;
|
||||
|
||||
// define the indicators we need
|
||||
this.addTalibIndicator('mymacd', 'macd', customMACDSettings);
|
||||
}
|
||||
|
||||
// What happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
|
||||
method.log = function() {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
// Based on the newly calculated
|
||||
// information, check if we should
|
||||
// update or not.
|
||||
method.check = function(candle) {
|
||||
var price = candle.close;
|
||||
var result = this.talibIndicators.mymacd.result;
|
||||
var macddiff = result['outMACD'] - result['outMACDSignal'];
|
||||
|
||||
if(this.settings.thresholds.down > macddiff && this.trend !== 'short') {
|
||||
this.trend = 'short';
|
||||
this.advice('short');
|
||||
|
||||
} else if(this.settings.thresholds.up < macddiff && this.trend !== 'long'){
|
||||
this.trend = 'long';
|
||||
this.advice('long');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,30 @@
|
||||
var method = {};
|
||||
// Prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'tulip-adx'
|
||||
this.trend = 'none';
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
this.addTulipIndicator('myadx', 'adx', this.settings);
|
||||
}
|
||||
// What happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
method.log = function() {
|
||||
// nothing!
|
||||
}
|
||||
method.check = function(candle) {
|
||||
var price = candle.close;
|
||||
var adx = this.tulipIndicators.myadx.result.result;
|
||||
// console.dir(adx)
|
||||
|
||||
if(this.settings.thresholds.down > adx && this.trend !== 'short') {
|
||||
this.trend = 'short';
|
||||
this.advice('short');
|
||||
} else if(this.settings.thresholds.up < adx && this.trend !== 'long'){
|
||||
this.trend = 'long';
|
||||
this.advice('long');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,58 @@
|
||||
// If you want to use your own trading methods you can
|
||||
// write them here. For more information on everything you
|
||||
// can use please refer to this document:
|
||||
//
|
||||
// https://github.com/askmike/gekko/blob/stable/docs/trading_methods.md
|
||||
|
||||
// Let's create our own method
|
||||
var method = {};
|
||||
|
||||
// Prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.name = 'tulip-macd'
|
||||
// keep state about the current trend
|
||||
// here, on every new candle we use this
|
||||
// state object to check if we need to
|
||||
// report it.
|
||||
this.trend = 'none';
|
||||
|
||||
// how many candles do we need as a base
|
||||
// before we can start giving advice?
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
var customMACDSettings = this.settings.parameters;
|
||||
|
||||
// define the indicators we need
|
||||
this.addTulipIndicator('mymacd', 'macd', customMACDSettings);
|
||||
}
|
||||
|
||||
// What happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
|
||||
method.log = function() {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
// Based on the newly calculated
|
||||
// information, check if we should
|
||||
// update or not.
|
||||
method.check = function(candle) {
|
||||
var price = candle.close;
|
||||
var result = this.tulipIndicators.mymacd.result;
|
||||
var macddiff = result['macd'] - result['macdSignal'];
|
||||
|
||||
if(this.settings.thresholds.down > macddiff && this.trend !== 'short') {
|
||||
this.trend = 'short';
|
||||
this.advice('short');
|
||||
|
||||
} else if(this.settings.thresholds.up < macddiff && this.trend !== 'long'){
|
||||
this.trend = 'long';
|
||||
this.advice('long');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,57 @@
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log.js');
|
||||
|
||||
var method = {};
|
||||
method.init = function() {
|
||||
// strat name
|
||||
this.name = 'tulip-multi-strat';
|
||||
// trend information
|
||||
this.trend = 'none'
|
||||
// tulip indicators use this sometimes
|
||||
this.requiredHistory = this.settings.historySize;
|
||||
// define the indicators we need
|
||||
this.addTulipIndicator('myadx', 'adx', this.settings);
|
||||
this.addTulipIndicator('mymacd', 'macd', this.settings);
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// tulip results
|
||||
this.adx = this.tulipIndicators.myadx.result.result;
|
||||
this.macd = this.tulipIndicators.mymacd.result.macdHistogram;
|
||||
}
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function() {
|
||||
log.debug(
|
||||
`---------------------
|
||||
Tulip ADX: ${this.adx}
|
||||
Tulip MACD: ${this.macd}
|
||||
`);
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
// just add a long and short to each array when new indicators are used
|
||||
const all_long = [
|
||||
this.adx > this.settings.up && this.trend!=='long',
|
||||
this.settings.macd_up < this.macd && this.trend!=='long',
|
||||
].reduce((total, long)=>long && total, true)
|
||||
const all_short = [
|
||||
this.adx < this.settings.down && this.trend!=='short',
|
||||
this.settings.macd_down > this.macd && this.trend!=='short',
|
||||
].reduce((total, long)=>long && total, true)
|
||||
|
||||
// combining all indicators with AND
|
||||
if(all_long){
|
||||
log.debug(`tulip-multi-strat In low`);
|
||||
this.advice('long');
|
||||
}else if(all_short){
|
||||
log.debug(`tulip-multi-strat In high`);
|
||||
this.advice('short');
|
||||
}else{
|
||||
log.debug(`tulip-multi-strat In no trend`);
|
||||
this.advice();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
@@ -0,0 +1,136 @@
|
||||
// helpers
|
||||
var _ = require('lodash');
|
||||
var log = require('../core/log');
|
||||
|
||||
// configuration
|
||||
var config = require('../core/util').getConfig();
|
||||
var settings = config.varPPO;
|
||||
var momentum = settings.momentum;
|
||||
var momentumName = momentum.toLowerCase();
|
||||
var momentumSettings = config[momentum];
|
||||
|
||||
// let's create our own method
|
||||
var method = {};
|
||||
|
||||
// prepare everything our method needs
|
||||
method.init = function() {
|
||||
this.trend = {
|
||||
direction: 'none',
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.requiredHistory = this.tradingAdvisor.historySize;
|
||||
|
||||
// define the indicators we need
|
||||
this.addIndicator('ppo', 'PPO', config.PPO);
|
||||
this.addIndicator(momentumName, momentum, momentumSettings);
|
||||
}
|
||||
|
||||
// what happens on every new candle?
|
||||
method.update = function(candle) {
|
||||
// nothing!
|
||||
}
|
||||
|
||||
// for debugging purposes log the last
|
||||
// calculated parameters.
|
||||
method.log = function(candle) {
|
||||
var digits = 8;
|
||||
var ppo = this.indicators.ppo.result;
|
||||
var result = ppo.ppo;
|
||||
var signal = ppo.PPOsignal;
|
||||
var hist = ppo.PPOhist;
|
||||
var momentumResult = this.indicators[momentumName][momentumName];
|
||||
|
||||
log.debug('\t', 'PPO:', result.toFixed(digits));
|
||||
log.debug('\t', 'PPOsignal:', signal.toFixed(digits));
|
||||
log.debug('\t', 'PPOhist:', hist.toFixed(digits));
|
||||
log.debug('\t', momentum + ':', momentumResult.toFixed(digits));
|
||||
log.debug('\t', 'price:', candle.close.toFixed(digits));
|
||||
}
|
||||
|
||||
method.check = function() {
|
||||
var ppo = this.indicators.ppo.result;
|
||||
var hist = ppo.PPOhist;
|
||||
|
||||
var value = this.indicators[momentumName][momentumName];
|
||||
|
||||
var thresholds = {
|
||||
low: momentumSettings.thresholds.low + hist * settings.thresholds.weightLow,
|
||||
high: momentumSettings.thresholds.high + hist * settings.thresholds.weightHigh
|
||||
};
|
||||
|
||||
if(value < thresholds.low) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'up')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'up',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In uptrend since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('long');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
} else if(value > thresholds.high) {
|
||||
|
||||
// new trend detected
|
||||
if(this.trend.direction !== 'down')
|
||||
this.trend = {
|
||||
duration: 0,
|
||||
persisted: false,
|
||||
direction: 'down',
|
||||
adviced: false
|
||||
};
|
||||
|
||||
this.trend.duration++;
|
||||
|
||||
log.debug('In downtrend since', this.trend.duration, 'candle(s)');
|
||||
|
||||
if(this.trend.duration >= settings.thresholds.persistence)
|
||||
this.trend.persisted = true;
|
||||
|
||||
if(this.trend.persisted && !this.trend.adviced) {
|
||||
this.trend.adviced = true;
|
||||
this.advice('short');
|
||||
} else
|
||||
this.advice();
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
log.debug('In no trend');
|
||||
|
||||
// we're not in an up nor in a downtrend
|
||||
// but for now we ignore sideways trends
|
||||
//
|
||||
// read more @link:
|
||||
//
|
||||
// https://github.com/askmike/gekko/issues/171
|
||||
|
||||
// this.trend = {
|
||||
// direction: 'none',
|
||||
// duration: 0,
|
||||
// persisted: false,
|
||||
// adviced: false
|
||||
// };
|
||||
|
||||
this.advice();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = method;
|
||||
Reference in new issue
Block a user