//@version=5
indicator("Zig Zag(auto Horizon lines)", overlay=true, max_lines_count=500, max_labels_count=500)
dev_threshold = input.float(title="Deviation (%)", defval=10.0, minval=0.00001, maxval=100.0)
depth = input.int(title="Depth", defval=10, minval=1)
line_color2 = input(title="Line Color", defval=
extend_to_last_bar = input(title="Extend to Last Bar", defval=true)
display_reversal_price = input(title="Display Reversal Price", defval=false)
display_cumulative_volume = input(title="Display Cumulative Volume", defval=false)
display_reversal_price_change = input(title="Display Reversal Price Change", defval=false, inline="price rev")
difference_price = input.string("Absolute", "", options=["Absolute", "Percent"], inline="price rev")
showzigzagfg = input.bool(title="Show ZigZag", defval=true)
noneColor = color.new(color.white, 100)
line_color = showzigzagfg ? line_color2 : noneColor
show0fg = input.bool(title="Show Horizon LINES", defval=true)
LINEMAX = input.int(title="MAX Horizon LINES", defval=15, minval=1)
pivots(src, length, isHigh) =>
p = nz(src[length])
if length == 0
[time, p]
else
isFound = true
for i = 0 to math.abs(length - 1)
if isHigh and src[i] > p
isFound := false
if not isHigh and src[i] < p
isFound := false
for i = length + 1 to 2 * length
if isHigh and src[i] >= p
isFound := false
if not isHigh and src[i] <= p
isFound := false
if isFound and length * 2 <= bar_index
[time[length], p]
else
[int(na), float(na)]
[iH, pH] = pivots(high, math.floor(depth / 2), true)
[iL, pL] = pivots(low, math.floor(depth / 2), false)
calc_dev(base_price, price) =>
100 * (price - base_price) / base_price
price_rotation_aggregate(price_rotation, pLast, cum_volume) =>
str = ""
if display_reversal_price
str += str.tostring(pLast, format.mintick) + " "
if display_reversal_price_change
str += price_rotation + " "
if display_cumulative_volume
str += "\n" + cum_volume
str
caption(isHigh, iLast, pLast, price_rotation, cum_volume) =>
price_rotation_str = price_rotation_aggregate(price_rotation, pLast, cum_volume)
if display_reversal_price or display_reversal_price_change or display_cumulative_volume
if not isHigh
label.new(iLast, pLast, text=price_rotation_str, style=label.style_none, xloc=xloc.bar_time, yloc=yloc.belowbar, textcolor=color.red)
else
label.new(iLast, pLast, text=price_rotation_str, style=label.style_none, xloc=xloc.bar_time, yloc=yloc.abovebar, textcolor=color.green)
price_rotation_diff(pLast, price) =>
if display_reversal_price_change
tmp_calc = price - pLast
str = difference_price == "Absolute"? (math.sign(tmp_calc) > 0? "+" : "") + str.tostring(tmp_calc, format.mintick) : (math.sign(tmp_calc) > 0? "+" : "-") + str.tostring((math.abs(tmp_calc) * 100)/pLast, format.percent)
str := "(" + str + ")"
str
else
""
var line lineLast = na
var label labelLast = na
var int iLast = 0
var float pLast = 0
var bool isHighLast = true // otherwise the last pivot is a low pivot
var int linesCount = 0
var float sumVol = 0
var float sumVolLast = 0
pivotFound(dev, isHigh, index, price) =>
if isHighLast == isHigh and not na(lineLast)
// same direction
if isHighLast ? price > pLast : price < pLast
if linesCount <= 1
line.set_xy1(lineLast, index, price)
line.set_xy2(lineLast, index, price)
label.set_xy(labelLast, index, price)
label.set_text(labelLast, price_rotation_aggregate(price_rotation_diff(line.get_y1(lineLast), price), price, str.tostring(sumVol + sumVolLast, format.volume)))
[lineLast, labelLast, isHighLast, false, sumVol + sumVolLast]
else
[line(na), label(na), bool(na), false, float(na)]
else // reverse the direction (or create the very first line)
if na(lineLast)
id = line.new(index, price, index, price, xloc=xloc.bar_time, color=line_color, width=2)
lb = caption(isHigh, index, price, price_rotation_diff(pLast, price), str.tostring(sumVol, format.volume))
[id, lb, isHigh, true, sumVol]
else
// price move is significant
if math.abs(dev) >= dev_threshold
id = line.new(iLast, pLast, index, price, xloc=xloc.bar_time, color=line_color, width=2)
lb = caption(isHigh, index, price, price_rotation_diff(pLast, price), str.tostring(sumVol, format.volume))
[id, lb, isHigh, true, sumVol]
else
[line(na), label(na), bool(na), false, float(na)]
sumVol += nz(volume[math.floor(depth / 2)])
if not na(iH) and not na(iL) and iH == iL
dev1 = calc_dev(pLast, pH)
[id2, lb2, isHigh2, isNew2, sum2] = pivotFound(dev1, true, iH, pH)
if isNew2
linesCount := linesCount + 1
if not na(id2)
lineLast := id2
labelLast := lb2
isHighLast := isHigh2
iLast := iH
pLast := pH
sumVolLast := sum2
sumVol := 0
dev2 = calc_dev(pLast, pL)
[id1, lb1, isHigh1, isNew1, sum1] = pivotFound(dev2, false, iL, pL)
if isNew1
linesCount := linesCount + 1
if not na(id1)
lineLast := id1
labelLast := lb1
isHighLast := isHigh1
iLast := iL
pLast := pL
sumVolLast := sum1
sumVol := 0
else
if not na(iH)
dev1 = calc_dev(pLast, pH)
[id, lb, isHigh, isNew, sum] = pivotFound(dev1, true, iH, pH)
if isNew
linesCount := linesCount + 1
if not na(id)
lineLast := id
labelLast := lb
isHighLast := isHigh
iLast := iH
pLast := pH
sumVolLast := sum
sumVol := 0
else
if not na(iL)
dev2 = calc_dev(pLast, pL)
[id, lb, isHigh, isNew, sum] = pivotFound(dev2, false, iL, pL)
if isNew
linesCount := linesCount + 1
if not na(id)
lineLast := id
labelLast := lb
isHighLast := isHigh
iLast := iL
pLast := pL
sumVolLast := sum
sumVol := 0
var line extend_line = na
var label extend_label = na
if extend_to_last_bar == true and barstate.islast == true
isHighLastPoint = not isHighLast
curSeries = isHighLastPoint ? high : low
if na(extend_line) and na(extend_label)
extend_line := line.new(line.get_x2(lineLast), line.get_y2(lineLast), time, curSeries, xloc=xloc.bar_time, color=line_color, width=2)
extend_label := caption(not isHighLast, time, curSeries, price_rotation_diff(line.get_y2(lineLast), curSeries), str.tostring(sumVol, format.volume))
line.set_xy1(extend_line, line.get_x2(lineLast), line.get_y2(lineLast))
line.set_xy2(extend_line, time, curSeries)
price_rotation = price_rotation_diff(line.get_y1(extend_line), curSeries)
remaingRealTimeVol = 0.
for i = math.abs(math.floor(depth / 2) - 1) to 0
remaingRealTimeVol += volume[i]
label.set_xy(extend_label, time, curSeries)
label.set_text(extend_label, price_rotation_aggregate(price_rotation, curSeries, str.tostring(sumVol+remaingRealTimeVol, format.volume)))
label.set_textcolor(extend_label, isHighLastPoint? color.green : color.red)
label.set_yloc(extend_label, yloc= isHighLastPoint? yloc.abovebar : yloc.belowbar)
//---------------- auto lines
push_to_array(arr, val, maxItems)=>
array.push(arr, val)
if(array.size(arr) > maxItems)
array.remove(arr, 0)
arr
var linearray = array.new_line()
var ax = array.new_int(LINEMAX, int(close))
var ay = array.new_float(LINEMAX, close)
x1 = line.get_x1(lineLast)
y1 = line.get_y1(lineLast)
x2 = line.get_x2(lineLast)
y2 = line.get_y2(lineLast)
isHighLast2 = isHighLast
if y1 != y1[1]
push_to_array(ax, x1, LINEMAX)
push_to_array(ay, y1, LINEMAX)
array.push(linearray, line.new(x1, y1, x2, y1, width=1, xloc=xloc.bar_time, color=show0fg ? (y1 >= y1[1] ? color.green : color.red) : noneColor, style=line.style_solid, extend=extend.right))
if array.size(linearray) > LINEMAX - 1
ln = array.shift(linearray)
line.delete(ln)
var line line1 = line.new(x2, y2, x2 + 1, y2, width=1, xloc=xloc.bar_time, color=show0fg ? color.silver : noneColor, style=line.style_solid, extend=extend.right)
line.set_xy1(line1, x2, y2)
line.set_xy2(line1, x2 + 1, y2)
alertfg = false
for i = 0 to LINEMAX - 1 by 1
alertfg := alertfg or (array.get(ay, i) > close and array.get(ay, i) <= close[1]) or (array.get(ay, i) < close and array.get(ay, i) >= close[1])
alertcondition(alertfg, title='ZigZag (auto Horizon lines) cross', message='ZigZag (auto Horizon lines) cross')
alertcondition(alertfg and close > close[1], title='ZigZag (auto Horizon lines) cross over', message='ZigZag (auto Horizon lines) cross over')
alertcondition(alertfg and close < close[1], title='ZigZag (auto Horizon lines) cross under', message='ZigZag (auto Horizon lines) cross under')
评论
发表评论