Harmonic Patterns Based Trend Follower
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed
//                                       ░▒             
//                                  ▒▒▒   ▒▒      
//                              ▒▒▒▒▒     ▒▒      
//                      ▒▒▒▒▒▒▒░     ▒     ▒▒          
//                  ▒▒▒▒▒▒           ▒     ▒▒          
//             ▓▒▒▒       ▒        ▒▒▒▒▒▒▒▒▒▒▒  
//   ▒▒▒▒▒▒▒▒▒▒▒ ▒        ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒         
//   ▒  ▒       ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░        
//   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒         
//   ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒                       
//    ▒▒▒▒▒         ▒▒▒▒▒▒▒                            
//                 ▒▒▒▒▒▒▒▒▒                           
//                ▒▒▒▒▒ ▒▒▒▒▒                          
//               ░▒▒▒▒   ▒▒▒▒▓      ████████╗██████╗ ███████╗███╗   ██╗██████╗  ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗
//              ▓▒▒▒▒     ▒▒▒▒      ╚══██╔══╝██╔══██╗██╔════╝████╗  ██║██╔══██╗██╔═══██╗██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝
//              ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒        ██║   ██████╔╝█████╗  ██╔██╗ ██║██║  ██║██║   ██║███████╗██║     ██║   ██║██████╔╝█████╗ 
//             ▒▒▒▒▒       ▒▒▒▒▒       ██║   ██╔══██╗██╔══╝  ██║╚██╗██║██║  ██║██║   ██║╚════██║██║     ██║   ██║██╔═══╝ ██╔══╝  
//            ▒▒▒▒▒         ▒▒▒▒▒      ██║   ██║  ██║███████╗██║ ╚████║██████╔╝╚██████╔╝███████║╚██████╗╚██████╔╝██║     ███████╗
//             ▒▒             ▒                        
//@version=5
import HeWhoMustNotBeNamed/rzigzag/10 as zg
import HeWhoMustNotBeNamed/eHarmonicpatternsLogScale/1 as hp

indicator('Harmonic Patterns Based Trend Follower', 'HP-TF [Trendoscope]', overlay=true, max_lines_count = 500)
base = input.string('CD', 'Base', ['minmax', 'correction', 'CD'], group="Supertrend", tooltip='Base for which entry/stop and thus range can be calculated')
entryPercent = input.int(30, 'Entry %', minval=30, maxval = 200, step=5, group='Supertrend', tooltip = 'Percent of Base from D for entry in pattern direction')
stopPercent = input.int(5, 'Stop %', minval=0, maxval = 30, step=5, group='Supertrend', tooltip = 'Percent of Base from D for exit in opposite direction')
useClosePrices = input.bool(true, 'Use Close Prices', group='Supertrend', tooltip = 'Use close prices for finding breakouts and calculate trend direction')

errorPercent = input.int(3, title='Error %', minval=3, step=5, maxval=21, group='Harmonic Patterns',
                             tooltip='Error threshold for calculation on patterns. Increased error threshold will reduce the accuracy of patterns. Lower error threshold will reduce number of patterns detected.')
zigzagLength = input.int(8, step=5, minval=3, title='Zigzag', group='Harmonic Patterns', tooltip='Zigzag Length on which pattern detection is built')
logScale = input.bool(false, 'Log Scale', group='Harmonic Patterns', tooltip='If selected, patterns are scanned on log scale instead of regular price scale')
showHarmonicPatterns = input.bool(true, 'Display Harmonic Patterns', group='Harmonic Patterns', tooltip='If selected, harmonic patterns scanned will be drawn on chart')
depth = 10

startIndex = 1
var float bullishRange = na
var float bearishRange = na
var int trend = na

[zigzagmatrix, zgFlags] = zg.zigzag(zigzagLength, array.from(high, low), depth, 0)
if(matrix.rows(zigzagmatrix) >= startIndex+5)
    existingPattern = false
    isHarmonicPattern = false
    x = matrix.get(zigzagmatrix, startIndex+4, 0)
    a = matrix.get(zigzagmatrix, startIndex+3, 0)
    b = matrix.get(zigzagmatrix, startIndex+2, 0)
    c = matrix.get(zigzagmatrix, startIndex+1, 0)
    d = matrix.get(zigzagmatrix, startIndex, 0)
    patterns = hp.isHarmonicPattern(x, a, b, c, d, array.new<bool>(), true, errorPercent, logScale=logScale)

    if(array.includes(patterns, true) and array.get(zgFlags, 1))
        xBar = matrix.get(zigzagmatrix, startIndex+4, 1)
        aBar = matrix.get(zigzagmatrix, startIndex+3, 1)
        bBar = matrix.get(zigzagmatrix, startIndex+2, 1)
        cBar = matrix.get(zigzagmatrix, startIndex+1, 1)
        dBar = matrix.get(zigzagmatrix, startIndex, 1)
        if showHarmonicPatterns
            line.new(int(xBar), x, int(aBar), a, color=color.blue, style = line.style_dotted, width = 0)
            line.new(int(aBar), a, int(bBar), b, color=color.blue, style = line.style_dotted, width = 0)
            line.new(int(bBar), b, int(cBar), c, color=color.blue, style = line.style_dotted, width = 0)
            line.new(int(cBar), c, int(dBar), d, color=color.blue, style = line.style_dotted, width = 0)
            line.new(int(bBar), b, int(dBar), d, color=color.blue, style = line.style_dotted, width = 0)
            line.new(int(xBar), x, int(bBar), b, color=color.blue, style = line.style_dotted, width = 0)
    
        dir = matrix.get(zigzagmatrix, startIndex, 3)
        baseValue = base == 'minmax' ? math.max(x,a,b,c,d) - math.min(x,a,b,c,d) : base == 'correction'? math.abs((dir > 0? math.max(a, c) : math.min(a, c)) - d) : math.abs(c-d)
        entryRange = d - math.sign(dir)*baseValue*(entryPercent/100)
        stopRange = d + math.sign(dir)*baseValue*(stopPercent/100)

        newBullishRange = math.max(entryRange, stopRange)
        newBearishRange = math.min(entryRange, stopRange)

        bullishRange := trend < 0? math.min(bullishRange, newBullishRange) : newBullishRange
        bearishRange := trend > 0? math.max(bearishRange, newBearishRange) : newBearishRange

highPrice = useClosePrices? close : high
lowPrice = useClosePrices ? close : low
trend := highPrice >= bullishRange? 1 : lowPrice <=bearishRange? -1 : trend
trendColor = trend>0? color.lime : trend < 0? color.red: color.silver

plot(bullishRange, "BullishRange", color=color.green)
plot(bearishRange, "BearishRange", color=color.red)

barcolor(trendColor)
plot(trend, "Trend", trendColor, display = display.data_window)