diff --git a/config/AlligatorBTCUSD.js b/config/AlligatorBTCUSD.js new file mode 100644 index 000000000..dc9c710cb --- /dev/null +++ b/config/AlligatorBTCUSD.js @@ -0,0 +1,381 @@ +// Everything is explained here: +// @link https://github.com/askmike/gekko/blob/stable/docs/Configuring_gekko.md + +var config = {}; + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// GENERAL SETTINGS +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.debug = true; // for additional logging / debugging + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// WATCHING A MARKET +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Monitor the live market +config.watch = { + + // see https://github.com/askmike/gekko#supported-exchanges + exchange: 'BTCE', + currency: 'USD', + asset: 'BTC' +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING TRADING ADVICE +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.tradingAdvisor = { + enabled: true, + method: 'Alligator', + candleSize: 60, + historySize: 25, + adapter: 'sqlite', + talib: { + enabled: false, + version: '1.0.2' + } +} + +config.Alligator = { + history = 60; +}; + +// Exponential Moving Averages settings: +config.DEMA = { + // EMA weight (α) + // the higher the weight, the more smooth (and delayed) the line + short: 10, + long: 21, + // amount of candles to remember and base initial EMAs on + // the difference between the EMAs (to act as triggers) + thresholds: { + down: -0.025, + up: 0.025 + } +}; + +// MACD settings: +config.MACD = { + // EMA weight (α) + // the higher the weight, the more smooth (and delayed) the line + short: 10, + long: 21, + signal: 9, + // the difference between the EMAs (to act as triggers) + thresholds: { + down: -0.025, + up: 0.025, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +}; + +// PPO settings: +config.PPO = { + // EMA weight (α) + // the higher the weight, the more smooth (and delayed) the line + short: 12, + long: 26, + signal: 9, + // the difference between the EMAs (to act as triggers) + thresholds: { + down: -0.025, + up: 0.025, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 2 + } +}; + +// RSI settings: +config.RSI = { + interval: 14, + thresholds: { + low: 30, + high: 70, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +}; + +// CCI Settings +config.CCI = { + constant: 0.015, // constant multiplier. 0.015 gets to around 70% fit + history: 90, // history size, make same or smaller than history + thresholds: { + up: 100, // fixed values for overbuy upward trajectory + down: -100, // fixed value for downward trajectory + persistence: 0 // filter spikes by adding extra filters candles + } +}; + +// StochRSI settings +config.StochRSI = { + interval: 3, + thresholds: { + low: 20, + high: 80, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 3 + } +}; + + +//x2MACD settings: +config.x2MACD = { +// EMA weight (α) +// the higher the weight, the more smooth (and delayed) the line +short: 53, +long: 109, +signal: 41, +// the difference between the EMAs (to act as triggers) +thresholds: { + down: -9999, + up: 0.00000001, +} +}; + +//nikiehihsa settings: +config.nikiehihsa = { +// EMA weight (α) +// the higher the weight, the more smooth (and delayed) the line +short: 53, +long: 109, +signal: 41, +// the difference between the EMAs (to act as triggers) +thresholds: { + down: -9999, + up: 0.00000001, +} +}; + +//x3nikiehihsa settings: +config.x3nikiehihsa = { +// EMA weight (α) +// the higher the weight, the more smooth (and delayed) the line +short: 53, +long: 109, +signal: 41, +// the difference between the EMAs (to act as triggers) +thresholds: { + down: -9999, + up: 0.00000001, +} +}; + +//ZERO settings: +config.ZERO = { +// EMA weight (α) +// the higher the weight, the more smooth (and delayed) the line +short: 257.608488, +long: 364.313417, +signal: 225.158074, +// how optimistic is the MACD extrapolation going to be? +crystalball: 0.00000173, +// how large is the stats window for sanity checking? +window: 2345, // SHOULD NOT be larger than your historySize!!! +// the difference between the EMAs (to act as triggers) +thresholds: { + down: -9999, + up: 0.00000042, +} +}; + + + +// custom settings: +config.custom = { + my_custom_setting: 10, +} + +config['talib-macd'] = { + // FastPeriod, SlowPeriod, SignalPeriod + parameters: [10, 21, 9], + thresholds: { + down: -0.025, + up: 0.025, + // How many candle intervals should a trend persist + // before we consider it real? + persistence: 1 + } +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING PLUGINS +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Want Gekko to perform real trades on buy or sell advice? +// Enabling this will activate trades for the market being +// watched by `config.watch`. +config.trader = { + enabled: false, + key: '', + secret: '', + username: '' // your username, only required for specific exchanges. +} + +config.adviceLogger = { + enabled: true +} + +// do you want Gekko to calculate the profit of its own advice? +config.profitSimulator = { + enabled: true, + // report the profit in the currency or the asset? + reportInCurrency: true, + // start balance, on what the current balance is compared with + simulationBalance: { + // these are in the unit types configured in the watcher. + asset: 1, + currency: 100, + }, + // how much fee in % does each trade cost? + fee: 0.25, + // how much slippage/spread should Gekko assume per trade? + slippage: 0.05 +} + +// want Gekko to send a mail on buy or sell advice? +config.mailer = { + enabled: false, // Send Emails if true, false to turn off + sendMailOnStart: true, // Send 'Gekko starting' message if true, not if false + + email: '', // Your Gmail address + + // You don't have to set your password here, if you leave it blank we will ask it + // when Gekko's starts. + // + // NOTE: Gekko is an open source project < https://github.com/askmike/gekko >, + // make sure you looked at the code or trust the maintainer of this bot when you + // fill in your email and password. + // + // WARNING: If you have NOT downloaded Gekko from the github page above we CANNOT + // guarantuee that your email address & password are safe! + + password: '', // Your Gmail Password - if not supplied Gekko will prompt on startup. + + tag: '[GEKKO] ', // Prefix all email subject lines with this + + // ADVANCED MAIL SETTINGS + // you can leave those as is if you + // just want to use Gmail + + server: 'smtp.gmail.com', // The name of YOUR outbound (SMTP) mail server. + smtpauth: true, // Does SMTP server require authentication (true for Gmail) + // The following 3 values default to the Email (above) if left blank + user: '', // Your Email server user name - usually your full Email address 'me@mydomain.com' + from: '', // 'me@mydomain.com' + to: '', // 'me@somedomain.com, me@someotherdomain.com' + ssl: true, // Use SSL (true for Gmail) + port: '', // Set if you don't want to use the default port +} + +config.ircbot = { + enabled: false, + emitUpdats: false, + channel: '#your-channel', + server: 'irc.freenode.net', + botName: 'gekkobot' +} + +config.xmppbot = { + enabled: false, + emitUpdats: false, + client_id: 'jabber_id', + client_pwd: 'jabber_pw', + client_host: 'jabber_server', + client_port: 5222, + status_msg: 'I\'m online', + receiver: 'jabber_id_for_updates' +} + +config.campfire = { + enabled: false, + emitUpdates: false, + nickname: 'Gordon', + roomId: null, + apiKey: '', + account: '' +} + +config.redisBeacon = { + enabled: false, + port: 6379, // redis default + host: '127.0.0.1', // localhost + // On default Gekko broadcasts + // events in the channel with + // the name of the event, set + // an optional prefix to the + // channel name. + channelPrefix: '', + broadcast: [ + 'candle' + ] +} + +config.candleWriter = { + adapter: 'sqlite', + enabled: true +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING ADAPTER +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.adapters = { + sqlite: { + path: 'plugins/sqlite', + + dataDirectory: './history', + version: 0.1, + + dependencies: [{ + module: 'sqlite3', + version: '3.1.4' + }] + } +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING BACKTESTING +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Note that these settings are only used in backtesting mode, see here: +// @link: https://github.com/askmike/gekko/blob/stable/docs/Backtesting.md + +config.backtest = { + adapter: 'sqlite', + daterange: 'scan', + batchSize: 50 +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CONFIGURING IMPORTING +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +config.importer = { + daterange: { + // NOTE: these dates are in UTC + from: "2015-09-09 12:00:00" + } +} + +// set this to true if you understand that Gekko will +// invest according to how you configured the indicators. +// None of the advice in the output is Gekko telling you +// to take a certain position. Instead it is the result +// of running the indicators you configured automatically. +// +// In other words: Gekko automates your trading strategies, +// it doesn't advice on itself, only set to true if you truly +// understand this. +// +// Not sure? Read this first: https://github.com/askmike/gekko/issues/201 +config['I understand that Gekko only automates MY OWN trading strategies'] = true; + +module.exports = config; diff --git a/core/baseTradingMethod.js b/core/baseTradingMethod.js index 1e348249d..696bb4623 100644 --- a/core/baseTradingMethod.js +++ b/core/baseTradingMethod.js @@ -57,6 +57,27 @@ var Indicators = { CCI: { factory: require(indicatorsPath + 'CCI'), input: 'candle' + }, + + ZERO: { + factory: require(indicatorsPath + 'ZERO'), + input: 'candle' + }, + x3nikiehihsa: { + factory: require(indicatorsPath + 'x3nikiehihsa'), + input: 'candle' + }, + nikiehihsa: { + factory: require(indicatorsPath + 'nikiehihsa'), + input: 'candle' + }, + x2MACD: { + factory: require(indicatorsPath + 'x2MACD'), + input: 'price' + }, + Alligator: { + factory: require(indicatorsPath + 'Alligator'), + input: 'candle' } }; diff --git a/methods/Alligator.js b/methods/Alligator.js new file mode 100644 index 000000000..7a68c1806 --- /dev/null +++ b/methods/Alligator.js @@ -0,0 +1,136 @@ +// helpers +var _ = require('lodash'); +var log = require('../core/log.js'); + +// configuration +var config = require('../core/util.js').getConfig(); +var settings = config.CCI; +var pposettings = config.PPO; + + +// let's create our own method +var method = {}; + +// teach our trading method events +var Util = require('util'); + +// prepare everything our method needs +method.init = function() { + this.currentTrend; + this.requiredHistory = config.tradingAdvisor.historySize; + + this.age = 0; + this.trend = { + direction: 'undefined', + duration: 0, + persisted: false, + adviced: false + }; + this.historySize = config.tradingAdvisor.historySize; + this.uplevel = config.CCI.thresholds.up; + this.downlevel = config.CCI.thresholds.down; + this.persisted = config.CCI.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('alli', 'Alligator', config.Alligator); +} + +// what happens on every new candle? +method.update = function(candle) { +} + +// for debugging purposes: log the last calculated +// EMAs and diff. +method.log = function() { + var alli = this.indicators.alli; + if (typeof(alli.result) == 'boolean') { + log.debug('Insufficient data available. Age: ', alli.size, ' of ', alli.maxSize); +// log.debug('ind: ', cci.TP.result, ' ', cci.TP.age, ' ', cci.TP.depth); + return; + } + + log.debug('calculated Alligator state for candle:'); + log.debug('\t', 'Price:\t\t\t', this.lastPrice); + log.debug('\t', 'SSMAJaws:\t', alli.SSMAJaws.toFixed(8)); + log.debug('\t', 'SSMATeeth:\t', alliSSMA.Teeth.toFixed(8)); + log.debug('\t', 'SSMALips/n:\t', alli.SSMALips.toFixed(8)); + if (typeof(cci.result) == 'boolean' ) + log.debug('\t In sufficient data available.'); + else + log.debug('\t', 'Alli:\t', cci.result.toFixed(2)); +} + +/* + * + */ +method.check = function() { + + + var price = this.lastPrice; + + + this.age++; + var alli = this.indicators.alli; + + if (typeof(alli.result) == 'number') { + + // overbought? + if (alli.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 (alli.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 (alli.result >= this.uplevel) { + this.trend.duration++; + if (this.trend.duration >= this.persisted) { + this.trend.persisted = true; + } + } else if (alli.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 (alli.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 (alli.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; diff --git a/methods/indicators/Alligator.js b/methods/indicators/Alligator.js new file mode 100644 index 000000000..84ee2fd4e --- /dev/null +++ b/methods/indicators/Alligator.js @@ -0,0 +1,116 @@ +var log = require('../../core/log'); + +var Indicator = function(settings) { + this.depth = settings.history; + this.result = false; + this.age = 0; + //this.history = []; + this.high = []; + this.low = []; + this.x = []; + + this.SSMAJaws = 0; + this.SSMATeeth = 0; + this.SSMALips = 0; + + /* + * 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.high.push(0.0); + this.low.push(0.0); + + this.x.push(i); + } + + log.debug("Created Alligator indicator with h: ", this.depth); +} + +Indicator.prototype.update = function(candle) { + + // We need sufficient history to get the right result. + if(this.result === false && this.age < this.depth) { + + //this.history[this.age] = price; + this.high[this.age] = candle.high; + this.low[this.age] = candle.low; + + 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.high[i] = this.high[i + 1]; + this.low[i] = this.low[i + 1]; + } + + //this.history[this.depth-1] = price; + this.high[this.depth-1] = candle.high; + this.low[this.depth-1] = candle.low; + + this.calculate(candle); + return; + } + +* +* Handle calculations +*/ +Indicator.prototype.calculate = function(candle) { +//Bill Williams Alligator for Think or Swim +//Mike Lapping 2010 +//Extra steps were taken because I cannot find proper +//documentation of the average() function +//can be used and modified by anyone for any reason. do not sell. + + // We will now work on the SSMA called jaws + var JOffset = 8; + var Javg13 = ((this.high[12 + JOffset] + this.low[12 + JOffset]) / 2); + var Javg12 = ((this.high[11 + JOffset] + this.low[11 + JOffset]) / 2); + var Javg11 = ((this.high[10 + JOffset] + this.low[10 + JOffset]) / 2); + var Javg10 = ((this.high[9 + JOffset] + this.low[9 + JOffset]) / 2); + var Javg9 = ((this.high[8 + JOffset] + this.low[8 + JOffset]) / 2); + var Javg8 = ((this.high[7 + JOffset] + this.low[7 + JOffset]) / 2); + var Javg7 = ((this.high[6 + JOffset] + this.low[6 + JOffset]) / 2); + var Javg6 = ((this.high[5 + JOffset] + this.low[5 + JOffset]) / 2); + var Javg5 = ((this.high[4 + JOffset] + this.low[4 + JOffset]) / 2); + var Javg4 = ((this.high[3 + JOffset] + this.low[3 + JOffset]) / 2); + var Javg3 = ((this.high[2 + JOffset] + this.low[2 + JOffset]) / 2); + var Javg2 = ((this.high[1 + JOffset] + this.low[1 + JOffset]) / 2); + var Javg1 = ((this.high[0 + JOffset] + this.low[0 + JOffset]) / 2); + + this.SSMAJaws = ( Javg1 + Javg2 + Javg3 + Javg4 + Javg5 + Javg6 + Javg7 + Javg8 + Javg9 + Javg10 + Javg11 + Javg12 + Javg13) / 13; + +//Now working on Lips + var LOffset = 5; + var Lavg8 = ((this.high[7 + LOffset] + this.low[7 + LOffset]) / 2); + var Lavg7 = ((this.high[6 + LOffset] + this.low[6 + LOffset]) / 2); + var Lavg6 = ((this.high[5 + LOffset] + this.low[5 + LOffset]) / 2); + var Lavg5 = ((this.high[4 + LOffset] + this.low[4 + LOffset]) / 2); + var Lavg4 = ((this.high[3 + LOffset] + this.low[3 + LOffset]) / 2); + var Lavg3 = ((this.high[2 + LOffset] + this.low[2 + LOffset]) / 2); + var Lavg2 = ((this.high[1 + LOffset] + this.low[1 + LOffset]) / 2); + var Lavg1 = ((this.high[0 + LOffset] + this.low[0 + LOffset]) / 2); + + this.SSMALips = (Lavg1 + Lavg2 + Lavg3 + Lavg4 + Lavg5 + Lavg6 + Lavg7 + Lavg8) / 8; + +//Work on teeth + var TOffset = 3; + + var Tavg5 = ((this.high[4 + TOffset] + this.low[4 + TOffset]) / 2); + var Tavg4 = ((this.high[3 + TOffset] + this.low[3 + TOffset]) / 2); + var Tavg3 = ((this.high[2 + TOffset] + this.low[2 + TOffset]) / 2); + var Tavg2 = ((this.high[1 + TOffset] + this.low[1 + TOffset]) / 2); + var Tavg1 = ((this.high[0 + TOffset] + this.low[0 + TOffset]) / 2); + + this.SSMATeeth = (Tavg1 + Tavg2 + Tavg3 + Tavg4 + Tavg5) / 5; + +} + +module.exports = Indicator; \ No newline at end of file