Tradingview: Daily ATR Stop

Picking an ideal stop placement, especially when there is not an obvious swing or breakout level to base it on can be tricky. As such, one method that is frequently requested is how to use the  Average True Range (ATR) level or multiple of it for our stop loss. In this post, we will attempt to do just that.

ATR Stops

The general theory behind using ATR to define your stop loss is that it should allow you to set the stop at a realistic distance from price. Further, because it is always updated by recent price action, it is self-calibrating.

ATR will tell us how far we expect price to move on any given bar. As such, setting a stop loss at 1 x ATR on the time-frame that we are trading should mean we don’t frequently get stopped out within one bar. Admittedly, the goal of not getting stopped out in one bar is not much of a goal! One would hope our trades would last longer than a few bars unless that is a specific part of your strategy! It is for this reason why people tend to lean towards using a multiple of ATR OR take an ATR value from a higher time-frame. Both these methods will ensure your trades are given additional breathing room and that you shouldn’t be whipped out of a profitable position just because your stop was too tight.

In the example code below, we use both methods. We shall set our stop using daily ATR levels and also provide an input to use a multiple of it.

The Code

//@version=3
strategy("Daily ATR Stop", overlay=true, precision=5)

smaFastLkb = input(14, minval=1, title='Fast SMA Period')
smaSlowLkb = input(28, minval=1, title='Slow SMA Period')
atrLkb = input(7, minval=1, title='ATR Stop Period')
atrRes = input("D", type=resolution, title='ATR Resolution')
atrMult = input(1, step=0.25, title='ATR Stop Multiplier') 


// Get the indicators
// -------------------
atr = security(tickerid, atrRes, atr(atrLkb))
smaFast = sma(close, smaFastLkb)
smaSlow = sma(close, smaSlowLkb)


longCondition = crossover(smaFast, smaSlow)
if (longCondition)
    strategy.entry("Long", strategy.long)

shortCondition = crossunder(smaFast, smaSlow)
if (shortCondition)
    strategy.entry("Short", strategy.short)

// Calc ATR Stops
longStop = na
longStop :=  shortCondition ? na : longCondition ? close - (atr * atrMult) : longStop[1]
shortStop = na
shortStop := longCondition ? na : shortCondition ? close + (atr * atrMult) : shortStop[1]
    
// Place the exits
strategy.exit("Long ATR Stop", "Long", stop=longStop)
strategy.exit("Short ATR Stop", "Short", stop=shortStop)
    
// Plot the stoplosses
plot(smaFast, color=orange, linewidth=2, title='Fast SMA')
plot(smaFast, color=purple, linewidth=2, title='Slow SMA')
plot(longStop, style=linebr, color=red,     title='Long ATR Stop')
plot(shortStop, style=linebr, color=maroon,  title='Short ATR Stop')

Commentary

The magic for this post is all contained within 4 lines. Prior to calculating the ATR stop levels, we only pull in the daily ATR value via the security()function and create a simple moving average crossover strategy.

The trick is to only update the longStopand shortStoplines under two conditions. For the sake of simplicity, we will just discuss the longStopas theshortStop follows the same logic in reverse.

Should we detect a shortConditionthen we setlongStopto na. We do this mainly for plotting aesthetics so we do not continuously draw a long stop level levels below price when we are in a short position. Next, If a longConditionis found, we setlongStopto the current atr value multiplied by atrMult. This becomes our initial stop level. After this, we simply continue to update the stop level with the previous value using longStop[1]. This means that whilst we are in a short position, the line will continue to be na. Conversely, whilst we are in a long position, the line with continue to be the same value as when we sent our entry order.

For more examples of this technique, you can check out the Tradingview: Save a variable / store a value for later tutorial.

Finally, if you plan to forward test this code, be sure to check out the upper time-frame repainting tutorial to see how to fix the daily ATR level whilst the daily bar is still being formed.

An Alternate Method

Since stop losses sit in the market until triggered, another method to meet this requirement would be to send a single stop loss order at the time of entry. To do this, we can place the strategy.exit()function with strategy.entry()inside the if statement. This would send the stop loss order only once at the time of the longCondition and would not be updated again. To expand further, this is because we cannot send an update to the “Long ATR Stop” order unless longConditionis met again and because this is a crossover strategy, we cannot crossover again until we have first crossed down and entered a short position!

The downside to the method is that, should you wish to plot the stop loss line, you would not be able to see a nice straight line unless you recreate the longStopcode from above.

Keeping Things Steady

If you have a longCondition that can be met whilst you are still in a long position, then the ATR value can accidentally be updated again. This will affect both methods discussed. When this happens, your stop loss level will shift to the latest ATR value when longConditionis seen for a second time.

Assuming that is undesirable, you need to add an extra  condition to check that we are not already in a position like so:

// Calc ATR Stops
longStop = na
longStop :=  shortCondition ? na : longCondition and strategy.position_size <=0 ? close - (atr * atrMult) : longStop[1]
shortStop = na
shortStop := longCondition ? na : shortCondition and strategy.position_size >=0 ? close + (atr * atrMult) : shortStop[1]

This will ensure that it is only updated once coming from a flat or short position (in the case of longs).

On the Charts and Verification

Placing the strategy on the charts, you should see something that looks like this:

Daily ATR Stop Loss on GBPUSD

Note that the lower the time-frame you use, the further away the daily ATR stop will appear on the chart. So if you don’t see it at first, adjust the scale! A daily ATR stop can be quite far away on the one-minute time-frame.

Next, let’s verify we are using the correct ATR levels. To do this we need to take a look at the daily ATR value and confirm whether that value is being applied to the stop loss.

When you do this yourself, be sure to make sure to:

  • Check that the ATR lookback period used in the strategy is the same as the indicator you are verifying it with. In this case, they both are set to 14 so there is no issue.
  • Check that the precision for both the indicator and strategy are the same. Otherwise, Tradingview will round the Indicator value and it can look like the value is wrong.

Why are the values delayed?

The eagle-eyed readers may have noticed that the dates in the last screenshot are misaligned. The ATR value used appears to be from the previous day. Specifically, on the 11th of March, we use the ATR value from the 8th of March (The weekend was in-between! The 8th was the previous bar).

This happens because we are using the security()function along with version 3 of pine script.  In version 3, pine script attempts to avoid “Lookahead Bias” by ensuring that the current day’s final ATR value is not available until after the current day bar closes. Some more information regarding this is covered our indicator repainting article.

Find This Post Useful?

If this post saved you time and effort, please consider support the site! There are many ways to support us and some won’t even cost you a penny. 

Backtest Rookies is a registered with Brave publisher!

Brave users can drop us a tip.

Alternatively, support us by switching to Brave using this referral link and we will receive some BAT! 


https://brave.com/bac691

Referral Link

Enjoying the content and thinking of subscribing to Tradingview? Support this site by clicking the referral link before you sign up! 

https://tradingview.go2cloud.org/SHpB


Donate with PayPal using any payment method you are comfortable with! 


3HxNVyh5729ieTfPnTybrtK7J7QxJD9gjD

0x9a2f88198224d59e5749bacfc23d79507da3d431

M8iUBnb8KamHR18gpgi2sSG7jopddFJi8S