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

//@version=5

// @description TODO: add library description here
library("TradeTrackerv2")

export type Trade
	int id = na
	bool isOpen = false
    bool isClosed = false // only true when it's been closed by hitting TP/SL
	bool isBuy = false
	float priceOpen = na
	float priceTp = na
	float priceSl = na
    float rTP = na // take profit in R instead of absolute price; will be calculated
	float profit = na
	float r = na // the calculated R
	float resultR = na // profit in R
	line lineOpen = na
	line lineTp = na
	line lineSl = na
	label labelStats = na

export method MakeTradeLines(Trade t, int barIdx) =>
    // open line
    line.new(chart.point.from_index(barIdx, t.priceOpen), chart.point.from_index(barIdx + 1, t.priceOpen), xloc = xloc.bar_index, extend = extend.right, color = color.new(color.aqua, 50), style = line.style_dotted, width = 1)
    // sl line
    line.new(chart.point.from_index(barIdx, t.priceSl), chart.point.from_index(barIdx + 1, t.priceSl), xloc = xloc.bar_index, extend = extend.right, color = color.new(color.red, 50), style = line.style_dotted, width = 1)
    // tp line
    line.new(chart.point.from_index(barIdx, t.priceTp), chart.point.from_index(barIdx + 1, t.priceTp), xloc = xloc.bar_index, extend = extend.right, color = color.new(color.blue, 50), style = line.style_dotted, width = 1)

export method UpdateLabel(Trade t) =>
	t.labelStats.set_color(color.new(color.black, 0))
    t.labelStats.set_tooltip(str.tostring(t.profit, "#.##") + " (" + str.tostring(t.resultR, "#.#") + "R)")
	t.labelStats.set_text(t.profit > 0 ? "+" : "-")

export method MakeLabel(Trade t, int barIdx) =>
	if t.isBuy
		t.labelStats := label.new(chart.point.from_index(barIdx, t.priceSl), "", xloc = xloc.bar_index, yloc = yloc.belowbar, color = color.new(color.black, 0), textcolor = color.new(color.white, 0), style = label.style_label_up)
	else
		t.labelStats := label.new(chart.point.from_index(barIdx, t.priceSl), "", xloc = xloc.bar_index, yloc = yloc.abovebar, color = color.new(color.black, 0), textcolor = color.new(color.white, 0), style = label.style_label_down)

export method CloseTrade(Trade t) =>
    t.lineOpen.delete()
    t.lineSl.delete()
    t.lineTp.delete()
    t.isOpen := false
    t.isClosed := true

export method OpenTrade(Trade t) =>
    t.isOpen := true

export method OpenCloseTrade(Trade t, float _close) =>
	// see if our trades need to open or close
	if not t.isOpen
		if (_close < t.priceOpen and t.isBuy) or (_close > t.priceOpen and not t.isBuy)
			// open trade
			t.OpenTrade()
	else
		// it's open, see if it should close on SL or TP
		// SL or TP
		if ((_close < t.priceSl and t.isBuy) or (_close > t.priceSl and not t.isBuy)) or ((_close > t.priceTp and t.isBuy) or (_close < t.priceTp and not t.isBuy))
			t.CloseTrade()

// @function Calculates profits/losses for the Trade, given _close price
export method CalculateProfits(Trade t, float _close) =>
	if t.isBuy
		t.profit := math.max(_close, t.priceSl) - t.priceOpen
	else
		t.profit := t.priceOpen - math.min(_close, t.priceSl)
	t.resultR := t.profit / t.r

export method UpdateTrade(Trade t, float _close) =>
    if not t.isClosed
        t.OpenCloseTrade(_close)
        if t.isOpen
            t.CalculateProfits(_close)
            t.UpdateLabel()

export method SetInitialValues(Trade t, int barIdx) =>
	t.r := math.abs(t.priceOpen - t.priceSl)
	t.isBuy := t.priceSl < t.priceOpen
    if na(t.priceTp) and not na(t.rTP)
        if t.isBuy
            t.priceTp := t.priceOpen + (t.r * t.rTP)
        else
            t.priceTp := t.priceOpen - (t.r * t.rTP)
	t.MakeLabel(barIdx)
    t.MakeTradeLines(barIdx)

export method UpdateAllTrades(Trade[] trades, float _close) =>
	for [index, value] in trades
		value.UpdateTrade(_close)

export method TrackTrade(Trade t, int barIdx) =>
	t.SetInitialValues(barIdx)