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.
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.
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)
shortCondition = crossunder(smaFast, smaSlow)
// Calc ATR Stops
longStop = na
longStop := shortCondition ? na : longCondition ? close - (atr * atrMult) : longStop
shortStop = na
shortStop := longCondition ? na : shortCondition ? close + (atr * atrMult) : shortStop
// 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')
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
shortStoplines under two conditions. For the sake of simplicity, we will just discuss the
shortStop follows the same logic in reverse.
Should we detect a
shortConditionthen we set
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 set
longStopto 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. 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
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
shortStop = na
shortStop := longCondition ? na : shortCondition and strategy.position_size >=0 ? close + (atr * atrMult) : shortStop
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:
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.
Support The Site!
If you like this article and are thinking to become a Tradingview subscriber, you can support the site by clicking on the affiliate link below before signing up!