Trade
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DevLucem
//  (                   (                            
//  )\ )                )\ )                         
// (()/(     (    )    (()/(   (          (     )    
//  /(_))   ))\  /((    /(_)) ))\   (    ))\   (     
// (_))_   /((_)(_))\  (_))  /((_)  )\  /((_)  )\  ' 
//  |   \ (_))  _)((_) | |  (_))(  ((_)(_))  _((_))  
//  | |) |/ -_) \ V /  | |__| || |/ _| / -_)| '  \() 
//  |___/ \___|  \_/   |____|\_,_|\__| \___||_|_|_| 
//@version=5

// @description A Trade Tracking Library
library("Trade", overlay = true)

// @function Signal Conditions
// @param conditions: Array of sequential conditions to be met for a signal
// @param reset: Condition for when to restart scanning the conditions
// @returns Boolean: True when all the conditions have occured
export method signal(array<bool> conditions, bool reset=false) =>
    var s = 0, signal = array.size(conditions)
    if reset
        s := 0
    if s==signal
        s:= 0
    for i=signal-1 to 0 by 1
        if s==i and array.get(conditions, i)
            s+= 1
    s==signal

// @Type Trade holds Parameters
export type Trade
	float stoploss
	float takeprofit
	float entry = close
    active = false
    bool sl_hit = false
    bool tp_hit = false
    open = time


// @function Update Trade Parameters
// @param this: Trade Object to be updated
// @param stoploss:
// @param takeprofit:
// @param entry: Optional price series for entry
// @returns nothing
export method update(Trade this, float stoploss = na, float takeprofit = na, float entry = close) =>
    if not na(stoploss)
        this.stoploss := stoploss
    if not na(takeprofit)
        this.takeprofit := takeprofit
    if not na(entry)
        this.entry := entry

// @function Clear Trade Parameters
// @param this: Trade Object to be cleared
// @returns nothing
export method clear(Trade this) =>
    this.entry := na, this.stoploss := na, this.takeprofit := na, this.active := false

// @function Track Trade Parameters
// @param this: Trade Object to be tracked
// @param _high: Optional price series for high value
// @param _low: Optional price series for low value
// @returns nothing
export method track(Trade this, float _high=high, float _low=low) => 
    this.active := this.active or this.entry == close or (not na(this.entry[1]) and _low <= this.entry and _high >= this.entry )
    this.sl_hit := this.active and not na(this.entry[1]) and (this.stoploss < this.takeprofit or this.stoploss < this.entry? _low <= this.stoploss: _high >= this.stoploss)
    this.tp_hit := this.active and not na(this.entry[1]) and (this.stoploss < this.takeprofit or this.takeprofit > this.entry? _high >= this.takeprofit: _low <= this.takeprofit)
    if this.sl_hit or this.tp_hit
        this.clear()

// @function New Trade with tracking
// @param stoploss:
// @param takeprofit:
// @param entry: Optional price series for trade entry price
// @param _high: Optional price series for high value
// @param _low: Optional price series for low value
// @param condition: When to update trade parameters
// @param update: Update trade parameters if condition is met again
// @returns a Trade with targets and updates if stoploss or takeprofit is hit
export new(float stoploss, float takeprofit, float entry=close, float _high=high, float _low=low, bool condition=true, bool update=false) =>
    var trade = Trade.new(na, na, na)
    if condition and (na(trade.entry) or not trade.active or update)
        trade.update(stoploss, takeprofit, entry)
    trade.track(_high, _low)
    trade

// @function New Empty Trade
// @returns an empty trade
export new() => Trade.new(na, na, na)




// Sample Usage


// **
// Enter a buy trade when RSI 
// crosses below 70 
// then crosses above 80
// before it crosses 40
//
// Note: If RSI crosses 40 before 80, No trade will be entered


rsi = ta.rsi(close, 21)

// Conditions to be met
buyConditions = array.new_bool()
buyConditions.push(ta.crossunder(rsi, 70))
// buyConditions.push(ta.crossover(rsi, 80))

// // Visual Conditions
// bgcolor(ta.crossunder(rsi, 70)? color.rgb(76, 144, 175, 61): na, title="Crossing Below 70")
// bgcolor(ta.crossover(rsi, 80)? color.rgb(76, 175, 79, 29): na, title="Crossing Above 80")
// bgcolor(ta.crossover(rsi, 40)? color.rgb(175, 76, 76, 29): na, title="Crossing Below 40")

// Condition that should not happen
buy = signal(buyConditions, ta.crossunder(rsi, 40))
trade = new(close-(100*syminfo.mintick), close +(200*syminfo.mintick), high, condition=buy)

plotshape(buy, "Buy", shape.labelup, location.belowbar, color.lime, 0, "BUY", color.black)
plot(trade.entry, "Entry", style=plot.style_circles, linewidth=4, color=color.aqua)
plot(trade.stoploss, "SL", style=plot.style_circles, linewidth=4, color=color.red)
plot(trade.takeprofit, "TP", style=plot.style_circles, linewidth=4, color=color.lime)

bgcolor(trade.active? color.rgb(76, 175, 79, 70): na, title="Show When Trade Active")


// **********
// If you wish to use the script in conditional statements,
// Start the trade with empty parameters 
// And use track, update or clear to make changes.


// © DevLucem
//  (                   (                            
//  )\ )                )\ )                         
// (()/(     (    )    (()/(   (          (     )    
//  /(_))   ))\  /((    /(_)) ))\   (    ))\   (     
// (_))_   /((_)(_))\  (_))  /((_)  )\  /((_)  )\  ' 
//  |   \ (_))  _)((_) | |  (_))(  ((_)(_))  _((_))  
//  | |) |/ -_) \ V /  | |__| || |/ _| / -_)| '  \() 
//  |___/ \___|  \_/   |____|\_,_|\__| \___||_|_|_| 