# How ZigZagHighLow() works?

#### hQrGrIg8lY

##### New member
https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/ZigZagHighLow.html

I am looking at the ZigZagHighLow() function in ToS. It seems to detect the high and low at trend reversal in a robust way. But I am not following the algorithm it follows. Can anybody help explain the algorithm in a human-friendly way? Thanks.

My question is how the top and bottom points of ZigZags are determined. I am not concerned whether it is repainting or not.

Could you help describe the algorithm in detail? Specifically, the doc says the following. Suppose A, B, C (from early time to later time) are a local low and two local highs, what if both point B and point C are higher than point A by the "certain calculated value" and C is a little higher than B? How to determine if A and B should be connected or A and C should be connected. The doc is not clear about this, but I don't understand how the code works to figure this out.

ZigZagHighLow plot identifies and connects swing points if the difference between their prices exceeds a certain calculated value. This value is equal to the specified percentage of price change plus Average True Range (ATR) multiplied by a factor. This sum can include an additional constant to increase the distance between the swing points.

Last edited by a moderator:
Solution
Could you help describe the algorithm in detail? Specifically, the doc says the following. Suppose A, B, C (from early time to later time) are a local low and two local highs, what if both point B and point C are higher than point A by the "certain calculated value" and C is a little higher than B? How to determine if A and B should be connected or A and C should be connected. The doc is not clear about this, but I don't understand how the code works to figure this out.

there are several IF statements like this,
if GetValue(state, 1) == GetValue(state.init, 0) {
that set several variables.
next, several fold loops look at a range of bars for a value.
from those values, a peak or a valley or n/a is found and plotted, zz.

i...
Could you help describe the algorithm in detail? Specifically, the doc says the following. Suppose A, B, C (from early time to later time) are a local low and two local highs, what if both point B and point C are higher than point A by the "certain calculated value" and C is a little higher than B? How to determine if A and B should be connected or A and C should be connected. The doc is not clear about this, but I don't understand how the code works to figure this out.

there are several IF statements like this,
if GetValue(state, 1) == GetValue(state.init, 0) {
that set several variables.
next, several fold loops look at a range of bars for a value.
from those values, a peak or a valley or n/a is found and plotted, zz.

i added a bubble at the end of the default code, that displays 16 variables, under each bar.
the bubble is yellow when a peak or valley is found.
maybe these numbers will help you understand what is going on.
you may have to zoom in to see all of a bubble.

Code:
``````# zigzaghl_test

# add bubbles to show var values

# zigzaghighlow
# TD Ameritrade IP Company, Inc. (c) 2013-2021
#

input priceH = high;
input priceL = low;
input percentageReversal = 5.0;
input absoluteReversal = 0.0;
input atrLength = 5;
input atrReversal = 1.5;
input tickReversal = 0;

Assert(percentageReversal >= 0, "'percentage reversal' must not be negative: " + percentageReversal);
Assert(absoluteReversal >= 0, "'absolute reversal' must not be negative: " + absoluteReversal);
Assert(atrReversal >= 0, "'atr reversal' must not be negative: " + atrReversal);
Assert(tickReversal >= 0, "'ticks' must not be negative: " + tickReversal);
Assert(percentageReversal != 0 or absoluteReversal != 0 or atrReversal != 0 or tickReversal != 0, "Either 'percentage reversal' or 'absolute reversal' or 'atr reversal' or 'tick reversal' must not be zero");

def absReversal;
if (absoluteReversal != 0) {
absReversal = absoluteReversal;
} else {
absReversal =  tickReversal * TickSize();
}

def hlPivot;
if (atrReversal != 0) {
hlPivot = percentageReversal / 100 + WildersAverage(TrueRange(high, close, low), atrLength) / close * atrReversal;
} else {
hlPivot = percentageReversal / 100;
}
def state = {default init, undefined, uptrend, downtrend};
def maxPriceH;
def minPriceL;
def newMax;
def newMin;
def prevMaxH = GetValue(maxPriceH, 1);
def prevMinL = GetValue(minPriceL, 1);

if GetValue(state, 1) == GetValue(state.init, 0) {
maxPriceH = priceH;
minPriceL = priceL;
newMax = yes;
newMin = yes;
state = state.undefined;
} else if GetValue(state, 1) == GetValue(state.undefined, 0) {
if priceH >= prevMaxH {
state = state.uptrend;
maxPriceH = priceH;
minPriceL = prevMinL;
newMax = yes;
newMin = no;
} else if priceL <= prevMinL {
state = state.downtrend;
maxPriceH = prevMaxH;
minPriceL = priceL;
newMax = no;
newMin = yes;
} else {
state = state.undefined;
maxPriceH = prevMaxH;
minPriceL = prevMinL;
newMax = no;
newMin = no;
}
} else if GetValue(state, 1) == GetValue(state.uptrend, 0) {
if priceL <= prevMaxH - prevMaxH * hlPivot - absReversal {
state = state.downtrend;
maxPriceH = prevMaxH;
minPriceL = priceL;
newMax = no;
newMin = yes;
} else {
state = state.uptrend;
if (priceH >= prevMaxH) {
maxPriceH = priceH;
newMax = yes;
} else {
maxPriceH = prevMaxH;
newMax = no;
}
minPriceL = prevMinL;
newMin = no;
}
} else {
if priceH >= prevMinL + prevMinL * hlPivot + absReversal {
state = state.uptrend;
maxPriceH = priceH;
minPriceL = prevMinL;
newMax = yes;
newMin = no;
} else {
state = state.downtrend;
maxPriceH = prevMaxH;
newMax = no;
if (priceL <= prevMinL) {
minPriceL = priceL;
newMin = yes;
} else {
minPriceL = prevMinL;
newMin = no;
}
}
}

def barNumber = BarNumber();
def barCount = HighestAll(If(IsNaN(priceH), 0, barNumber));
def newState = GetValue(state, 0) != GetValue(state, 1);
def offset = barCount - barNumber + 1;
def highPoint = state == state.uptrend and priceH == maxPriceH;
def lowPoint = state == state.downtrend and priceL == minPriceL;

def lastH;
if highPoint and offset > 1 {
lastH = fold iH = 1 to offset with tH = priceH while !IsNaN(tH) and !GetValue(newState, -iH) do if GetValue(newMax, -iH) or iH == offset - 1 and GetValue(priceH, -iH) == tH then Double.NaN else tH;
} else {
lastH = Double.NaN;
}

def lastL;
if lowPoint and offset > 1 {
lastL = fold iL = 1 to offset with tL = priceL while !IsNaN(tL) and !GetValue(newState, -iL) do if GetValue(newMin, -iL) or iL == offset - 1 and GetValue(priceL, -iL) == tL then Double.NaN else tL;
} else {
lastL = Double.NaN;
}

plot ZZ;
if barNumber == 1 {
ZZ = fold iF = 1 to offset with tP = Double.NaN while IsNaN(tP) do if GetValue(state, -iF) == GetValue(state.uptrend, 0) then priceL else if GetValue(state, -iF) == GetValue(state.downtrend, 0) then priceH else Double.NaN;
} else if barNumber == barCount {
ZZ = if highPoint or state == state.downtrend and priceL > minPriceL then priceH else if lowPoint or state == state.uptrend and priceH < maxPriceH then priceL else Double.NaN;
} else {
ZZ = if !IsNaN(lastH) then lastH else if !IsNaN(lastL) then lastL else Double.NaN;
}
ZZ.SetDefaultColor(GetColor(1));
ZZ.EnableApproximation();

# ------------------

# add bubble to show var values

"bn " + barnumber
+ "\nRev " + absReversal
+ "\nHL " + hlPivot
+ "\nMx " + maxPriceH
+ "\nMn " + minPriceL
+ "\nNmx " + newMax
+ "\nNmn " + newMin
+ "\nPmx" + prevMaxH
+ "\nPmn" + prevMinL
+ "\nState " + State
+ "\nNst " + newState
+ "\nHp " + highPoint
+ "\nLp " + lowPoint
+ "\nLH " + lastH
+ "\nLL" + lastL
+ "\nZZ " + zz
, (if !isnan(zz) then color.yellow else color.gray), no);

#``````

Last edited:
there are several IF statements like this,
if GetValue(state, 1) == GetValue(state.init, 0) {
that set several variables.
next, several fold loops look at a range of bars for a value.
from those values, a peak or a valley or n/a is found and plotted, zz.

i added a bubble at the end of the default code, that displays 16 variables, under each bar.
the bubble is yellow when a peak or valley is found.
maybe these numbers will help you understand what is going on.
you may have to zoom in to see all of a bubble.

Code:
``````# zigzaghl_test

# add bubbles to show var values

# zigzaghighlow
# TD Ameritrade IP Company, Inc. (c) 2013-2021
#

input priceH = high;
input priceL = low;
input percentageReversal = 5.0;
input absoluteReversal = 0.0;
input atrLength = 5;
input atrReversal = 1.5;
input tickReversal = 0;

Assert(percentageReversal >= 0, "'percentage reversal' must not be negative: " + percentageReversal);
Assert(absoluteReversal >= 0, "'absolute reversal' must not be negative: " + absoluteReversal);
Assert(atrReversal >= 0, "'atr reversal' must not be negative: " + atrReversal);
Assert(tickReversal >= 0, "'ticks' must not be negative: " + tickReversal);
Assert(percentageReversal != 0 or absoluteReversal != 0 or atrReversal != 0 or tickReversal != 0, "Either 'percentage reversal' or 'absolute reversal' or 'atr reversal' or 'tick reversal' must not be zero");

def absReversal;
if (absoluteReversal != 0) {
absReversal = absoluteReversal;
} else {
absReversal =  tickReversal * TickSize();
}

def hlPivot;
if (atrReversal != 0) {
hlPivot = percentageReversal / 100 + WildersAverage(TrueRange(high, close, low), atrLength) / close * atrReversal;
} else {
hlPivot = percentageReversal / 100;
}
def state = {default init, undefined, uptrend, downtrend};
def maxPriceH;
def minPriceL;
def newMax;
def newMin;
def prevMaxH = GetValue(maxPriceH, 1);
def prevMinL = GetValue(minPriceL, 1);

if GetValue(state, 1) == GetValue(state.init, 0) {
maxPriceH = priceH;
minPriceL = priceL;
newMax = yes;
newMin = yes;
state = state.undefined;
} else if GetValue(state, 1) == GetValue(state.undefined, 0) {
if priceH >= prevMaxH {
state = state.uptrend;
maxPriceH = priceH;
minPriceL = prevMinL;
newMax = yes;
newMin = no;
} else if priceL <= prevMinL {
state = state.downtrend;
maxPriceH = prevMaxH;
minPriceL = priceL;
newMax = no;
newMin = yes;
} else {
state = state.undefined;
maxPriceH = prevMaxH;
minPriceL = prevMinL;
newMax = no;
newMin = no;
}
} else if GetValue(state, 1) == GetValue(state.uptrend, 0) {
if priceL <= prevMaxH - prevMaxH * hlPivot - absReversal {
state = state.downtrend;
maxPriceH = prevMaxH;
minPriceL = priceL;
newMax = no;
newMin = yes;
} else {
state = state.uptrend;
if (priceH >= prevMaxH) {
maxPriceH = priceH;
newMax = yes;
} else {
maxPriceH = prevMaxH;
newMax = no;
}
minPriceL = prevMinL;
newMin = no;
}
} else {
if priceH >= prevMinL + prevMinL * hlPivot + absReversal {
state = state.uptrend;
maxPriceH = priceH;
minPriceL = prevMinL;
newMax = yes;
newMin = no;
} else {
state = state.downtrend;
maxPriceH = prevMaxH;
newMax = no;
if (priceL <= prevMinL) {
minPriceL = priceL;
newMin = yes;
} else {
minPriceL = prevMinL;
newMin = no;
}
}
}

def barNumber = BarNumber();
def barCount = HighestAll(If(IsNaN(priceH), 0, barNumber));
def newState = GetValue(state, 0) != GetValue(state, 1);
def offset = barCount - barNumber + 1;
def highPoint = state == state.uptrend and priceH == maxPriceH;
def lowPoint = state == state.downtrend and priceL == minPriceL;

def lastH;
if highPoint and offset > 1 {
lastH = fold iH = 1 to offset with tH = priceH while !IsNaN(tH) and !GetValue(newState, -iH) do if GetValue(newMax, -iH) or iH == offset - 1 and GetValue(priceH, -iH) == tH then Double.NaN else tH;
} else {
lastH = Double.NaN;
}

def lastL;
if lowPoint and offset > 1 {
lastL = fold iL = 1 to offset with tL = priceL while !IsNaN(tL) and !GetValue(newState, -iL) do if GetValue(newMin, -iL) or iL == offset - 1 and GetValue(priceL, -iL) == tL then Double.NaN else tL;
} else {
lastL = Double.NaN;
}

plot ZZ;
if barNumber == 1 {
ZZ = fold iF = 1 to offset with tP = Double.NaN while IsNaN(tP) do if GetValue(state, -iF) == GetValue(state.uptrend, 0) then priceL else if GetValue(state, -iF) == GetValue(state.downtrend, 0) then priceH else Double.NaN;
} else if barNumber == barCount {
ZZ = if highPoint or state == state.downtrend and priceL > minPriceL then priceH else if lowPoint or state == state.uptrend and priceH < maxPriceH then priceL else Double.NaN;
} else {
ZZ = if !IsNaN(lastH) then lastH else if !IsNaN(lastL) then lastL else Double.NaN;
}
ZZ.SetDefaultColor(GetColor(1));
ZZ.EnableApproximation();

# ------------------

# add bubble to show var values

"bn " + barnumber
+ "\nRev " + absReversal
+ "\nHL " + hlPivot
+ "\nMx " + maxPriceH
+ "\nMn " + minPriceL
+ "\nNmx " + newMax
+ "\nNmn " + newMin
+ "\nPmx" + prevMaxH
+ "\nPmn" + prevMinL
+ "\nState " + State
+ "\nNst " + newState
+ "\nHp " + highPoint
+ "\nLp " + lowPoint
+ "\nLH " + lastH
+ "\nLL" + lastL
+ "\nZZ " + zz
, (if !isnan(zz) then color.yellow else color.gray), no);

#``````
How to just plot the bubble when zz is not nan?

I see `if` must have both `then` and `else` branches. Is there a `if` that just have `then` but not `else`?

https://tlc.thinkorswim.com/center/...als/Basic/Chapter-5---Conditional-Expressions

How to just plot the bubble when zz is not nan?

I see `if` must have both `then` and `else` branches. Is there a `if` that just have `then` but not `else`?

https://tlc.thinkorswim.com/center/...als/Basic/Chapter-5---Conditional-Expressions

the bubble draws all the time because the first parameter is set to 1 ( true)

if you want a bubble (or label) to show up only when a condition is true, then put the condition formula (or variable) as the first parameter

to have the bubble appear only when the bar is a peak or valley, is when zz has a valid number.
replace 1 with !isnan(zz)
the ! is NOT , the opposite of
this says, when zz is NOT an error , it is true

Code:
``````addchartbubble(
!isnan(zz),
low *0.992,
"bn " + barnumber
+ "\nRev " + absReversal
+ "\nHL " + hlPivot
+ "\nMx " + maxPriceH
+ "\nMn " + minPriceL
+ "\nNmx " + newMax
+ "\nNmn " + newMin
+ "\nPmx" + prevMaxH
+ "\nPmn" + prevMinL
+ "\nState " + State
+ "\nNst " + newState
+ "\nHp " + highPoint
+ "\nLp " + lowPoint
+ "\nLH " + lastH
+ "\nLL" + lastL
+ "\nZZ " + zz
, (if !isnan(zz) then color.yellow else color.gray), no);``````

### Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
272 Online

## The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
• Exclusive indicators
• Proven strategies & setups
• Private Discord community
• Exclusive members-only content
• Add-ons and resources
• 1 full year of unlimited support

### Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?