first commit

This commit is contained in:
Francois Klieber committed 2021-06-06 20:25:14 +02:00
commit aa7869eb33
386 files changed
+69375

No files matched your search

+125
View File
@@ -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;
+37
View File
@@ -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;
+45
View File
@@ -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;
+73
View File
@@ -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;
+134
View File
@@ -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;
+148
View File
@@ -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;
+105
View File
@@ -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;
+121
View File
@@ -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;
+34
View File
@@ -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;
+98
View File
@@ -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;
+98
View File
@@ -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;
+63
View File
@@ -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;
+75
View File
@@ -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;
+19
View File
@@ -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;
+36
View File
@@ -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;
+115
View File
@@ -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;
+27
View File
@@ -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;
+37
View File
@@ -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;
+53
View File
@@ -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;
+21
View File
@@ -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;
+28
View File
@@ -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;
+38
View File
@@ -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;
+45
View File
@@ -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;
+13
View File
@@ -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;
+59
View File
@@ -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;
+30
View File
@@ -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;
+58
View File
@@ -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;
+57
View File
@@ -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;
+136
View File
@@ -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;