Ichimoku indicator error
werlang opened this issue · 2 comments
It seems that the Ichimoku indicator spanA and spanB lines are calculated wrong.
They should be plotted displacement
periods in the future, but they are not.
Here is a reference for the calculation:
https://www.investopedia.com/terms/i/ichimoku-cloud.asp
I created a chart using TradingvVew lightweight charts, and plotted the ichimoku values as lines. To compare, I picked the same chart on TradingView with the ichimoku indicator enabled. It seems clear to me that the conversion and base lines are plotted right, but spanA and spanB are not plotted in the future, as they should be.
I believe the error is here. The commented part is responsible for the displacement.
technicalindicators/lib/ichimoku/IchimokuCloud.js
Lines 55 to 60 in 7740f17
I corrected this error. Tried to follow the instructions to create a pull request, but got a bunch of errors.
This should fix the error:
class IchimokuCloud extends Indicator {
constructor(input) {
super(input);
this.result = [];
var defaults = {
conversionPeriod: 9,
basePeriod: 26,
spanPeriod: 52,
displacement: 26
};
var params = Object.assign({}, defaults, input);
var currentConversionData = new FixedSizeLinkedList(params.conversionPeriod * 2, true, true, false);
var currentBaseData = new FixedSizeLinkedList(params.basePeriod * 2, true, true, false);
var currenSpanData = new FixedSizeLinkedList(params.spanPeriod * 2, true, true, false);
let spanHistory = [];
this.generator = (function* () {
let result;
let tick;
let period = Math.max(params.conversionPeriod, params.basePeriod, params.spanPeriod, params.displacement);
let periodCounter = 1;
tick = yield;
while (true) {
// Keep a list of lows/highs for the max period
currentConversionData.push(tick.high);
currentConversionData.push(tick.low);
currentBaseData.push(tick.high);
currentBaseData.push(tick.low);
currenSpanData.push(tick.high);
currenSpanData.push(tick.low);
if (periodCounter < period) {
periodCounter++;
}
else {
// Tenkan-sen (ConversionLine): (9-period high + 9-period low)/2))
let conversionLine = (currentConversionData.periodHigh + currentConversionData.periodLow) / 2;
// Kijun-sen (Base Line): (26-period high + 26-period low)/2))
let baseLine = (currentBaseData.periodHigh + currentBaseData.periodLow) / 2;
// Senkou Span A (Leading Span A): (Conversion Line + Base Line)/2))
let spanA = (conversionLine + baseLine) / 2;
// Senkou Span B (Leading Span B): (52-period high + 52-period low)/2))
let spanB = (currenSpanData.periodHigh + currenSpanData.periodLow) / 2;
// Senkou Span A / Senkou Span B offset by 26 periods
spanHistory.push([ spanA, spanB ]);
result = {
conversion: conversionLine,
base: baseLine,
};
if(spanHistory.length == params.displacement) {
[ spanA, spanB ] = spanHistory.shift();
result.spanA = spanA;
result.spanB = spanB;
}
}
tick = yield result;
}
})();
this.generator.next();
input.low.forEach((tick, index) => {
var result = this.generator.next({
high: input.high[index],
low: input.low[index],
});
if (result.value) {
this.result.push(result.value);
}
});
}
nextValue(price) {
return this.generator.next(price).value;
}
}