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

//@version=5

// @description This library contains functions to output trailing stop lines.
library("TrailingStops", overlay=true)

string in_showFunction = input.string(defval="f_complexStop", title="Function to display", options=["f_marketStructureStop","f_complexStop"])

in_restartMode_MS = input.string(defval="Flip", title="Restart Mode", group="f_marketStructureStop", options=["Always On","Flip","Manual"], tooltip="All options set _restartMode to the same value. \"Always On\" and \"Flip\" are built-in options in the function. If you choose \"Manual\", _restartLow and _restartHigh must then be calculated outside the function and supplied to it. Here, there is some demo code to do that. You can turn the trails on manually when your indicator takes a position, or just leave them always on here and sample them however you like.")
in_flipMode_MS = input.string(defval="Wick", title="Flip On", options=["Wick","Close"], tooltip="Whether the stop lines flip when exceeded by a wick or a close in price.")
in_strictMode_MS = input.bool(defval=false, title="Use Lows, Highs in Strict Order")

in_flipMode_CX = input.string(defval="Wick", title="Flip On", group="f_complexStop", options=["Wick","Close"], tooltip="Whether the stop lines flip when exceeded by a wick or a close in price.")

// Variables derived solely from inputs
bool showMS = in_showFunction == "f_marketStructureStop"
bool showComplex = in_showFunction == "f_complexStop"

import SimpleCryptoLife/MarketStructure/1 as SCL_MS
var string errorTextLibraryName = "TrailingStops"  // Defined globally for use in error messages.

// @function f_marketStructureStop - Outputs a stop line based on market structure generated by Local Lows and Highs. This is NOT a market structure indicator (for that, I've written a free, open-source indicator, and you could easily construct one yourself using my MarketStructure library). This function gives intuitive, rule-based places to put a stop loss; that's all.
// @param string _restartMode="Flip" - Defines how the stop lines persist. Allowed values are:
    //  "Always On" - The stop lines are always present and they just reset when they're crossed. This is the least intuitive but perhaps most useful option. Simply sample the line when you want to take a position; long and short stops are always available.
    //  "Flip" - The stop lines flip when they're crossed. This looks like one would expect a trailing stop to look. 
    //  "Manual" - The stop lines turn off when they're crossed, and turn back on again when _restartLowIn or _restartHighIn are passed into the function as true.
// @param string _flipMode="Wick" - Defines whether the stop lines are broken by wicks or closes. Allowed values are "Wick", and "Close". If you choose "Close", the stop line that's displayed can regress on a wick, but the underlying value for flipping does not regress. This is by design and is not a bug. The function doesn't know when you might take a position in either direction. If you choose to take a long when the long stop line has been pierced by a wick but not a close, it will display the appropriate value for a stop on that bar, which is the bar's Low.
// @param bool _restartLowIn=true - If _restartMode is "Manual", passing this parameter as true restarts the Low stop line.
// @param bool _restartHighIn=true - If _restartMode is "Manual", passing this parameter as true restarts the High stop line.
// @param bool _strictMode - If false, this library uses Enhanced Simple Highs/Lows from the MarketStructure library. If true, it uses True Highs/Lows.
// @returns - floats for the Low and High stop lines.


// 🟦🟦🟦🟦  MARKET STRUCTURE TRAILING STOP  🟦🟦🟦🟦

export f_marketStructureStop(string _restartMode="Flip", string _flipMode="Wick", bool _restartLowIn=true, bool _restartHighIn=true, bool _strictMode=false) =>
    // Error-checking
    var string _errorTextFunctionName = "f_marketStructureStop"
    if _flipMode != "Wick" and _flipMode != "Close"
        runtime.error("_flipMode must be either \"Wick\" or \"Close\". [Error 01:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _restartMode != "Always On" and _restartMode != "Flip" and _restartMode != "Manual"
        runtime.error("_restartMode must be one of \"Always On\", \"Flip\", or \"Manual\". [Error 02:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    // Define variables
    var float _lowStop = na, var float _highStop = na  // The actual stop lines. We need them to be continuable (it's a word now).
    var bool _isActiveLow = true, var bool _isActiveHigh = true  // We have to start off active.
    var bool _restartLow = na, var bool _restartHigh = na  // Should we restart the stop lines?
    var bool _restartLowFlip = true, var bool _restartHighFlip = true  // Separate bools for whether we should restart the stop lines in Flip mode
    // Choose the low and high break values (close/wicks)
    float _lowValue = switch _flipMode
        "Wick" => low
        "Close" => close
    float _highValue = switch _flipMode
        "Wick" => high
        "Close" => close
    // Get the market structure values
    [_isEnhancedHigh, _isEnhancedLow, _enhancedHighIndex, _enhancedLowIndex, _enhancedHighTrail, _enhancedHigh, _enhancedLowTrail, _enhancedLow, _enhancedBullish, _enhancedBearish] = SCL_MS.f_enhancedSimpleLowHigh()
    [_isTrueHigh, _isTrueLow, _trueHighIndex, _trueLowIndex, _trueHighTrail, _trueHigh, _trueLowTrail, _trueLow, _trueBullish, _trueBearish] = SCL_MS.f_trueLowHigh()

    // Choose the market structure values
    _pivotHighTrail = _strictMode ? _trueHighTrail : _enhancedHighTrail
    _pivotHigh = _strictMode ? _trueHigh : _enhancedHigh
    _pivotLowTrail = _strictMode ? _trueLowTrail : _enhancedLowTrail
    _pivotLow = _strictMode ? _trueLow : _enhancedLow

	// Sample the market structure lines to create the stop lines
    float _lowLine = na(_pivotLow) ? _pivotLowTrail : na(_pivotLowTrail) ? _pivotLow : math.min(_pivotLow, _pivotLowTrail)
	float _highLine = na(_pivotHigh) ? _pivotHighTrail : na(_pivotHighTrail) ? _pivotHigh : math.max(_pivotHigh, _pivotHighTrail)
    // Are the stop lines broken?
    bool _brokenLow = _lowValue < _pivotLow
    bool _brokenHigh = _highValue > _pivotHigh
    // Set restart values according to the restart mode
    _restartLowFlip := _brokenLow ? false : _brokenHigh ? true : _restartLowFlip  // Restart the Low stop line when the High stop line is broken
    _restartHighFlip := _brokenHigh ? false : _brokenLow ? true : _restartHighFlip  // Restart the High stop line when the Low stop line is broken
    _restartLow := switch _restartMode
        "Always On" => true
        "Manual" => _restartLowIn  // The inputs only take effect in Manual mode
        "Flip" => _restartLowFlip  // Using a separate variable isn't the most compact way but it's the clearest
    _restartHigh := switch _restartMode
        "Always On" => true
        "Manual" => _restartHighIn
        "Flip" => _restartHighFlip    
    // Start off with the stop lines active and make them active and inactive according to the restart values
    _isActiveLow := _restartLow ? true : _brokenLow ? false : _isActiveLow
    _isActiveHigh := _restartHigh ? true : _brokenHigh ? false : _isActiveHigh
    // Actually set and unset the stop lines. Easy now we did all the other bits.
    _lowStop := _isActiveLow ? _lowLine : na   
    _highStop := _isActiveHigh ? _highLine : na
    [_lowStop, _highStop]



// 🟦🟦🟦  Example usage  🟦🟦🟦

// Calculate when to restart the stop lines when _restartMode is "Manual". This is just an example and you can do it any way.
var bool restartLowIn_MS = false, var bool restartHighIn_MS = false
if in_restartMode_MS == "Manual"
    restartLowIn_MS := close > high[1] ? true : false  // Restart the low stop line on the next bullish candle 
    restartHighIn_MS := close < low[1] ? true : false  // Restart the high stop line on the next bearish candle

// Call the function
[lowStop_MS, highStop_MS] = f_marketStructureStop(_restartMode=in_restartMode_MS, _flipMode=in_flipMode_MS, _restartLowIn=restartLowIn_MS, _restartHighIn=restartHighIn_MS, _strictMode=in_strictMode_MS)

// Plots
plot(showMS ? lowStop_MS : na, title="Market Structure: Low Stop Background", linewidth=5, color=color.new(color.blue,70), style=plot.style_linebr)
plot(showMS ? lowStop_MS : na, title="Market Structure: Low Stop Line", linewidth=2, color=color.green, style=plot.style_linebr)
plot(showMS ? highStop_MS : na, title="Market Structure: High Stop Background", linewidth=5, color=color.new(color.orange,70), style=plot.style_linebr)
plot(showMS ? highStop_MS : na, title="Market Structure: High Stop Line", linewidth=2, color=color.red, style=plot.style_linebr)



// 🟦🟦🟦🟦  COMPLEX TRAILING STOP  🟦🟦🟦🟦

// @function f_complexStop - Outputs a stop line using more complex criteria than f_marketStructureStop, but still based on market structure generated by Local Lows and Highs. This is NOT a market structure indicator (for that, I've written a free, open-source indicator, and you could easily construct one yourself using my MarketStructure library). This function gives intuitive, rule-based places to put a stop loss; that's all.
// @param float _high - Defaults to the High of the current chart bar. Included for HTF compatibility.
// @param float _low - Defaults to the Low of the current chart bar. Included for HTF compatibility.
// @param float _close - Defaults to the Close of the current chart bar. Included for HTF compatibility.
// @param string _flipMode="Wick" - Defines whether the stop lines are broken by wicks or closes. Allowed values are "Wick", and "Close". If you choose "Close", the stop line that's displayed can regress on a wick, but the underlying value for flipping does not regress. This is by design and is not a bug. The function doesn't know when you might take a position in either direction. If you choose to take a long when the long stop line has been pierced by a wick but not a close, it will display the appropriate value for a stop on that bar, which is the bar's Low.
// @param float _lowBuffer - You can add a buffer to the low stop.
// @param float _highBuffer - You can add a buffer to the high stop.
// @returns - floats for the Low and High stop lines. Bools for whether the lines are active.


export f_complexStop(float _high=high, float _low=low, float _close=close, string _flipMode="Wick", float _lowBuffer=0, float _highBuffer=0) =>
    var string _errorTextFunctionName = "f_complexStop"
    if _flipMode != "Wick" and _flipMode != "Close"
        runtime.error("_flipMode must be either \"Wick\" or \"Close\". [Error 01:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    
    // Get the market structure values
    [_isEnhancedHigh, _isEnhancedLow, _null3, _null4, _pivotHighTrail, _pivotHigh, _pivotLowTrail, _pivotLow, _null5, _null6] = SCL_MS.f_enhancedSimpleLowHigh()
	
    // 🟦🟦 HIGH AND LOW LINES
    // Sample the market structure lines to create the high and low lines
    float _lowLine = na(_pivotLow) ? _pivotLowTrail : na(_pivotLowTrail) ? _pivotLow : math.min(_pivotLow, _pivotLowTrail)
	float _highLine = na(_pivotHigh) ? _pivotHighTrail : na(_pivotHighTrail) ? _pivotHigh : math.max(_pivotHigh, _pivotHighTrail)
    // Are the high/low lines broken?
    bool _brokenLowLine = _close < _pivotLow  // Close makes it slightly less reactive than low/high
    bool _brokenHighLine = _close > _pivotHigh
    // Has the line tightened?
    var bool _lowLineTightened = na, var bool _highLineTightened = na, 
    _lowLineTightened := _brokenLowLine ? false : _lowLine > _lowLine[1] ? true : _lowLineTightened  // Has the low line moved up?
    _highLineTightened := _brokenHighLine ? false : _highLine < _highLine[1] ? true : _highLineTightened  // Has the high line moved down?
    
    // 🟦🟦 STOP LINES
    var float _lowStop = na, var float _highStop = na  // The actual stop lines. We need them to be continuable (it's a word now).
    // Choose the low and high break values (close/wicks)
    float _lowStopFlipValue = switch _flipMode
        "Wick" => _low 
        "Close" => _close
    float _highStopFlipValue = switch _flipMode
        "Wick" => _high
        "Close" => _close
    // Are the stop lines broken?
    bool _brokenLowStop = _lowStopFlipValue < _lowStop
    bool _brokenHighStop = _highStopFlipValue > _highStop
    // Is the stop line active? In Compression, prevent the stop lines advancing until we reach Expansion again (the absence of tightened opposite line)
    var bool _isLowStopActive = na, var bool _isHighStopActive = na
    var bool _isLowStopActive_setup1 = na, var bool _isHighStopActive_setup1 = na
    var bool _isLowStopActive_setup2 = na, var bool _isHighStopActive_setup2 = na
    
    _isLowStopActive_setup1 := not _brokenLowStop ? true : _brokenLowStop ? false : _isLowStopActive_setup1  // Setup1 starts when the stop line stops being broken, and persists until it is.
    _isLowStopActive_setup2 := _isEnhancedLow ? true : _brokenLowStop ? false : _isLowStopActive_setup2  // Setup 2 starts on an Enhanced Low and persists until setup1 is gone.
    bool _isLowStopActive_trigger = _isLowStopActive_setup1 and _isLowStopActive_setup2 and not _highLineTightened  // Trigger is absence of opposite tightened line plus setups

    _isHighStopActive_setup1 := not _brokenHighStop ? true : _brokenHighStop ? false : _isHighStopActive_setup1
    _isHighStopActive_setup2 := _isEnhancedHigh ? true : _brokenHighStop ? false : _isHighStopActive_setup2
    bool _isHighStopActive_trigger = _isHighStopActive_setup1 and _isHighStopActive_setup2 and not _lowLineTightened

    _isLowStopActive := _brokenLowStop ? false : _isLowStopActive_trigger ? true : _isLowStopActive  // Active when trigger fires; off when the stop line is broken 
    _isHighStopActive := _brokenHighStop ? false : _isHighStopActive_trigger ? true : _isHighStopActive    
    
    // Define and persist the stop lines. We output them constantly; you can colour them differently or hide them when not active in your script.
    var float lowLineBuffered = na, var float highLineBuffered = na
    lowLineBuffered := ta.change(_lowLine) ? _lowLine - _lowBuffer : lowLineBuffered // Only add the buffer when the line changes, so the stop line stays flat.
    highLineBuffered := ta.change(_highLine) ? _highLine + _highBuffer : highLineBuffered

    bool _goChangeLowStop = not (lowLineBuffered < _lowStop)  // This awkwardness is required for the case where lines are na at the beginnig of the chart
    bool _goChangeHighStop = not (highLineBuffered > _highStop)
    
    _lowStop := _brokenLowStop ? lowLineBuffered : _goChangeLowStop and not _highLineTightened? lowLineBuffered : _lowStop  // Finally we can be happy!
    _highStop := _brokenHighStop ? highLineBuffered : _goChangeHighStop and not _lowLineTightened ? highLineBuffered : _highStop

    [_lowStop, _highStop, _isLowStopActive, _isHighStopActive]


// 🟦🟦🟦🟦  Example usage  🟦🟦🟦🟦

[lowStop_CX, highStop_CX, isLowStopActive_CX, isHighStopActive_CX] = f_complexStop(_high=high, _low=low, _close=close, _flipMode=in_flipMode_CX, _lowBuffer=0, _highBuffer=0)

plot(showComplex ? lowStop_CX : na, title="Complex: Low Stop Background", linewidth=5, color=isLowStopActive_CX ? color.new(color.blue,70) : na, style=plot.style_linebr)
plot(showComplex ? lowStop_CX : na, title="Complex: Low Stop Line", linewidth=2, color=isLowStopActive_CX ? color.green : color.new(color.gray,70), style=plot.style_linebr)
plot(showComplex ? highStop_CX : na, title="Complex: High Stop Background", linewidth=5, color=isHighStopActive_CX ? color.new(color.orange,70) : na, style=plot.style_linebr)
plot(showComplex ? highStop_CX : na, title="Complex: High Stop Line", linewidth=2, color=isHighStopActive_CX ? color.red : color.new(color.gray,70), style=plot.style_linebr)


// ======================================================== //
//                                                          //
//   (╯°□°）╯︵ ˙ɹǝllᴉʞ-puᴉɯ ǝɥʇ sᴉ ɹɐǝℲ ˙ɹɐǝɟ ʇou llᴉʍ I   //
//                                                          //
// ======================================================== //