02-07-2018, 01:25 AM
Hi,
After some testing and discussion with Road.Apps it seems that the current indicator and strategy need more flexibility in order to be usable in live trading. I won't have the time to work on further improvements, because I am busy with other projects, so I will post the current code here in this thread. Anyone willing to take over the implementation of this feature can use the code as a starting point or just as a reference. Mike, please keep the bounty for whomever decides to take over the project (the strategy is not giving very good results in its current form).
The following code goes to
gekko/strategies/TDM_SEQ.js
And the following code goes to
gekko/strategies/indicators/TDM_SEQ.js
After some testing and discussion with Road.Apps it seems that the current indicator and strategy need more flexibility in order to be usable in live trading. I won't have the time to work on further improvements, because I am busy with other projects, so I will post the current code here in this thread. Anyone willing to take over the implementation of this feature can use the code as a starting point or just as a reference. Mike, please keep the bounty for whomever decides to take over the project (the strategy is not giving very good results in its current form).
The following code goes to
gekko/strategies/TDM_SEQ.js
Code:
var strat = {
init: function() {
this.name = 'TDM_SEQ';
this.numCountdowns = 0;
this.pendingAdvice = null;
this.addIndicator('tdmSeq', 'TDM_SEQ');
},
update: function(candle) {
var seqResult;
this.indicators.tdmSeq.update(candle);
seqResult = this.indicators.tdmSeq.result;
if (this.numCountdowns != seqResult.numCountdowns) {
console.log('TDM_SEQ(s): New complete countdown (', seqResult.lastCdType, ') after candle on', candle.start);
this.numCountdowns = seqResult.numCountdowns;
this.pendingAdvice = seqResult.lastCdType;
}
},
log: function () {},
check: function() {
switch (this.pendingAdvice) {
case 'buy':
console.log('TDM_SEQ(s): Advising long');
this.advice('long');
this.pendingAdvice = null;
break;
case 'sell':
console.log('TDM_SEQ(s): Advising short');
this.advice('short');
this.pendingAdvice = null;
break;
default:
// console.log('TDM_SEQ(s): Advising none');
this.advice();
break;
}
}
};
module.exports = strat;
And the following code goes to
gekko/strategies/indicators/TDM_SEQ.js
Code:
/*
* Tom DeMark's Sequential indicator
*
* The return variable is an object with following properties:
*
* numCountdowns: Number of countdowns found so far (integer)
* lastCdType: Last countdown type. Can be 'buy' or 'sell' (string)
* lastCdPerfectSetup: A boolean flag indicating whether last countdown had a perfect setup. (boolean)
*
*/
var Indicator = function() {
this.histData = [];
this.histMax = 20;
this.state = 'none';
this.perfectSetup = null;
this.countdownCandles = null;
this.countdownCandleEight = null;
this.stateToNone();
this.result = {numCountdowns: 0, lastCdType: null, lastCdPerfectSetup: null};
// console.log('TDM_SEQ(i): Created an indicator');
};
Indicator.prototype.update = function(candle) {
this.addToHistory(candle);
this.updateFromHistory();
};
Indicator.prototype.addToHistory = function(candle) {
this.histData.unshift(candle);
if (this.histData.length > this.histMax) {
this.histData.pop();
}
// console.log('TDM_SEQ(i): Added a candle:', JSON.stringify(candle));
};
Indicator.prototype.updateFromHistory = function() {
/*
console.log(
'TDM_SEQ(i): Variables before candle',
JSON.stringify({state: this.state, perfectSetup: this.perfectSetup, countdownCandles: this.countdownCandles})
);
*/
for (;;) {
if (this.updateAttempt()) {
break;
}
this.stateToNone();
}
/*
console.log(
'TDM_SEQ(i): Variables after candle',
JSON.stringify({state: this.state, perfectSetup: this.perfectSetup, countdownCandles: this.countdownCandles})
);
*/
};
Indicator.prototype.updateAttempt = function() {
switch (this.state) {
case 'none':
if (this.checkBuySetup()) {
// console.log('TDM_SEQ(i): Found a buy setup');
this.state = 'buy_countdown';
this.countdownCandles = 0;
this.perfectSetup = Math.min(this.histData [0].low, this.histData [1].low) < Math.min(this.histData [2].low, this.histData [3].low);
} else if (this.checkSellSetup()) {
// console.log('TDM_SEQ(i): Found a sell setup');
this.state = 'sell_countdown';
this.countdownCandles = 0;
this.perfectSetup = Math.max(this.histData [0].high, this.histData [1].high) > Math.min(this.histData [2].high, this.histData [3].high);
}
return (true);
case 'buy_countdown':
if (this.checkSellSetup()) {
// console.log('TDM_SEQ(i): Canceling buy countdown because of a sell setup');
this.stateToNone();
return (false);
}
if (this.histData [0].close <= this.histData [2].low) {
++this.countdownCandles;
}
if (this.countdownCandles == 8) {
this.countdownCandleEight = this.histData [0];
}
if ((this.countdownCandles >= 13) && (this.histData [0].low <= this.countdownCandleEight.close)) {
this.addCountdownToResult('buy');
this.stateToNone();
}
return (true);
case 'sell_countdown':
if (this.checkBuySetup()) {
// console.log('TDM_SEQ(i): Canceling sell countdown because of a buy setup');
this.stateToNone();
return (false);
}
if (this.histData [0].close >= this.histData [2].high) {
++this.countdownCandles;
}
if (this.countdownCandles == 8) {
this.countdownCandleEight = this.histData [0];
}
if ((this.countdownCandles >= 13) && (this.histData [0].high >= this.countdownCandleEight.close)) {
this.addCountdownToResult('sell');
this.stateToNone();
}
return (true);
default:
// console.log('TDM_SEQ(i): Unknown state', this.state);
this.stateToNone();
return (false);
}
};
Indicator.prototype.checkBuySetup = function() {
var i;
if (this.histData.length < 13) {
return (false);
}
for (i = 0; i < 9; ++i) {
if (this.histData [i].close >= this.histData [i+4].close) {
return (false);
}
}
return (true);
};
Indicator.prototype.checkSellSetup = function() {
var i;
if (this.histData.length < 13) {
return (false);
}
for (i = 0; i < 9; ++i) {
if (this.histData [i].close <= this.histData [i+4].close) {
return (false);
}
}
return (true);
};
Indicator.prototype.stateToNone = function() {
this.state = 'none';
this.perfectSetup = null;
this.countdownCandles = null;
this.countdownCandleEight = null;
};
Indicator.prototype.addCountdownToResult = function(type) {
++this.result.numCountdowns;
this.result.lastCdType = type;
this.result.lastCdPerfectSetup = this.perfectSetup;
};
module.exports = Indicator;