Extension %
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © valpatrad

//@version=5
indicator('Extension', '', true, max_boxes_count=500, max_labels_count=500)



////////////////////////////
//                        //
//          Menu          //
//                        //
////////////////////////////

ext = input.string('Gap', 'Extension', ['Candles', 'Gap'])
mode = input.string('Percentage', 'Mode', ['Percentage', 'Price', 'Ticks'])
th = input.float(2.5, 'Threshold       +/-', 0, step=0.01)
dir = input.string('All', 'Direction', ['All', 'Bearish', 'Bullish'])
i_day = input.bool(false, 'Intraday Range', inline='Intraday')
rng = input.session('0930-1000', '', inline='Intraday', tooltip='Time range to consider on intraday charts.')
earns_only = input.bool(false, 'Earnings Only', tooltip='This will work on daily charts for stocks only.')

	//Gap
g1 = input.string('High/Low', '', ['Open/Close', 'High/Low'], inline='Gap', group='Gap')
g2 = input.string('High/Low', '—', ['Open/Close', 'High/Low'], inline='Gap', group='Gap')

	//Candles
candles = input.int(2, 'Candles', 1, group='Candles')
input_p1 = input.string('Open', '', ['Open', 'High', 'Low', 'Close'], inline='Price', group='Candles')
p1 = switch input_p1
	'Open'       => open
	'High'       => high
	'Low'        => low
	'Close'      => close
input_p2 = input.string('Close', '—', ['Open', 'High', 'Low', 'Close'], inline='Price', group='Candles')
p2 = switch input_p2
	'Open'       => open
	'High'       => high
	'Low'        => low
	'Close'      => close
box_limit = input.int(3, 'Box limit', 0, group='Candles', tooltip='Maximum number of candles to be framed by boxes. Set to zero to hide them.')

	//Label Style
lcol_bull = input.color(#FFFFFF, 'Bullish Colors           BKGD', group='Label', inline='Bullish')
tcol_bull = input.color(#000000, 'Text', group='Label', inline='Bullish')
lcol_bear = input.color(#FFFFFF, 'Bearish Colors          BKGD', group='Label', inline='Bearish')
tcol_bear = input.color(#000000, 'Text', group='Label', inline='Bearish')
in_font = input.string('Default', 'Font', ['Default', 'Monospace'], group='Label')
font = switch in_font
	'Default' => font.family_default
	'Monospace' => font.family_monospace
input_size = input.string('Normal', 'Size', ['Auto', 'Tiny', 'Small', 'Normal', 'Large', 'Huge'], group='Label')
size = switch input_size
    'Auto'   => size.auto
    'Tiny'   => size.tiny
    'Small'  => size.small
    'Normal' => size.normal
    'Large'  => size.large
    'Huge'   => size.huge

	//Box Style
box_bg = input.color(color.new(#FFFFFF, 95), 'Background', group='Box')
box_col = input.color(color.new(#FFFFFF, 60), 'Border', group='Box')
box_wd = input.int(1, 'Border width', 0, 4, group='Box')
box_stl_input = input.string('Solid', 'Border style', ['Solid', 'Dotted', 'Dashed'], group='Box')
box_stl = switch box_stl_input
	'Solid'  => line.style_solid
	'Dotted' => line.style_dotted
	'Dashed' => line.style_dashed



/////////////////////////////
//                         //
//        Variables        //
//                         //
/////////////////////////////

o = open
h = high
l = low
c = close

earns = request.earnings(syminfo.tickerid, earnings.actual, barmerge.gaps_on, ignore_invalid_symbol=true)

_calc(a, b) =>
	if mode == 'Percentage'
		(a-b)/b*100
	else if mode == 'Price'
		(a-b)
	else
		syminfo.type == 'forex' ? ((a-b)/syminfo.mintick)/10 : (a-b)/syminfo.mintick

_lab(lbl) =>
	if not earns_only or (earns_only and earns and timeframe.isdaily)
		if (not i_day and timeframe.isintraday or timeframe.isdwm) or (i_day and timeframe.isintraday and time(timeframe.period, rng))
			label.new(bar_index, lbl >= th and dir != 'Bearish' ? h : lbl <= -th and dir != 'Bullish' ? l : na, mode == 'Percentage' ? str.tostring(lbl, format.percent) : (mode == 'Price' and syminfo.type == 'forex') or (mode == 'Price' and syminfo.type == 'bond') ? str.tostring(lbl) : mode == 'Price' and syminfo.type == 'economic' ? str.tostring(lbl, format.volume) : mode == 'Price' ? str.tostring(lbl, '#.##') : str.tostring(lbl, syminfo.type == 'forex' ? '#.#' : '#'), color=lbl >= th ? lcol_bull : lcol_bear, style=lbl >= th ? label.style_label_down : label.style_label_up, textcolor=lbl >= th ? tcol_bull : tcol_bear, size=size, text_font_family=font)



////////////////////////////
//                        //
//          Plot          //
//                        //
////////////////////////////

	//Price Extension
if ext == 'Candles'
	cdl = _calc(p2, p1[candles-1])
	_lab(cdl)
	if candles <= box_limit
		if cdl >= th and dir != 'Bearish' or cdl <= -th and dir != 'Bullish'
			if not earns_only or (earns_only and earns and timeframe.isdaily)
				if (not i_day and timeframe.isintraday or timeframe.isdwm) or (i_day and timeframe.isintraday and time(timeframe.period, rng))
					box.new(bar_index[candles-1], p1[candles-1], bar_index, p2, box_col, box_wd, box_stl, bgcolor=box_bg)

	//Gap Extension
if ext == 'Gap'
	gn = c >= o
	rd = c < o
	gn_1 = c[1] >= o[1]
	rd_1 = c[1] < o[1]
	gg_up = o > c[1]
	gr_dn = o < o[1]
	gr_up = c > c[1]
	gg_dn = c < o[1]
	if g1 == 'High/Low' and g2 == 'High/Low'
		h_l = _calc(l, h[1])
		l_h = _calc(h, l[1])
		if l > h[1]  and h_l >= th
			_lab(h_l)
		if h < l[1] and l_h <= -th
			_lab(l_h)
	if g1 == 'Open/Close' and g2 == 'Open/Close'
		c_o = _calc(o, c[1])
		o_o = _calc(o, o[1])
		c_c = _calc(c, c[1])
		o_c = _calc(c, o[1])
		if gn_1
			if rd and gr_dn and o_o <= -th
				_lab(o_o)
			if gn and gg_up and c_o >= th
				_lab(c_o)
			if gn and gg_dn and o_c <= -th
				_lab(o_c)
			if rd and gr_up and c_c >= th
				_lab(c_c)
		if rd_1
			if gn and o > o[1] and o_o >= th
				_lab(o_o)
			if rd and o < c[1] and c_o <= -th
				_lab(c_o)
			if rd and c > o[1] and o_c >= th
				_lab(o_c)
			if gn and c < c[1] and c_c <= -th
				_lab(c_c)
	if g1 == 'High/Low' and g2 == 'Open/Close'
		h_o = _calc(o, h[1])
		l_o = _calc(o, l[1])
		h_c = _calc(c, h[1])
		l_c = _calc(c, l[1])
		if gn and gg_up and h_o >= th
			_lab(h_o)
		if rd and gr_dn and l_o <= -th
			_lab(l_o)
		if rd and gr_up and h_c >= th
			_lab(h_c)
		if gn and gg_dn and l_c <= -th
			_lab(l_c)
	if g1 == 'Open/Close' and g2 == 'High/Low'
		c_l = _calc(l, c[1])
		o_h = _calc(h, o[1])
		o_l = _calc(l, o[1])
		c_h = _calc(h, c[1])
		if gn_1
			if l > c[1] and c_l >= th
				_lab(c_l)
			if h < o[1] and o_h <= -th
				_lab(o_h)
		if rd_1
			if l > o[1] and o_l >= th
				_lab(o_l)
			if h < c[1] and c_h <= -th
				_lab(c_h)