Stop Loss Clustering (Breakouts) [Kioseff Trading]

KioseffTrading · study · 1793 行 · 点赞 3,409 · TradingView 原页

本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。

Pine Script

// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © KioseffTrading
 
//@version=6
indicator("Stop Loss Clustering (Breakouts) [Kioseff Trading]", overlay = false, calc_bars_count = 10000, dynamic_requests = true, max_boxes_count = 500, max_labels_count = 500, max_lines_count = 500, max_polylines_count = 100)
import TradingView/ta/12
 
 
enum granularitySetting
 
    lower  = "Lower"
    higher = "Higher (Heavy)"
 
model             = input.string(defval = "Absorbtion Extremes", title = "Model", options = ["Absorbtion Extremes", "Volatility-At-Entry"])
 
showxRay          = input.bool(defval = true, title = "X-ray", group = "Absorbtion Extremes")
intensity         = input.bool(defval = false, title = "Set Color Intensity by Stop Cluster Size", group = "Absorbtion Extremes")
xRayTop           = input.int(defval = 2, minval = 1, title = "Stop Cluster Buys", group = "Absorbtion Extremes" , inline = "Stop Cluster Buys")
xRayBot           = input.int(defval = 2, minval = 1, title = "Stop Cluster Sells", group = "Absorbtion Extremes", inline = "Stop Cluster Sells")
oldStopsLimitUp   = input.int(defval = 2, minval = 0, title = "Old Stop Cluster Sells", group = "Absorbtion Extremes", inline = "Stop Cluster Sells")
oldStopsLimitDn   = input.int(defval = 2, minval = 0, title = "Old Stop Clusters Buys", group = "Absorbtion Extremes", inline = "Stop Cluster Buys")
ltfGran           = input.timeframe(defval = "1", title = "Lower Timeframe Vol. Data", group = "Absorbtion Extremes")
clusterCol        = input.color(defval = #55ffda, title = "Cluster Color", group = "Absorbtion Extremes", inline = "Col")
clusterCol2       = input.color(defval = #ff65fb, title = "Old Cluster Color", group = "Absorbtion Extremes", inline = "Col")
 
granularity       = input.enum(defval = granularitySetting.lower, title = "Level Granularity", options = [granularitySetting.lower, granularitySetting.higher], group = "Time-Scaled Volatility")
timeScaledVolaIn  = input.timeframe(defval = "1", title = "Time-Scaled Volatility TF", group = "Time-Scaled Volatility")
strongClusterColT = input.color(defval = #ff65fb, title = "Strong Cluster Color", group = "Time-Scaled Volatility")
weakClusterColT   = input.color(defval = #6929F2, title = "Weak Cluster Color", group = "Time-Scaled Volatility")
showHist          = input.bool (defval = false, title = "Show Historical Triggers", group = "Time-Scaled Volatility")
showSize          = input.bool (defval = false, title = "Show Active Cluster Size", group = "Time-Scaled Volatility")
forceTypicalMove  = input.bool (defval = false, title = "Force Find Typical Move (Less Similar)", group = "Optionals")
showRatioMeter    = input.bool (defval = true, title = "Show Cluster Ratio Meter", group = "Optionals")
 
type swingData 
 
    float V
    float P 
    int   T
    float P2
    int   vioT
    float intraBarMove
 
type volTime 
 
    float V
    int   T
 
type stopClusterDraw
 
    array<box>  stopClusterZone
    array<line> lineOut
    label       information
    float       V
 
type barData 
 
    float H 
    float L 
    int   T
 
type timeScaledVola 
 
    map<int, volTime> dataMap
    array<barData>    barStats
    map<int, volTime> removedDataMap
    array<int>        keysArr 
    array<int>        keysArrRemove
 
type timeScaledVolaDrawings 
 
    array<box>   gridBox 
    array<line>  hotLines 
    array<line>  gridLines
 
type timeScaledVolaLastBarData
 
    array<float> finVol 
    array<float> topClusters
    array<int>   startTime 
    array<int>   endTime   
    array<float> levels
 
type offChartData 
 
    array<float> buyStopsArr
    array<float> sellStopsArr
    float buyStops 
    float sellStops
    float sellStopPrice
    float buyStopPrice 
    float sellStopVol 
    float buyStopVol
    float sumSellsActive
    float sumSellsRemoved
    float sumBuysActive
    float sumBuysRemoved
    array<float> similarBuysArr 
    array<float> similarSellsArr
 
type lowerGranularity
 
    array<float> levels 
    array<volTime> dataArr
    array<volTime> removedDataArr
 
type similarities 
 
    array<float> sortedSize
    array<float> sortedMoves 
    array<float> absDist 
 
var timeArrBin = array.new<int>(), timeArrBin.push(time), var barMs = timeframe.in_seconds(timeframe.period) * 1000 
 
method updatePivot(array<chart.point> id, bool isContinuation, float pricePoint) => 
 
    getRec = id.first(), getP = id.last()
 
    switch isContinuation
 
        true => id.set(1, chart.point.from_time(getRec.time, getP.price))  , id.set(0, chart.point.from_time(time, pricePoint))
        =>      id.set(1, chart.point.from_time(getRec.time, getRec.price)), id.set(0, chart.point.from_time(time, pricePoint))
 
    id.first()
 
IQZZ(float atrMult) =>
    
    atr          = ta.atr(14) * atrMult
    var points   = array.from(chart.point.from_time(time, close), chart.point.from_time(time, close))
    pointPrev    = points.last(), pointP2      = pointPrev[1]
    
    var pointArr = array.new<float>()
    var timeArr  = array.new<int>  ()
    var dir      = 0
 
    getRec = points.first()
 
    if dir == 1
 
        price = math.max(getRec.price, high)
 
        if price == high 
            getRec := points.updatePivot(true, high)
            
        if low <= getRec.price - atr  and high != getRec.price
 
            dir   := -1 
            points.updatePivot(false, low)
 
    else if dir == -1
 
        price = math.min(low, getRec.price)
 
        if price == low 
            getRec := points.updatePivot(true, low)
 
        if high >= getRec.price + atr and low != getRec.price
 
            dir   := 1
            points.updatePivot(false, high)
 
    if dir == 0 
 
        if high >= getRec.price + atr 
 
            dir   := 1
            points.updatePivot(false, high)
 
        else if low <= getRec.price - atr
 
            dir   := -1 
            points.updatePivot(false, low)
 
    if not na(pointP2)
        if pointP2.price != pointPrev.price
        
            pointArr.push(pointPrev.price)
            timeArr .push(pointPrev.time)
    
    [dir, pointArr, timeArr]
    
[marketState, pointArr, timeArr] = IQZZ(2)
 
method qCurve(array<polyline> stopConnect, float startLevel, int minTime, string direction, bool isLive = false) =>
 
    x1 = minTime, y1 = startLevel  
 
    getIndex       = timeArrBin.binary_search_rightmost(x1)
    getPindex      = timeArr   .binary_search_rightmost(x1) 
 
    cond = getPindex + 1 > pointArr.size() - 1
 
    [y2, getEndTime] = switch cond
 
        false => [pointArr.get(getPindex + 1), timeArr .get(getPindex + 1)]
        =>       [low, time]
       
    xCount = math.round(math.round((getEndTime - x1) / barMs))
 
    if xCount != 0
 
        points = array.new<chart.point>()
 
        a = (y2 - y1) / math.pow(xCount, 2) 
 
        end = switch isLive 
 
            true => timeArrBin.size() - 1 
            =>      timeArrBin.binary_search_rightmost(getEndTime)
 
        for z = getIndex to end
            
            curvedP  =   math.pow((z - getIndex) / xCount, 2.5)
 
            y = y1 + a * math.pow(curvedP * xCount, 2)  
            
            y := switch direction 
 
                "Down" => math.max(y, y2)
                "Up"   => math.min(y, y2)
 
            points.push(chart.point.from_time(timeArrBin.get(z), y))
 
        curveCol = switch direction 
 
            "Down" => #ff65fb
            "Up"   => #55ffda
 
        stopConnect.push(polyline.new(points, xloc = xloc.bar_time, line_color = curveCol, 
                                             line_style    = line.style_dashed, 
                                             force_overlay = true
                                             ))
    
        if stopConnect.size() > 50 
            stopConnect.shift().delete()
 
        points.last()
 
method gradBox(array<stopClusterDraw> id, array<swingData> swingPoints, map<string, float> gradCoords, float top, float bot, string direction, int n200, array<stopClusterDraw> id2, array<swingData> swingPoints2) => 
 
    similar         = similarities.new(
                             array.new<float>(), 
                             array.new<float>(), 
                             array.new<float>()
                             )
 
    var stopConnect = array.new<polyline>()
    n500            = time("", -250), connectSize = stopConnect.size() - 1
 
    if connectSize > 0 
        for i = 0 to connectSize
 
            stopConnect.shift().delete()
 
    gran    = 400 / (xRayBot + xRayTop + oldStopsLimitDn + oldStopsLimitUp)
 
    relevantPoint = float(na), relevantCluster = float(na)
    sumActive     = 0.       , sumRemoved      = 0. 
    
    spSize2       = swingPoints2.size()
 
    if spSize2 > 0
 
        for x = spSize2 - 1 to 0
 
            data       = swingPoints2.get(x)
            sumActive += data.V
 
            dist   = math.abs(data.P - data.P2) / gran
            mid    = math.avg(data.P , data.P2)
            logVal = math.log(math.abs(data.V))   
 
            getIndex = similar.sortedSize.binary_search_rightmost(logVal)
 
            similar.sortedSize .insert(getIndex, logVal)
            similar.sortedMoves.insert(getIndex, data.intraBarMove)
 
            [condCheck, tip, y] = switch direction
 
                "Up" => [x <= oldStopsLimitUp - 1, "Filled Market Buys Attached To This Low (Estimate)"   , mid - gran * dist]
                =>      [x <= oldStopsLimitDn - 1, "Filled Market Sells Attached To This High (Estimate)" , mid + gran * dist]
                
            if condCheck
                
                gradCoords.put("Max", math.max(nz(gradCoords.get("Max")), math.abs(data.V)))
                gradCoords.put("Min", math.min(nz(gradCoords.get("Min")), math.abs(data.V)))
 
                id2.push(
                    stopClusterDraw.new(
 
                        array.new<box> (),
                        array.from(
                            
                             line.new(data.T, mid, data.vioT, mid, xloc = xloc.bar_time, color = clusterCol2, 
                                                          width         = 2, 
                                                          force_overlay = true
                                                          ), 
                             line.new(data.T, mid, data.vioT, mid, xloc = xloc.bar_time, color = color.new(clusterCol2, 94), 
                                                          width         = 4, 
                                                          force_overlay  = true
                                                          ),
                             line.new(data.T, mid, data.vioT, mid, xloc = xloc.bar_time, color = color.new(clusterCol2, 96), 
                                                          width         = 10,
                                                          force_overlay = true
                                                          ),
                             line.new(data.T, mid, data.vioT, mid, xloc = xloc.bar_time, color = color.new(clusterCol2, 98), 
                                                          width         = 20,
                                                          force_overlay = true
                                                          ),
                             line.new(data.T, mid, data.vioT, mid, xloc = xloc.bar_time, color = color.new(clusterCol2, 98), 
                                                          width         = 25,
                                                          force_overlay = true
                                                          )),
 
                        label.new(data.T, y, text = str.tostring(data.V, format.volume), xloc = xloc.bar_time, 
                                                         style         = label.style_text_outline, 
                                                         size          = size.small, 
                                                         color         = color.new(clusterCol2, 90), 
                                                         textcolor     = clusterCol2, 
                                                         tooltip       = tip, 
                                                         force_overlay = true
                                                         ),
                        data.V
 
                    )
                )    
 
                getRecDrawing = id2.last()
 
                for i = 0 to gran
                
                    col = switch
 
                        i <= gran / 2 => color.from_gradient(i, 0, gran / 2   , color.new(clusterCol2, 98), color.new(clusterCol2, 85))
                        i  > gran / 2 => color.from_gradient(i, gran / 2, gran, color.new(clusterCol2, 85), color.new(clusterCol2, 98))
 
                    [y1, y2] = switch direction 
 
                        "Down"   => [data.P + i * dist, data.P + ((i + 1) * dist)]
                        "Up"     => [data.P - i * dist, data.P - ((i + 1) * dist)]
 
                    gradCoords.put("Min Range", math.min(y1, y2,  nz(gradCoords.get("Min Range"), 1e8)))
                    gradCoords.put("Max Range", math.max(y1, y2,  nz(gradCoords.get("Max Range"), -1)))
 
                    getRecDrawing.stopClusterZone.push(
                    
                                     box.new(data.T, y1 , data.vioT, y2, border_color = color(na), border_width = 1, 
                                                                 xloc          = xloc.bar_time, 
                                                                 bgcolor       = col, 
                                                                 force_overlay = true
                                                                 )
 
                                 )
 
    spSize  = swingPoints.size()
 
    if spSize > 0
 
        for x = spSize - 1 to 0
 
            data        = swingPoints.get(x), sumRemoved += data.V
 
            dist        = math.abs(data.P - data.P2) / gran
            mid         = math.avg(data.P , data.P2)
 
            getEnd      = stopConnect.qCurve(data.P, data.T, direction)
 
            [condCheck, tip] = switch direction
 
                "Up" => [x <= xRayTop - 1, "Filled Market Buys Attached To This Low (Estimate)"   ]
                =>      [x <= xRayBot - 1, "Filled Market Sells Attached To This High (Estimate)" ]
                
            if condCheck
                
                relevantPoint   := mid
                relevantCluster := data.V
                    
                gradCoords.put("Max", math.max(nz(gradCoords.get("Max")), math.abs(data.V)))
                gradCoords.put("Min", math.min(nz(gradCoords.get("Min")), math.abs(data.V)))
 
                id.push(
 
                    stopClusterDraw.new(
 
                        array.new<box> (),
 
                        array.from(
 
                             line.new(data.T, mid, n200, mid, xloc = xloc.bar_time, color = clusterCol, 
                                                             width = 2,
                                                             force_overlay = true
                                                             ), 
                             line.new(data.T, mid, n200, mid, xloc = xloc.bar_time, color = color.new(clusterCol, 94), 
                                                             width = 4,
                                                             force_overlay = true
                                                             ),
                             line.new(data.T, mid, n200, mid, xloc = xloc.bar_time, color = color.new(clusterCol, 96), 
                                                             width = 10,
                                                             force_overlay = true
                                                             ),
                             line.new(data.T, mid, n200, mid, xloc = xloc.bar_time, color = color.new(clusterCol, 98), 
                                                             width = 20,
                                                             force_overlay = true
                                                             ),
                             line.new(data.T, mid, n200, mid, xloc = xloc.bar_time, color = color.new(clusterCol, 98), 
                                                             width = 25,
                                                             force_overlay = true
                                                             )),
                        
                        label.new(n500, mid, text = str.tostring(data.V, format.volume), xloc = xloc.bar_time, 
                                                             style         = label.style_text_outline, 
                                                             size          = size.small, 
                                                             color         = color.new(clusterCol, 90), 
                                                             textcolor     = clusterCol, 
                                                             tooltip       = tip, 
                                                             force_overlay = true),
                        data.V
 
                    )
                )    
 
                getRecDrawing = id.last()
 
                for i = 0 to gran
                
                    col = switch
 
                        i <= gran / 2 => color.from_gradient(i, 0, gran / 2   , color.new(clusterCol, 98), color.new(clusterCol, 85))
                        i  > gran / 2 => color.from_gradient(i, gran / 2, gran, color.new(clusterCol, 85), color.new(clusterCol, 98))
 
                    [y1, y2] = switch direction 
 
                        "Down"   => [data.P + i * dist, data.P + ((i + 1) * dist)]
                        "Up"     => [data.P - i * dist, data.P - ((i + 1) * dist)]
 
                    gradCoords.put("Min Range", math.min(y1, y2,  nz(gradCoords.get("Min Range"), 1e8)))
                    gradCoords.put("Max Range", math.max(y1, y2,  nz(gradCoords.get("Max Range"), -1)))
 
                    getRecDrawing.stopClusterZone.push(
                    
                                     box.new(data.T, y1 , n200, y2, border_color = color(na), 
                                                     border_width  = 1, 
                                                     xloc          = xloc.bar_time, 
                                                     bgcolor       = col, 
                                                     force_overlay = true
                                                     )
 
                                 )
 
                [data.V, relevantPoint, relevantCluster, sumActive, sumRemoved, similar]
 
 
method xRay(map<string, float> gradCoords, int n200) => 
 
    if showxRay
 
        top = gradCoords.get("Max Range")
        bot = gradCoords.get("Min Range")
    
        var gradXRAY = array.new<box>(50)
    
        for data in gradXRAY
            data.delete()
    
        dist = math.abs(top - bot) / 50
    
        for i = 0 to 49
        
            grad = switch  
            
                i <= 24 => color.from_gradient(i, 0 , 24, color.new(#6929F2, 85), color.new(#FF22CC, 85))
                =>         color.from_gradient(i, 25, 49, color.new(#FF22CC, 85), color.new(#6929F2, 85))
    
            gradXRAY.push(box.new(0, bot + (dist * i), n200, bot + (dist * (i + 1)), border_color = na, 
                                             bgcolor       = grad,
                                             xloc          = xloc.bar_time,
                                             extend        = extend.none, 
                                             force_overlay = true
                                             ))
 
 
method checkVioandAddRec(array<swingData> id, array<float> pivotFills, string direction, array<swingData> id2) => 
 
    getSize = id.size()
 
    sellStopsHit = 0., buyStopsHit = 0.
 
    if getSize > 0 
 
        if direction == "Down"
            for i = getSize - 1 to 0
                if high >= id.get(i).P2
 
                    id2.unshift(id.remove(i))
                    
                    if id2.size() > 0
 
                        getRec = id2.first(), getRec.vioT := time, sellStopsHit += getRec.V
 
                        getRec.intraBarMove := math.abs(high / math.min(getRec.P, getRec.P2) - 1),
 
 
        else 
 
            for i = getSize - 1 to 0
                if low <= id.get(i).P2
 
                    id2.unshift(id.remove(i))
 
                    if id2.size() > 0
 
                        getRec = id2.first(), getRec.vioT := time, buyStopsHit += getRec.V 
 
                        getRec.intraBarMove := math.abs(low / math.max(getRec.P, getRec.P2) - 1)
 
        if id.size() > 0
 
            recentSwing    = id.first()
            recentSwing.V += pivotFills.first()
 
    [buyStopsHit, sellStopsHit]
 
getClusterPoints(string side) => 
 
    var clusterData  = array.new<swingData>(), var clusterOld   = array.new<swingData>()
    var pivotFills   = array.new<float>    (), var sellSide     = "Sell Side", 
                                     var buySide  = "Buy Side"
 
    ltfVol           = request.security_lower_tf(syminfo.tickerid, ltfGran, volume * math.sign(close - close[1]))
    atr              =                            nz(ta.atr(14), high - low)
 
    [genClassifier, targetSign] = switch side
 
        sellSide => ["Down", -1]
        buySide  => ["Up",    1]
 
    if ltfVol.size() > 0 
 
        barFills = 0.
 
        for data in ltfVol 
            if math.sign(data) == targetSign
 
                barFills += data
 
        pivotFills.unshift(barFills)
 
    getPointSize                = pointArr.size(), getChange = ta.change(getPointSize) != 0
 
    [buyStopsHit, sellStopsHit] = clusterData.checkVioandAddRec(pivotFills, genClassifier, clusterOld)
 
    if getChange 
 
        if getPointSize > 1
 
            conditionCheck = switch side 
 
                sellSide =>  pointArr.last() > pointArr.get(-2)
                buySide  =>  pointArr.last() < pointArr.get(-2)
 
            if pivotFills.size() > 0 and conditionCheck
 
                barsDiff = switch syminfo.type 
 
                    "crypto" => math.round((time - timeArr.last()) / barMs)
                    =>          bar_index - timeArrBin.binary_search_rightmost(timeArr.last())
 
                if barsDiff <= pivotFills.size()
 
                    getPpoint  = pointArr.get(-2)
                    getPTpoint = timeArr .get(-2)
    
                    volNow = pivotFills.slice(0, barsDiff).sum()
    
                    getLvl = switch side
 
                        sellSide => high[barsDiff] + syminfo.mintick 
                        buySide  => low [barsDiff] - syminfo.mintick
 
                    getLvl2 = switch side
 
                        sellSide => getLvl + atr / 4 
                        buySide  => getLvl - atr / 4 
 
                    if clusterData.size() > 0
                    
                        getRecent    = clusterData.first()
                        getRecent.V -= volNow
    
                    clusterData.unshift(swingData.new(volNow, getLvl, time[barsDiff], getLvl2))
                    pivotFills .clear()
 
    [clusterData, clusterOld, buyStopsHit, sellStopsHit]
 
 
method reMove(array<stopClusterDraw> clusters) => 
 
    clustersSize = clusters.size()
 
    if clustersSize > 0
        for i = clustersSize - 1 to 0
 
            getIndex    = clusters.get(i)
            getDrawSize = getIndex.stopClusterZone.size()
 
            getIndex.information.delete()
            
            if getDrawSize > 0 
                for x = 0 to getDrawSize - 1
 
                    getIndex.stopClusterZone.shift().delete()
 
            for data in getIndex.lineOut
                data.delete()
 
            clusters.remove(i)
 
method lastBarDrawSwingMethod(array<swingData> id, array<swingData> id2, array<swingData> id3, array<swingData> id4) => 
    
    if barstate.islast
 
        var clusters    = array.new<stopClusterDraw>()
        var clustersOld = array.new<stopClusterDraw>()
        gradCoords      = map.new  <string, float>  ()
        n200            =       time("", -200)
 
        clusters   .reMove(), clustersOld.reMove()
 
        top = gradCoords.get("Max Range")
        bot = gradCoords.get("Min Range")
 
        [volDD, sellStopPrice, sellStopVol, sumSellsActive, sumSellsRemoved, sellsSimilar]  = 
                                 clusters.gradBox(id , gradCoords, top, bot, "Down", n200, clustersOld, id3)
 
        [volDU, buyStopPrice , buyStopVol, sumBuysActive, sumBuysRemoved, buysSimilar]      = 
                                 clusters.gradBox(id2, gradCoords, top, bot, "Up"  , n200, clustersOld, id4)
        
        gradCoords.xRay(n200)
 
        [volN, pulseCol] = switch marketState 
 
            1  => [str.tostring(volDU, format.volume), clusterCol]
            -1 => [str.tostring(volDD, format.volume), clusterCol2]
 
        var pulse = label.new(bar_index + 10, ohlc4, text = volN, color = color.new(pulseCol, 90), 
                                                     textcolor     = pulseCol, 
                                                     size          = 10, 
                                                     style         = label.style_text_outline, 
                                                     force_overlay = true
                                                     )
 
        pulse.set_text       (volN)  , pulse.set_xy     (bar_index + 10, ohlc4)
        pulse.set_textcolor(pulseCol), pulse.set_color(color.new(pulseCol, 90))
 
        if intensity 
 
            if clusters.size() > 0 
        
                getHighestVol = gradCoords.get("Max")
                getLowestVol  = gradCoords.get("Min")
 
                for data in clusters 
 
                    grad = color.from_gradient(math.abs(data.V), getLowestVol, getHighestVol, color.new(clusterCol, 90), clusterCol)
 
                    data.lineOut.first().set_color(grad)
                    data.information.set_textcolor(grad)
    
        [sellStopPrice, buyStopPrice, sellStopVol, buyStopVol, sumSellsActive, sumSellsRemoved, sumBuysActive, sumBuysRemoved, sellsSimilar, buysSimilar]
 
method findStart(array<int> startTimeArr, int endIndex, float bot, float dist, timeScaledVola timeScalingVola, int endTime, float botN, float topN, int i) => 
 
    getIndex = timeArrBin.binary_search_rightmost(endTime)
 
    for x = getIndex - 1 to 0
 
        data = timeScalingVola.barStats.get(x)
 
        if math.max(data.L, botN) <= math.min(data.H, topN)
 
            startTimeArr.set(i, data.T)
            break
 
sq(tfInMin) => 
 
    var t0 = timeframe.in_seconds(timeScaledVolaIn) / 60
 
    math.sqrt(tfInMin / t0)
 
 
method deleteOrder(array<int> id) =>
 
    dir = -1
 
    if id.size() > 1000
 
        p1 = math.abs(math.floor(id.max() * syminfo.mintick) - close)
 
        p2 = math.abs(close - math.floor(id.min() * syminfo.mintick)) 
 
        if p1 <= p2
            dir := 0
 
    dir
 
method deleteOrderLower(array<float> id) =>
 
    dir = -1
 
    if id.size() > 1000
 
        bottom = id.first()
        top    = id.last ()
 
        if math.abs(top - close) <= math.abs(close - bottom) 
 
            dir := 0
 
    dir
 
 
method removeFurthest(map<int, volTime> dataMap, array<int> keysArr, int dir, bool sort) => 
 
    if keysArr.size() > 25000
 
        if sort 
            keysArr.sort(order.ascending)
 
        while keysArr.size() > 20000 
 
            getKey = keysArr.get(dir)
 
            dataMap .remove(getKey)
            keysArr .remove(dir)
 
method removeFurthestLower(array<float> levels, array<volTime> levelData, array<volTime> levelDataRemoved, int dir) => 
 
    while levels.size() > 2500
    
        levels          .remove(dir)
        levelData       .remove(dir)
        levelDataRemoved.remove(dir)
 
method findStartNow(lowerGranularity lowerGran, timeScaledVola timeScalingVola, int addedIndexes, float frozenLowerGranProxy, bool isUnshift) => 
 
    if not na(addedIndexes)
 
        barStatsSize = timeScalingVola.barStats.size() - 1
        sizeArr      = lowerGran.dataArr.size()
 
        [start, end] = switch isUnshift
 
            true => [0, addedIndexes]
            =>      [sizeArr - (1 + addedIndexes), sizeArr - 1]
 
        endNested = math.max(0, barStatsSize - 1000)
 
        for i = start to end
 
            getStruct = lowerGran.dataArr.get(i)
            getLevel  = lowerGran.levels.get(i)
 
            getStruct.T := time 
 
            for x = barStatsSize to endNested
 
                getData = timeScalingVola.barStats.get(x)
 
                if math.max(getData.L, getLevel) <= math.min(getData.H, getLevel + frozenLowerGranProxy)
                    
                    getStruct.T := getData.T
                    break
 
                getStruct.T := getData.T
 
method findStartEnd(timeScaledVolaLastBarData lastBarDataRemoved, timeScaledVola timeScalingVola, float frozenLowerGranProxy, int i, timeScaledVolaLastBarData lastBarData) => 
    
    getStruct    = lastBarDataRemoved.endTime.get(i)
    getLevel     = lastBarDataRemoved.levels .get(i)
 
    start        = timeArrBin.binary_search_leftmost(getStruct)
 
    endLoop      = math.max(0, start - 1000), endTime = int(na)
 
    for x = start - 1 to endLoop
        
        getData = timeScalingVola.barStats.get(x)
        overlap = math.max(getData.L, getLevel) <= math.min(getData.H, getLevel + frozenLowerGranProxy)
 
        if overlap or x == endLoop
            
            endTime := getData.T    
            break
 
    endTime 
 
 
method lowerGranLastBar(array<volTime> dataArr, lowerGranularity lowerGran, timeScaledVolaLastBarData lastBarData, int index, int endIndex, string direction, int zoneCount = na,float frozenLowerGranProxy, timeScaledVola timeScalingVola) => 
    
    if not na(zoneCount) and zoneCount < 450 or na(zoneCount) 
 
        getStruct = dataArr          .get(index)
        getLevel  = lowerGran.levels .get(index)
 
        switch direction 
 
            "Up" =>  lastBarData.finVol.push   (getStruct.V), lastBarData.startTime.push(getStruct.T), 
                     lastBarData.levels.push   (getLevel)
 
            =>       lastBarData.finVol.unshift(getStruct.V), lastBarData.startTime.unshift(getStruct.T), 
                     lastBarData.levels.unshift(getLevel)
 
        if getStruct.V > nz(lastBarData.topClusters.min())
        
            lastBarData.topClusters.insert(lastBarData.topClusters.binary_search_rightmost(getStruct.V), getStruct.V)
 
            lastBarData.topClusters.shift()
 
method findTypical(similarities id, offChartData offChart, string direction, array<float> id2) =>
 
    if barstate.islast and not na(id)
            
        logVal = switch direction 
 
            "Buys" => math.log(math.abs(offChart.buyStopVol))
            =>        math.log(math.abs(offChart.sellStopVol))
 
        if not forceTypicalMove
            if not na(id.sortedSize)
            
                sortedSize = id.sortedSize.size()
 
                if sortedSize > 1
                    for i = 1 to sortedSize - 1
 
                        id.absDist.push(id.sortedSize.get(i) - id.sortedSize.get(i - 1))
 
            medAmt   = id.absDist   .percentile_nearest_rank(75)
            getStart = id.sortedSize.binary_search_rightmost(logVal - medAmt)
 
            if getStart < id.sortedSize.size() - 1
 
                for x = getStart to id.sortedSize.binary_search_leftmost(logVal + medAmt)
 
                    getVal = id.sortedSize.get(x)
 
                    if getVal >= logVal - medAmt and getVal <= logVal + medAmt
 
                        id2.push(id.sortedMoves.get(x))
 
        else 
 
            lowerInd = id.sortedSize.binary_search_leftmost(logVal)
 
            absUp = math.abs(logVal - id.sortedSize.get(lowerInd + 1)), 
            absDn = math.abs(logVal - id.sortedSize.get(lowerInd))
 
            switch 
 
                absUp <  absDn => id2.push(id.sortedMoves.get(lowerInd + 1))
                absUp == absDn => id2.push(id.sortedMoves.get(lowerInd + 1)), id2.push(id.sortedMoves.get(lowerInd))
                =>                id2.push(id.sortedMoves.get(lowerInd))
 
req() => 
 
    [ta.atr(14), volume * math.sign(close - close[1]) * -1, hlc3, low, high]
 
timeScaled() => 
 
    [volaTradersUseForStops, signVol, ltfhlc, ltfL, ltfH] = request.security_lower_tf(syminfo.tickerid, "1", req())
    lowerGranProxy                                        = ta.sma(ta.atr(14), 50)
 
    var array<float> factors = array.new<float>(), var hover = "-", 
 
    var lowerGran = lowerGranularity.new(
                             array.new<float>  (), 
                             array.new<volTime>(), 
                             array.new<volTime>()
                             )
 
    sumBuysActive = 0., sumSellsActive = 0., var sumBuysRemoved = 0., var sumSellsRemoved = 0.
    closestBuyP = float(na), closestSellP = float(na), closestBuyV = float(na), closestSellV = float(na)
 
    if barstate.isfirst
            
        hover := str.repeat(hover, 250)
 
        for h in array.from(sq(1), sq(5), sq(15), sq(30), sq(60), sq(240))
            for m in array.from             (1, 1.5, 2)
 
                factors.push(h * m)
    
    var timeScalingVola = timeScaledVola.new(
             map.new<int, volTime>(),
             array.new<barData>   (), 
             map.new<int, volTime>(), 
             array.new<int>       (),
             array.new<int>       ()
 
     )
 
    timeScalingVola.barStats.push(barData.new(high, low, time))
 
    if signVol.size() > 0 
        
        var masterTime  = time, buyStopsHit = 0., sellStopsHit = 0., var frozenLowerGranProxy = 0.
    
        var total = 6 * 3
 
        if granularity == granularitySetting.higher
 
            dir        = timeScalingVola.keysArr      .deleteOrder()
            dirRemoved = timeScalingVola.keysArrRemove.deleteOrder()
        
            for [i, data] in signVol
 
                direction = math.sign(data) 
                v0        = volaTradersUseForStops.get(i)
                domPoint  = ltfhlc.get(i)
                ltfLow    = ltfL  .get(i)
                ltfHigh   = ltfH  .get(i)
 
                for f in factors
 
                    fin   = v0 * f
 
                    level = domPoint + fin * direction 
 
                    ind = math.floor(level / syminfo.mintick)
 
                    if not timeScalingVola.dataMap.contains(ind)
 
                        exists = timeScalingVola.keysArr.binary_search_rightmost(ind)
                        timeScalingVola.keysArr         .insert(exists, ind)
 
                    getStruct = timeScalingVola.dataMap.get(ind)
 
                    if na(getStruct)
                        
                        timeScalingVola.dataMap.put(ind, volTime.new(data / total, time))
 
                    else
 
                        getStruct.V += data / total
                        getStruct.T := time
 
                getLow  = timeScalingVola.keysArr.binary_search_rightmost(math.floor(ltfLow  / syminfo.mintick))
                getHigh = timeScalingVola.keysArr.binary_search_leftmost (math.floor(ltfHigh / syminfo.mintick)) + 1
                
                if getHigh > getLow 
                
                    for z = getHigh - 1 to getLow
 
                        valData = timeScalingVola.keysArr.get(z)
                        stopVal = timeScalingVola.dataMap.remove(valData)
 
                        if time > stopVal.T 
 
                            if not timeScalingVola.removedDataMap.contains(valData)
 
                                timeScalingVola.keysArrRemove.push(valData)
 
                            timeScalingVola.removedDataMap.put(valData, volTime.new(stopVal.V, stopVal.T))
 
                        switch math.sign(stopVal.V)
 
                            -1 => buyStopsHit  += stopVal.V, sumBuysRemoved  += stopVal.V
                            1  => sellStopsHit += stopVal.V, sumSellsRemoved += stopVal.V
 
                        timeScalingVola.keysArr.remove(z)
                    
            timeScalingVola.dataMap       .removeFurthest(timeScalingVola.keysArr, dir, false)
            timeScalingVola.removedDataMap.removeFurthest(timeScalingVola.keysArrRemove, dirRemoved, true)
 
            if timeframe.change("1D") and timeScalingVola.keysArr.size() > 0 
                
                isGap = open > high[1] or open < low[1]
 
                if isGap
                    
                    [lowPrice, highPrice] = switch 
 
                        open > high[1] => [high[1], open]
                        =>                [open, low[1]]
 
                    getLow  = timeScalingVola.keysArr.binary_search_rightmost(math.floor(lowPrice  / syminfo.mintick))
                    getHigh = timeScalingVola.keysArr.binary_search_leftmost (math.floor(highPrice / syminfo.mintick)) + 1
                
                    if getHigh > getLow 
                    
                        for z = getHigh - 1 to getLow
 
                            valData = timeScalingVola.keysArr.get(z)
                            stopVal = timeScalingVola.dataMap.remove(valData)
 
                            if time > stopVal.T 
 
                                if not timeScalingVola.removedDataMap.contains(valData)
 
                                    timeScalingVola.keysArrRemove.push(valData)
 
                                timeScalingVola.removedDataMap.put(valData, volTime.new(stopVal.V, stopVal.T))
 
                            switch math.sign(stopVal.V)
 
                                -1 => buyStopsHit  += stopVal.V, sumBuysRemoved  += stopVal.V
                                1  => sellStopsHit += stopVal.V, sumSellsRemoved += stopVal.V
 
                            timeScalingVola.keysArr.remove(z)
                        
 
        else if not na(lowerGranProxy)
 
            addedLower = int(na), addedHigher = int(na)
 
            if lowerGran.levels.size() == 0 
 
                frozenLowerGranProxy         := lowerGranProxy / 4
                lowerGran.levels             := array.from(open - frozenLowerGranProxy, open, open + frozenLowerGranProxy)
                lowerGran.removedDataArr     := array.from(volTime.new(0, 0), volTime.new(0, 0), volTime.new(0, 0))
                lowerGran.dataArr            := array.from(volTime.new(0, 0), volTime.new(0, 0), volTime.new(0, 0))
 
            dir                              = lowerGran.levels.deleteOrderLower()
            barStatsSize                     = timeScalingVola.barStats.size() - 1
 
            for [i, data] in signVol
 
                direction = math.sign(data) 
                v0        = volaTradersUseForStops.get(i)
                domPoint  = ltfhlc.get(i)
                ltfLow    = ltfL  .get(i)
                ltfHigh   = ltfH  .get(i)
 
                for f in factors
 
                    fin   = v0 * f
 
                    level = domPoint + fin * direction 
 
                    getHighestLevel = lowerGran.levels.last()
                    getLowestLevel  = lowerGran.levels.first()
 
                    while math.max(ltfHigh, level) >= getHighestLevel
 
                        getHighestLevel += frozenLowerGranProxy
 
                        lowerGran.levels         .push(getHighestLevel)
                        lowerGran.dataArr        .push(volTime.new(0, 0))
                        lowerGran.removedDataArr .push(volTime.new(0, 0))
                        addedHigher :=            nz(addedHigher, -1) + 1
 
 
                    while math.min(ltfLow, level) <= getLowestLevel 
 
                        getLowestLevel -= frozenLowerGranProxy
 
                        lowerGran.levels         .unshift(getLowestLevel)
                        lowerGran.dataArr        .unshift(volTime.new(0, 0))
                        lowerGran.removedDataArr .unshift(volTime.new(0, 0))
                        addedLower :=             nz(addedLower, -1) + 1
 
                    exists    = lowerGran.levels .binary_search_leftmost(level)
                    getStruct = lowerGran.dataArr.get(exists)
                    getLevel  = lowerGran.levels .get(exists)
 
                    getStruct.V += data / total
 
                getLow  = lowerGran.levels.binary_search_rightmost(ltfLow)
                getHigh = lowerGran.levels.binary_search_leftmost (ltfHigh) + 1
 
                if getHigh > getLow 
                
                    for z = getHigh - 1 to getLow
 
                        valData      = lowerGran.levels .get(z)
                        stopVal      = lowerGran.dataArr.get(z)
                        getLevelData = lowerGran.removedDataArr.get(z)
 
                        getLevelData.V := stopVal.V
                        getLevelData.T := stopVal.T
  
                        switch math.sign(stopVal.V)
 
                            -1 => buyStopsHit  += stopVal.V, sumBuysRemoved  += stopVal.V
                            1  => sellStopsHit += stopVal.V, sumSellsRemoved += stopVal.V 
 
                        stopVal.T      := time 
                        stopVal.V      := 0
 
            lowerGran.levels.removeFurthestLower(lowerGran.dataArr, lowerGran.removedDataArr, dir)            
            lowerGran       .findStartNow       (timeScalingVola, addedLower , frozenLowerGranProxy, true)
            lowerGran       .findStartNow       (timeScalingVola, addedHigher, frozenLowerGranProxy, false)
 
            if timeframe.change("1D") and lowerGran.levels.size() > 0 
                
                isGap = open > high[1] or open < low[1]
 
                if isGap
 
                    [lowPrice, highPrice] = switch 
 
                        open > high[1] => [high[1], open]
                        =>                [open, low[1]]
 
                    getLow  = lowerGran.levels.binary_search_rightmost(lowPrice)
                    getHigh = lowerGran.levels.binary_search_leftmost (highPrice) + 1
 
                    if getHigh > getLow 
                    
                        for z = getHigh - 1 to getLow
 
                            valData      = lowerGran.levels .get(z)
                            stopVal      = lowerGran.dataArr.get(z)
                            getLevelData = lowerGran.removedDataArr.get(z)
 
                            getLevelData.V := stopVal.V
                            getLevelData.T := stopVal.T
    
                            switch math.sign(stopVal.V)
 
                                -1 => buyStopsHit  += stopVal.V, sumBuysRemoved  += stopVal.V
                                1  => sellStopsHit += stopVal.V, sumSellsRemoved += stopVal.V 
 
                            stopVal.T      := time 
                            stopVal.V      := 0
 
 
        if barstate.islast
 
            keys  = timeScalingVola.dataMap       .keys()
            keysR = timeScalingVola.removedDataMap.keys()
 
            keys.sort(order.ascending), keysR.sort(order.ascending)
 
            [top, topR, bot, botR, endIndex, endIndexR, startTimeSize] = switch granularity
 
                granularitySetting.higher => [keys .get(-1) * syminfo.mintick, keysR.get(-1) * syminfo.mintick, 
                                              keys .get(0)  * syminfo.mintick, keysR.get(0)  * syminfo.mintick, 
                                                                 495, 450, 451]
 
                granularitySetting.lower  => [lowerGran.levels.last (), lowerGran.levels.last(), lowerGran.levels.first(), 
                                              lowerGran.levels.first(), lowerGran.levels.size(), lowerGran.levels.size (), 0]
             
 
            var timeScaledDrawings        = timeScaledVolaDrawings.new(
                                                         array.new<box>(endIndex + 1), 
                                                         array.new<line>(10)
                                                         )
 
            var timeScaledDrawingsRemoved = timeScaledVolaDrawings.new(
                                                         hotLines  = array.new<line>(10), 
                                                         gridLines = array.new<line>(endIndexR + 1)
                                                         )
            var hoverLabels               = array.new<label>(496)
 
            for boxes in timeScaledDrawings.gridBox
                boxes.delete()
 
            for lines in timeScaledDrawingsRemoved.gridLines
                lines.delete()
 
            for lines in timeScaledDrawings.hotLines 
                lines.delete()
 
            for lines in timeScaledDrawingsRemoved.hotLines
                lines.delete()
 
            for labels in hoverLabels
                labels.delete()
 
            timeScaledDrawings.gridBox          := array.new<box> (endIndex  + 1)
            timeScaledDrawingsRemoved.gridLines := array.new<line>(endIndexR + 1)
 
            dist = (top - bot) / endIndex, distR = (topR - botR) / endIndexR
 
            lastBarData        = timeScaledVolaLastBarData.new(
                                                         array.new<float>(), 
                                                         array.new<float>(5, 0), 
                                                         array.new<int>(), 
                                                         levels = array.new<float>()
                                                         )
 
            lastBarDataRemoved = timeScaledVolaLastBarData.new(
                                                         array.new<float>(), 
                                                         array.new<float>(5, 0), 
                                                         array.new<int>(startTimeSize), 
                                                         array.new<int>(), 
                                                         levels = array.new<float>()
                                                         )
 
            closestLevelIndex = int(na)
 
            if granularity == granularitySetting.lower
 
                closestLevelIndex := lowerGran.levels.binary_search_leftmost(close)
 
            zoneCount = 0, indexUp = closestLevelIndex, indexDn = closestLevelIndex 
 
            if granularity == granularitySetting.higher
 
                for i = 0 to endIndex
 
                    botN = bot + dist * i
                    topN = bot + dist * (i + 1)
                        
                    effAdd = 0., startTime = int(na)
 
                    if granularity == granularitySetting.higher
 
                        idxB  = math.floor(botN / syminfo.mintick)
                        idxT  = math.floor(topN / syminfo.mintick)
 
                        slice = keys.slice(keys.binary_search_leftmost(idxB), keys.binary_search_rightmost(idxT))
 
                        for data in slice
 
                            getStruct = timeScalingVola.dataMap.get(data)
                            effAdd   += math.abs(getStruct.V)
 
                            startTime := switch na(startTime)
 
                                true => getStruct.T
                                =>      math.min(startTime, getStruct.T)
 
                        lastBarData.finVol   .push(effAdd)
                        lastBarData.startTime.push(startTime)
 
                        if effAdd > nz(lastBarData.topClusters.min())
 
                            ins       = lastBarData.topClusters.binary_search_rightmost(effAdd)
 
                            lastBarData.topClusters.insert(ins, effAdd)
                            lastBarData.topClusters.shift()
 
            else 
 
                for i = 0 to endIndex
 
                    if zoneCount >= 495 
                        break 
 
                    if indexUp == indexDn
 
                        lowerGran.dataArr.lowerGranLastBar(lowerGran, lastBarData, indexUp, endIndex, "Up", 
                                                                     int(na), 
                                                                     frozenLowerGranProxy, 
                                                                     timeScalingVola
                                                                     )
                        indexUp  += 1, zoneCount += 1, indexDn -= 1
 
                    else 
                        
                        if indexUp < endIndex
 
                            lowerGran.dataArr.lowerGranLastBar(lowerGran, lastBarData, indexUp, endIndex, "Up", 
                                                                     int(na), 
                                                                     frozenLowerGranProxy, 
                                                                     timeScalingVola
                                                                     )
                            indexUp += 1, zoneCount += 1
 
                        if indexDn > -1 
 
                            lowerGran.dataArr.lowerGranLastBar(lowerGran, lastBarData, indexDn, endIndex, "Down", 
                                                                     int(na), 
                                                                     frozenLowerGranProxy, 
                                                                     timeScalingVola
                                                                     )
                            indexDn -= 1, zoneCount += 1 
 
                if showHist
 
                    finVolSortRemoved = array.new<float>()
 
                    for data in lowerGran.removedDataArr 
                        finVolSortRemoved.push(math.abs(data.V))
 
                    finVolSortRemoved.sort(order.descending)
 
                    nestedEnd             = finVolSortRemoved.size() - 1
                    includesArr           = array.new<float>()
 
                    for i = 0 to nestedEnd
 
                        data = finVolSortRemoved.get(i)
 
                        for x = 0 to nestedEnd 
                            
                            getCompData = lowerGran.removedDataArr.get(x)
                            getLevel    = lowerGran.levels        .get(x)
 
                            if data == math.abs(getCompData.V) and not includesArr.includes(getLevel)
 
                                lastBarDataRemoved.finVol   .push(getCompData.V)
                                lastBarDataRemoved.levels   .push(math.avg(getLevel, getLevel + frozenLowerGranProxy))
                                lastBarDataRemoved.endTime  .push(getCompData.T) 
                                includesArr                 .push(getLevel)
                                break 
 
                        if i == 449 
                            break 
 
                    lastBarDataRemoved.topClusters := finVolSortRemoved.slice(0, math.min(5, nestedEnd))
 
                    for i = 0 to lastBarDataRemoved.levels.size() - 1
 
                        lastBarDataRemoved.startTime.push(
                                 lastBarDataRemoved .findStartEnd(timeScalingVola, frozenLowerGranProxy, i, lastBarDataRemoved
                                     ))
            
            if granularity == granularitySetting.higher
 
                for i = 0 to endIndexR
 
                    botN =  botR + distR * i
                    topN =  botR + distR * (i + 1)
 
                    idxB = math.floor(botN / syminfo.mintick)
                    idxT = math.floor(topN / syminfo.mintick)
 
                    slice = keysR.slice(keysR.binary_search_leftmost(idxB), keysR.binary_search_rightmost(idxT))
 
                    effAdd = 0., endTime = int(na)
 
                    for data in slice
 
                        getStruct = timeScalingVola.removedDataMap.get(data)
                        effAdd   += math.abs(getStruct.V)
 
                        endTime := switch na(endTime)
 
                            true => getStruct.T
                            =>      math.max(endTime, getStruct.T)
 
                    lastBarDataRemoved.startTime.findStart(endIndexR, botR, distR, timeScalingVola, endTime, botN, topN, i)
 
                    lastBarDataRemoved.finVol     .push(effAdd)
                    lastBarDataRemoved.endTime    .push(endTime)
 
                    if effAdd > nz(lastBarDataRemoved.topClusters.min())
 
                        ins       = lastBarDataRemoved.topClusters.binary_search_rightmost(effAdd)
                        
                        lastBarDataRemoved.topClusters.insert(ins, effAdd)
                        lastBarDataRemoved.topClusters.shift()
 
            maxVol   = lastBarData.finVol       .max(), minVol  = lastBarData.finVol       .min(),
            maxVolR  = lastBarDataRemoved.finVol.max(), minVolR = lastBarDataRemoved.finVol.min(),
                               histSize      = timeScalingVola.barStats.size()
 
            changePoint   = lastBarData       .finVol.percentile_nearest_rank(95)
            changePointR  = lastBarDataRemoved.finVol.percentile_nearest_rank(95) 
 
            hotThres      = lastBarData       .topClusters.min(), hotCount  = 0
            hotThresR     = lastBarDataRemoved.topClusters.min(), hotCountR = 0,
                                      nTime = time("", -1)
 
            end = switch granularity
 
                granularitySetting.higher => endIndex 
                =>                           lastBarData.levels.size() - 1
 
            removedSize = lastBarDataRemoved.endTime.size()
 
            for i = 0 to end
                
                botN = switch granularity
 
                    granularitySetting.higher => bot + dist * i
                    =>                           lastBarData.levels.get(i)
 
                topN = switch granularity 
 
                    granularitySetting.higher => bot + dist * (i + 1)
                    =>                           botN + frozenLowerGranProxy
 
                avg      = math.avg(botN, topN)
                getVol   = lastBarData.finVol    .get(i)
                getStart = lastBarData.startTime .get(i)
 
                switch avg < close  
 
                    true => sumBuysActive  += math.abs(getVol)
                    =>      sumSellsActive += math.abs(getVol)
 
                [grad, staticCol] = switch getVol >= changePoint
 
                    false => [color.from_gradient(getVol, minVol, maxVol, color.new(chart.bg_color, 98), color.new(weakClusterColT, 90)), weakClusterColT]
                    =>       [color.from_gradient(getVol, changePoint, maxVol, color.new(strongClusterColT, 95), color.new(strongClusterColT, 90)), strongClusterColT]
 
                timeScaledDrawings.gridBox.set(i, box.new(getStart, topN, nTime, botN, extend = extend.right, 
                                                                 border_color  = grad, 
                                                                 bgcolor       = grad, 
                                                                 xloc          = xloc.bar_time, 
                                                                 force_overlay = true
                                                                 ))
 
                if showSize 
 
                    hoverLabels.set(i, (label.new(bar_index + 50, avg, color = #00000000, 
                                                                 text          = str.tostring(getVol, format.volume), 
                                                                 size          = size.small, 
                                                                 style         = label.style_label_left, 
                                                                 textcolor     = staticCol, 
                                                                 force_overlay = true, 
                                                                 tooltip       = str.tostring(getVol, format.volume)
                                                                 )))
 
                if math.abs(getVol) >= hotThres and hotCount < 10
 
                    timeScaledDrawings.hotLines.set(hotCount    , line.new(getStart, avg, nTime, avg, xloc = xloc.bar_time, 
                                                                         color         = strongClusterColT, 
                                                                         extend        = extend.right, 
                                                                         force_overlay = true
                                                                         ))
 
                    timeScaledDrawings.hotLines.set(hotCount + 1, line.new(getStart, avg, nTime, avg, xloc = xloc.bar_time, 
                                                                         color         = color.new(strongClusterColT, 90), 
                                                                         width         = 5, 
                                                                         extend        = extend.right, 
                                                                         force_overlay = true
                                                                         ))
                    hotCount += 2
 
                    switch avg < close  
 
                        true => closestBuyP  := math.max(avg, nz(closestBuyP))
                        =>      closestSellP := math.min(avg, nz(closestSellP, 10e10))
 
                    if avg == closestBuyP
 
                        closestBuyV  := math.abs(getVol)
 
                    if avg == closestSellP 
 
                        closestSellV := math.abs(getVol) 
 
                if granularity == granularitySetting.lower and showHist
 
                    if i < removedSize
 
                        getOldVol   = lastBarDataRemoved.finVol   .get(i), getOldStart = lastBarDataRemoved.startTime.get(i)
                        getEnd      = lastBarDataRemoved.endTime  .get(i), avgR        = lastBarDataRemoved.levels   .get(i)
 
                        oldGrad = color.new(color.from_gradient(1, 0, 2, weakClusterColT, strongClusterColT), 50)
 
                        timeScaledDrawingsRemoved.gridLines.set(i, line.new(getOldStart, avgR, getEnd, avgR, extend = extend.none,      
                                                                     color         = oldGrad, 
                                                                     xloc          = xloc.bar_time, 
                                                                     force_overlay = true, 
                                                                     width         = 1
                                                                     ))
 
                        if math.abs(getOldVol) >= hotThresR and hotCountR < 10
 
                            timeScaledDrawingsRemoved.hotLines.set(hotCountR    , line.new(getOldStart, avgR, getEnd, avgR, 
                                                                     xloc          = xloc.bar_time, 
                                                                     color         = strongClusterColT, 
                                                                     extend        = extend.none, 
                                                                     force_overlay = true, 
                                                                     width         = 1
                                                                     ))
 
                            timeScaledDrawingsRemoved.hotLines.set(hotCountR + 1, line.new(getOldStart, avgR, getEnd, avgR, 
                                                                     xloc          = xloc.bar_time, 
                                                                     color         = color.new(strongClusterColT, 90), 
                                                                     width         = 5, 
                                                                     extend        = extend.none, 
                                                                     force_overlay = true
                                                                     ))
                            hotCountR += 2
 
 
            if granularity == granularitySetting.higher
 
                for i = 0 to endIndexR
 
                    botN =  botR + distR * i
                    topN =  botR + distR * (i + 1)
 
                    getVol   = lastBarDataRemoved.finVol       .get(i)
                    getStart = nz(lastBarDataRemoved.startTime .get(i), masterTime)
                    getEnd   = lastBarDataRemoved.endTime      .get(i)
 
                    avg      = math.avg(topN, botN)
 
                    grad = switch getVol >= changePointR
 
                        false => color.from_gradient(getVol, minVolR, maxVolR, color.new(chart.bg_color, 98), color.new(weakClusterColT, 90))
                        =>       color.from_gradient(getVol, changePointR, maxVolR, color.new(strongClusterColT, 95), color.new(strongClusterColT, 90))
 
                    timeScaledDrawingsRemoved.gridLines.set(i, line.new(nz(getStart, masterTime), avg, getEnd, avg, 
                                                             color         = grad, 
                                                             xloc          = xloc.bar_time, 
                                                             force_overlay = true
                                                             ))
 
                    if getVol >= hotThresR
 
                        timeScaledDrawingsRemoved.hotLines   .set(hotCountR, line.new (getStart, avg, getEnd, avg, xloc = xloc.bar_time, 
                                                             color         = strongClusterColT, 
                                                             force_overlay = true
                                                             ))
 
                        timeScaledDrawingsRemoved.hotLines   .set(hotCountR + 1, line.new (getStart, avg, getEnd, avg, xloc = xloc.bar_time, 
                                                             color         = color.new(strongClusterColT, 90), 
                                                             width         = 5, 
                                                             force_overlay = true
                                                             ))
 
                        hotCountR += 2
 
        [buyStopsHit, sellStopsHit, sumBuysActive, sumSellsActive, sumBuysRemoved, sumSellsRemoved, closestBuyP, closestSellP, closestBuyV, closestSellV]
 
var offChart = offChartData.new(
                         array.new<float>(), 
                         array.new<float>(), 
                         0, 
                         0, 
                         similarBuysArr = array.new<float> (), 
                         similarSellsArr = array.new<float>()
                         )
 
if model == "Absorbtion Extremes"
 
    [clusterHighs, clusterHighsOld, proxy, sellStopsHit] = getClusterPoints("Sell Side")
    [clusterLows , clusterLowsOld, buyStopsHit, proxy1]  = getClusterPoints("Buy Side")
 
    [sellStopPrice, buyStopPrice, sellStopVol, buyStopVol, sumSellsActive, sumSellsRemoved, sumBuysActive, sumBuysRemoved, sellsSimilar, buysSimilar] 
                             = clusterHighs.lastBarDrawSwingMethod(clusterLows, clusterHighsOld, clusterLowsOld) 
 
    offChart.buyStops            := buyStopsHit * -1, offChart.sellStops           := sellStopsHit * -1, offChart.sellStopPrice       := sellStopPrice
    offChart.buyStopPrice        := buyStopPrice,     offChart.sellStopVol         := sellStopVol,       offChart.buyStopVol          := buyStopVol
    offChart.sumSellsActive      := sumSellsActive,   offChart.sumSellsRemoved     := sumSellsRemoved,   offChart.sumBuysActive       := sumBuysActive
    offChart.sumBuysRemoved      := sumBuysRemoved
 
    if buyStopsHit != 0 
        offChart.buyStopsArr.push(offChart.buyStops)
 
    if sellStopsHit != 0 
        offChart.sellStopsArr.push(offChart.sellStops)
 
    buysSimilar .findTypical(offChart, "Buys" , offChart.similarBuysArr )
    sellsSimilar.findTypical(offChart, "Sells", offChart.similarSellsArr)
 
if model == "Volatility-At-Entry"
 
    [buyStopsHit, sellStopsHit, sumBuysActive, sumSellsActive, sumBuysRemoved, sumSellsRemoved, buyStopPrice, sellStopPrice, buyStopVol, sellStopVol] 
                                                             = timeScaled()
 
    offChart.buyStops        := buyStopsHit 
    offChart.sellStops       := sellStopsHit 
    offChart.sumBuysActive   := sumBuysActive
    offChart.sumSellsActive  := sumSellsActive 
    offChart.sumBuysRemoved  := sumBuysRemoved
    offChart.sumSellsRemoved := sumSellsRemoved 
    offChart.sellStopPrice   := sellStopPrice
    offChart.buyStopPrice    := buyStopPrice
    offChart.sellStopVol     := sellStopVol
    offChart.buyStopVol      := buyStopVol
 
    if buyStopsHit != 0 
        offChart.buyStopsArr.push(offChart.buyStops)
 
    if sellStopsHit != 0 
        offChart.sellStopsArr.push(offChart.sellStops)
 
 
sellStopMed  = ta.sma(offChart.sellStopsArr.percentile_nearest_rank(75), 50), sellStopsAvg = ta.sma(offChart.sellStops, 50)
buyStopMed   = ta.sma(offChart.buyStopsArr .percentile_nearest_rank(25), 50), buyStopsAvg  = ta.sma(offChart.buyStops, 50),                                
 
var volaAtEntryModel = model == "Volatility-At-Entry", var gCol = #55ffda, var pCol = #ff65fb
 
[buyThres, sellThres] = switch volaAtEntryModel
 
    true => [buyStopsAvg, sellStopsAvg]
    =>      [buyStopMed , sellStopMed]
 
[transpBuy, radiateB] = switch offChart.buyStops <= buyThres
 
    true => [0 , true ]
    =>      [50, false]
 
[transpSell, radiateS] = switch offChart.sellStops >= sellThres
 
    true => [0 , true ] 
    =>      [50, false]
 
var active = false, 
 
if not active and (offChart.sellStops != 0 or offChart.buyStops != 0)
    active := true
    
zero = plot(active ? 0 : na, linewidth = 1, color = bar_index % 3 == 0 ? chart.fg_color : na, display = display.pane)
 
buyStopsMedPlot  = plot(buyStopMed , color = #00000000 , display = display.none)
sellStopsMedPlot = plot(sellStopMed, color =  #00000000, display = display.none)
buyAvgPlot       = plot(volaAtEntryModel ? buyStopsAvg  : na, color = gCol)
sellAvgPlot      = plot(volaAtEntryModel ? sellStopsAvg : na, color = pCol)
 
fill(zero, buyStopsMedPlot, 
                     top_value    = 0, 
                     bottom_value = buyStopMed, 
                     top_color    = #00000000, 
                     bottom_color = color.new(#55ffda, volaAtEntryModel ? 100 : 95), 
                     display      = display.none
                     )
 
fill(zero, sellStopsMedPlot, 
                     top_value    = sellStopMed, 
                     bottom_value = 0, 
                     top_color    = color.new(pCol, volaAtEntryModel ? 100 : 95), 
                     bottom_color = #00000000, 
                     display      = display.none)
 
fill(zero, buyAvgPlot, 
                     top_value    = 0, 
                     bottom_value = buyStopsAvg, 
                     top_color    = #00000000, 
                     bottom_color = color.new(#55ffda, volaAtEntryModel ? 95 : 100), 
                     display      = display.none)
 
fill(zero, sellAvgPlot, 
                     top_value    = sellStopsAvg, 
                     bottom_value = 0, 
                     top_color    = color.new(pCol, volaAtEntryModel ? 95 : 100), 
                     bottom_color = #00000000, 
                     display      = display.none)
 
 
var drawType = switch volaAtEntryModel
 
    true => plot.style_columns
    =>      plot.style_circles
 
 
plot(offChart.buyStops  != 0 ? offChart.buyStops  : na, color = color.new(gCol, transpBuy ), style = plot.style_circles)
plot(offChart.sellStops != 0 ? offChart.sellStops : na, color = color.new(pCol, transpSell), style = plot.style_circles)
 
plot(radiateB                ? offChart.buyStops  : na, color = color.new(gCol, 85), linewidth = 3,  style = plot.style_circles, display = display.pane)
plot(radiateS                ? offChart.sellStops : na, color = color.new(pCol, 85), linewidth = 3,  style = plot.style_circles, display = display.pane)
plot(radiateB                ? offChart.buyStops  : na, color = color.new(gCol, 90), linewidth = 5,  style = plot.style_circles, display = display.pane)
plot(radiateS                ? offChart.sellStops : na, color = color.new(pCol, 90), linewidth = 5,  style = plot.style_circles, display = display.pane)
plot(radiateB                ? offChart.buyStops  : na, color = color.new(gCol, 95), linewidth = 7,  style = plot.style_circles, display = display.pane)
plot(radiateS                ? offChart.sellStops : na, color = color.new(pCol, 95), linewidth = 7,  style = plot.style_circles, display = display.pane)
plot(radiateB                ? offChart.buyStops  : na, color = color.new(gCol, 98), linewidth = 10, style = plot.style_circles, display = display.pane)
plot(radiateS                ? offChart.sellStops : na, color = color.new(pCol, 98), linewidth = 10, style = plot.style_circles, display = display.pane)
 
plot(volaAtEntryModel ? buyStopsAvg  : na, color = color.new(gCol, 94), linewidth = 4,  display = display.pane)
plot(volaAtEntryModel ? buyStopsAvg  : na, color = color.new(gCol, 96), linewidth = 6,  display = display.pane)
plot(volaAtEntryModel ? buyStopsAvg  : na, color = color.new(gCol, 97), linewidth = 10, display = display.pane)
plot(volaAtEntryModel ? sellStopsAvg : na, color = color.new(pCol, 94), linewidth = 4,  display = display.pane)
plot(volaAtEntryModel ? sellStopsAvg : na, color = color.new(pCol, 96), linewidth = 6,  display = display.pane)
plot(volaAtEntryModel ? sellStopsAvg : na, color = color.new(pCol, 97), linewidth = 10, display = display.pane)
 
 
 
if barstate.islast 
 
    var expCol = #181b27
    var tab    = table.new(position.top_right, 99, 99, 
                                    bgcolor       = #20222C, 
                                    border_color  = #363843, 
                                    frame_color   = #363843, 
                                    border_width  = 1, 
                                    frame_width   = 1, 
                                    force_overlay = true
                                    )
 
    medBuys  = offChart.similarBuysArr .median()
    medSells = offChart.similarSellsArr.median()
 
    [headBuy, headSell] = switch model 
 
        "Volatility-At-Entry" => ["% Of All Buy-Stop Clusters", "% Of All Sell-Stop Clusters"]
        =>                       ["Typical Move", "Typical Move"]
 
    typBuys = switch na(medBuys)
 
        true => "None Similar"
        =>      str.tostring(medBuys * 100, format.percent)
 
    typSells = switch na(medSells)
 
        true => "None Similar"
        =>      str.tostring(medSells * 100, format.percent)
 
    if model == "Volatility-At-Entry"
 
        typBuys   := str.tostring(offChart.buyStopVol  / offChart.sumBuysActive  * 100, format.percent)
        typSells  := str.tostring(offChart.sellStopVol / offChart.sumSellsActive * 100, format.percent)
 
    tab.cell(0, 0, text = "Stop-Loss Clustering",     text_color = color.white)
    tab.cell(0, 1, text = "Nearest Buy-Stop Cluster", text_color = #96ffe8, text_size = 12, bgcolor   = expCol)
    tab.cell(0, 2, text = "Price",                    text_color = color.rgb(81, 207, 180), text_size = size.small)
    tab.cell(1, 2, text = "Cluster",                  text_color = color.rgb(81, 207, 180), text_size = size.small)
    tab.cell(2, 2, text = headBuy,                    text_color = color.rgb(81, 207, 180), text_size = size.small)
 
    tab.cell(0, 3, text = str.tostring(offChart.buyStopPrice, format.mintick), text_color = color.white, text_size = size.small, bgcolor = expCol)
    tab.cell(1, 3, text = str.tostring(offChart.buyStopVol  , format.volume ), text_color = color.white, text_size = size.small, bgcolor = expCol)
    
    tab.cell(2, 3, text = typBuys, bgcolor = expCol  , text_color = color.white             , text_size = size.small)
    tab.cell(3, 1, text = "Nearest Sell-Stop Cluster", text_color = color.rgb(255, 165, 252), text_size = 12, bgcolor = expCol)
    
    tab.cell(3, 2, text = "Price"  , text_color = pCol, text_size = size.small)
    tab.cell(4, 2, text = "Cluster", text_color = pCol, text_size = size.small)
    tab.cell(5, 2, text = headSell , text_color = pCol, text_size = size.small)
 
    tab.cell(3, 3, text = str.tostring(offChart.sellStopPrice, format.mintick)  , text_size = size.small , text_color = color.white, bgcolor = expCol)
    tab.cell(4, 3, text = str.tostring(offChart.sellStopVol  , format.volume)   , text_size = size.small , text_color = color.white, bgcolor = expCol)
    tab.cell(5, 3, text = typSells, bgcolor = expCol, text_color = color.white, text_size = size.small)
 
    tab.merge_cells(0, 1, 2, 1), tab.merge_cells(0, 0, 5, 0), 
                     tab.merge_cells(3, 1, 5, 1)
 
    if showRatioMeter
 
        var ratioMeter = table.new(position.bottom_center, 99, 99, 
                                         bgcolor       = expCol, 
                                         force_overlay = true, 
                                         frame_color   = na, 
                                         frame_width   = 1
                                         )
 
        absBuys = math.abs(offChart.sumBuysActive), absSells = math.abs(offChart.sumSellsActive)
 
        maxStops  = math.max(absBuys, absSells)
        minStops  = math.min(absBuys, absSells)
        dom       = nz((maxStops - minStops) / minStops) * 10
 
        domBlocks      = math.min(10, math.round(dom)) * (math.sign(absSells - absBuys))
        sellNormalized = 10 + domBlocks
 
        sumRemoved            = math.abs(offChart.sumBuysRemoved) + math.abs(offChart.sumSellsRemoved) 
        ratioBuyStopsRemoved  = math.abs(offChart.sumBuysRemoved) / sumRemoved
 
        ratioSellStopsRemoved = 1 -  ratioBuyStopsRemoved
        sellNormalizedRemoved = 20 * ratioSellStopsRemoved 
        buyNormalizedRemoved  = 20 * ratioBuyStopsRemoved 
 
        for i = 0 to 21
 
            if i <= 19
 
                col = switch i <= sellNormalized
 
                    true => color.from_gradient(i, 0, sellNormalized , #ffb0fc, pCol)
                    =>      color.from_gradient(i, sellNormalized + 1, 19, gCol, color.rgb(176, 255, 238))
 
                colR = switch i <= sellNormalizedRemoved
 
                    true => color.from_gradient(i, 0, sellNormalizedRemoved , #ffb0fc, pCol)
                    =>      color.from_gradient(i, sellNormalizedRemoved + 1, 19, gCol, color.rgb(176, 255, 238))
 
                ratioMeter.cell(i + 1, 1, bgcolor = expCol, text = "█", text_color = col, height = 3)
                ratioMeter.cell(i + 1, 4, bgcolor = expCol, text = "▢", text_color = colR, height = 3)
 
            ratioMeter.cell(i, 0, height = 3)
            ratioMeter.cell(i, 3, height = 3)
 
        ratioMeter.cell(0 , 0, text = "Active Buy-Stop Clusters"   , text_color = color.white, text_size = size.small)
        ratioMeter.cell(21, 0, text = "Active Sell-Stop Clusters"  , text_color = color.white, text_size = size.small)
        ratioMeter.cell(0 , 3, text = "Violated Buy-Stop Clusters" , text_color = color.white, text_size = size.small)
        ratioMeter.cell(21, 3, text = "Violated Sell-Stop Clusters", text_color = color.white, text_size = size.small)
 
        ratioMeter.cell(0, 1,     text       = str.tostring(math.abs(offChart.sumSellsActive), format.volume),
                                  text_color = color.white, 
                                  bgcolor    = color.new(#7a0074, 80), 
                                  text_size  = size.small)
        
        ratioMeter.cell(21, 1, text = str.tostring(offChart.sumBuysActive, format.volume), 
                                 text_color = color.white, 
                                 bgcolor    = color.new(#007a5f, 80), 
                                 text_size  = size.small)
 
        ratioMeter.cell(0, 4,  text = str.tostring(math.abs(offChart.sumSellsRemoved), format.volume), 
                                 text_color = color.white, 
                                 bgcolor    = color.new(#7a0074, 80), 
                                 text_size  = size.small
                                 )
 
        ratioMeter.cell(21, 4, text = str.tostring(offChart.sumBuysRemoved, format.volume), 
                                 text_color = color.white, 
                                 bgcolor    = color.new(#007a5f, 80), 
                                 text_size  = size.small)
 
alertcondition(radiateB, title = "Large Buy-Stop Cluster Triggered"  , message = "Large Buy-Stop Cluster Triggered")
alertcondition(radiateS, title = "Large Sell-Stop Cluster Triggered" , message = "Large Sell-Stop Cluster Triggered")
 
if radiateB 
    alert("Large Buy-Stop Cluster Triggered" , freq = alert.freq_once_per_bar)
if radiateS
    alert("Large Sell-Stop Cluster Triggered", freq = alert.freq_once_per_bar)

← 返回列表