@react-financial-charts/indicators
Version:
Indicators for react-financial-charts
200 lines • 8.02 kB
JavaScript
import { functor, merge, path } from "../utils";
import atr from "./atr";
import { Kagi as defaultOptions } from "./defaultOptionsForComputation";
export default function () {
let options = defaultOptions;
let dateAccessor = (d) => d.date;
let dateMutator = (d, date) => {
d.date = date;
};
const calculator = (data) => {
const { reversalType, windowSize, reversal, sourcePath } = options;
const source = path(sourcePath);
let reversalThreshold;
if (reversalType === "ATR") {
const atrAlgorithm = atr().options({ windowSize });
const atrCalculator = merge()
.algorithm(atrAlgorithm)
.merge((d, c) => {
d["atr" + windowSize] = c;
});
atrCalculator(data);
reversalThreshold = (d) => d["atr" + windowSize];
}
else {
reversalThreshold = functor(reversal);
}
const kagiData = [];
let prevPeak;
let prevTrough;
let direction;
let line = {};
data.forEach(function (d) {
if (line.from === undefined) {
dateMutator(line, dateAccessor(d));
line.from = dateAccessor(d);
if (!line.open) {
line.open = d.open;
}
line.high = d.high;
line.low = d.low;
if (!line.close) {
line.close = source(d);
}
line.startOfYear = d.startOfYear;
line.startOfQuarter = d.startOfQuarter;
line.startOfMonth = d.startOfMonth;
line.startOfWeek = d.startOfWeek;
}
if (!line.startOfYear) {
line.startOfYear = d.startOfYear;
if (line.startOfYear) {
line.date = d.date;
}
}
if (!line.startOfQuarter) {
line.startOfQuarter = d.startOfQuarter;
if (line.startOfQuarter && !line.startOfYear) {
line.date = d.date;
}
}
if (!line.startOfMonth) {
line.startOfMonth = d.startOfMonth;
if (line.startOfMonth && !line.startOfQuarter) {
line.date = d.date;
}
}
if (!line.startOfWeek) {
line.startOfWeek = d.startOfWeek;
if (line.startOfWeek && !line.startOfMonth) {
line.date = d.date;
}
}
line.volume = (line.volume || 0) + d.volume;
// @ts-ignore
line.high = Math.max(line.high, d.high);
// @ts-ignore
line.low = Math.min(line.low, d.low);
line.to = dateAccessor(d);
// @ts-ignore
const priceMovement = source(d) - line.close;
if (
// @ts-ignore
(line.close >= line.open /* going up */ && priceMovement > 0) /* and moving in same direction */ ||
// @ts-ignore
(line.close < line.open /* going down */ && priceMovement < 0) /* and moving in same direction */) {
line.close = source(d);
// @ts-ignore
if (prevTrough && line.close < prevTrough) {
// going below the prevTrough, so change from yang to yin
// A yin line forms when a Kagi line breaks below the prior trough.
line.changePoint = prevTrough;
if (line.startAs !== "yin") {
line.changeTo = "yin";
// line.startAs = "yang";
}
}
// @ts-ignore
if (prevPeak && line.close > prevPeak) {
// going above the prevPeak, so change from yin to yang
// A yang line forms when a Kagi line breaks above the prior peak
line.changePoint = prevPeak;
if (line.startAs !== "yang") {
line.changeTo = "yang";
// line.startAs = "yin";
}
}
}
else if (
// @ts-ignore
(line.close >= line.open /* going up */ &&
priceMovement < 0 /* and moving in other direction */ &&
Math.abs(priceMovement) > reversalThreshold(d)) /* and the movement is big enough for reversal */ ||
// @ts-ignore
(line.close < line.open /* going down */ &&
priceMovement > 0 /* and moving in other direction */ &&
/* and the movement is big enough for reversal */
Math.abs(priceMovement) > reversalThreshold(d))) {
// reverse direction
const nextLineOpen = line.close;
// @ts-ignore
direction = (line.close - line.open) / Math.abs(line.close - line.open);
let nextChangePoint;
let nextChangeTo;
if (direction < 0 /* if direction so far has been -ve*/) {
// compare with line.close becomes prevTrough
if (prevPeak === undefined) {
prevPeak = line.open;
}
prevTrough = line.close;
if (source(d) > prevPeak) {
nextChangePoint = prevPeak;
nextChangeTo = "yang";
}
}
else {
if (prevTrough === undefined) {
prevTrough = line.open;
}
prevPeak = line.close;
if (source(d) < prevTrough) {
nextChangePoint = prevTrough;
nextChangeTo = "yin";
}
}
if (line.startAs === undefined) {
line.startAs = direction > 0 ? "yang" : "yin";
}
const startAs = line.changeTo || line.startAs;
line.added = true;
kagiData.push(line);
direction = -1 * direction; // direction is reversed
line = Object.assign({}, line);
line.open = nextLineOpen;
line.close = source(d);
line.startAs = startAs;
line.changePoint = nextChangePoint;
line.changeTo = nextChangeTo;
line.added = false;
line.from = undefined;
line.volume = 0;
}
else {
// console.log("MOVING IN REV DIR BUT..", line.open, line.close, source(d));
}
line.current = source(d);
// @ts-ignore
let dir = line.close - line.open;
dir = dir === 0 ? 1 : dir / Math.abs(dir);
// @ts-ignore
line.reverseAt = dir > 0 ? line.close - reversalThreshold(d) : line.open - reversalThreshold(d);
});
if (!line.added) {
kagiData.push(line);
}
return kagiData;
};
calculator.options = (newOptions) => {
if (newOptions === undefined) {
return options;
}
options = Object.assign(Object.assign({}, defaultOptions), newOptions);
return calculator;
};
calculator.dateMutator = (newDateMutator) => {
if (newDateMutator === undefined) {
return dateMutator;
}
dateMutator = newDateMutator;
return calculator;
};
calculator.dateAccessor = (newDateAccessor) => {
if (newDateAccessor === undefined) {
return dateAccessor;
}
dateAccessor = newDateAccessor;
return calculator;
};
return calculator;
}
//# sourceMappingURL=kagi.js.map