Open-source scriptindicator/v1
Zig Zag
Connect confirmed alternating swing highs and lows with a configurable Zig Zag path.
#pivots#swing-trading#zig-zag
Description
Zig Zag filters minor price movements and connects confirmed pivot highs and lows. Pivot depth, path width, color, history length, and optional price labels are configurable. Pivots appear only after the selected right-side confirmation period, so historical legs do not use future information before confirmation.
Open-source script. Everyone can inspect the source code, use the script on a chart, and save an editable copy.
Release notes
Version history · 1 published version
Version 1CurrentChart updatedZig Zag · Aug 31, 2026
Source codempl-2.0View sourceHide source
// Connect confirmed swing highs and lows without using future data.
indicator("Zig Zag", { overlay: true, maxPolylines: 1, maxLabels: 100 });
const depth = input.int("Pivot depth", 5, { min: 2, max: 50 });
const maxPivots = input.int("Maximum pivots", 40, { min: 4, max: 100 });
const showPrices = input.bool("Show pivot prices", false);
const lineColor = input.color("Line color", "#22d3ee");
const lineWidth = input.int("Line width", 2, { min: 1, max: 4 });
const highs = pivothigh(high, depth, depth);
const lows = pivotlow(low, depth, depth);
const pivots = [];
function acceptPivot(kind, index, price) {
const candidate = { kind, index, price };
const previous = pivots[pivots.length - 1];
if (!previous) {
pivots.push(candidate);
return;
}
if (previous.kind === kind) {
const moreExtreme = kind === "high" ? price >= previous.price : price <= previous.price;
if (moreExtreme) pivots[pivots.length - 1] = candidate;
return;
}
pivots.push(candidate);
}
for (let confirmation = 0; confirmation < close.length; confirmation++) {
const pivotIndex = confirmation - depth;
if (pivotIndex < 0) continue;
if (Number.isFinite(highs[confirmation])) acceptPivot("high", pivotIndex, highs[confirmation]);
if (Number.isFinite(lows[confirmation])) acceptPivot("low", pivotIndex, lows[confirmation]);
}
const visible = pivots.slice(-maxPivots);
if (visible.length >= 2) {
const points = visible.map((pivot) => chart.point.from_index(pivot.index, pivot.price));
polyline.new({ points, xloc: "index", color: lineColor, width: lineWidth });
if (showPrices) {
visible.forEach((pivot) => label.new({
x: pivot.index,
y: pivot.price,
xloc: "index",
text: pivot.price.toFixed(2),
position: pivot.kind === "high" ? "top" : "bottom",
color: "#0f172a",
textColor: lineColor,
size: "small",
}));
}
}
Community discussion
0 comments
No comments yet. Start the discussion.