labelpine-scriptlinepine-script-v5overlap

I want to place multiple(3) labels for line.new in pinescript without them overlapping when zoomed out


Not sure if this is possible, but sure would be nice. I have a currently published script that I'm improving to a better version. The script locates user-defined moving averages of multiple kinds, of user-defined lookbacks, of user-defined resolutions and plots lines with labels of those moving averages. For example, say the user trades on a 5 minute chart and wants to know where a 20 period SMA is on the 15 minute, hourly, 4h, and daily timeframes. This script will plot a line of some length extending from the current bar, with the lookback, MA type, and resolution labeled. The issue I have is that because these are all different variables that the user selects, I can't just make one label for each line (I don't think). I have to use three labels (lookback, MA type, resolution). I have a decent way of determining distances between each label, but if possible I would like to improve on this, for when the user zooms out the labels scrunch on top of each other.

One thing I will do is add a global option for text size so that the user can make it smaller (won't be helpful to those with poor eyesight, but it's something). However, wondering if there is anything else I could do. I really just want the lookback and resolution labels to have a fixed distance from the matype label. I think it's because I'm using xloc.bar_time and not bar_index, but I'm not sure, and I'm not sure how to get around that.

To be completely clear, I am not concerned with a line-label set overlapping other line-label sets (there's really no way around this if the MAs are close together). What I would like to improve if possible is an individual moving average-line's trio of labels being squished.

Thank you!

Zoomed in

Zoomed out

Code below. The matype label's x coordinate is the same as the line's x2. The lookback and resolution labels' coordinates straddle either side of the matype label.



bar_index_duration = time - time[1]


////INPUTS FOR FIRST MOVING AVERAGE
//DISPLAY LINES AND LABELS?
onoff = input(defval=true, title='Display Line and Label?', group='Moving Average #1')


//LOOKBACK
i_ma1len = input.int(defval=9, title='Lookback', group='Moving Average #1')


//TYPE
i_ma1 = input.string(defval='EMA', options=['EMA', 'HMA', 'SMA', 'SMMA', 'VWMA', 'WMA'], title='Moving Average Type', group='Moving Average #1')
ma1 = i_ma1 == 'EMA' ? ta.ema(close, i_ma1len) : i_ma1 == 'HMA' ? ta.hma(close, i_ma1len) : i_ma1 == 'SMA' ? ta.sma(close, i_ma1len) : i_ma1 == 'SMMA' ? ta.rma(close, i_ma1len) : i_ma1 == 'VWMA' ? ta.vwma(close, i_ma1len) : i_ma1 == 'WMA' ? ta.wma(close, i_ma1len) : na

//RESOLUTION
i_ma1res = input.string(defval='1h', options=['5m', '12m', '15m', '30m', '1h', '4h', '8h', '12h', 'D', '3D', '4D', 'W', 'M'], title='Resolution', group='Moving Average #1')
ma1res = i_ma1res == '5m' ? '5' : i_ma1res == '12m' ? '12' : i_ma1res == '15m' ? '15' : i_ma1res == '30m' ? '30' : i_ma1res == '1h' ? '60' : i_ma1res == '4h' ? '240' : i_ma1res == '8h' ? '480' : i_ma1res == '12h' ? '720' : i_ma1res == 'D' ? 'D' : i_ma1res == '3D' ? '3D' : i_ma1res == '4D' ? '4D' : i_ma1res == 'W' ? 'W' : i_ma1res == 'M' ? 'M' : na 

//
line_color = input(color.purple, title='Line Color', group='Moving Average #1')
line_style = input.string(defval='Solid', options=['Dashed', 'Dotted', 'Solid'], title='Line Style', group='Moving Average #1')
line_width = input.int(defval=1, maxval=4, title="Line Width", group='Moving Average #1')
lkbk_term = input.int(defval=3, title='Moving Average Lookback Label Distance', group='Moving Average #1')
line_term = input(defval=25, title='Line Length and Moving Average Type Label', group='Moving Average #1')
res_term = input(defval=3, title='Resolution Label Distance', group='Moving Average #1')


//
_ma1_security = request.security(syminfo.tickerid, ma1res, ma1)


//LINES AND LABELS
x1 = timenow
y1 = _ma1_security
x2 = timenow + (line_term * bar_index_duration)
y2 = _ma1_security
xloc = xloc.bar_time
color = line_color
style = line_style == 'Dotted' ? line.style_dotted : line_style == 'Dashed' ? line.style_dashed : line_style == 'Solid' ? line.style_solid : na

ma1_line = onoff ? line.new(x1, y1, x2, y2, xloc, color=color, style=style, width=line_width) : na
line.delete(ma1_line[1])

ma1_lkbklabel = onoff ? label.new(x2 - (lkbk_term * bar_index_duration), y1, xloc=xloc, style=label.style_none, text=str.tostring(i_ma1len), textcolor=line_color) : na
label.delete(ma1_lkbklabel[1])

ma1_typelabel = onoff ? label.new(x2, y1, xloc=xloc, style=label.style_none, text=i_ma1, textcolor=line_color) : na
label.delete(ma1_typelabel[1])

ma1_reslabel = onoff ? label.new(x2 + (res_term * bar_index_duration), y1, xloc=xloc, style=label.style_none, text=i_ma1res, textcolor=line_color) : na
label.delete(ma1_reslabel[1])```

Solution

  • Instaed using 3 labels for each Moving Average why dont use only one?

    text=str.tostring(i_ma1len) + " | " + str.tostring(i_ma1) + " | " + str.tostring(i_ma1res)
    

    If you want to change the position consider to use label.style_label_left and set the label trasparence to 100

    EDIT: I'll try to give you a more complete answer. since you have already created a label.new, on that price level. adding more labels on the same value causes overlapping. by reducing your 3 labels to just one, We wouldn't have this problem because the text is all written on the same line.

    I added a new optional plot method in case you would like to show it on the right side of the line, so as to have the value on the same level.

    The workaround you used by adding a space in the input parameters is bad practice. For this I created a new variable where you can format the string as you like.

    //@version=5
    indicator("My script", overlay = true)
    
    bar_index_duration = time - time[1]
    
    
    ////INPUTS FOR FIRST MOVING AVERAGE
    //DISPLAY LINES AND LABELS?
    onoff = input(defval=true, title='Display Line and Label?', group='Moving Average #1')
    
    
    //LOOKBACK
    i_ma1len = input.int(defval=9, title='Lookback', group='Moving Average #1')
    
    
    //TYPE
    i_ma1 = input.string(defval='EMA', options=['EMA', 'HMA', 'SMA', 'SMMA', 'VWMA', 'WMA'], title='Moving Average Type', group='Moving Average #1')
    ma1 = i_ma1 == 'EMA' ? ta.ema(close, i_ma1len) : i_ma1 == 'HMA' ? ta.hma(close, i_ma1len) : i_ma1 == 'SMA' ? ta.sma(close, i_ma1len) : i_ma1 == 'SMMA' ? ta.rma(close, i_ma1len) : i_ma1 == 'VWMA' ? ta.vwma(close, i_ma1len) : i_ma1 == 'WMA' ? ta.wma(close, i_ma1len) : na
    
    //RESOLUTION
    i_ma1res = input.string(defval='1h', options=['5m', '12m', '15m', '30m', '1h', '4h', '8h', '12h', 'D', '3D', '4D', 'W', 'M'], title='Resolution', group='Moving Average #1')
    ma1res = i_ma1res == '5m' ? '5' : i_ma1res == '12m' ? '12' : i_ma1res == '15m' ? '15' : i_ma1res == '30m' ? '30' : i_ma1res == '1h' ? '60' : i_ma1res == '4h' ? '240' : i_ma1res == '8h' ? '480' : i_ma1res == '12h' ? '720' : i_ma1res == 'D' ? 'D' : i_ma1res == '3D' ? '3D' : i_ma1res == '4D' ? '4D' : i_ma1res == 'W' ? 'W' : i_ma1res == 'M' ? 'M' : na 
    
    //
    line_color = input(color.purple, title='Line Color', group='Moving Average #1')
    line_style = input.string(defval='Solid', options=['Dashed', 'Dotted', 'Solid'], title='Line Style', group='Moving Average #1')
    line_width = input.int(defval=1, maxval=4, title="Line Width", group='Moving Average #1')
    lkbk_term = input.int(defval=3, title='Moving Average Lookback Label Distance', group='Moving Average #1')
    line_term = input(defval=25, title='Line Length and Moving Average Type Label', group='Moving Average #1')
    res_term = input(defval=3, title='Resolution Label Distance', group='Moving Average #1')
    
    
    //
    _ma1_security = request.security(syminfo.tickerid, ma1res, ma1)
    
    
    //LINES AND LABELS
    x1 = timenow
    y1 = _ma1_security
    x2 = timenow + (line_term * bar_index_duration)
    y2 = _ma1_security
    xloc = xloc.bar_time
    color = line_color
    style = line_style == 'Dotted' ? line.style_dotted : line_style == 'Dashed' ? line.style_dashed : line_style == 'Solid' ? line.style_solid : na
    
    ma1_line = onoff ? line.new(x1, y1, x2, y2, xloc, color=color, style=style, width=line_width) : na
    line.delete(ma1_line[1])
    
    //             EDIT
    // ----------------- 
    
    // You can create a new variable and format as you prefer
    string lbl_1_str = "          " + str.tostring(i_ma1len) + "      " + i_ma1 + "        " + i_ma1res
    
    // Then paas it the new variable as parameter to "text". 
    ma1_l_label = onoff ? label.new(x2 - (lkbk_term * bar_index_duration), y1, xloc=xloc, style=label.style_none, text=lbl_1_str, textcolor=line_color) : na
    
    // OPTIONAL: you can plot on the right side of the line in this way you'll have the label on the same leve of the line i added a label with transparence to 100
    ///ma1_l_label = onoff ? label.new(x2 - (lkbk_term * bar_index_duration), y1, xloc=xloc, style=label.style_label_left, text=lbl_1_str, textcolor=line_color, color=color.new(#000000,100)) : na
    
    label.delete(ma1_l_label[1])
    
    // ----------------- 
    

    PS.You won't get an error if you use str.tostring() with a string. It's just a slightly useless but harmless practice. Try it yourself.

    Result