Volume Forks [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
indicator("Volume Forks [Trendoscope]", "VF [Trendoscope]", overlay = true, max_lines_count=500, max_bars_back = 3000)
import HeWhoMustNotBeNamed/DrawingTypes/2 as dr
import HeWhoMustNotBeNamed/DrawingMethods/2
import HeWhoMustNotBeNamed/ZigzagTypes/5 as zg
import HeWhoMustNotBeNamed/ZigzagMethods/6
import HeWhoMustNotBeNamed/PitchforkTypes/2 as p
import HeWhoMustNotBeNamed/PitchforkMethods/2
import HeWhoMustNotBeNamed/utils/1 as ut

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.')
zigzagLength = input.int(13, step=5, minval=3, title='Length', group='Zigzag', tooltip='Zigzag length for level 0 zigzag')
depth = input.int(50, "Depth", step=25, maxval=500, group='Zigzag', tooltip='Zigzag depth refers to max number of pivots to show on chart')
useRealTimeBars = input.bool(true, 'Use Real Time Bars', group='Zigzag', tooltip = 'If enabled real time bars are used for calculation. Otherwise, only confirmed bars are used')

typeTooltip = 'Handle Type' +
                 '\nandrews - Pivot A' +
                 '\nschiff - X of Pivot A and y from median of Pivot A and B' +
                 '\nmschiff - X and Y are median of Pivot A and Pivot B' +
             '\n\nNeck Type' +
                 '\nmedian - median of Pivot B and Pivot C' +
                 '\ninside - Pivot C'

pitchforkType = input.string("andrews", "Type", ["andrews", "schiff", "mschiff"], group="Pitchfork", inline="t")
neckType = input.string('inside', '', ['median', 'inside'], group="Pitchfork", inline="t", tooltip=typeTooltip)
handle = pitchforkType=='andrews'?'regular': pitchforkType
inside = neckType == 'inside'

ratioFrom = input.float(0.25, 'Ratio', minval=0.0, maxval=0.5, group='Pitchfork', inline='r')
ratioTo = input.float(1, '', minval=0.5, maxval=1.618, group='Pitchfork', inline='r', tooltip='Range of ratio for which drawing pitchfork is allowed')
numberOfForks = input.int(100, 'Forks', group = 'Pitchfork', inline='f', minval=10, maxval = 200, step=25, tooltip = 'Number of volume forks')
usePercentile = input.bool(false, 'Percentile', group='Pitchfork', tooltip = 'Use percentile of volume to determine the length of forks instead of percentage')
useConfirmedPivot = input.bool(true, 'Use Confirmed Pivots', group="Pitchfork", tooltip="If set to true, uses last confirmed pivot and ignores the current moving pivot")

extend = false
fill = false

offset = useRealTimeBars? 0 : 1

indicators = matrix.new<float>()
indicatorNames = array.new<string>()

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

startIndex = useConfirmedPivot? 1:0

method get_price(dr.Line this, int bar)=>
	stepPerBar = (this.end.price - this.start.price)/(this.end.bar - this.start.bar)
	distance = bar - this.start.bar
	this.start.price + distance*stepPerBar

array<p.Fork> forks = array.new<p.Fork>()
for i=0 to numberOfForks-1
	forks.push(p.Fork.new(i/(numberOfForks-1)))

p.PitchforkProperties properties = p.PitchforkProperties.new(forks, handle, inside)

if(barstate.islast)
    var array<p.Pitchfork> pitchforks = array.new<p.Pitchfork>()
    pitchforks.clear()
    mlzigzag = zigzag
    while(mlzigzag.zigzagPivots.size() >= 3+startIndex)
        lineColor = themeColors.pop()
        themeColors.unshift(lineColor)

        p3 = mlzigzag.zigzagPivots.get(startIndex)
        p3Point = p3.point
        p2Point = mlzigzag.zigzagPivots.get(startIndex+1).point
        p1Point = mlzigzag.zigzagPivots.get(startIndex+2).point

        if (p3.ratio >= ratioFrom and p3.ratio <= ratioTo and p3Point.bar - p1Point.bar <500)
            dr.LineProperties lProperties = dr.LineProperties.new(color=lineColor)
            p.PitchforkDrawingProperties dProperties = p.PitchforkDrawingProperties.new(extend, fill, commonColor=color.new(lineColor, 70))
            p.Pitchfork pitchFork = p.Pitchfork.new(p1Point, p2Point, p3Point, properties, dProperties, lProperties)
            drawing = pitchFork.createDrawing()
            array<float> forkVolumes = array.new<float>(drawing.forkLines.size(), 0.0)
            for bar=p1Point.bar to p3Point.bar
                bOpen = open[bar_index-bar]
                bClose = close[bar_index-bar]
                bHigh = high[bar_index-bar]
                bLow = low[bar_index-bar]
                bVol = volume[bar_index-bar]
                for [index, forkLine] in drawing.forkLines
                    linePrice = forkLine.get_price(bar)
                    vMultiplier = linePrice >= math.min(bOpen, bClose) and linePrice <= math.max(bOpen, bClose)? 2 :
                                     linePrice >= bLow and linePrice <= bHigh? 1 : 0
                    forkVolumes.set(index, forkVolumes.get(index)+bVol*vMultiplier)

            for [index, forkLine] in drawing.forkLines
                vPercent = usePercentile? forkVolumes.percentrank(index)/100 : forkVolumes.get(index)/forkVolumes.max()
                forkLine.end.bar := forkLine.start.bar + int(vPercent*(forkLine.end.bar - forkLine.start.bar))
                forkLine.end.price := forkLine.start.price + vPercent*(forkLine.end.price - forkLine.start.price)
            drawing.draw()
            pitchforks.push(pitchFork)
        mlzigzag := mlzigzag.nextlevel()