Help With Bullish Pennant/Flag Script

Galvan

New member
I have found Pennant/Flag price patterns to be some of the most profitable in my experience trading, yet after scouring this forum, I've yet to find an script that does a great job of characterizing this pattern on charts.

After doing some research, I have the blueprint in my mind for a script that would be a very accurate detector of these patterns, but my Thinkscript coding skills are holding me back. I'm reaching out to the community with hopes that we can make this a thing.

Here are the foundations of the idea:

To start, the ZigZagHighLow indicator is used to identify pivot points in price along trendlines.
Here's a picture for reference(CRSP stock D chart):
Screenshot 2024-02-07 at 8.35.36 PM.png


As we can see, the combination of 2 ZigZag indicators at different lengths correctly identify many highs/lows that make up this pennant. This means that if we can find a way to connect the relevant points in this pattern and disregard the others, then we should have a beautiful pennant/trendline indicator.

My plan for doing this is fairly simple, but I'm not sure how to materialize it in code. To start, you would want to make all of the High ZigZag points and Low ZigZag points that occur be price variables. Then, you would need to document how many bars ago that these ZigZag points occurred.

Those two data points of Price and number of Bars since the pivot points are all that would be needed for the final calculation.

Once you have these variables, the last step would be finding a way to align the relevant high lows points that form a trend, my idea for this is to calculate the slope between these points and plot trend lines where 3 or more ZigZag points have a similar slope.

here's a oversimplified outline of what I mean in code for the bottom part of the pennant (I know you can't use [ ] like this but consider it to mean the price [number] zigzag lows ago)

Code:
def ZZLow = x
def ZZLow[1] = y
def ZZlow[2] = z

def BarsSinceZZlow = a
def BarsSinceZZlow[1] = b
def BarsSinceZZlow[2] = c

def ZZSlope1 = (y-z)/(b-c)
def ZZSlope2 = (x-y)/(a-b)

plot bottomtrendline = if ZZSlope1  ≈ ZZSlope2 then trendline else Double.Nan
(I know you can't use [ ] like this but consider it to mean the price [number] zigzag lows ago)

This logic obviously isn't functional at all, but it shows a conceptual idea of how this would work. If all three points coexist on the trendline, then the slope between the points should be very similar. All that's missing is sorting through the irrelevant data points and reverse engineering it for the upper trendline. I think I can do both of those thing myself if someone can help figure out how to reference the price of previous ZigZag's as a variable and monitor how many bars ago the high/low occurred.

Thanks to anyone who spent the time reading this, I hope the community can come together and make this work. Similar logic could also be used for finding accurate support/resistance or other chart patterns.

I'm also going to attach the double length ZigZag code:
Code:
#
# TD Ameritrade IP Company, Inc. (c) 2013-2024
#

input priceH = high;
input priceL = low;
input percentageReversala = 2.0;
input absoluteReversala = 0.0;
input atrLengtha = 10;
input atrReversala = .6;
input tickReversala = 0;

input percentageReversalb = 2.0;
input absoluteReversalb = 0.0;
input atrLengthb = 10;
input atrReversalb = 1.5;
input tickReversalb = 0;

script Zag {

    input priceH = high;
    input priceL = low;
    input percentageReversala = 5.0;
    input absoluteReversalb = 0.0;
    input atrLengthc = 5;
    input atrReversald = 1.5;
    input tickReversale = 0;

    Assert(percentageReversala >= 0, "'percentage reversal' must not be negative: " + percentageReversala);
    Assert(absoluteReversalb >= 0, "'absolute reversal' must not be negative: " + absoluteReversalb);
    Assert(atrReversald >= 0, "'atr reversal' must not be negative: " + atrReversald);
    Assert(tickReversale >= 0, "'ticks' must not be negative: " + tickReversale);
    Assert(percentageReversala != 0 or absoluteReversalb != 0 or atrReversald != 0 or tickReversale != 0, "Either 'percentage reversal' or 'absolute reversal' or 'atr reversal' or 'tick reversal' must not be zero");

    def absReversal;
    if (absoluteReversalb != 0) {
        absReversal = absoluteReversalb;
    } else {
        absReversal =  tickReversale * TickSize();
    }

    def hlPivot;
    if (atrReversald != 0) {
        hlPivot = percentageReversala / 100 + WildersAverage(TrueRange(high, close, low), atrLengthc) / close * atrReversald;
    } else {
        hlPivot = percentageReversala / 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;
    }
}


plot Zag = zag(priceH, priceL, percentageReversala, absoluteReversala, atrLengtha, atrReversala, tickReversala);
Zag.SetDefaultColor(GetColor(1));
Zag.EnableApproximation();

plot Zag2 = zag(priceH, priceL, percentageReversalb, absoluteReversalb, atrLengthb, atrReversalb, tickReversalb);
Zag2.SetDefaultColor(GetColor(1));
Zag2.EnableApproximation();
 
Last edited:
I have found Pennant/Flag price patterns to be some of the most profitable in my experience trading, yet after scouring this forum, I've yet to find an script that does a great job of characterizing this pattern on charts.

After doing some research, I have the blueprint in my mind for a script that would be a very accurate detector of these patterns, but my Thinkscript coding skills are holding me back. I'm reaching out to the community with hopes that we can make this a thing.

Here are the foundations of the idea:

To start, the ZigZagHighLow indicator is used to identify pivot points in price along trendlines.
Here's a picture for reference(CRSP stock D chart):
View attachment 20967

As we can see, the combination of 2 ZigZag indicators at different lengths correctly identify many highs/lows that make up this pennant. This means that if we can find a way to connect the relevant points in this pattern and disregard the others, then we should have a beautiful pennant/trendline indicator.

My plan for doing this is fairly simple, but I'm not sure how to materialize it in code. To start, you would want to make all of the High ZigZag points and Low ZigZag points that occur be price variables. Then, you would need to document how many bars ago that these ZigZag points occurred.

Those two data points of Price and number of Bars since the pivot points are all that would be needed for the final calculation.

Once you have these variables, the last step would be finding a way to align the relevant high lows points that form a trend, my idea for this is to calculate the slope between these points and plot trend lines where 3 or more ZigZag points have a similar slope.

here's a oversimplified outline of what I mean in code for the bottom part of the pennant (I know you can't use [ ] like this but consider it to mean the price [number] zigzag lows ago)

Code:
def ZZLow = x
def ZZLow[1] = y
def ZZlow[2] = z

def BarsSinceZZlow = a
def BarsSinceZZlow[1] = b
def BarsSinceZZlow[2] = c

def ZZSlope1 = (y-z)/(b-c)
def ZZSlope2 = (x-y)/(a-b)

plot bottomtrendline = if ZZSlope1  ≈ ZZSlope2 then trendline else Double.Nan
(I know you can't use [ ] like this but consider it to mean the price [number] zigzag lows ago)

This logic obviously isn't functional at all, but it shows a conceptual idea of how this would work. If all three points coexist on the trendline, then the slope between the points should be very similar. All that's missing is sorting through the irrelevant data points and reverse engineering it for the upper trendline. I think I can do both of those thing myself if someone can help figure out how to reference the price of previous ZigZag's as a variable and monitor how many bars ago the high/low occurred.

Thanks to anyone who spent the time reading this, I hope the community can come together and make this work. Similar logic could also be used for finding accurate support/resistance or other chart patterns.

I'm also going to attach the double length ZigZag code:
Code:
#
# TD Ameritrade IP Company, Inc. (c) 2013-2024
#

input priceH = high;
input priceL = low;
input percentageReversala = 2.0;
input absoluteReversala = 0.0;
input atrLengtha = 10;
input atrReversala = .6;
input tickReversala = 0;

input percentageReversalb = 2.0;
input absoluteReversalb = 0.0;
input atrLengthb = 10;
input atrReversalb = 1.5;
input tickReversalb = 0;

script Zag {

    input priceH = high;
    input priceL = low;
    input percentageReversala = 5.0;
    input absoluteReversalb = 0.0;
    input atrLengthc = 5;
    input atrReversald = 1.5;
    input tickReversale = 0;

    Assert(percentageReversala >= 0, "'percentage reversal' must not be negative: " + percentageReversala);
    Assert(absoluteReversalb >= 0, "'absolute reversal' must not be negative: " + absoluteReversalb);
    Assert(atrReversald >= 0, "'atr reversal' must not be negative: " + atrReversald);
    Assert(tickReversale >= 0, "'ticks' must not be negative: " + tickReversale);
    Assert(percentageReversala != 0 or absoluteReversalb != 0 or atrReversald != 0 or tickReversale != 0, "Either 'percentage reversal' or 'absolute reversal' or 'atr reversal' or 'tick reversal' must not be zero");

    def absReversal;
    if (absoluteReversalb != 0) {
        absReversal = absoluteReversalb;
    } else {
        absReversal =  tickReversale * TickSize();
    }

    def hlPivot;
    if (atrReversald != 0) {
        hlPivot = percentageReversala / 100 + WildersAverage(TrueRange(high, close, low), atrLengthc) / close * atrReversald;
    } else {
        hlPivot = percentageReversala / 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;
    }
}


plot Zag = zag(priceH, priceL, percentageReversala, absoluteReversala, atrLengtha, atrReversala, tickReversala);
Zag.SetDefaultColor(GetColor(1));
Zag.EnableApproximation();

plot Zag2 = zag(priceH, priceL, percentageReversalb, absoluteReversalb, atrLengthb, atrReversalb, tickReversalb);
Zag2.SetDefaultColor(GetColor(1));
Zag2.EnableApproximation();

comments,

every time someone mentions the word simple, it never is...

not finding a study is a good indicator of how hard this will be

you didn't state what the goal is. what do you want to see on a chart? draw 2 lines of a wedge? detect a breakout? different things require different points of reference for data collecting.

more than half of the peaks and valleys , from 2 different zigzags, don't match up or don't have a line going through them. so that theory doesn't hold up.

if you look back in time for peaks and valleys, then you are collecting data on a bar after it happened. so you can't use it to draw trend lines. you would need to look at future bars.

example of collecting data from future bars,
https://usethinkscript.com/threads/fib-levels-for-futures.18004/
at a certain bar, 6pm, this has a loop that looks at future bars for certain criteria (highest high and lowest low). then calcs a slope. i didn't use zigzag.

things to think about,
when to start a pattern,
when and how many peaks to skip, (when looking at other peaks beyond them)
how many bars to look at for a pattern? all bars on a chart? 50? 100? 200?
when to give up and cancel a possible pattern
 

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

comments,

every time someone mentions the word simple, it never is...

not finding a study is a good indicator of how hard this will be

you didn't state what the goal is. what do you want to see on a chart? draw 2 lines of a wedge? detect a breakout? different things require different points of reference for data collecting.

more than half of the peaks and valleys , from 2 different zigzags, don't match up or don't have a line going through them. so that theory doesn't hold up.

if you look back in time for peaks and valleys, then you are collecting data on a bar after it happened. so you can't use it to draw trend lines. you would need to look at future bars.

example of collecting data from future bars,
https://usethinkscript.com/threads/fib-levels-for-futures.18004/
at a certain bar, 6pm, this has a loop that looks at future bars for certain criteria (highest high and lowest low). then calcs a slope. i didn't use zigzag.

things to think about,
when to start a pattern,
when and how many peaks to skip, (when looking at other peaks beyond them)
how many bars to look at for a pattern? all bars on a chart? 50? 100? 200?
when to give up and cancel a possible pattern
@halcyonguy @MerryDay
Thanks for your reply, I made a video trying to clarify my ideas a little better around how it would work.
Video Explanation
I'd appreciate if you'd let me know if this makes sense as well as if it's possible using ThinkScript.
 
Last edited:
I have found Pennant/Flag price patterns to be some of the most profitable in my experience trading, yet after scouring this forum, I've yet to find an script that does a great job of characterizing this pattern on charts.

After doing some research, I have the blueprint in my mind for a script that would be a very accurate detector of these patterns, but my Thinkscript coding skills are holding me back. I'm reaching out to the community with hopes that we can make this a thing.

Here are the foundations of the idea:

To start, the ZigZagHighLow indicator is used to identify pivot points in price along trendlines.
Here's a picture for reference(CRSP stock D chart):
View attachment 20967

As we can see, the combination of 2 ZigZag indicators at different lengths correctly identify many highs/lows that make up this pennant. This means that if we can find a way to connect the relevant points in this pattern and disregard the others, then we should have a beautiful pennant/trendline indicator.

My plan for doing this is fairly simple, but I'm not sure how to materialize it in code. To start, you would want to make all of the High ZigZag points and Low ZigZag points that occur be price variables. Then, you would need to document how many bars ago that these ZigZag points occurred.

Those two data points of Price and number of Bars since the pivot points are all that would be needed for the final calculation.

Once you have these variables, the last step would be finding a way to align the relevant high lows points that form a trend, my idea for this is to calculate the slope between these points and plot trend lines where 3 or more ZigZag points have a similar slope.

here's a oversimplified outline of what I mean in code for the bottom part of the pennant (I know you can't use [ ] like this but consider it to mean the price [number] zigzag lows ago)

Code:
def ZZLow = x
def ZZLow[1] = y
def ZZlow[2] = z

def BarsSinceZZlow = a
def BarsSinceZZlow[1] = b
def BarsSinceZZlow[2] = c

def ZZSlope1 = (y-z)/(b-c)
def ZZSlope2 = (x-y)/(a-b)

plot bottomtrendline = if ZZSlope1  ≈ ZZSlope2 then trendline else Double.Nan
(I know you can't use [ ] like this but consider it to mean the price [number] zigzag lows ago)

This logic obviously isn't functional at all, but it shows a conceptual idea of how this would work. If all three points coexist on the trendline, then the slope between the points should be very similar. All that's missing is sorting through the irrelevant data points and reverse engineering it for the upper trendline. I think I can do both of those thing myself if someone can help figure out how to reference the price of previous ZigZag's as a variable and monitor how many bars ago the high/low occurred.

Thanks to anyone who spent the time reading this, I hope the community can come together and make this work. Similar logic could also be used for finding accurate support/resistance or other chart patterns.

I'm also going to attach the double length ZigZag code:
Code:
#
# TD Ameritrade IP Company, Inc. (c) 2013-2024
#

input priceH = high;
input priceL = low;
input percentageReversala = 2.0;
input absoluteReversala = 0.0;
input atrLengtha = 10;
input atrReversala = .6;
input tickReversala = 0;

input percentageReversalb = 2.0;
input absoluteReversalb = 0.0;
input atrLengthb = 10;
input atrReversalb = 1.5;
input tickReversalb = 0;

script Zag {

    input priceH = high;
    input priceL = low;
    input percentageReversala = 5.0;
    input absoluteReversalb = 0.0;
    input atrLengthc = 5;
    input atrReversald = 1.5;
    input tickReversale = 0;

    Assert(percentageReversala >= 0, "'percentage reversal' must not be negative: " + percentageReversala);
    Assert(absoluteReversalb >= 0, "'absolute reversal' must not be negative: " + absoluteReversalb);
    Assert(atrReversald >= 0, "'atr reversal' must not be negative: " + atrReversald);
    Assert(tickReversale >= 0, "'ticks' must not be negative: " + tickReversale);
    Assert(percentageReversala != 0 or absoluteReversalb != 0 or atrReversald != 0 or tickReversale != 0, "Either 'percentage reversal' or 'absolute reversal' or 'atr reversal' or 'tick reversal' must not be zero");

    def absReversal;
    if (absoluteReversalb != 0) {
        absReversal = absoluteReversalb;
    } else {
        absReversal =  tickReversale * TickSize();
    }

    def hlPivot;
    if (atrReversald != 0) {
        hlPivot = percentageReversala / 100 + WildersAverage(TrueRange(high, close, low), atrLengthc) / close * atrReversald;
    } else {
        hlPivot = percentageReversala / 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;
    }
}


plot Zag = zag(priceH, priceL, percentageReversala, absoluteReversala, atrLengtha, atrReversala, tickReversala);
Zag.SetDefaultColor(GetColor(1));
Zag.EnableApproximation();

plot Zag2 = zag(priceH, priceL, percentageReversalb, absoluteReversalb, atrLengthb, atrReversalb, tickReversalb);
Zag2.SetDefaultColor(GetColor(1));
Zag2.EnableApproximation();
hal_slope

if you want to use the peaks when a long and a short zigzag occur,
and every one of those points is the long one, then the short one isn't needed.

here is something to experiment with,
it uses just the long zigzag.
it draws a slope line , from 1 peak to the next, and calcs the slope.
on each peak, it compares the previous slope to the next slope.

i included 4 code lines to find peaks and valleys, that could be used to replace all of that complicated zigzag code

Code:
#flag_pattern

#https://usethinkscript.com/threads/help-with-bullish-pennant-flag-script.17891/

input priceH = high;
input priceL = low;
input percentageReversala = 2.0;
input absoluteReversala = 0.0;
input atrLengtha = 10;
input atrReversala = .6;
input tickReversala = 0;

input percentageReversalb = 2.0;
input absoluteReversalb = 0.0;
input atrLengthb = 10;
input atrReversalb = 1.5;
input tickReversalb = 0;

script Zag {

    input priceH = high;
    input priceL = low;
    input percentageReversala = 5.0;
    input absoluteReversalb = 0.0;
    input atrLengthc = 5;
    input atrReversald = 1.5;
    input tickReversale = 0;

    Assert(percentageReversala >= 0, "'percentage reversal' must not be negative: " + percentageReversala);
    Assert(absoluteReversalb >= 0, "'absolute reversal' must not be negative: " + absoluteReversalb);
    Assert(atrReversald >= 0, "'atr reversal' must not be negative: " + atrReversald);
    Assert(tickReversale >= 0, "'ticks' must not be negative: " + tickReversale);
    Assert(percentageReversala != 0 or absoluteReversalb != 0 or atrReversald != 0 or tickReversale != 0, "Either 'percentage reversal' or 'absolute reversal' or 'atr reversal' or 'tick reversal' must not be zero");

    def absReversal;
    if (absoluteReversalb != 0) {
        absReversal = absoluteReversalb;
    } else {
        absReversal =  tickReversale * TickSize();
    }

    def hlPivot;
    if (atrReversald != 0) {
        hlPivot = percentageReversala / 100 + WildersAverage(TrueRange(high, close, low), atrLengthc) / close * atrReversald;
    } else {
        hlPivot = percentageReversala / 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;
    }
}


#plot Zag = zag(priceH, priceL, percentageReversala, absoluteReversala, atrLengtha, atrReversala, tickReversala);
#Zag.SetDefaultColor(GetColor(1));
#Zag.EnableApproximation();
#zag.setdefaultcolor(color.cyan);

plot Zag2 = zag(priceH, priceL, percentageReversalb, absoluteReversalb, atrLengthb, atrReversalb, tickReversalb);
Zag2.SetDefaultColor(GetColor(1));
Zag2.EnableApproximation();
zag2.setdefaultcolor(color.yellow);


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


def na = double.nan;
def bn = barnumber();

#--------------------------
# find peaks , valleys ,  instead of zigzag
#input length = 7;
#def offset = Min(length - 1, lastbn - bn);
#def peak = high > Highest(high[1], length - 1) and high == GetValue(Highest(high, length), -offset);
#def valley = low < Lowest(low[1], length - 1) and low == GetValue(Lowest(low, length), -offset);
#--------------------------


# long and short zigzags
#def pkval = !isnan(zag) and !isnan(zag2);
#def pkval = zag == zag2;
def pkval = !isnan(zag2);


def ispk = if !pkval then 0
 else if zag2 == high then 1
# else if high > high[1] and high > high[-1] then 1
 else 0;

def isval = if !pkval then 0
# else if low < low[1] and low < low[-1] then 1
 else if zag2 == low then 1
 else 0;


def zzpr;
def zzbn;
if bn == 1 then {
 zzpr = 0;
 zzbn = 0;
} else if pkval then {
 zzpr = close;
 zzbn = bn;
} else {
 zzpr = 0;
 zzbn = 0;
}



def n = 1000;

def pkoffset;
def pkslope;
def pkline;
if bn == 1 then {
 pkoffset = 0;
 pkslope = 0;
 pkline = 0;
} else if ispk then {
# loop to next peak
 pkoffset = fold a = 1 to n
 with p = 1
 while getvalue(ispk, -a) == 0
 do p + 1;
 pkslope = if pkoffset == n then 0
  else (getvalue(high, -pkoffset) - high)/pkoffset;
 pkline = if pkoffset < n then high else na;
} else {
 pkoffset = pkoffset[1];
 pkslope = pkslope[1];
 pkline = pkline[1] + pkslope;
}


plot z = if pkoffset < n and pkline > 0 then pkline else na;
z.setdefaultcolor(color.white);

input slope_tol_per = 10.0;

def slope_chg_per = (100*(pkslope[0] - pkslope[1]) / pkslope[1]);

def samepkslope = if !ispk then 0
 else if absvalue(slope_chg_per) <= slope_tol_per then 1
 else 0;


addchartbubble(
ispk,
high,
 pkslope[1] + "\n" +
 pkslope + "\n" +
 slope_chg_per,
(if samepkslope then color.green else color.gray)
, yes);



input test_bub1 = no;
addchartbubble(test_bub1 and pkval, low*0.997,
#zag + "\n" +
zag2 + " z2\n" +
pkval + " pv\n" +
ispk + " pk\n" +
pkoffset + " off\n" +
pkslope + " sl\n" +
pkline + "\n"
#z
, color.yellow, no);

#

fqwXEtK.jpeg
 
Last edited:

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
501 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • 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?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top