chooka
Pine Script Master
Pine Script Master
Posts: 18
Joined: February 7th, 2022

Re: Display of Labels & Create Alerts from higher timeframe closes

Hey there, thanks!

I've setup tests overnight and will review the results in the morning!

chooka
Pine Script Master
Pine Script Master
Posts: 18
Joined: February 7th, 2022

Re: Display of Labels & Create Alerts from higher timeframe closes

Hi, it's been running now for 6hrs and not one alert through yet unfortunately, even though I can see divergence has played out on a few tickers.
Note I am using the exact code (copy paste)

processingclouds
Pine Script Master
Pine Script Master
Posts: 115
Joined: January 30th, 2022

Re: Display of Labels & Create Alerts from higher timeframe closes

Oh sorry to hear that. Let me sit and think on this again from start. Maybe miscalculating something here.

processingclouds
Pine Script Master
Pine Script Master
Posts: 115
Joined: January 30th, 2022

Re: Display of Labels & Create Alerts from higher timeframe closes

I have added more debug information and added some code in the divergence function itself, to return two additional values. One is bool value to tell if the current bar is realtime or not. Before it was sending the result of previous candle close if bar was realtime, now it returns current bar only. Also it returns the time that the candle is in.

We can remove tf1_isNewPeriod check and also the other time calculation check for now.
In the debug plots, it will plot new labels, when the signal starts.

Now according to the time printed, it is showing that it signals exactly before the minute close candle. So if displayed time is 3:15, it will trigger at 3:14.
This will show as plot line for the timeframe so for 15 min timeframe you will see thickline starting at 3:14 for the next 15 mins. This happens as the values do not update till next Higher time frame candle update. (this can be ignored as we can trigger the alert on the initial one)

So when you see the time printed as a label,
1. it shows at what time of higher time frame it was triggered,
2. it prints on the close of higher time frame

If its not clear what i am trying to show , than let me know. This debug can help us adjust the alert trigger properly now.

Code: Select all



//@version=5
indicator("RSI Bull_Div Alert MTF")


enable_tf1 = true
enable_tf2 = true
enable_tf3 = true
alert_tf1 = true
alert_tf2 = true
alert_tf3 = true

tf1_col = color.blue
tf2_col = color.yellow
tf3_col = color.green


timeframe1 = "15"
timeframe2 = "30"
timeframe3 = "60"
lookback = 90
lengthRSI = 14


// ---------------------------------------------------------------------------------------------- //
cutLastDigit(str) =>
    s = str + ';'
    r = str.replace_all(s, '1;', '')
    if r != s
        [r, 1]
    else
        r := str.replace_all(s, '2;', '')
        if r != s
            [r, 2]
        else
            r := str.replace_all(s, '3;', '')
            if r != s
                [r, 3]
            else
                r := str.replace_all(s, '4;', '')
                if r != s
                    [r, 4]
                else
                    r := str.replace_all(s, '5;', '')
                    if r != s
                        [r, 5]
                    else
                        r := str.replace_all(s, '6;', '')
                        if r != s
                            [r, 6]
                        else
                            r := str.replace_all(s, '7;', '')
                            if r != s
                                [r, 7]
                            else
                                r := str.replace_all(s, '8;', '')
                                if r != s
                                    [r, 8]
                                else
                                    r := str.replace_all(s, '9;', '')
                                    if r != s
                                        [r, 9]
                                    else
                                        r := str.replace_all(s, '0;', '')
                                        if r != s
                                            [r, 0]

strToNumInt(str) =>
    integer = 0  // integer part of the number
    s_new = str
    position = 0.0  // handled position of the number.
    sign = 1  // sign of the number. 1.0 is PLUS and -1.0 is MINUS.
    for i = 0 to 30 by 1
        [s, digit] = cutLastDigit(s_new)  // here we'll cut the digits by one from the string till the string is empty.
        integer += digit * int(math.pow(10, position))  // it's just a regular digit.
        position += 1
        if s == ''
            break
        s_new := s  // If we are here, then there are more digits in the string. Let's handle the next one!
        s_new
    sign * integer  // We've exited from the loop. Build the returning value.

calculateTimeDividedBy(res) =>
    timeDividiedBy = time(timeframe.period)
    if strToNumInt(res) < 1440
        timeDividiedBy := strToNumInt(res) * 60 * 1000
        timeDividiedBy
    for i = 1 to 50 by 1
        if res == str.tostring(i) + 'D' or res == 'D'
            timeDividiedBy := i * 1 * 86400000
            break
        else if res == str.tostring(i) + 'W' or res == 'W'
            timeDividiedBy := i * 7 * 86400000
            break
        else if res == str.tostring(i) + 'M' or res == 'M'
            timeDividiedBy := i * 30 * 86400000
            break
    timeDividiedBy



tf1_isNewPeriod = ta.change(time(timeframe1)) 
tf2_isNewPeriod = ta.change(time(timeframe2))
tf3_isNewPeriod = ta.change(time(timeframe3))

tf1_multiplier = calculateTimeDividedBy(timeframe1) / calculateTimeDividedBy(timeframe.period)
tf2_multiplier = calculateTimeDividedBy(timeframe2) / calculateTimeDividedBy(timeframe.period)
tf3_multiplier = calculateTimeDividedBy(timeframe3) / calculateTimeDividedBy(timeframe.period)


//////////////////////////////////////////////////////////////

rsi = ta.rsi(close, lengthRSI)


len = 14  //input(14, minval=1, title="RSI Length")
ob = 70  //input(defval=70, title="Overbought", type=input.integer, minval=0, maxval=100)
os = 30  //input(defval=30, title="Oversold", type=input.integer, minval=0, maxval=100)


hline(50, title='Fifty-Line', color=color.gray, linestyle=hline.style_solid)
seventy = hline(ob, color=color.gray, linestyle=hline.style_dashed)
thirty = hline(os, color=color.gray, linestyle=hline.style_dashed)
fill(thirty, seventy, color.new(color.purple, 90))

pl_rsi = plot(rsi, title='RSI Oscillator', color=color.new(color.purple, 0), linewidth=1)





// Inputs
scalePercentage = input.int(100, "Vertical %", step = 10, maxval = 100)
lookBack = input.int(100, "Last Bars to Look", minval = 2, step = 20)

scaleFactor = 100 / scalePercentage
//plot(volume, color = color.orange, title="Volume", style=plot.style_columns, transp=0) 
hi_limit = ta.highest(rsi, lookBack)
lo_limit = ta.lowest(rsi, lookBack)
high_div =hi_limit * scaleFactor
low_div = lo_limit * scaleFactor
plot(high_div, "High", #00000000)
plot(low_div, "Low", #00000000)




/////////////////////////////////////
// Find Divergence 
///////////////////////////////////

find_divergence(price_close, oscillator, lookback, isReal) =>
    highest_bar = math.abs(ta.highestbars(oscillator, lookback))  // Finds bar with highest value in last X bars
    lowest_bar = math.abs(ta.lowestbars(oscillator, lookback))  // Finds bar with lowest value in last X bars

    // Defining variable
    max_price = float(na)
    max_osc = float(na)
    min_price = float(na)
    min_osc = float(na)
    pivoth = bool(na)
    pivotl = bool(na)
    divbear = bool(na)
    divbull = bool(na)

    // If bar with lowest / highest is current bar, use it's value
    max_price := highest_bar == 0 ? price_close : na(max_price[1]) ? price_close : max_price[1]
    max_osc := highest_bar == 0 ? oscillator : na(max_osc[1]) ? oscillator : max_osc[1]
    min_price := lowest_bar == 0 ? price_close : na(min_price[1]) ? price_close : min_price[1]
    min_osc := lowest_bar == 0 ? oscillator : na(min_osc[1]) ? oscillator : min_osc[1]


    // Compare high of current bar being examined with previous bar's high
    // If curr bar high is higher than the max bar high in the lookback window range
    if price_close > max_price  // we have a new high
        max_price := price_close  // change variable "max" to use current bar's high value
        max_price
    if oscillator > max_osc  // we have a new high
        max_osc := oscillator  // change variable "max_rsi" to use current bar's RSI value
        max_osc
    if price_close < min_price  // we have a new low
        min_price := price_close  // change variable "min" to use current bar's low value
        min_price
    if oscillator < min_osc  // we have a new low
        min_osc := oscillator  // change variable "min_rsi" to use current bar's RSI value
        min_osc


    // Finds pivot point with at least 2 right candles with lower value
    pivoth := max_osc == max_osc[2] and max_osc[2] != max_osc[3] ? true : na
    pivotl := min_osc == min_osc[2] and min_osc[2] != min_osc[3] ? true : na

    // Detects divergences between price and indicator with 1 candle delay so it filters out repeating divergences
    if max_price[1] > max_price[2] and oscillator[1] < max_osc and oscillator <= oscillator[1]
        divbear := true
        divbear
    if min_price[1] < min_price[2] and oscillator[1] > min_osc and oscillator >= oscillator[1]
        divbull := true
        divbull
    [divbull, divbear, time, isReal]


[tf1_divbull, tf1_divbear, tf1_time, tf1_real] = request.security(syminfo.ticker, timeframe1, find_divergence(close, rsi, lookback, barstate.isrealtime))
[tf2_divbull, tf2_divbear, tf2_time, tf2_real] = request.security(syminfo.ticker, timeframe2, find_divergence(close, rsi, lookback, barstate.isrealtime))
[tf3_divbull, tf3_divbear, tf3_time, tf3_real] = request.security(syminfo.ticker, timeframe3, find_divergence(close, rsi, lookback, barstate.isrealtime))


////////////////////////////
// Draw Labels 
/////////////////////////

if enable_tf1 and not tf1_real// and tf1_isNewPeriod
    tf1_divergence = (tf1_divbear ? label.new(bar_index - 1 * tf1_multiplier, high_div, timeframe1, color=barstate.islast and not barstate.isconfirmed ? color.new(tf1_col, 80) : tf1_col, textcolor=color.black, style=label.style_label_down, yloc=yloc.price, size=size.normal) : tf1_divbull ? label.new(bar_index - 1 * tf1_multiplier, low_div, timeframe1, color=barstate.islast and not barstate.isconfirmed ? color.new(tf1_col, 80) : tf1_col, textcolor=color.black, style=label.style_label_up, yloc=yloc.price, size=size.normal) : na)

if enable_tf2 and not tf2_real// and tf2_isNewPeriod
    tf2_divergence = (tf2_divbear ? label.new(bar_index - 1 * tf2_multiplier, high_div+10, timeframe2, color=barstate.islast and not barstate.isconfirmed ? color.new(tf2_col, 80) : tf2_col, textcolor=color.black, style=label.style_label_down, yloc=yloc.price, size=size.normal) : tf2_divbull ? label.new(bar_index - 1 * tf2_multiplier, low_div-10, timeframe2, color=barstate.islast and not barstate.isconfirmed ? color.new(tf2_col, 80) : tf2_col, textcolor=color.black, style=label.style_label_up, yloc=yloc.price, size=size.normal) : na)

if enable_tf3 and not tf3_real //and tf3_isNewPeriod
    tf3_divergence = (tf3_divbear ? label.new(bar_index - 1 * tf3_multiplier, high_div+20, timeframe3, color=barstate.islast and not barstate.isconfirmed ? color.new(tf3_col, 80) : tf3_col, textcolor=color.black, style=label.style_label_down, yloc=yloc.price, size=size.normal) : tf3_divbull ? label.new(bar_index - 1 * tf3_multiplier, low_div-20, timeframe3, color=barstate.islast and not barstate.isconfirmed ? color.new(tf3_col, 80) : tf3_col, textcolor=color.black, style=label.style_label_up, yloc=yloc.price, size=size.normal) : na)


//////////////////////////
// Create Alerts
///////////////////////

if alert_tf1 and not tf1_real// and tf1_isNewPeriod and (time >= time_close(timeframe1)-(time_close-time))
    if  tf1_divbull
        alert(syminfo.ticker + ' Bullish Divergence TESTING - Price: ' + str.tostring(close) + ' - Timeframe: ' + timeframe1, alert.freq_once_per_bar_close)
    if tf1_divbear
        alert(syminfo.ticker + ' Bearish Divergence TESTING - Price: ' + str.tostring(close) + ' - Timeframe: ' + timeframe1, alert.freq_once_per_bar_close)

if alert_tf2 and not tf2_real// and tf2_isNewPeriod  and (time >= time_close(timeframe2)-(time_close-time))
    if  tf2_divbull
        alert(syminfo.ticker + ' Bullish Divergence TESTING - Price: ' + str.tostring(close) + ' - Timeframe: ' + timeframe2, alert.freq_once_per_bar_close)
    if tf2_divbear
        alert(syminfo.ticker + ' Bearish Divergence TESTING - Price: ' + str.tostring(close) + ' - Timeframe: ' + timeframe2, alert.freq_once_per_bar_close)
        
if alert_tf3 and not tf3_real //and tf3_isNewPeriod  and (time >= time_close(timeframe3)-(time_close-time))
    if  tf3_divbull
        alert(syminfo.ticker + ' Bullish Divergence TESTING - Price: ' + str.tostring(close) + ' - Timeframe: ' + timeframe3, alert.freq_once_per_bar_close)
    if tf3_divbear
        alert(syminfo.ticker + ' Bearish Divergence TESTING - Price: ' + str.tostring(close) + ' - Timeframe: ' + timeframe3, alert.freq_once_per_bar_close)

//yyy = calculateTimeDividedBy(time)
//var label xxx = na
//if ((not tf1_real and (tf1_divbull or tf1_divbear)) or (not tf1_real and (tf2_divbull or tf2_divbear)) or (not tf1_real and (tf3_divbull or tf3_divbear)) )
//    xxx := label.new(bar_index, high, str.tostring(tf1_time) + '-' + str.tostring(tf2_time) + '-' + str.tostring(tf3_time) + '\n' + str.tostring(tf1_divbull) + ':' + str.tostring(tf1_divbear)+ '\n' + str.tostring(tf2_divbull)+ ':' + str.tostring(tf2_divbear)+ '\n' + str.tostring(tf3_divbull)+ ':' + str.tostring(tf3_divbear)) 
//if barstate.islast
//    label.delete(xxx[1])
    //xxx := label.new(bar_index, high, str.tostring(tf1_isNewPeriod) + '-' + str.tostring(tf2_isNewPeriod) +'\n' + str.tostring(time(timeframe2)) + '\n' + str.tostring(time(timeframe3))+ '\n' + str.tostring(time))
//plot(tf1_time,display = display.none)


//DEBUGS
plot(tf1_divbear?-1:na,"tf1_divbear",color=color.red, linewidth=3)
plot(tf1_divbull?-1:na,"tf1_divbull",color=color.blue, linewidth=3)
plot(tf1_real?-1:na,"real")

plot(tf2_divbear?-3:na,"tf2_divbear",color=color.purple, linewidth=3)
plot(tf2_divbull?-3:na,"tf2_divbull",color=color.black, linewidth=3)
plot(tf2_real?-3:na,"real")

plot(tf3_divbear?-5:na,"tf3_divbear",color=color.olive, linewidth=3)
plot(tf3_divbull?-5:na,"tf3_divbull",color=color.orange, linewidth=3)
plot(tf3_real?-5:na,"real")

var label zzz = na
var label zzz2 = na
var label zzz3 = na

if (tf1_divbear and not tf1_divbear[1]) or (tf1_divbull and not tf1_divbull[1])
    zzz := label.new(bar_index, 80,  'TimeF 1: ' +str.format("@{0,time,HH:mm}",tf1_time)+' Bear-'+str.tostring(tf1_divbear)+' Bull-'+str.tostring(tf1_divbull),color=color.green, textcolor=color.yellow)
if (tf2_divbear and not tf2_divbear[1]) or (tf2_divbull and not tf2_divbull[1])
    zzz2 := label.new(bar_index, 90, 'TimeF 2: ' +  str.format("@{0,time,HH:mm}",tf2_time)+' Bear-'+str.tostring(tf2_divbear)+' Bull-'+str.tostring(tf3_divbull),color=color.green, textcolor=color.yellow)
if (tf3_divbear and not tf3_divbear[1]) or (tf3_divbull and not tf3_divbull[1])
    zzz3 := label.new(bar_index, 100, 'TimeF 3: ' + str.format("@{0,time,HH:mm}",tf3_time)+' Bear-'+str.tostring(tf3_divbear)+' Bull-'+str.tostring(tf3_divbull),color=color.green, textcolor=color.yellow)

//plot(tf2_time/1000,"tf2_time",display=display.none)
//plot(tf3_time/1000,"tf3_time",display=display.none)
//plot(tf1_multiplier,"multiplier", display = display.none)


chooka
Pine Script Master
Pine Script Master
Posts: 18
Joined: February 7th, 2022

Re: Display of Labels & Create Alerts from higher timeframe closes

Perfect, this is great.

I am now working through a series of alert combinations to try to trigger it on the correct HTF bar close.

I only want it to trigger when the current HTF candle is closed, not on each LTF confirmed bar within the HTF bar when the signal is true, which is easier said than done.

I'll keep testing/trialling :)

Return to “Pine Script Q&A”