1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
558 lines of Pine Script v6 from PineScript/ZigzagImpulseStats.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
ZigzagImpulseStats.pine
//@version=6
indicator("Zigzag Impulse Stats", overlay = true, max_lines_count = 500, max_labels_count = 500, max_bars_back = 5000)
if barstate.isfirst
log.warning("Zigzag Impulse Stats initialized. Collecting data...")
// ═══════════════════════════════════════════════════════════════
// Zigzag Settings
// ═══════════════════════════════════════════════════════════════
float devStart = input.float(0.1, "Deviation Start (%)", minval = 0.00001, maxval = 100.0, step = 0.1, group = "Zigzag")
float devEnd = input.float(0.5, "Deviation End (%)", minval = 0.00001, maxval = 100.0, step = 0.1, group = "Zigzag")
float devStep = input.float(0.1, "Deviation Step (%)", minval = 0.00001, maxval = 100.0, step = 0.1, group = "Zigzag")
int depthInput = input.int(10, "Depth", minval = 2, group = "Zigzag")
color lineClr = input.color(#2962FF, "Line color", group = "Zigzag")
bool extLast = input.bool(true, "Extend to last bar", group = "Zigzag")
int maxSegBars = input.int(2000, "Max bars per segment (timeout limit)", minval = 10, group = "Display")
bool showRZ = input.bool(true, "Show Ratio Zigzag (rz)", group = "Metrics")
bool showZD = input.bool(true, "Show Zigzag Depth (zd)", group = "Metrics")
bool showH = input.bool(true, "Show Heterogeneity (h)", group = "Metrics")
bool showUnif = input.bool(true, "Show Uniformity (unif)", group = "Metrics")
bool showDevLine= input.bool(true, "Show Dev Line (devL)", group = "Metrics")
bool showDevCand= input.bool(true, "Show Dev Candle (devC)", group = "Metrics")
bool showArea = input.bool(true, "Show Envelope Area (area)", group = "Metrics")
// ═══════════════════════════════════════════════════════════════
// Impulse Mode
// ═══════════════════════════════════════════════════════════════
bool impulseMode = input.bool(false, "Enable Impulse Mode", group = "Impulse Mode", tooltip = "If enabled, only segments satisfying the conditions below are highlighted as impulses")
int impMinBars = input.int(10, "Min bars in segment", minval = 1, maxval = 300, group = "Impulse Mode")
bool impUseRZ = input.bool(true, "Filter by RZ <", group = "Impulse Mode", inline = "irz")
int impThreshRZ = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "irz")
bool impUseZD = input.bool(true, "Filter by ZD <", group = "Impulse Mode", inline = "izd")
int impThreshZD = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "izd")
bool impUseH = input.bool(true, "Filter by H <", group = "Impulse Mode", inline = "ih")
int impThreshH = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "ih")
bool impUseUnif = input.bool(true, "Filter by Unif <", group = "Impulse Mode", inline = "iunif")
int impThreshUnif = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "iunif")
bool impUseDevL = input.bool(true, "Filter by DevL <", group = "Impulse Mode", inline = "idevl")
int impThreshDevL = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "idevl")
bool impUseDevC = input.bool(true, "Filter by DevC <", group = "Impulse Mode", inline = "idevc")
int impThreshDevC = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "idevc")
bool impUseArea = input.bool(true, "Filter by Area <", group = "Impulse Mode", inline = "iarea")
int impThreshArea = input.int(30, "", minval = 0, maxval = 100, group = "Impulse Mode", inline = "iarea")
// ═══════════════════════════════════════════════════════════════
// Calculation Functions
// ═══════════════════════════════════════════════════════════════
getCross(float x1, float y1, float x2, float y2, float x3, float y3) =>
float dx1 = x2 - x1
float dy1 = y2 - y1
float dx2 = x3 - x2
float dy2 = y3 - y2
dx1 * dy2 - dy1 * dx2
update_envelope(array<float> upX, array<float> upY, array<float> dnX, array<float> dnY, float idx, float h, float l) =>
while array.size(upX) >= 2
int sz = array.size(upX)
if getCross(array.get(upX, sz - 2), array.get(upY, sz - 2), array.get(upX, sz - 1), array.get(upY, sz - 1), idx, h) >= 0
array.pop(upX)
array.pop(upY)
else
break
array.push(upX, idx)
array.push(upY, h)
while array.size(dnX) >= 2
int sz = array.size(dnX)
if getCross(array.get(dnX, sz - 2), array.get(dnY, sz - 2), array.get(dnX, sz - 1), array.get(dnY, sz - 1), idx, l) <= 0
array.pop(dnX)
array.pop(dnY)
else
break
array.push(dnX, idx)
array.push(dnY, l)
calc_area_metric(array<float> upX, array<float> upY, array<float> dnX, array<float> dnY, float startPrice, float endPrice, int duration) =>
float stat_area = 0.0
float fullLen = math.abs(endPrice - startPrice)
if fullLen > 1e-10
float upArea = 0.0
if array.size(upX) > 1
for i = 1 to array.size(upX) - 1
upArea += (array.get(upX, i) - array.get(upX, i - 1)) * (array.get(upY, i - 1) + array.get(upY, i)) / 2.0
float dnArea = 0.0
if array.size(dnX) > 1
for i = 1 to array.size(dnX) - 1
dnArea += (array.get(dnX, i) - array.get(dnX, i - 1)) * (array.get(dnY, i - 1) + array.get(dnY, i)) / 2.0
float sumArea = upArea - dnArea
float maxArea = fullLen * duration
if maxArea > 1e-10
stat_area := math.max(0.0, math.min(sumArea / maxArea, 1.0))
stat_area
calc_heterogeneity(int startIndex, int duration, float minP, float maxP) =>
float diffPart = 1.0
float rangeP = maxP - minP
if rangeP > 1e-10
int BINS = 10
array<int> bins = array.new<int>(BINS, 0)
float binSize = rangeP / BINS
for step = 0 to duration
int bIdx = startIndex + step
float l = low[bar_index - bIdx]
float h = high[bar_index - bIdx]
int startBin = math.max(0, math.min(int((l - minP) / binSize), BINS - 1))
int endBin = math.max(0, math.min(int((h - minP) / binSize), BINS - 1))
for b = startBin to endBin
array.set(bins, b, array.get(bins, b) + 1)
int firstThird = array.get(bins, 0) + array.get(bins, 1) + array.get(bins, 2)
int lastThird = array.get(bins, 7) + array.get(bins, 8) + array.get(bins, 9)
int whole = 0
for b = 0 to BINS - 1
whole += array.get(bins, b)
if whole > 0
diffPart := math.abs(firstThird - lastThird) / float(whole)
diffPart
calc_rz_metric(array<float> L, array<float> H, int duration) =>
float stat_rz = na
if duration > 0
int maxExtremaBarCount = 0
array<int> stack = array.new<int>() // Monotonic stack of increasing L values
for i = 0 to duration
float l_i = array.get(L, i)
// Maintain stack
while array.size(stack) > 0
int top_idx = array.get(stack, array.size(stack) - 1)
if array.get(L, top_idx) > l_i
array.pop(stack)
else
break
array.push(stack, i)
if i <= maxExtremaBarCount
continue
float h_i = array.get(H, i)
float l_min = h_i
bool found = false
if i < duration
for j = i + 1 to duration
float l_j = array.get(L, j)
if h_i < l_j
break
if l_j < l_min
l_min := l_j
found := true
if found
// Binary search on monotonic stack instantly finds the break index backward!
int low_idx = 0
int high_idx = array.size(stack) - 1
int best_k = -1
while low_idx <= high_idx
int mid_idx = low_idx + int((high_idx - low_idx) / 2)
int stack_val_idx = array.get(stack, mid_idx)
float s_val = array.get(L, stack_val_idx)
if s_val <= l_min
best_k := stack_val_idx
low_idx := mid_idx + 1
else
high_idx := mid_idx - 1
int count = best_k != -1 ? (i - best_k) : (i + 1)
if count > maxExtremaBarCount
maxExtremaBarCount := count
stat_rz := maxExtremaBarCount / float(duration)
stat_rz
calc_uniformity(int startIndex, int duration) =>
float stat_unif = 0.0
if duration > 0
array<float> points = array.new<float>()
// Collect all H and L
for step = 0 to duration
int bIdx = startIndex + step
float cL = low[bar_index - bIdx]
float cH = high[bar_index - bIdx]
array.push(points, cL)
array.push(points, cH)
array.sort(points)
// Unique intervals and calculate counts
float currentPoint = array.get(points, 0)
float sumCounts = 0.0
float maxCount = 0.0
int validIntervals = 0
for i = 1 to array.size(points) - 1
float nextPoint = array.get(points, i)
if nextPoint - currentPoint > 1e-10
float localMin = currentPoint
float localMax = nextPoint
int cdlCount = 0
for step = 0 to duration
int bIdx = startIndex + step
float cL = low[bar_index - bIdx]
float cH = high[bar_index - bIdx]
if cL <= localMin + 1e-10 and cH >= localMax - 1e-10
cdlCount += 1
if cdlCount == 0
cdlCount := 1
sumCounts += cdlCount
if cdlCount > maxCount
maxCount := cdlCount
validIntervals += 1
currentPoint := nextPoint
if validIntervals > 0 and maxCount > 0
float n = sumCounts / validIntervals
float m = maxCount
stat_unif := 1.0 - (n / m)
stat_unif
calc_zd_metric(array<float> L, array<float> H, int duration, float totalRange) =>
float stat_zd = 0.0
if duration > 0 and totalRange > 1e-10
float max_depth = 0.0
for i = 0 to duration
float h_i = array.get(H, i)
float local_max_depth = 0.0
if i < duration
for j = i + 1 to duration
float l_j = array.get(L, j)
if h_i < l_j
break
float depth = h_i - l_j
if depth > local_max_depth
local_max_depth := depth
if local_max_depth > max_depth
max_depth := local_max_depth
stat_zd := max_depth / totalRange
stat_zd
calc_all_stats(int startIndex, float startPrice, int endIndex, float endPrice, bool calcRZ, bool calcH, bool calcUnif, bool calcDevLine, bool calcDevCand, bool calcArea, bool calcZD) =>
bool isUp = endPrice > startPrice
int duration = endIndex - startIndex
float stat_rz = na
float stat_hMax = na
float stat_unif = na
float stat_devLine = na
float stat_devCand = na
float stat_area = na
float stat_zd = na
if duration > 0
float minP = math.min(startPrice, endPrice)
float maxP = math.max(startPrice, endPrice)
float totalRange = math.abs(endPrice - startPrice)
float maxDistanceLine = 0.0
float maxDistanceCand = 0.0
array<float> L = (calcRZ or calcZD) ? array.new<float>(duration + 1) : na
array<float> H = (calcRZ or calcZD) ? array.new<float>(duration + 1) : na
array<float> movementExtrema = calcDevCand ? array.new<float>(duration + 1) : na
array<float> devCLs = calcDevCand ? array.new<float>(duration + 1) : na
array<float> devCHs = calcDevCand ? array.new<float>(duration + 1) : na
array<float> upX = calcArea ? array.new<float>() : na
array<float> upY = calcArea ? array.new<float>() : na
array<float> dnX = calcArea ? array.new<float>() : na
array<float> dnY = calcArea ? array.new<float>() : na
// --- SINGLE PASS: GATHER DATA & ON-THE-FLY ALGORITHMS ---
for step = 0 to duration
int bIdx = startIndex + step
float cL = low[bar_index - bIdx]
float cH = high[bar_index - bIdx]
minP := math.min(minP, cL)
maxP := math.max(maxP, cH)
if calcRZ or calcZD
float rzL = cL
float rzH = cH
if not isUp
rzL := -cH
rzH := -cL
array.set(L, step, rzL)
array.set(H, step, rzH)
if calcDevCand
array.set(devCLs, step, cL)
array.set(devCHs, step, cH)
float movementExtremum = isUp ? cH : cL
array.set(movementExtrema, step, movementExtremum)
if calcDevLine and step > 0 and step < duration and totalRange > 1e-10
float t = step / float(duration)
float lineValue = startPrice + (endPrice - startPrice) * t
float distHigh = math.abs(cH - lineValue)
float distLow = math.abs(cL - lineValue)
float dist = math.max(distHigh, distLow)
if dist > maxDistanceLine
maxDistanceLine := dist
if calcArea
float h = cH
float l = cL
if step == 0
h := startPrice
l := startPrice
if step == duration
h := endPrice
l := endPrice
update_envelope(upX, upY, dnX, dnY, float(startIndex + step), h, l)
// --- CALC RESULTS ---
if calcDevLine
stat_devLine := totalRange > 1e-10 ? math.min(maxDistanceLine / totalRange, 1.0) : 0.0
if calcDevCand
if duration > 1 and totalRange > 1e-10
for step = 1 to duration - 1
float candleExtremum = isUp ? array.get(devCLs, step) : array.get(devCHs, step)
float dist = 1e10
for j = 0 to duration
float d = math.abs(array.get(movementExtrema, j) - candleExtremum)
if d < dist
dist := d
if dist > maxDistanceCand
maxDistanceCand := dist
stat_devCand := totalRange > 1e-10 ? math.min(maxDistanceCand / totalRange, 1.0) : 0.0
if calcArea
stat_area := calc_area_metric(upX, upY, dnX, dnY, startPrice, endPrice, duration)
if calcH
stat_hMax := calc_heterogeneity(startIndex, duration, minP, maxP)
if calcUnif
stat_unif := calc_uniformity(startIndex, duration)
if calcRZ
stat_rz := calc_rz_metric(L, H, duration)
if calcZD
stat_zd := calc_zd_metric(L, H, duration, totalRange)
[stat_rz, stat_hMax, stat_unif, stat_devLine, stat_devCand, stat_area, stat_zd]
// ═══════════════════════════════════════════════════════════════
// ZigZag Type & Methods
// ═══════════════════════════════════════════════════════════════
type ZigZag
float dev
color clr
array<float> price
array<int> bar
array<bool> isHigh
array<line> lines
array<label> labels
label liveLabel
line extLine
method draw_segment_label(ZigZag zz, int sBar, float sPrc, int eBar, float ePrc, line ln, int barDiff, bool isLive) =>
bool doCalcRZ = showRZ or (impulseMode and impUseRZ)
bool doCalcZD = showZD or (impulseMode and impUseZD)
bool doCalcH = showH or (impulseMode and impUseH)
bool doCalcUnif = showUnif or (impulseMode and impUseUnif)
bool doCalcDevL = showDevLine or (impulseMode and impUseDevL)
bool doCalcDevC = showDevCand or (impulseMode and impUseDevC)
bool doCalcArea = showArea or (impulseMode and impUseArea)
[stat_rz, stat_hMax, stat_unif, stat_devLine, stat_devCand, stat_area, stat_zd] = calc_all_stats(sBar, sPrc, eBar, ePrc, doCalcRZ, doCalcH, doCalcUnif, doCalcDevL, doCalcDevC, doCalcArea, doCalcZD)
bool isImpulse = true
if impulseMode
if barDiff < impMinBars
isImpulse := false
if impUseRZ and (stat_rz * 100 >= impThreshRZ)
isImpulse := false
if impUseZD and (stat_zd * 100 >= impThreshZD)
isImpulse := false
if impUseH and (stat_hMax * 100 >= impThreshH)
isImpulse := false
if impUseUnif and (stat_unif * 100 >= impThreshUnif)
isImpulse := false
if impUseDevL and (stat_devLine * 100 >= impThreshDevL)
isImpulse := false
if impUseDevC and (stat_devCand * 100 >= impThreshDevC)
isImpulse := false
if impUseArea and (stat_area * 100 >= impThreshArea)
isImpulse := false
if impulseMode
if isImpulse
line.set_color(ln, zz.clr)
line.set_width(ln, 3)
else
line.set_color(ln, color.new(zz.clr, 80))
line.set_width(ln, 1)
else
line.set_color(ln, zz.clr)
line.set_width(ln, 2)
label lb = na
if not impulseMode or isImpulse
array<string> lines = array.new<string>()
array.push(lines, "[" + str.tostring(math.round(zz.dev, 2)) + "%]")
if showRZ
array.push(lines, "rz: " + str.tostring(int(stat_rz * 100)))
if showZD
array.push(lines, "zd: " + str.tostring(int(stat_zd * 100)))
if showH
array.push(lines, "h: " + str.tostring(int(stat_hMax * 100)))
if showUnif
array.push(lines, "unif: " + str.tostring(int(stat_unif * 100)))
if showDevLine
array.push(lines, "devL: " + str.tostring(int(stat_devLine * 100)))
if showDevCand
array.push(lines, "devC: " + str.tostring(int(stat_devCand * 100)))
if showArea
array.push(lines, "area: " + str.tostring(int(stat_area * 100)))
string txt = array.join(lines, "\n")
if txt != ""
string lblStyle = ePrc > sPrc ? label.style_label_down : label.style_label_up
lb := label.new(eBar, ePrc, txt, xloc = xloc.bar_index, style = lblStyle, color = color.new(color.blue, 30), textcolor = color.white, size = size.normal)
if not isLive
zz.labels.push(lb)
lb
method process_unfinalized_segment(ZigZag zz) =>
int _dummy = 0
int n = zz.price.size()
if n >= 2
int sBar = zz.bar.get(n - 2)
int eBar = zz.bar.get(n - 1)
float sPrc = zz.price.get(n - 2)
float ePrc = zz.price.get(n - 1)
line ln = zz.lines.get(n - 2)
if not na(zz.liveLabel)
label.delete(zz.liveLabel)
zz.liveLabel := na
int age = bar_index - sBar
int barDiff = math.abs(eBar - sBar)
if age <= 4500 and barDiff <= maxSegBars
zz.liveLabel := zz.draw_segment_label(sBar, sPrc, eBar, ePrc, ln, barDiff, true)
_dummy := 1
method finalize_segment(ZigZag zz) =>
int _dummy = 0
int n = zz.price.size()
if n >= 2
int sBar = zz.bar.get(n - 2)
int eBar = zz.bar.get(n - 1)
float sPrc = zz.price.get(n - 2)
float ePrc = zz.price.get(n - 1)
line ln = zz.lines.get(n - 2)
if not na(zz.liveLabel)
label.delete(zz.liveLabel)
zz.liveLabel := na
int age = bar_index - sBar
int barDiff = math.abs(eBar - sBar)
if age <= 4500 and barDiff <= maxSegBars
zz.draw_segment_label(sBar, sPrc, eBar, ePrc, ln, barDiff, false)
_dummy := 1
method update_zigzag(ZigZag zz, float pivotPrice, int pivBar, bool isHigh) =>
int _dummy = 0
int n = zz.price.size()
if n == 0
zz.price.push(pivotPrice)
zz.bar.push(pivBar)
zz.isHigh.push(isHigh)
_dummy := 1
else
bool lastIsHigh = zz.isHigh.get(n - 1)
float lastPrice = zz.price.get(n - 1)
if lastIsHigh == isHigh
if (isHigh and pivotPrice > lastPrice) or (not isHigh and pivotPrice < lastPrice)
zz.price.set(n - 1, pivotPrice)
zz.bar.set(n - 1, pivBar)
if zz.lines.size() > 0
zz.lines.get(zz.lines.size() - 1).set_xy2(pivBar, pivotPrice)
zz.process_unfinalized_segment()
_dummy := 2
else
float dev = 100.0 * (pivotPrice - lastPrice) / math.abs(lastPrice)
bool validDev = isHigh ? (dev >= zz.dev) : (dev <= -zz.dev)
if validDev
zz.finalize_segment()
zz.price.push(pivotPrice)
zz.bar.push(pivBar)
zz.isHigh.push(isHigh)
line ln = line.new(zz.bar.get(zz.bar.size() - 2), zz.price.get(zz.price.size() - 2), pivBar, pivotPrice, color = zz.clr, width = 2)
zz.lines.push(ln)
zz.process_unfinalized_segment()
_dummy := 3
// ═══════════════════════════════════════════════════════════════
// Zigzag Execution
// ═══════════════════════════════════════════════════════════════
var array<ZigZag> zigzags = array.new<ZigZag>()
if barstate.isfirst
float d = devStart
float step = devStep > 0 ? devStep : 0.1
int count = 0
while d <= devEnd + 0.00001 and count < 20
zigzags.push(ZigZag.new(d, lineClr, array.new<float>(), array.new<int>(), array.new<bool>(), array.new<line>(), array.new<label>(), na, na))
d += step
count += 1
int depth = depthInput < 4 ? 2 : int(depthInput / 2)
float ph = ta.pivothigh(high, depth, depth)
float pl = ta.pivotlow(low, depth, depth)
if not na(ph)
for zz in zigzags
zz.update_zigzag(ph, bar_index - depth, true)
if not na(pl)
for zz in zigzags
zz.update_zigzag(pl, bar_index - depth, false)
if extLast and barstate.islast
for zz in zigzags
int n = zz.price.size()
if n > 0
if not na(zz.extLine)
line.delete(zz.extLine)
bool lastIsHigh = zz.isHigh.get(n - 1)
float curPrice = lastIsHigh ? low : high
zz.extLine := line.new(zz.bar.get(n - 1), zz.price.get(n - 1), bar_index, curPrice, color = zz.clr, width = 2, style = line.style_dashed)
Overview
Zigzag Impulse Stats measures segment ratio, depth, heterogeneity, uniformity, trendline deviation, candle deviations, and envelope area. An optional Impulse Mode highlights valid impulse movements based on customizable threshold filters for each metric. Users can configure deviation settings, depth parameters, and toggle individual statistical metrics on or off. Note that because this script relies on zigzag pivot detection, line endpoints repaint on unconfirmed active bars until a new swing point is confirmed.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Deviation Start (%)
float
0.1
devStart
Deviation End (%)
float
0.5
devEnd
Deviation Step (%)
float
0.1
devStep
Depth
int
10
depthInput
Line color
color
#2962FF
lineClr
Extend to last bar
bool
true
extLast
Max bars per segment (timeout limit)
int
2000
maxSegBars
Show Ratio Zigzag (rz)
bool
true
showRZ
Show Zigzag Depth (zd)
bool
true
showZD
Show Heterogeneity (h)
bool
true
showH
Show Uniformity (unif)
bool
true
showUnif
Show Dev Line (devL)
bool
true
showDevLine
Show all 30 settings
Setting
Type
Default
Variable
Show Dev Candle (devC)
bool
true
showDevCand
Show Envelope Area (area)
bool
true
showArea
Enable Impulse Mode
bool
false
impulseMode
Min bars in segment
int
10
impMinBars
Filter by RZ <
bool
true
impUseRZ
impThreshRZ
int
30
impThreshRZ
Filter by ZD <
bool
true
impUseZD
impThreshZD
int
30
impThreshZD
Filter by H <
bool
true
impUseH
impThreshH
int
30
impThreshH
Filter by Unif <
bool
true
impUseUnif
impThreshUnif
int
30
impThreshUnif
Filter by DevL <
bool
true
impUseDevL
impThreshDevL
int
30
impThreshDevL
Filter by DevC <
bool
true
impUseDevC
impThreshDevC
int
30
impThreshDevC
Filter by Area <
bool
true
impUseArea
impThreshArea
int
30
impThreshArea
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
This indicator detects equal highs and lows by comparing prices within user-defined left and right bars using a tolerance percentage. It plots horizontal lines on the price chart for equal highs in one color and lows in another. A screener allows input of mult
This indicator fits a linear regression through stored pivot highs and lows and draws the resulting channel on the price chart. A function returns predicted values, maximum and minimum deviations, and the average absolute deviation. Confirmed two-bar pivots up
AI Trend is an indicator that plots pivot points, trend lines, and support and resistance levels directly on the price chart. It marks high and low pivots based on user-defined length, draws trend lines that check for violations at specified points, and identi
This indicator plots Ichimoku Cloud elements on the price chart, including Tenkan-sen, Kijun-sen, Senkou Span A and B lines with optional Chikou Span. It signals trend changes via Tenkan-sen and Kijun-sen crosses and places arrows at Kijun-sen direction shifts
This indicator identifies local swing highs and swing lows to construct a Zig Zag pattern on the main price chart. It evaluates pivot points based on a customizable number of surrounding left and right bars. Using persistent variables, the script tracks previo
Dynamic Linear Regression calculates and plots linear regression trendlines and standard deviation channels directly on the price chart over a user-selected time period. Traders can configure static channels locked at the end time, dynamic channels that contin