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

//@version=5
import boitoki/Utilities/5 as util

// @description Currently position indicator
library("CurrentlyPositionIndicator", overlay=true)


f_chg_string (_value, _type) =>
	switch str.lower(_type)
		'price' 	=> str.tostring(_value, format.mintick)
		'pips'		=> str.tostring(util.toPips(_value), '0.0')
		=> str.tostring(_value, format.mintick)


f_history (_x, _price, _stoploss, _high, _low, _side, _entered, _colors, _show, _number_format = 'Price') =>
	var color_buy		= array.get(_colors, 0)
	var color_sell		= array.get(_colors, 1)
	var color_sl		= array.get(_colors, 2)
	var color_tp		= array.get(_colors, 3)
	var color_default 	= array.get(_colors, 4)
	var color_none 		= color.new(#000000, 100)
	var color_text		= color.black
	var color_text_tp	= color.from_gradient(85, 0, 100, color_tp, #000000)
	var color_text_sl	= color.from_gradient(75, 0, 100, color_sl, #ffffff)

	var text_font		= font.family_monospace
	var text_size 		= size.small
	var text_num_format	= _number_format

    if barstate.isconfirmed and (bar_index - _x > 1) and _show
        x1 				= _x
		x2 				= bar_index - 1
		x3				= math.floor(math.avg(x1, x2))

        y1 				= _price
		y2 				= _stoploss

		chg				= _side == 1 ? _high - y1 	: y1 - _low
		tp_y			= _side == 1 ? _high 		: _low
		sl_y			= _side == 1 ? _low  		: _high
		sl_s			= _side == 1 ? label.style_label_up : label.style_label_down
		color_entry 	= _side == 1 ? color_buy 	: color_sell

		label_text 		= f_chg_string(chg, text_num_format)
        line_style_sl 	= line.style_solid

        // StopLoss level に 到達していないなら color_gray
        if (_side == -1 and _stoploss > _high) or (_side == 1 and _stoploss < _low)
            line_style_sl := line.style_dotted

        if _entered and (chg != 0.0)
            line.new(x1, y1, x2, y1, width=1, color=color.new(color_entry, 0))
            line.new(x1, y2, x2, y2, width=1, color=color.new(color_sl, 30), style=line_style_sl) // StopLoss level
            box.new (x1, y1, x2, tp_y, bgcolor=color.new(color_tp, 90), border_color=color.new(color_tp, 70))
            box.new (x1, y1, x2, sl_y, bgcolor=color.new(color_default, 90), border_color=color.new(color_default, 70))

            label.new(x3, tp_y, label_text, color=color_tp, textcolor=color_text_tp, size=text_size, yloc=yloc.price, style=label.style_text_outline)

            true
        else 
            box.new (x1, _high, x2, _low, bgcolor=color.new(color_default, 85), border_color=color.new(color_default, 30), border_style=line.style_dotted)
            true


f_main (_x, _y, _lc, _high, _low, _side, _entered, _colors, _position_left = 4, _box_width = 3) => 
	var a_boxes = array.new<box>()
	var a_lines = array.new<line>()
	var a_labels = array.new<label>()

	// Defines
    // Colors
    var color_buy       = array.get(_colors, 0)
    var color_sell      = array.get(_colors, 1)
    var color_sl        = array.get(_colors, 2)
    var color_tp        = array.get(_colors, 3)
    var color_gray      = array.get(_colors, 4)
    var color_range     = color_gray
	var color_none		= color.new(#000000, 100)

	// Text
	var text_size 		= size.normal
	var text_font		= font.family_monospace
	var text_style		= label.style_label_left
	var BUY				= 'B'
	var SELL 			= 'S'
	var SL	   			= 'SL' // ×

	if barstate.islast
		util.clear_boxes(a_boxes)
		util.clear_lines(a_lines)
		util.clear_labels(a_labels)

		x               = bar_index + _position_left
		y               = _y

		padding_x       = 1
		box_width       = _box_width

		box_x1          = x + padding_x
		box_x2          = box_x1 + box_width

		label_x         = box_x2 + padding_x
		label_text      = str.tostring(_y , format.mintick) + ' ' + (_side == 1 ? BUY : SELL)
		label_text_lc   = str.tostring(_lc, format.mintick) + ' ' + SL

		color_entry     = _side == 1 ? color_buy : color_sell
		color_progress  = _side == 1 
		  ? close > y ? color_tp : color_sl
		  : close < y ? color_tp : color_sl

		array.push(a_boxes, box.new(box_x1, _high, box_x2, _low, border_color=color.new(color_range, 70), bgcolor=color.new(color_range, 80)) )

		// Stoploss line
		array.push(a_lines,  line.new (label_x, _lc, x, _lc, color=color_sl, width=2))
		array.push(a_lines,  line.new (_x, _lc, x - 2, _lc, color=color_sl, width=1))
		array.push(a_labels, label.new(label_x - 1, _lc, label_text_lc, textcolor=color_sl, style=text_style, color=color_none, text_font_family=text_font, size=text_size))

		// Entry line
		array.push(a_lines,  line.new (label_x, y, x, y, color=color_entry, width=2))
		array.push(a_lines,  line.new (_x, y, x - 2, y, color=color_entry, width=1))
		array.push(a_labels, label.new(label_x - 1, y, label_text, textcolor=color_entry, style=text_style, color=color_none, text_font_family=text_font, size=text_size))

		if _entered
			array.push(a_boxes, box.new (box_x1, y, box_x2, close, border_color=color.new(color_progress, 70), border_width=1, bgcolor=color.new(color_progress, 60)) )
			array.push(a_lines, line.new(box_x1, close, box_x2, close, color=color_progress, width=2) )


// @function Currently positions indicator
// @param _index entry index
// @param _price entry price
// @param _stoploss stoploss price
// @param _high range high
// @param _low range low
// @param _is_entered is entered
// @param _colors color array
// @param _position_left Left position(option)
// @param _box_width box's width(option)
// @returns
export run(int _index, float _price, float _stoploss, float _high, float _low, int _side, bool _is_entered, array<color> _colors, int _position_left = 4, int _box_width = 3) =>
	f_main(_index, _price, _stoploss, _high, _low, _side, _is_entered, _colors, _position_left, _box_width)


// @function History
// @param _index Entry index
// @param _price Entry price
// @param _stoploss StopLoss price
// @param _high Range high price
// @param _low Range low price
// @param _side Num. of entry side (buy is 1, sell is -1)
// @param _entered Is the entry entered
// @param _colors Color array
// @param _show show trigger
// @param _number_format Text number format 'Price' | 'Pips'
export history(int _index, float _price, float _stoploss, float _high, float _low, int _side, bool _entered, array<color> _colors, bool _show, string _number_format = 'Price') =>
	f_history(_index, _price, _stoploss, _high, _low, _side, _entered, _colors, _show, _number_format)

/////////////
// Example //
// Inputs
i_position_left = input.int(4, minval=0)
i_box_width = input.int(3, minval=1)
i_num_format = input.string('Price', options=['Price', 'Pips'])

// Calc
rsi = ta.sma(ta.rsi(close, 14), 7)
cond_buy = ta.crossover(rsi, 50)
cond_sell = ta.crossunder(rsi, 50)
highest_high = ta.highest(high, 5)
lowest_low = ta.lowest(low, 5)

var colors = array.from(color.blue, color.red, color.purple, color.orange, color.gray)
var entry_index = 0
var entry_price = 0.0
var entry_sl = 0.0
var entry_side = 0
var entry_range_high = 0.0
var entry_range_low = 0.0
var entry_entered = false

f_history(entry_index, entry_price, entry_sl, entry_range_high, entry_range_low, entry_side, entry_entered, colors, (cond_buy or cond_sell), i_num_format)

if cond_buy
	entry_index := bar_index
	entry_price := high
	entry_sl	:= lowest_low
	entry_side  := 1
	entry_range_high := high
	entry_range_low := low
else if cond_sell
	entry_index := bar_index
	entry_price := low
	entry_sl	:= highest_high
	entry_side  := -1
	entry_range_high := high
	entry_range_low := low
else
	entry_range_high := math.max(entry_range_high, high)
	entry_range_low := math.min(entry_range_low, low)

    if (entry_side == 1 and entry_price < high) or (entry_side == -1 and entry_price > low)
        entry_entered := true

run(entry_index, entry_price, entry_sl, entry_range_high, entry_range_low, entry_side, entry_entered, colors, i_position_left, i_box_width)