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.
//@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')
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.
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.
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]
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:
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.
Original Article on Backtest-Rookies.com
Source: BacktestRookies
Interactive Brokers Modular IB strategy is ready to automate Futures, Options, Equities & FX via IB API
Sign Up Now