Osmosis
Updated
January 29, 2024
TradingView

For free use on the TradingView platform

Osmosis is a multi indicator, multi period heatmap. Lookback periods can be mysterious as it can tend to seem very arbitrary. This tool allows users to see how price/volume reacts to short to long periods by visualizing all of the periods at the same time. This is useful because small periods are only good for short term movements while long periods are useful for long term movements. This more detailed view of market trends is analogues of multi time frame analysis. The lookback periods are arranged from bottom up, where the bottom of the indicator is the shortest period while the top is the longest period.

One major feature of this indicator is its ability to signal potential trend reversals. For example, a shift in the direction at the lower end of the heatmap can indicate a weakening of the current trend, suggesting a possible reversal. On the other hand, when the heatmap is fully saturated at all levels, it may indicate a strong trend that could be nearing a reversal point.

Another important and unique aspect of the Osmosis indicator is its automatic highlighting feature. This feature emphasizes regions within the heatmap that score exceptionally high or low, drawing attention to significant market movements or potential anomalies.

All of the indicators are normalized using min/max scaling driven by the highest highs and lows. The period of this scaling is adjustable by changing the "Lookback" parameter under settings. Delta length changes the lookback for "MA Delta" and "Volume Delta". A longer period corresponds to a smoother output. Fast Mode scales back the range of the indicator, literally halving the increment.

Here is a short description of what each input does:

  • Alternate Source: A choice to use a different data source for the indicator.
  • Source: An option to turn on or off the alternate data source.
  • Style: A selection menu to choose the visual style of the indicator.
  • Lookback: Adjusts how far back in time the indicator looks for its calculations.
  • Delta Length: Changes the length of time over which changes are measured.
  • Fast Mode: A setting that adjusts the range of the indicator for quicker analysis.
  • Enable Smoothing: A choice to smooth out the data for a cleaner look.
  • Smooth: Activates the smoothing feature.
  • Max Region: Highlights the highest value regions in the heatmap.
  • Max Threshold: Sets the threshold for what counts as a 'max' region.
  • Minimum Max Width: Determines the smallest size for a 'max' region to be highlighted.
  • Max Region Color: Chooses the color for the maximum value regions.
  • Max Top Line Alpha: Adjusts the transparency of the top line in max regions.
  • Max Bottom Line Alpha: Adjusts the transparency of the bottom line in max regions.
  • Line Width: Sets the thickness of the lines in the max regions.
  • Region Start Indication: Specifies where the max region starts.
  • Fill Max: Decides if the max regions should be filled with color and sets the transparency level for the color fill in max regions.
  • Minimum Region: Highlights the lowest value regions in the heatmap.
  • Minimum Threshold: Sets the threshold for what counts as a 'min' region.
  • Minimum Minimum Width: Determines the smallest size for a 'min' region to be highlighted.
  • Minimum Region Color: Chooses the color for the minimum value regions.
  • Minimum Top Line Alpha: Adjusts the transparency of the top line in min regions.
  • Minimum Bottom Line Alpha: Adjusts the transparency of the bottom line in min regions.
  • Minimum Line Width: Sets the thickness of the lines in the min regions.
  • Minimum Region Start Indication: Specifies where the min region starts.
  • Fill Minimum: Decides if the min regions should be filled with color and sets the transparency level for the color fill in min regions.
  • Color Presets: Provides pre-set color schemes.
  • Invert Color Scale: Flips the color scale.
  • Gradient Colors: Customizes individual colors for the gradient scale.

Available styles include:

'MACD Histogram'

'Normalized MACD'

'Slow MACD'

'MACD Percent Rank'

'MA Delta'(Delta Length set to 2)

'BB Width'

'BB Width Percentile'

'Stochastic'

'RSI'

'True Range OSC'

'Normalized Volume'

'Volume Delta'

'True Range'

'Rate of Change'(Smoothing set to 1)

'OBV'(Smoothing set to 1)

'MFI'(Smoothing set to 1)

'Trend Angle'(Smoothing set to 2 and fast mode off)


//@version=5
indicator("Osmosis [ChartPrime]", precision=0, max_labels_count=500)

heatmapColor(series float source, simple string colorScheme, simple bool invert, simple array customColors) =>
    float value = invert ? source : 100.0 - source
    if colorScheme == "Prime"
        switch
            value <   0.0 => #000000
            value <  14.3 => color.from_gradient(value,  0.0,  14.3, #6600FF, #0000FF)
            value <  28.6 => color.from_gradient(value, 14.3,  28.6, #0000FF, #00FFFF)
            value <  42.9 => color.from_gradient(value, 28.6,  42.9, #00FFFF, #00FF00)
            value <  57.1 => color.from_gradient(value, 42.9,  57.1, #00FF00, #FFFF00)
            value <  71.4 => color.from_gradient(value, 57.1,  71.4, #FFFF00, #FF8000)
            value <  85.7 => color.from_gradient(value, 71.4,  85.7, #FF8000, #FF0000)
            value <=100.0 => color.from_gradient(value, 85.7, 100.0, #FF0000, #FF00FF)
            =>               #FFFFFF
    else if colorScheme == "Standard"
        switch
            value <   0.0  => #000000
            value <   20.0 => color.from_gradient(value,  0.0,  20.0, #008000, #00FF00)
            value <   40.0 => color.from_gradient(value, 20.0,  40.0, #00FF00, #FFFF00)
            value <   60.0 => color.from_gradient(value, 40.0,  60.0, #FFFF00, #FF8000)
            value <   80.0 => color.from_gradient(value, 60.0,  80.0, #FF8000, #FF0000)
            value <= 100.0 => color.from_gradient(value, 80.0, 100.0, #FF0000, #800000)
            =>                #FFFFFF
    else // colorScheme == "User Defined"
        switch
            value <    0.0 => #000000
            value <   25.0 => color.from_gradient(value,  0.0,  25.0, customColors.get(0), customColors.get(1))
            value <   50.0 => color.from_gradient(value, 25.0,  50.0, customColors.get(1), customColors.get(2))
            value <   75.0 => color.from_gradient(value, 50.0,  75.0, customColors.get(2), customColors.get(3))
            value <= 100.0 => color.from_gradient(value, 75.0, 100.0, customColors.get(3), customColors.get(4))
            =>                #FFFFFF

max(series float source, simple int outlier_level, simple int dev_lookback) =>
    var float maxima = na
    array src = array.new()
    float      stdev = math.abs((source - ta.ema(source, dev_lookback)) / ta.stdev(source, dev_lookback))
    src.push(stdev < outlier_level ? source : -1.7976931348623157e+308)
    maxima := math.max(nz(maxima[1]), src.get(0))

min(series float source, simple int outlier_level, simple int dev_lookback) =>
    var float minima = na
    array src = array.new()
    float      stdev = math.abs((source - ta.ema(source, dev_lookback)) / ta.stdev(source, dev_lookback))
    src.push(stdev < outlier_level ? source : 1.7976931348623157e+308)
    minima := math.min(nz(minima[1]), src.get(0))

min_max(series float source, simple int outlier_level, simple int dev_lookback) =>
    float out = (source - min(source, outlier_level, dev_lookback)) / (max(source, outlier_level, dev_lookback) - min(source, outlier_level, dev_lookback)) * 100.0
    math.max(0.0, math.min(100.0, out))

diff(series float source, simple int length) =>
    switch length
        1  =>  source -     source[1]
        2  => (source -     source[2]) / 2
        3  => (source +     source[1] -      source[2] - source[3]) / 4
        4  => (source + 2 * source[1] -  2 * source[3] - source[4]) / 8
        5  => (source + 3 * source[1] +  2 * source[2] -  2 * source[3] -  3 * source[4] -      source[5]) / 16
        6  => (source + 4 * source[1] +  5 * source[2] -  5 * source[4] -  4 * source[5] -      source[6]) / 32
        7  => (source + 5 * source[1] +  9 * source[2] +  5 * source[3] -  5 * source[4] -  9 * source[5] -  5 * source[6] -      source[7]) / 64
        8  => (source + 6 * source[1] + 14 * source[2] + 14 * source[3] - 14 * source[5] - 14 * source[6] -  6 * source[7] -      source[8]) / 128
        9  => (source + 7 * source[1] + 20 * source[2] + 28 * source[3] + 14 * source[4] - 14 * source[5] - 28 * source[6] - 20 * source[7] - 7 * source[8] - source[ 9]) / 256
        =>    (source + 8 * source[1] + 27 * source[2] + 48 * source[3] + 42 * source[4] - 42 * source[6] - 48 * source[7] - 27 * source[8] - 8 * source[9] - source[10]) / 512 // 10

average_true_range(series float source, simple int length) =>
    float true_range = high - low
    float     _range = math.abs(open - close)
    float    a_range = (true_range * 2.0 + _range) / 3.0
    ta.rma(a_range, length)

tr_score(series float source, simple int length) =>
    (source - ta.sma(source, length)) / average_true_range(source, length)

tr_algo(series float source, simple int length, smooth) =>
    min_max(ta.sma(tr_score(source, length), smooth), 1, length)

ema(series float source, simple float length) =>
    var float alpha = 2.0 / (length + 1)
    var float  beta = 1.0 - alpha
    float smoothed = na
    smoothed := alpha * source + beta * nz(smoothed[1], source)

preFilter(series float source, simple int period) =>
    var float halfPeriod = 0.5 * period
    var int  periodPlus1 =   1 + period
    if period > 1
        float E = 0.0
        float W = 0.0
        for int i=1 to period
            float w = if i < halfPeriod
                i + 1
            else
                periodPlus1 - i
            E += w * source[i - 1]
            W += w
        E / W
    else
        source

method find_streak(series array  self,
                   simple float    threshold,
                   simple bool   bullishMode,
                   simple int streakDuration,
                   simple bool        enable) =>
    var int sizeOf = self.size() - 1
    int streak_start_index     = na
    int max_streak_start_index = na
    int max_streak_end_index   = na
    int streak_maxima          = 0
    int streak_count           = 0
    if enable
        int i = 0
        while i < sizeOf
            if streak_maxima > sizeOf
                break
            float current = self.get(i)
            if(not bullishMode and current <= threshold)
               or (bullishMode and current >= threshold)
                streak_count := 1
                int j = i + 1
                while j < sizeOf and ((bullishMode and self.get(j) >= threshold)
                               or (not bullishMode and self.get(j) <= threshold))
                    streak_count += 1
                    j            += 1
                if streak_count > streak_maxima
                    streak_maxima          := streak_count
                    max_streak_start_index := i
                    max_streak_end_index   := j + 1
                i := j
            else
                i += 1
    if streak_maxima >= streakDuration
        [max_streak_end_index + 1, max_streak_start_index + 1]
    else
        [na, na]

normalize(series float source, simple int length) =>
    float lowestLow = ta.lowest(source, length)
    (source - lowestLow) / (ta.highest(source, length) - lowestLow) * 100.0

stdev(series float source, simple int length) =>
    math.sqrt(math.sum(math.pow(source - ta.ema(source, length), 2), length) * 0.5) * 0.5

fastMode(simple int length, simple bool fastMode) =>
    fastMode ? int(length / 2) : length

rsi(series float source, simple int length) =>
    float rsi = ta.rsi(source, length)
    float   f =  -math.pow(math.abs(math.abs(rsi - 50.0) - 50.0), 1.0 + math.pow(length, 0.707) - 1.0) /      // 0.618 14 ###############################
                                                         math.pow(50.0, math.pow(length, 0.707) - 1.0) + 50.0 // 0.618 14 ###############################
    if rsi > 50.0
        50.0 + f
    else
        50.0 - f

trend_angle(series float source, simple int length, simple int preFilterLength) =>
    float degrees = preFilter((math.todegrees(math.atan((source - source[length]) / ta.atr(length))) + 90.0) / 180.0 * 100.0, preFilterLength)



string   sources = "Alternative Input Sourcing"
float  altsource = input.source(close,     " Source:", group=sources, inline=sources)
float highsource = input.source( high,       " High:", group=sources, inline=sources)
float  lowsource = input.source(  low,        " Low:", group=sources, inline=sources)
int    preFilter = input.int   (    1, "PreSmoothing", group=sources, minval=1)

string  settings = "Algorithm Settings"
string algorithm = input.string("Normalized MACD", "Select an Algorithm", group=settings,
   options=["MACD Histogram", "Normalized MACD", "Slow MACD", "MACD Percent Rank", "MA Delta *",
  "BB Width", "BB Width Percentile", "RSI", "Stochastic", "True Range Algo", "Normalized Volume",
  "Volume Delta *", "True Range", "Rate of Change", "OBV", "MFI", "Trend Angle"])
int normLength = input.int (   90, "Normalization Lookback", group=settings, minval=1, tooltip="???????????????????????????????")
int  madLength = input.int (    4,         "Delta Length *", group=settings, minval=1, maxval=10, tooltip="???????????????????????????????")
bool  fastMode = input.bool(false,       "Enable Fast Mode", group=settings, tooltip="???????????????????????????????")

string  colors = "Heatmap Color Options"
bool    invert = input.bool  (   false,   "Invert Colors     ", group=colors, inline=colors)
string  scheme = input.string( "Standard",       "Color Schemes:", group=colors, inline=colors, options=["Standard","Prime","User Defined"])
color color000 = input.color(#0000FF, "User Defined Colors:", group=colors, inline=colors)
color color025 = input.color(#00FFFF,                     "", group=colors, inline=colors)
color color050 = input.color(#00Ff00,                     "", group=colors, inline=colors)
color color075 = input.color(#FFFF00,                     "", group=colors, inline=colors)
color color100 = input.color(#FF0000,                     "", group=colors, inline=colors)

string     regions = "Trend Streak Options"
string   emphasize = input.string(   "Both",         "Emphasize Trends", group=regions, options=["Both","Bullish","Bearish","Disabled"])
int upperThreshold = input.int   (       70,      "Thresholds - Upper:", group=regions, inline="thresh", minval=50, maxval=100, step=5)
int lowerThreshold = input.int   (       30,                   "Lower:", group=regions, inline="thresh", minval= 0, maxval= 50, step=5, tooltip="Upper threshold range: 50 to 100\nLower threshold range:  0 to 50")
int    maxDuration = input.int   (        5,   "   Duration - Bullish:", group=regions, inline="widths", minval= 0, maxval= 30, step=5)
int    minDuration = input.int   (        5,                 "Bearish:", group=regions, inline="widths", minval= 0, maxval= 30, step=5, tooltip="???????????????????????????????")
string   bullOnset = input.string( "Bottom", "Onset Markers - Bullish:", group=regions, inline="onsets", options=["Both","Top","Bottom","None"])
string   bearOnset = input.string(    "Top",                 "Bearish:", group=regions, inline="onsets", options=["Both","Top","Bottom","None"], tooltip="???????????????????????????????")
int  lineThickness = input.int   (        3,              "Line Widths", group=regions, minval=2, maxval=4)
color bearishColor = input.color (#008000,  "Trend Colors - Bearish:", group=regions, inline="colors")
color bullishColor = input.color (#800000,                 "Bullish:", group=regions, inline="colors")



indicator_picker(length) =>
    float alt_src   = preFilter( altsource, preFilter)
    float alt_high  = preFilter(highsource, preFilter)
    float alt_low   = preFilter( lowsource, preFilter)
    int fastLength2 = fastMode(2, fastMode)
    int fastLength3 = fastMode(3, fastMode)
    int fastLength4 = fastMode(4, fastMode)
    int fastLength5 = fastMode(5, fastMode)
    int fastLength6 = fastMode(6, fastMode)
    int tempLength5 = 5 + length * fastLength5
    switch algorithm
        "Normalized MACD"     =>  (length == 1 ? normalize(ta.ema(alt_src, fastLength3) - ta.ema(alt_src,              fastLength5), normLength) :
                                                 normalize(ta.ema(alt_src, fastLength5) - ta.ema(alt_src, 1 + length * fastLength6), normLength))
        "MACD Histogram"      => normalize(( ema(    alt_src, fastLength2 * length * 0.5 + 2.0)  -
                                             ema(    alt_src, fastLength4 * length * 0.5 + 2.0)) -
                                             ema(ema(alt_src, fastLength2 * length * 0.5 + 2.0)  -
                                             ema(    alt_src, fastLength4 * length * 0.5 + 2.0), 9), normLength) 
        "MACD Percent Rank"   => ta.percentrank(ta.ema(alt_src, fastLength5) - ta.ema(alt_src, 1 + fastLength6 * length) , normLength)
        "Slow MACD"           => normalize(ta.ema(alt_src, fastMode(15, fastMode)) - ta.ema(alt_src, fastMode(20, fastMode) + length * fastLength5), normLength)
        "RSI"                 => rsi(preFilter(alt_src, preFilter), length)
        "True Range Algo"     => tr_algo(alt_src, fastLength2 * length, 3)
        "Normalized Volume"   => normalize(preFilter(     ta.wma(volume, 1 + (fastLength3 * (length - 1))),           preFilter), normLength)
        "Volume Delta *"      => normalize(preFilter(diff(ta.wma(volume, 1 + (fastLength3 * (length - 1))), madLength), preFilter), normLength)
        "MA Delta *"          => normalize(diff(ta.ema(alt_src, fastLength6 * length), madLength), normLength)
        "True Range"          => normalize(ta.rma(high - low,   fastLength2 * length),           normLength)
        "Rate of Change"      => normalize(ta.roc(alt_src,      fastLength3 * length),           normLength)
        "OBV"                 => normalize(preFilter(ta.obv - ta.wma(ta.obv, 2 + fastLength2 * length), preFilter), normLength)
        "MFI"                 => normalize(ta.mfi(preFilter(hlc3, preFilter),    fastLength3 + length), normLength)
        "BB Width"            => normalize((( ta.sma(alt_src, tempLength5) + ta.stdev(alt_src, tempLength5))  -
                                             (ta.sma(alt_src, tempLength5) - ta.stdev(alt_src, tempLength5))) /
                                              ta.sma(alt_src, tempLength5), normLength)
        "BB Width Percentile" => ta.percentrank(((ta.sma(alt_src, tempLength5) + ta.stdev(alt_src, tempLength5))  -
                                                 (ta.sma(alt_src, tempLength5) - ta.stdev(alt_src, tempLength5))) /
                                                  ta.sma(alt_src, tempLength5), normLength)
        "Stochastic"          => ta.sma(ta.stoch(alt_src, alt_high, alt_low, fastLength2 * length), 3)
        =>                       trend_angle(close, fastLength2 * length, preFilter) // "Trend Angle"



float algo02 = indicator_picker( 2)
float algo03 = indicator_picker( 3)
float algo04 = indicator_picker( 4)
float algo05 = indicator_picker( 5)
float algo06 = indicator_picker( 6)
float algo07 = indicator_picker( 7)
float algo08 = indicator_picker( 8)
float algo09 = indicator_picker( 9)

float algo10 = indicator_picker(10)
float algo11 = indicator_picker(11)
float algo12 = indicator_picker(12)
float algo13 = indicator_picker(13)
float algo14 = indicator_picker(14)
float algo15 = indicator_picker(15)
float algo16 = indicator_picker(16)
float algo17 = indicator_picker(17)
float algo18 = indicator_picker(18)
float algo19 = indicator_picker(19)

float algo20 = indicator_picker(20)
float algo21 = indicator_picker(21)
float algo22 = indicator_picker(22)
float algo23 = indicator_picker(23)
float algo24 = indicator_picker(24)
float algo25 = indicator_picker(25)
float algo26 = indicator_picker(26)
float algo27 = indicator_picker(27)
float algo28 = indicator_picker(28)
float algo29 = indicator_picker(29)

var array customColors = array.from(color000, color025, color050, color075, color100)

plot( 2,  "2", heatmapColor(algo02, scheme, invert, customColors), style=plot.style_area, histbase= 1, editable=false, display=display.pane)
plot( 3,  "3", heatmapColor(algo03, scheme, invert, customColors), style=plot.style_area, histbase= 2, editable=false, display=display.pane)
plot( 4,  "4", heatmapColor(algo04, scheme, invert, customColors), style=plot.style_area, histbase= 3, editable=false, display=display.pane)
plot( 5,  "5", heatmapColor(algo05, scheme, invert, customColors), style=plot.style_area, histbase= 4, editable=false, display=display.pane)
plot( 6,  "6", heatmapColor(algo06, scheme, invert, customColors), style=plot.style_area, histbase= 5, editable=false, display=display.pane)
plot( 7,  "7", heatmapColor(algo07, scheme, invert, customColors), style=plot.style_area, histbase= 6, editable=false, display=display.pane)
plot( 8,  "8", heatmapColor(algo08, scheme, invert, customColors), style=plot.style_area, histbase= 7, editable=false, display=display.pane)
plot( 9,  "9", heatmapColor(algo09, scheme, invert, customColors), style=plot.style_area, histbase= 8, editable=false, display=display.pane)
plot(10, "10", heatmapColor(algo10, scheme, invert, customColors), style=plot.style_area, histbase= 9, editable=false, display=display.pane)

plot(11, "11", heatmapColor(algo11, scheme, invert, customColors), style=plot.style_area, histbase=10, editable=false, display=display.pane)
plot(12, "12", heatmapColor(algo12, scheme, invert, customColors), style=plot.style_area, histbase=11, editable=false, display=display.pane)
plot(13, "13", heatmapColor(algo13, scheme, invert, customColors), style=plot.style_area, histbase=12, editable=false, display=display.pane)
plot(14, "14", heatmapColor(algo14, scheme, invert, customColors), style=plot.style_area, histbase=13, editable=false, display=display.pane)
plot(15, "15", heatmapColor(algo15, scheme, invert, customColors), style=plot.style_area, histbase=14, editable=false, display=display.pane)
plot(16, "16", heatmapColor(algo16, scheme, invert, customColors), style=plot.style_area, histbase=15, editable=false, display=display.pane)
plot(17, "17", heatmapColor(algo17, scheme, invert, customColors), style=plot.style_area, histbase=16, editable=false, display=display.pane)
plot(18, "18", heatmapColor(algo18, scheme, invert, customColors), style=plot.style_area, histbase=17, editable=false, display=display.pane)
plot(19, "19", heatmapColor(algo19, scheme, invert, customColors), style=plot.style_area, histbase=18, editable=false, display=display.pane)
plot(20, "20", heatmapColor(algo20, scheme, invert, customColors), style=plot.style_area, histbase=19, editable=false, display=display.pane)

plot(21, "21", heatmapColor(algo21, scheme, invert, customColors), style=plot.style_area, histbase=20, editable=false, display=display.pane)
plot(22, "22", heatmapColor(algo22, scheme, invert, customColors), style=plot.style_area, histbase=21, editable=false, display=display.pane)
plot(23, "23", heatmapColor(algo23, scheme, invert, customColors), style=plot.style_area, histbase=22, editable=false, display=display.pane)
plot(24, "24", heatmapColor(algo24, scheme, invert, customColors), style=plot.style_area, histbase=23, editable=false, display=display.pane)
plot(25, "25", heatmapColor(algo25, scheme, invert, customColors), style=plot.style_area, histbase=24, editable=false, display=display.pane)
plot(26, "26", heatmapColor(algo26, scheme, invert, customColors), style=plot.style_area, histbase=25, editable=false, display=display.pane)
plot(27, "27", heatmapColor(algo27, scheme, invert, customColors), style=plot.style_area, histbase=26, editable=false, display=display.pane)
plot(28, "28", heatmapColor(algo28, scheme, invert, customColors), style=plot.style_area, histbase=27, editable=false, display=display.pane)
plot(29, "29", heatmapColor(algo29, scheme, invert, customColors), style=plot.style_area, histbase=28, editable=false, display=display.pane)

array algoArray = array.from( algo02, algo03, algo04, algo05, algo06, algo07, algo08, algo09, algo10, algo11, algo12, algo13, algo14, algo15,
                                     algo16, algo17, algo18, algo19, algo20, algo21, algo22, algo23, algo24, algo25, algo26, algo27, algo28, algo29)
[upperBullishStreak, lowerBullishStreak] = algoArray.find_streak(upperThreshold,  true, maxDuration, emphasize=="Both" or emphasize=="Bullish")
[upperBearishStreak, lowerBearishStreak] = algoArray.find_streak(lowerThreshold, false, minDuration, emphasize=="Both" or emphasize=="Bearish")

plot(upperBullishStreak, "Bullish Region Top",    bearishColor, lineThickness-1, plot.style_circles, display=display.pane)
plot(lowerBullishStreak, "Bullish Region Bottom", bearishColor, lineThickness,   plot.style_linebr,  display=display.pane)
plot(upperBearishStreak, "Bearish Region Top",    bullishColor, lineThickness,   plot.style_linebr,  display=display.pane)
plot(lowerBearishStreak, "Bearish Region Bottom", bullishColor, lineThickness-1, plot.style_circles, display=display.pane)

if not na(lowerBullishStreak) and na(lowerBullishStreak[1])
    if bullOnset=="Top"    or bullOnset=="Both"
        label.new(bar_index, upperBullishStreak, "⬤", style=label.style_text_outline, textcolor=bearishColor, color=#00000000, size=size.normal)
    if bullOnset=="Bottom" or bullOnset=="Both"
        label.new(bar_index, lowerBullishStreak, "⬤", style=label.style_text_outline, textcolor=bearishColor, color=#00000000, size=size.normal)
if not na(lowerBearishStreak) and na(lowerBearishStreak[1])
    if bearOnset=="Top"    or bearOnset=="Both"
        label.new(bar_index, upperBearishStreak, "⬤", style=label.style_text_outline, textcolor=bullishColor, color=#00000000, size=size.normal)
    if bearOnset=="Bottom" or bearOnset=="Both"
        label.new(bar_index, lowerBearishStreak, "⬤", style=label.style_text_outline, textcolor=bullishColor, color=#00000000, size=size.normal)

Get access to our Exclusive
premium indicators