Support and Resistance: Triangles [YinYangAlgorithms]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@    ,@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      @@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        @@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@         @@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@           @@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        .@@@@@@@@@@@@@@@            @@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@          *@@@@@@@@@@@@@@             @@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@         @@@@@@@@@@@@@@@               @@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     @@@@@@@@@@@@@@@@                 @@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                  @@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.                    @@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                      @@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.                         @
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                             @
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,                                       @
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                                                @
// @@@@@@@@@@@@@@@@@@@@@@@@@@@                                                    @
// @@@@@@@@@@@@@@@@@@@@@@@@@                                                     @@
// @@@@@@@@@@@@@@@@@@@@@@@                                                       @@
// @@@@@@@@@@@@@@@@@@@@@@                                                       @@@
// @@@@@@@@@@@@@@@@@@@@@*                @@@@@                                 @@@@
// @@@@@@@@@@@@@@@@@@@@@               @@@@@@@@@                              @@@@@
// @@@@@@@@@@@@@@@@@@@@@              @@@@@@@@@@@                           @@@@@@@
// @@@@@@@@@@@@@@@@@@@@@               @@@@@@@@%                           @@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@                                                @@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@                                            @@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@                                        %@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@                                   @@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@                           @@@@@@@@@@@@@@@@@@@@@@@@
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// © YinYangAlgorithms

//@version=5
indicator("Support and Resistance: Triangles [YinYangAlgorithms]", overlay=true)
// ~~~~~~~~ INPUTS ~~~~~~~~ //
showTriangles = input.bool(true, "Show Triangles", tooltip="Should we plot triangles on the screen?")
createZonesFor = input.string("Both", "Triangle Zones", options=["Upwards", "Downwards", "Both", "Neither"], tooltip="What types of triangles should we create our zones for?")
dev = input.float(0.0001, "Max Deviation Allowed", tooltip="Maximum Deviation up or down from the last bars High/Low for potential to create a Triangle")
lookBack = input.int(50, "Lookback Distance", tooltip="How far back we look to see for potential of a High/Low within Deviation range")
minDistance = input.int(10, "Min Distance", tooltip="This is so triangles are spaced properly and not from 2 bars beside each other. Min distance allocated between 2 points to create a Triangle")
eachBarPercentIncrease = input.float(0.005, "Bar Percent Increase", tooltip="How much % multiplier do we apply for each bar spacing of the triangle. 0.005 creates a close to Equilateral Triangle, but other values like 0.004 and 0.006 seem to work well too.")

// ~~~~~~~~ VARIABLES ~~~~~~~~ //
createUpwardsTriangle = createZonesFor == "Both" or createZonesFor == "Upwards"
createDownwardsTriangle = createZonesFor == "Both" or createZonesFor == "Downwards"
float lt = 0.
float lb = 0.
float ht = 0.
float hb = 0.

// ~~~~~~~~ FUNCTIONS ~~~~~~~~ //
//Create the triangles and send back its Support and Resistance locations
createTriangle(_up, _src, _color) =>
    //Calculate the maximum upwards and downwards deviation allowed based on source
    srcDown = _src[1] - (_src[1] * dev)
    srcUp = _src[1] + (_src[1] * dev)
    //see if any of the same source between minDistance and lookBack are within the deviation allowment
    triangleDev = 0.
    triangleIndex = 0
    for i = minDistance to lookBack
        cur = _src[i]
        if cur >= srcDown and cur <= srcUp
            triangleDev := cur
            triangleIndex := i
            break
    //calculate the support and resistance locations based on this triangle
    triangleTop = -1.
    triangleBot = -1.
    //did we find a valid triangle?
    if triangleDev != 0
        //the middle of the triangle (bar index location)
        midDist = triangleIndex / 2
        //how much of a % change is there (we generally want to create an Equilateral Triangle)
        percentIncrease = triangleIndex * eachBarPercentIncrease
        //modify the mid point based on if its an upward or downward triangle
        midPoint = _up ? triangleDev * (1 + percentIncrease) : triangleDev * (1 - percentIncrease)
        //create the triangle
        if showTriangles and ((_up and createUpwardsTriangle) or (not _up and createDownwardsTriangle))
            line.new(bar_index - triangleIndex, triangleDev, bar_index[1], _src[1], color=_color, width=1)
            line.new(bar_index - triangleIndex, triangleDev, bar_index - midDist, midPoint, color=_color, width=1)
            line.new(bar_index - midDist, midPoint, bar_index[1], _src[1], color=_color, width=1)
        //save the support and resistance locations
        triangleTop := midPoint
        triangleBot := triangleDev
    //return the support and resistance locations (if there were any)
    [triangleTop, triangleBot]

// ~~~~~~~~ CALCULATIONS ~~~~~~~~ //
[lowTop, lowBot] = createTriangle(true, low, color.green)
[highTop, highBot] = createTriangle(false, high, color.red)

lt := lowTop != -1 ? lowTop : lt[1]
lb := lowBot != -1 ? lowBot : lb[1]
ht := highTop != -1 ? highTop : ht[1]
hb := highBot != -1 ? highBot : hb[1]

// ~~~~~~~~ PLOTS ~~~~~~~~ //
plot(createUpwardsTriangle ? lt : na, color=color.red, style=plot.style_circles)
plot(createUpwardsTriangle ? lb : na, color=color.orange, style=plot.style_circles)
plot(createDownwardsTriangle ? ht : na, color=color.green, style=plot.style_circles)
plot(createDownwardsTriangle ? hb : na, color=color.blue, style=plot.style_circles)

// ~~~~~~~~ END ~~~~~~~~ //