// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © TradingView //@version=5 // Volatility Stop // v3, 2022.05.13 // This code was written using the recommendations from the Pine Script™ User Manual's Style Guide: // https://www.tradingview.com/pine-script-docs/en/v5/writing/Style_guide.html indicator("Volatility Stop", "V-Stop", true) import PineCoders/Time/1 as pcTimeLib // ———————————————————— Constants { // —————————— Base colors var color GREEN1 = #00FF00FF var color DEEPSKY = #00C0FFFF var color RED1 = #FF0000FF var color DEEPPINK = #FF0080FF var color CLEARBLACK = #00000000 // —————————— Strings var string VSRC_TT = "Source to offset the stop from." var string VLEN_TT = "The length (bars back) to use in the ATR calculation." var string VATR_TT = "A multiplier to allow adjustment of the stop as a function of the ATR value. Example: 1 is 100% of the calculated ATR value." var string REV_TT = "Triggers an alert on changes of the trend's direction. If a delay is used, the alert will be delayed until the close has remained crossed over the stop for the number of minutes defined in the 'Delay' setting." var string UP_TT = "Triggers an alert on a cross up of a short stop. If a delay is used, the alert will be delayed until the close has remained crossed over the stop for the number of minutes defined in the 'Delay' setting." var string DN_TT = "Triggers an alert on a cross down of a long stop. If a delay is used, the alert will be delayed until the close has remained crossed over the stop for the number of minutes defined in the 'Delay' setting." var string SEC_TT = "Minutes for which price must breach the line before an alert triggers. Use '0.5' for 30 seconds." var string C01 = "Lime/Red" var string C02 = "Aqua/Pink" var string S01 = "Line" var string S02 = "Circles" var string S03 = "Diamonds" var string S04 = "Arrows" var string A1 = alert.freq_once_per_bar var string A2 = alert.freq_once_per_bar_close // } // ———————————————————— Inputs { var string GRP1 = "════════    Stop Calculations    ════════" float srcInput = input.source(close, "Source", group = GRP1, tooltip = VSRC_TT) int lenInput = input.int(20, "ATR Length", group = GRP1, tooltip = VLEN_TT, minval = 2) float atrInput = input.float(2.0, "ATR Factor", group = GRP1, tooltip = VATR_TT, minval = 0.25, step = 0.25) var string GRP2 = "════════════ Line Style ═══════════" string colorInput = input.string(C01, "Color Scheme", group = GRP2, options = [C01, C02]) string styleInput = input.string(S01, "Indicator style", group = GRP2, options = [S01, S02, S03, S04]) int widthInput = input.int(2, "Line Width", group = GRP2, options = [0, 1, 2, 3]) var string GRP3 = "═════════ Alert Conditions ═════════" bool revInput = input.bool(false, "Reversal Alert", group = GRP3, tooltip = REV_TT) bool upInput = input.bool(false, "Breach of downtrend stop", group = GRP3, tooltip = UP_TT) bool dnInput = input.bool(false, "Breach of uptrend stop", group = GRP3, tooltip = DN_TT) float delayInput = input.float(0.0, "Delay in minutes", group = GRP3, tooltip = SEC_TT, minval = 0.0, step = 0.5) string freqInput = input.string(A1, "Alert Frequency", group = GRP3, options = [A1, A2]) // } // ———————————————————— Calculations { // @function Calculates a value from the ATR, which is offset from price action to use as a stop loss or trend detection. // @param source (series int/float) The source to calculate the stop value from. // @param atrLength (simple int) The length over which to calcualte the RMA of the true range. (number of bars back). // @param atrFactor (series int/float) A multiplier to allow adjustment of the stop as a function of the ATR value. Example: 1 is 100% of the calculated ATR value. // @returns ([float, bool]) A tuple of the volatility stop value and the trend direction as a bool. vStop(series float source, simple int atrLength, series float atrFactor) => float src = nz(source, close) var bool trendUp = true var float max = src var float min = src var float stop = 0.0 float atrM = nz(ta.atr(atrLength) * atrFactor, ta.tr) max := math.max(max, src) min := math.min(min, src) stop := nz(trendUp ? math.max(stop, max - atrM) : math.min(stop, min + atrM), src) trendUp := src - stop >= 0.0 if trendUp != nz(trendUp[1], true) max := src min := src stop := trendUp ? max - atrM : min + atrM [stop, trendUp] var bool defaultScheme = colorInput == C01 var bool showCircles = styleInput == S02 var bool showDiamonds = styleInput == S03 var bool showArrows = styleInput == S04 // Calculate Volatility Stop [stop, trendUp] = vStop(srcInput, lenInput, atrInput) // Get conditions for colors, plotting, and alerts bool trendReversal = trendUp != trendUp[1] bool trendChangeToUp = trendUp and not trendUp[1] bool trendChangeToDn = not trendUp and trendUp[1] // Get duration of each condition float secondsUp = pcTimeLib.secondsSince(trendChangeToUp, false) float secondsDn = pcTimeLib.secondsSince(trendChangeToDn, false) // Timed alerts bool alertUp = secondsUp >= delayInput * 60 bool alertDn = secondsDn >= delayInput * 60 bool alertRev = alertUp or alertDn // Get colors color colorUp = defaultScheme ? GREEN1 : DEEPSKY color colorDown = defaultScheme ? RED1 : DEEPPINK color schemeColor = defaultScheme ? trendUp ? GREEN1 : RED1 : trendUp ? DEEPSKY : DEEPPINK color lineColor = trendReversal ? CLEARBLACK : schemeColor // } // ———————————————————— Plotting { plotshape(showDiamonds or showCircles ? stop : na, "Diamonds & Circles", showDiamonds ? shape.diamond : shape.circle, location.absolute, schemeColor) plotchar(showArrows and trendUp ? stop : na, "Arrows Up", "⮝", location.absolute, colorUp) plotchar(showArrows and not trendUp ? stop : na, "Arrows Dn", "⮟", location.absolute, colorDown) plot(widthInput != 0 ? stop : na, "V-Stop", lineColor, widthInput) plot(trendReversal ? stop : na, "Beg. Circle", schemeColor, math.max(widthInput, 1) + 2, plot.style_circles) plot(trendReversal ? stop : na, "Beg. Small circle", #000000ff, math.max(widthInput, 1), plot.style_circles) // } // ———————————————————— Alerts { bool timed = delayInput != 0 float duration = trendChangeToUp ? secondsUp : secondsDn if alertRev and revInput string alertMsg = "Trend Reversal " + (timed ? "Close crossed stop for " + str.tostring(duration / 60, "#.##m") : "") alert(alertMsg, freqInput) if alertUp and upInput string alertMsg = "Breach of downtrend stop. " + (timed ? "Close was above stop for " + str.tostring(secondsUp / 60, "#.##m") : "") alert(alertMsg, freqInput) if alertDn and dnInput string alertMsg = "Breach of uptrend stop. " + (timed ? "Close was above stop for " + str.tostring(secondsDn / 60, "#.##m") : "") alert(alertMsg, freqInput) // }