LTB Zigzag Circles
"LTB Zigzag Circles" by LonesomeTheBlue, MPL-2.0. Ported to FloScript by FloSignals; modifications under MPL-2.0. Original: LTB Zigzag Circles (v4.0), read 2026-10-12T16:08:39.062Z; source language v4.
Licence: Mozilla Public License 2.0 (MPL-2.0) — full text. The source is published with it, modifications included, under the same licence.
LTB Zigzag Circles by LonesomeTheBlue, ported from Pine v4. Overlay indicator with 4 marker series, drawings, alerts and 8 inputs.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © LonesomeTheBlue
// ------------------------------------------------------------------------------------------------
// Original author: LonesomeTheBlue
// Original title: LTB Zigzag Circles
// Licence: MPL-2.0 (the header above is the original notice, kept verbatim)
// Pine version: 4
// Fetched: 2026-10-12
// Ported to FloScript by FloSignals; modifications under MPL-2.0.
// Converter: @flosignals/floscript 0.1.0, strict mode, score 100/100
// ------------------------------------------------------------------------------------------------
//@floscript=1
//@version=5
// Converted by the FloScript converter from a Pine-compatible v4 source.
indicator("LTB Zigzag Circles", overlay=true, max_lines_count=500)
prd = input.int(defval=10, title="ZigZag Period", minval=2, maxval=30)
showzigzag = input(defval=true, title="Show Zigzag")
showlevels = input(defval=true, title="Show Levels")
zzlinestyle_ = input.string(defval="Dashed", title="Zigzag Line Style", options=["Solid", "Dashed", "Dotted"])
upcol = input(defval=color.lime, title="Line Colors", inline="col")
dncol = input(defval=color.red, title="", inline="col")
uptcol = input(defval=color.black, title="Text Colors", inline="tcol")
dntcol = input(defval=color.white, title="", inline="tcol")
// check highesy/lowest levels and create zigzag
float ph = ta.highestbars(high, prd) == 0 ? high : na
float pl = ta.lowestbars(low, prd) == 0 ? low : na
var dir = 0
dir := ph and na(pl) ? 1 : pl and na(ph) ? -1 : dir
var zigzag = array.new_float(0)
// keep last point
oldzigzag = array.new_float(2)
if array.size(zigzag) > 1
array.set(oldzigzag, 0, array.get(zigzag, 0))
array.set(oldzigzag, 1, array.get(zigzag, 1))
add_to_zigzag(value, bindex) =>
array.unshift(zigzag, bindex)
array.unshift(zigzag, value)
if array.size(zigzag) > 8
array.pop(zigzag)
array.pop(zigzag)
update_zigzag(value, bindex) =>
if array.size(zigzag) == 0
add_to_zigzag(value, bindex)
else
if dir == 1 and value > array.get(zigzag, 0) or dir == -1 and value < array.get(zigzag, 0)
array.set(zigzag, 0, value)
array.set(zigzag, 1, bindex)
0.
dirchanged = dir != dir[1]
if ph or pl
if dirchanged
add_to_zigzag(dir == 1 ? ph : pl, bar_index)
else
update_zigzag(dir == 1 ? ph : pl, bar_index)
// init zigzag lines
var zzlines = array.new_line(0)
var zzlinestyle = line.style_dashed
if barstate.isfirst
zzlinestyle := zzlinestyle_ == "Solid" ? line.style_solid : zzlinestyle_ == "Dashed" ? line.style_dashed : line.style_dotted
array.push(zzlines, line.new(x1=bar_index, y1=close, x2=bar_index - 1, y2=close, style=zzlinestyle))
array.push(zzlines, line.new(x1=bar_index, y1=close, x2=bar_index - 1, y2=close, style=zzlinestyle))
// draw zigzag
if showzigzag and array.size(zigzag) >= 6
line.set_xy1(array.get(zzlines, 0), math.round(array.get(zigzag, 1)), array.get(zigzag, 0))
line.set_xy2(array.get(zzlines, 0), math.round(array.get(zigzag, 3)), array.get(zigzag, 2))
line.set_color(array.get(zzlines, 0), dir == 1 ? upcol : dncol)
line.set_xy1(array.get(zzlines, 1), math.round(array.get(zigzag, 3)), array.get(zigzag, 2))
line.set_xy2(array.get(zzlines, 1), math.round(array.get(zigzag, 5)), array.get(zigzag, 4))
line.set_color(array.get(zzlines, 1), dir == 1 ? dncol : upcol)
// definitions
width = (ta.highest(292) - ta.lowest(292)) / 107
var up_circle_lines = array.new_line(0)
var dn_circle_lines = array.new_line(0)
// to get rid of consistency warning, keep close in a array
var cls = array.new_float(500, na)
array.unshift(cls, close)
array.pop(cls)
// calculate location of y level in a point
calc_pos_circle_rad(center_y_lev, center_bindex, radius, cal_bindex) =>
loc_diff = math.abs(center_bindex - cal_bindex)
rad = math.sqrt(math.pow(radius, 2) - math.pow(loc_diff * width, 2))
// draw uptrend circle until last candle
draw_up_circle(radius) =>
ind = dir == 1 ? 0 : 2
nxt = dir == 1 ? 2 : 4
broken = false
if bar_index - 1 > array.get(zigzag, nxt + 1)
last_x = math.round(array.get(zigzag, nxt + 1))
last_y = array.get(zigzag, nxt)
for x = array.get(zigzag, nxt + 1) + 1 to bar_index - 1
yloc = array.get(zigzag, ind) - calc_pos_circle_rad(array.get(zigzag, ind), array.get(zigzag, ind + 1), radius, x)
if na(yloc)
break
if not na(yloc) and array.get(cls, math.round(bar_index - x)) < yloc
broken := true
break
array.push(up_circle_lines, line.new(x1=last_x, y1=last_y, x2=math.round(x), y2=yloc, color=upcol, width=2))
last_x := last_x + 1
last_y := yloc
broken
// draw downtrend circle until last candle
draw_down_circle(radius) =>
ind = dir == 1 ? 2 : 0
nxt = dir == 1 ? 4 : 2
broken = false
if bar_index - 1 > array.get(zigzag, nxt + 1)
last_x = math.round(array.get(zigzag, nxt + 1))
last_y = array.get(zigzag, nxt)
for x = array.get(zigzag, nxt + 1) + 1 to bar_index - 1
yloc = array.get(zigzag, ind) + calc_pos_circle_rad(array.get(zigzag, ind), array.get(zigzag, ind + 1), radius, x)
if na(yloc)
break
if not na(yloc) and array.get(cls, math.round(bar_index - x)) > yloc
broken := true
break
array.push(dn_circle_lines, line.new(x1=last_x, y1=last_y, x2=math.round(x), y2=yloc, color=dncol, width=2))
last_x := last_x + 1
last_y := yloc
broken
// variables to check if any circle broken
var bool uptbroken = false
var bool downtbroken = false
uptexist = false
downtexist = false
// labels
var label upt_label = na
var label downt_label = na
if array.size(zigzag) >= 6
// radius of circles
r1 = math.sqrt(math.pow(array.get(zigzag, 0) - array.get(zigzag, 2), 2) + math.pow((array.get(zigzag, 1) - array.get(zigzag, 3)) * width, 2))
r2 = math.sqrt(math.pow(array.get(zigzag, 2) - array.get(zigzag, 4), 2) + math.pow((array.get(zigzag, 3) - array.get(zigzag, 5)) * width, 2))
radius_up = dir == 1 ? r1 : r2
radius_down = dir == 1 ? r2 : r1
// draw uptrend circle if needed
if dirchanged or width != width[1] or dir == 1 and (array.get(zigzag, 0) != array.get(oldzigzag, 0) or array.get(zigzag, 1) != array.get(oldzigzag, 1))
uptbroken := false
for x = 0 to array.size(up_circle_lines) > 0 ? array.size(up_circle_lines) - 1 : na
line.delete(array.pop(up_circle_lines))
uptbroken := draw_up_circle(radius_up)
// draw downtrend circle if needed
if dirchanged or width != width[1] or dir == -1 and (array.get(zigzag, 0) != array.get(oldzigzag, 0) or array.get(zigzag, 1) != array.get(oldzigzag, 1))
downtbroken := false
for x = 0 to array.size(dn_circle_lines) > 0 ? array.size(dn_circle_lines) - 1 : na
line.delete(array.pop(dn_circle_lines))
downtbroken := draw_down_circle(radius_down)
// calculate/check and draw part of uptrend circle for last last
if not uptbroken
ind = dir == 1 ? 0 : 2
yloc = array.get(zigzag, ind) - calc_pos_circle_rad(array.get(zigzag, ind), array.get(zigzag, ind + 1), radius_up, bar_index)
if not na(yloc) and close >= yloc
uptexist := true
lasty = array.get(zigzag, ind) - calc_pos_circle_rad(array.get(zigzag, ind), array.get(zigzag, ind + 1), radius_up, bar_index - 1)
array.push(up_circle_lines, line.new(x1=bar_index - 1, y1=lasty, x2=bar_index, y2=yloc, color=upcol, width=2))
if showlevels
label.delete(upt_label)
upt_label := label.new(x=bar_index, y=yloc, text=str.tostring(round_to_mintick(yloc)), color=upcol, textcolor=uptcol, style=label.style_label_up)
if not na(yloc) and close < yloc
uptbroken := true
// calculate/check and draw part of downtrend circle for last last
if not downtbroken
ind = dir == 1 ? 2 : 0
yloc = array.get(zigzag, ind) + calc_pos_circle_rad(array.get(zigzag, ind), array.get(zigzag, ind + 1), radius_down, bar_index)
if not na(yloc) and close <= yloc
downtexist := true
lasty = array.get(zigzag, ind) + calc_pos_circle_rad(array.get(zigzag, ind), array.get(zigzag, ind + 1), radius_down, bar_index - 1)
array.push(dn_circle_lines, line.new(x1=bar_index - 1, y1=lasty, x2=bar_index, y2=yloc, color=dncol, width=2))
if showlevels
label.delete(downt_label)
downt_label := label.new(x=bar_index, y=yloc, text=str.tostring(round_to_mintick(yloc)), color=dncol, textcolor=dntcol, style=label.style_label_down)
if not na(yloc) and close > yloc
downtbroken := true
// fill the orders
var long = false
exitlong = false
if downtexist[1] and not downtexist and uptexist
long := true
if long and downtexist
long := false
exitlong := true
var short = false
exitshort = false
if uptexist[1] and not uptexist and downtexist
short := true
if short and uptexist
short := false
exitshort := true
plotshape(long and not long[1], style=shape.triangleup, color=upcol, location=location.belowbar, size=size.small)
plotshape(exitlong, style=shape.xcross, color=upcol, location=location.abovebar, size=size.small)
plotshape(short and not short[1], style=shape.triangledown, color=dncol, location=location.abovebar, size=size.small)
plotshape(exitshort, style=shape.xcross, color=dncol, location=location.belowbar, size=size.small)
alertcondition(long and not long[1], title='Long', message='Enter Long')
alertcondition(exitlong, title='Exit Long', message='Exit Long')
alertcondition(short and not short[1], title='Short', message='Enter Short')
alertcondition(exitshort, title='Exit Short', message='Exit Short')
This file is under the Mozilla Public License 2.0; it is a port of the original by LonesomeTheBlue, with the original licence header kept and the changes made for FloScript noted in the file.
- v2
Library refresh 2026-10-14
- v1
Library refresh 2026-10-12
Disclaimer. Scripts are written and published by members, or ported by FloSignals from open-source originals, and are their authors' own work and opinion. FloSignals does not review them for correctness and nothing in a script is financial advice or a recommendation to buy or sell anything — read the Risk Disclaimer. Posting here is under the Community Rules; a script that is not the poster's to post can be reported as the Copyright & Takedowns page says.

Comments (0)
No comments yet. Members can leave one.