Recursive Reversal Chart Patterns [Trendoscope]
// This work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © Trendoscope Pty Ltd
//                                       ░▒             
//                                  ▒▒▒   ▒▒      
//                              ▒▒▒▒▒     ▒▒      
//                      ▒▒▒▒▒▒▒░     ▒     ▒▒          
//                  ▒▒▒▒▒▒           ▒     ▒▒          
//             ▓▒▒▒       ▒        ▒▒▒▒▒▒▒▒▒▒▒  
//   ▒▒▒▒▒▒▒▒▒▒▒ ▒        ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒         
//   ▒  ▒       ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░        
//   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒         
//   ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒                       
//    ▒▒▒▒▒         ▒▒▒▒▒▒▒                            
//                 ▒▒▒▒▒▒▒▒▒                           
//                ▒▒▒▒▒ ▒▒▒▒▒                          
//               ░▒▒▒▒   ▒▒▒▒▓      ████████╗██████╗ ███████╗███╗   ██╗██████╗  ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗
//              ▓▒▒▒▒     ▒▒▒▒      ╚══██╔══╝██╔══██╗██╔════╝████╗  ██║██╔══██╗██╔═══██╗██╔════╝██╔════╝██╔═══██╗██╔══██╗██╔════╝
//              ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒        ██║   ██████╔╝█████╗  ██╔██╗ ██║██║  ██║██║   ██║███████╗██║     ██║   ██║██████╔╝█████╗ 
//             ▒▒▒▒▒       ▒▒▒▒▒       ██║   ██╔══██╗██╔══╝  ██║╚██╗██║██║  ██║██║   ██║╚════██║██║     ██║   ██║██╔═══╝ ██╔══╝  
//            ▒▒▒▒▒         ▒▒▒▒▒      ██║   ██║  ██║███████╗██║ ╚████║██████╔╝╚██████╔╝███████║╚██████╗╚██████╔╝██║     ███████╗
//             ▒▒             ▒                        
//@version=5
import HeWhoMustNotBeNamed/DrawingTypes/2 as dr
import HeWhoMustNotBeNamed/DrawingMethods/2
import HeWhoMustNotBeNamed/ZigzagTypes/5 as zg
import HeWhoMustNotBeNamed/ZigzagMethods/6
import HeWhoMustNotBeNamed/utils/1 as ut

import HeWhoMustNotBeNamed/TradeTracker/1 as t
import HeWhoMustNotBeNamed/ReversalChartPatternLibrary/4 as rcp
import HeWhoMustNotBeNamed/iLogger/1 as l

indicator("Recursive Reversal Chart Patterns [Trendoscope]", "RRCP[Trendoscope]", overlay = true, max_lines_count=500, max_labels_count=500, max_bars_back = 1000)
theme = input.string('Dark', title='Theme', options=['Light', 'Dark'], group='Generic Settings',
         tooltip='Chart theme settings. Line and label colors are generted based on the theme settings. If dark theme is selected, '+
         'lighter colors are used and if light theme is selected, darker colors are used.', display=display.none)

var logger = l.Logger.new(pageSize=20, minimumLevel = 'DEBUG')
logger.init()

method createPattern(zg.Zigzag this, int patternType = 1, color patternColor = color.blue, rcp.ReversalChartTradeProperties properties = na, int offset=0)=>
    zigzag = this.copy()
    patternPivots = zigzag.zigzagPivots.slice(offset, (patternType >= 5? 5 : (patternType %2 == 1 ? 4 : 6)+offset))
    tProperties = na(properties)?rcp.ReversalChartTradeProperties.new():properties
    tProperties.variableTargetRatios := na(tProperties.variableTargetRatios)? array.from(1.0) : tProperties.variableTargetRatios
    rcp.ReversalChartPattern.new(patternPivots, patternType, patternColor, tProperties)

zigzagLength = input.int(8, step=5, minval=3, title='Length', group='Zigzag', tooltip='Zigzag length for level 0 zigzag', display=display.none)
depth = input.int(50, "Depth", step=25, maxval=500, group='Zigzag', tooltip='Zigzag depth refers to max number of pivots to be held for multi level calculations', display=display.none)
minimumZigZagLevel = input.int(0, "Minimum Zigzag Level", group='Zigzag', minval = 0, tooltip = 'Minimum zigzag level to consider for pattern scanning', display=display.none)
useRealTimeBars = true

enableDoubleTap = input.bool(true, 'Double Tap', group='Patterns', display=display.none)
enableTripleTap = input.bool(true, 'Triple Tap', group='Patterns', display=display.none)
enableCupAndHandle = input.bool(false, 'Cup And Handle', group='Patterns', display=display.none)
enableHeadAndShoulders = input.bool(false, 'Head And Shoulders', group='Patterns', display=display.none)

errorPercent = input.int(13, 'Error Percent', 0, 50, 5, group='Scanner', tooltip = 'Error percent threshold used for ratio based calculations', display=display.none)
shoulderStart = input.float(0.1, 'Shoulder Start', 0.1, 1.0, 0.05, group='Scanner', tooltip= 'Start of shoulder ratio range. Used for identifying handle, necks, shoulders', display=display.none)
shoulderEnd = input.float(0.5, 'Shoulder End', 0.5, 1.0, 0.05, group='Scanner', tooltip = 'End of shoulder ratio range. Used for identifying handle, necks, shoulders', display=display.none)
riskAdjustment = input.int(13, 'Risk Adjustment Percent', 0, 50, 5, group='Scanner', tooltip = 'Additional padding on the risk. Used for stop loss calculation', display=display.none)

enableRsi = input.bool(false, 'RSI', group='Indicators', inline='rsi', display=display.none)
rsiLength = input.int(14, '', group='Indicators', inline='rsi', tooltip='Enable and configure RSI indicator. When checked, an additional check for divergence is used for confirming Patterns', display=display.none)

enableMfi = input.bool(false, 'MFI', group='Indicators', inline='mfi', display=display.none)
mfiLength = input.int(14, '', group='Indicators', inline='mfi', tooltip='Enable and configure MFI indicator. When checked, an additional check for divergence is used for confirming Patterns', display=display.none)

enableObv = input.bool(false, 'OBV', group='Indicators', inline='obv', tooltip = 'Enable and configure OBV indicator. When checked, an additional check for divergence is used for confirming Patterns', display=display.none)

enableCustom = input.bool(false, '', group='Custom External Indicator', inline='custom', display=display.none)
customName = input.string('Custom', '', group='Custom External Indicator', inline='custom', display=display.none)
customValue = input.source(close, '', group='Custom External Indicator', inline='custom', tooltip = 'Enable and configure custom external indicator. When checked, an additional check for divergence is used for confirming Double tap', display=display.none)
showLabel = false

offset = useRealTimeBars? 0 : 1

validPatterns = array.from(enableDoubleTap?1:0, enableTripleTap?2:0, enableCupAndHandle?3:0, enableHeadAndShoulders?4:0)
indicators = matrix.new<float>()
indicatorNames = array.new<string>()
if(enableRsi)
    indicatorNames.unshift('RSI'+str.tostring(rsiLength))
    indicators.add_row(0, array.from(ta.rsi(high, rsiLength), ta.rsi(low, rsiLength), ta.rsi(close, rsiLength)))
    showLabel := true

if(enableMfi)
    indicatorNames.unshift('MFI'+str.tostring(mfiLength))
    indicators.add_row(0, array.from(ta.mfi(high, mfiLength), ta.mfi(low, mfiLength), ta.mfi(close, mfiLength)))
    showLabel := true

if(enableObv)
    indicatorNames.unshift('OBV')
    indicators.add_row(0, array.from(ta.obv, ta.obv, ta.obv))

if(enableCustom and customValue!=close)
    indicatorNames.unshift(customName)
    indicators.add_row(0, array.from(customValue, customValue, customValue))

var themeColors = ut.getColors(theme)
var zg.Zigzag zigzag = zg.Zigzag.new(zigzagLength, depth, offset)
zigzag.calculate(array.from(high, low), indicators, indicatorNames)

var array<rcp.ReversalChartPattern> patterns = array.new<rcp.ReversalChartPattern>()

var count = 0
rcp.ReversalChartTradeProperties drawingProps = rcp.ReversalChartTradeProperties.new(riskAdjustment)
if zigzag.flags.newPivot
    mlzigzag = zigzag
    while(mlzigzag.zigzagPivots.size() > 4)
        if(mlzigzag.level >= minimumZigZagLevel)
            patternType = mlzigzag.scan(patterns, errorPercent, shoulderStart, shoulderEnd, validPatterns)
            if(patternType != 0)
                count += 1
                patternColor = themeColors.remove(0)
                themeColors.push(patternColor)
                pattern = mlzigzag.createPattern(patternType, patternColor, drawingProps).init().draw()
                patterns.lpush(pattern)
                alert(pattern.getDescription())

        mlzigzag := mlzigzag.nextlevel()

logger.info('Number of Patterns : '+str.tostring(count))