/**
* ZigZag Indicator
* Filters out market noise and shows major price swings
*/
export function calculateZigZag(closes, deviation = 5 // Minimum deviation in percentage
) {
if (closes.length < 3) {
return {
zigzag: null,
direction: null,
swingPoints: [],
lastSwing: null,
};
}
const swingPoints = [];
let lastSwing = null;
let trend = 'up'; // Start assuming uptrend
// First, find initial swing point
let maxValue = closes[0];
let minValue = closes[0];
let maxValueIndex = 0;
let minValueIndex = 0;
for (let i = 1; i < closes.length; i++) {
if (closes[i] > maxValue) {
maxValue = closes[i];
maxValueIndex = i;
}
if (closes[i] < minValue) {
minValue = closes[i];
minValueIndex = i;
}
}
// Start with the first significant move
if (maxValueIndex < minValueIndex) {
// Started with up move
swingPoints.push({ index: 0, value: closes[0], type: 'low' });
lastSwing = { index: maxValueIndex, value: maxValue, type: 'high' };
swingPoints.push(lastSwing);
trend = 'down';
}
else {
// Started with down move
swingPoints.push({ index: 0, value: closes[0], type: 'high' });
lastSwing = { index: minValueIndex, value: minValue, type: 'low' };
swingPoints.push(lastSwing);
trend = 'up';
}
// Process remaining data
for (let i = lastSwing.index + 1; i < closes.length; i++) {
const currentValue = closes[i];
if (trend === 'down') {
// Looking for lower low
if (currentValue < lastSwing.value * (1 - deviation / 100)) {
// Found new swing low
lastSwing = { index: i, value: currentValue, type: 'low' };
swingPoints.push(lastSwing);
trend = 'up';
}
else if (currentValue > lastSwing.value) {
// Price moved up significantly, update swing point
lastSwing = { index: i, value: currentValue, type: 'high' };
swingPoints[swingPoints.length - 1] = lastSwing;
}
}
else {
// Looking for higher high
if (currentValue > lastSwing.value * (1 + deviation / 100)) {
// Found new swing high
lastSwing = { index: i, value: currentValue, type: 'high' };
swingPoints.push(lastSwing);
trend = 'down';
}
else if (currentValue < lastSwing.value) {
// Price moved down significantly, update swing point
lastSwing = { index: i, value: currentValue, type: 'low' };
swingPoints[swingPoints.length - 1] = lastSwing;
}
}
}
// Determine current direction
let direction = null;
if (swingPoints.length >= 2) {
const lastTwo = swingPoints.slice(-2);
if (lastTwo[1].type === 'high' && lastTwo[1].value > lastTwo[0].value) {
direction = 'up';
}
else if (lastTwo[1].type === 'low' && lastTwo[1].value < lastTwo[0].value) {
direction = 'down';
}
else {
direction = 'sideways';
}
}
return {
zigzag: lastSwing ? lastSwing.value : null,
direction,
swingPoints,
lastSwing,
};
}