This weeks post comes courtesy of CryptoJoncis, a Backtest Rookies reader who got in touch via Twitter looking for some help with setting stop losses in Tradingview. So without further ado, let’s dive into it.
Stop Loss Fundamentals
Before we dive into code examples showing how to set a stop loss, it is useful to understand the fundamentals behind stop losses/stop orders. Once you have a good grasp on the basics, the strange behaviour that you think you are seeing likely has a valid reason.
It may seem counter-intuitive but stop losses are not always used for stopping losses. This is because you can actually send a stop order to the market when you don’t even hold a current position. Due to this, they are often called stop orders instead of stop losses.
Stop orders really refer to an order to buy or sell something at a disadvantageous price. In other words, a price that is worse than you can have now. So when you are in a long position, that makes sense. We set the stop price at a level lower than the current (or entry) price even though we could sell now for a better price.
So why would you send a stop order when you are not in a position and why would you choose to do this at a worse price than you could have now? Well, one case might be to send a stop order right on a breakout level above the current price. This will allow you to enter the position right at the exact moment price breaks out. If you take a look at Tradingview’s built-in PSAR strategy, you will see that the strategy does just that!
Triggering
Once a stop order is sent it sits on your broker’s or the stock exchange’s order book (If you have direct market access). The order then sits there until it is triggered, expires or is cancelled. In the real world, triggering means that someone else has executed a market order and is willing to trade at the level you set. The two trades are then matched up and a transaction occurs.
However, In our backtesting isolation, things do not quite work this way. We are testing with historical data and without any transaction history. It is just assumed that you would be filled at the order level you set. This assumption is fine for very liquid markets like S&P500 stocks, Bonds, Forex etc but not for small-cap stocks. This is because in markets where there are not a lot of people buying and selling,, there might not actually be anyone willing to buy at that your stop loss level! You will then be filled at the next closest price that someone is willing to buy at. This is known as slippage and probably a topic for another day.
Sending Stop Orders:
Ok, so back to Tradingview and Pine-Script. There are actually a number different functions that we can use to send a stop order into the market. These are:
strategy.entry()
strategy.exit()
strategy.order()
One of the first questions that spring to mind for beginners is which one should you use? Unfortunately, the answer is all of them! There is a reason why the Tradingview team offer multiple functions and each is useful in different scenarios. So instead of telling you which to use, let’s take a look at ideal use-cases for each function.
If you are looking to use a simple stop loss to EXIT a position, it is easier to work with strategy.exit()
.
If you want to use a stop order to ENTER a position rather than exit one, then strategy.entry()
is the function to use. All you need to do is add the stop
parameter and provide a valid price that is worse than the current price. In other words, a higher price for a long entry or a lower price for a short entry. If you are wondering what the stop
parameter is and how to use it, don’t worry, an example shall follow later.
Finally, if you just want to perform some advanced position management (e.g take of half a position) or link orders together (so one order cancels another), then the strategy.order()
function is for you.
Simple Stop Loss Example
In our first example, we are going to set a simple stop loss using the strategy.exit()
function. To demonstrate this effectively, we should already be in a position. Therefore the code below includes some simple entry criteria. In addition, it shall also show you how to plot the stop loss on the chart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//@version=3 strategy("Stop Loss Example: Simple Stoploss", overlay=true) sma_per = input(200, title='SMA Lookback Period', minval=1) sl_inp = input(2.0, title='Stop Loss %', type=float)/100 tp_inp = input(4.0, title='Take Profit %', type=float)/100 sma = sma(close, sma_per) stop_level = strategy.position_avg_price * (1 - sl_inp) take_level = strategy.position_avg_price * (1 + tp_inp) strategy.entry("Simple SMA Entry", strategy.long, when=crossover(close, sma)) strategy.exit("Stop Loss/TP","Simple SMA Entry", stop=stop_level, limit=take_level) plot(sma, color=orange, linewidth=2) plot(stop_level, color=red, style=linebr, linewidth=2) plot(take_level, color=green, style=linebr, linewidth=2) |
Commentary
To calculate the stop level, our example uses strategy.position_avg_price
as a base. This allows use to determine the entry price. When we know our entry price, we can easily set a stop loss at a level above/below that price. Note that strategy.position_avg_price
works well if you only have one entry. If you like to work into positions over a few trades, then this might not be the best option for you as each new entry would affect the average price and therefore the stop loss.
The example has been coded in a way that we can return a value for the stop loss every single bar. This allows us to easily plot the stop loss. Plotting the stop-loss lines enables us to quickly see what has happened in our strategy and why the position has exited.
It is also worth noting that, when we don’t have a position in the market the strategy.position_avg_price
value equals na
. This is again helpful for plotting because we are able to plot the stop loss line only when we are in a position. na
is never plotted.
However, to ensure this is plotted tidily, the linebr
style should be used. This will ensure that you don’t have long lines joining up gaps between when the value is na
. You will see the difference if you just use a normal line style.
Loss vs Stop
If you read the documentation for strategy.exit()
, you might notice that there are two very similar sounding parameters. These are stop
and loss
so what is that all about?
Some people love to work in ticks, others prefer to work with price levels. That pretty sums up the difference between these two parameters. Theloss
parameter expects to receive a value measured in ticks whilst the stop
parameter expects to receive a specific price.
Here is a quick example of code using ticks to define the loss
level.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//@version=3 strategy("Stop Loss Example: Loss Param", overlay=true) sma_per = input(200, title='SMA Lookback Period', minval=1) sl_inp = input(100, title='Stop Loss in Ticks', type=integer) tp_inp = input(50, title='Take Profit in Ticks', type=integer) sma = sma(close, sma_per) strategy.entry("Simple SMA Entry", strategy.long, when=crossover(close, sma)) strategy.exit("Stop Loss/TP", "Simple SMA Entry", loss=sl_inp, profit=tp_inp) plot(sma, color=orange, linewidth=2) |
As you will see in the code, the using the loss
parameter is a bit easier. You just provide the number of ticks you want to set and away you go. However, this method is not without its drawbacks. First of all, it is harder to calculate you $ amount of risk unless you work with the syminfo.mintick
parameter to determine the value of one tick.
If you are new to trading, the minimum tick is the smallest amount that price can move from one level to another. In other words, stocks, shares etc cannot be valued at any price imaginable. The move in small fixed steps.
Incorrect Prices
The next thing one must be aware of is that you set your stop loss levels on the correct side of price. If you don’t do this, the stop loss will trigger immediately at the open of the next bar. Let’s take a look:
The following example shows a chart where the stop loss level is passed for a long trade above the current price level.
We can see that the stop loss is triggered immediately on the bar it entered the market. If we look at a long example, this is because the price is already below the stop level we requested. As we learned in the fundamentals section, a stop order is triggered whenever price touches or goes beyond the stop level. Therefore it must be triggered immediately.
Strategy.Order()
The strategy.order()
is perhaps the most flexible function as it can be used to both enter and exit positions. In addition, it can be used to add or remove a specific qty
from a position.
The added flexibility, also brings added complexity and as such, there are a number of things one must be aware of when using it:
- First of all, we need to set whether the order is an order to buy or an order to sell. We do this using
true
orfalse
as you will see in the example code below. - We cannot place a stop and limit level inside the same order.
- Once an order is in the market, we need to remember to cancel it once we no longer want it. Otherwise, it will sit there forever until it is triggered. This is especially true when working with multiple orders such as a take profit and a stop loss at the same time. I.e Once the take profit is triggered, we no longer need the stop loss. If you don’t cancel the order, it could eventually force you into a new position. To avoid this we are able to link orders together using the
oca_name
andoca_type
parameters so that when one order is triggered, the other is automatically cancelled. - Tradingview places a limit on backtests of 2000 orders. Using
strategy.order()
can end up eating into the 2000 order limit very quickly. To combat this, it is a good idea to create the order inside anif
statement or use thewhen
parameter to limit how often an order is sent. This will ensure you don’t fire off a new order every single bar!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//@version=3 strategy("Stop Loss Example: Simple Stoploss", overlay=true) sma_per = input(200, title='SMA Lookback Period', minval=1) sl_inp = input(2.0, title='Stop Loss %', type=float)/100 tp_inp = input(4.0, title='Take Profit %', type=float)/100 sma = sma(close, sma_per) stop_level = strategy.position_avg_price * (1 - sl_inp) take_level = strategy.position_avg_price * (1 + tp_inp) strategy.entry("Simple SMA Entry", strategy.long, when=crossover(close, sma)) strategy.order("Stop Loss", false, stop=stop_level, oca_name='L', oca_type=strategy.oca.cancel, when=crossover(close, sma) and strategy.position_size > 0) strategy.order("Take Profit", false, limit=take_level, oca_name='L', oca_type=strategy.oca.cancel, when=crossover(close, sma) and strategy.position_size > 0) plot(sma, color=orange, linewidth=2) plot(stop_level, color=red, style=linebr, linewidth=2) plot(take_level, color=green, style=linebr, linewidth=2) |
This script should act in the same manner as the first simple stop loss example above. However, you will immediately notice that the code is more complex. Therefore I don’t recommend using strategy.order()
for simple stops. Save this one for when you want to start experimenting with more complex strategies and having multiple orders in the market.
Stop Order Entry Example
Moving onto our final example, we are going to use stop orders to enter the market. We will do this using the stop
parameter of strategy.entry()
. The result is that we are able to enter the market at the exact moment when price touches a 200 period moving average.
1 2 3 4 5 6 7 8 9 10 |
//@version=3 strategy("Stop Loss Example: Stop Entry", overlay=true) sma = sma(close, 200) strategy.entry("My Long Entry Id", strategy.long, stop=sma, when=close < sma) strategy.entry("My Short Entry Id", strategy.short, stop=sma, when=close > sma) plot(sma, color=orange, linewidth=2) |
In this example, we can see that we are using a stop order to enter the market but we don’t actually enter a position until the order is triggered. I.e until price touches the 200 day MA.
And there we have it…
I hope this provided an easy to follow overview of how to set a stop loss in pine-script. If not, let us know in the comments below and the article will be updated!
Very well written tutorial and clearly explained. Thank you for your effort
Hello,
really good work done. extremelly appreciated. Can you help with a simple SL/TP for the RSI? thank you
Hi Hanna,
Thank you for the kind words. What simple SL/TP are you thinking of? I assume something not covered in the article.
Setting a stop loss is always based on price level. You could manually close the position when the RSI reaches a certain level if that is what you are after?
Great article, thanks a lot for the detailed description.
I was wondering if you have any guidance for combining the stop order entry example with the simple stop loss example. Basically if I want to have a stop loss & take profit for long as well as short entries. I tried something like this but it does not really work properly:
strategy.entry(“My Long Entry Id”, strategy.long, stop=sma, when=close sma)
strategy.exit(“Stop Loss/TP”,”My Long Entry Id”, stop=stop_level, limit=take_level)
strategy.exit(“Stop Loss/TP”,”My Short Entry Id”, stop=stop_level, limit=take_level)
The idea is to have my long or short entry when the criteria is fulfilled but also that each long or short entry has their own stop loss/take profit. Further, this stop loss/take profit exit should be canceled if the market goes in the opposite direction and enters an e.g. short entry even though my take profit or stop loss have not yet been hit.
Any ideas? Thanks you
Sorry, one line was missing:
strategy.entry(“My Long Entry Id”, strategy.long, stop=sma, when=close sma)
strategy.exit(“Stop Loss/TP”,”My Long Entry Id”, stop=stop_level, limit=take_level)
strategy.exit(“Stop Loss/TP”,”My Short Entry Id”, stop=stop_level, limit=take_level)
And for the classic stop?
How do I put the Stop in the Low of the bar that generated the entry?
Hi Marcelo,
You can log the low at the time of the entry and then use that level with the stop parameter.
Could be a good suggestion for a future article
I am be interested in this “last bar low” stop as well. Has an article been written about it?
Thank you for a very well written post. Your website has helped a lot and clarifying various concepts. However, there is one point which is still pending. I have searched around on your website for that but could not find an example and so posting it is a query here.
Usually, we can exit either due to the stoploss being met on the profit targets being met, or due to any other technical indicator. For example, let’s say when the ADX is less than 25.
So how will we be able to use strategy.exit in order to get out from a position when the ADX is less than 25?
I was wondering if strategy.close has to be used. This pseudo code should look like this
strategy.exit(“Exit Strategy”,”Strategy Name”, profit=profittarget_ticks, loss = lossticks)
and then
if (adx < 25)
strategy.close("Strategy Name")
// do we have to cancel the exit order since adx can be less than 25 before either the profit target or stoploss
// happens and then we will have an extra exit order pending which needs to be cancelled
strategy.cancel("Exit Strategy")
You can use
strategy.close()
for this. However, you don’t close the strategy name as your wrote, you close the position/entry name.so assuming you had a long entry like:
strategy.entry("Long", true, 1, when = open > high[1])
to close when ADX is less than 25, you would do:
strategy.close("Long", when= adx < 25)
Assuming the exit order is linked to your entry order like
strategy.exit(“TP/SL”,”Long”, profit=profittarget_ticks, loss = lossticks)
Then you don't need to cancel it because you are only using the stop loss for the
Long
entry and we just closed it.Note that if you use
strategy.order()
you do need to cancel it.Hope that helps.
Fantastic article thank you. How would we convert this to a study ?
Thanks for this example. I’m combining it and your recent swing high/low example to set stops based on recent swing lows. It seems to work well, but I am getting some of those “incorrect pricing” issues you noticed. I’m guessing my entry signal is below the recent swing low calculation due to the delay in printing that value. Is there a way to only take a long position IF the swing low is below current price? I’m guessing I just need to add a variable comparing current price vs. the swing low price and only enter if above it as a condition for entry.
I am trying out this example with my script but i cannot make it work.
I want to have a fixed TP and SL defined by user
//Strategy entry signal long
if(testPeriod() and (tradeType == “LONG” or tradeType == “BOTH” ))
strategy.entry(“Long”, strategy.long, when=longCondition)
//Strategy entry signal short
if(testPeriod() and (tradeType == “SHORT” or tradeType == “BOTH” ))
strategy.entry(“short”, strategy.short, when=shortCondition)
How would i use the strategy.exit or strategy.close for both long and shorts?
If I am setting my stop loss at 1% why is it that the strategy tester shows my largest loss at 12%, I have 0 slippage applied. Any idea?
//STOP LOSSES
stoplossshort = strategy.position_avg_price + (strategy.position_avg_price * .1)
stoplosslong = strategy.position_avg_price – (strategy.position_avg_price * .1)
strategy.exit(“Stop Loss/TP”,”Long”, stop=stoplosslong, limit=takeprofitlong)
strategy.exit(“Stop Loss/TP”,”Short”, stop=stoplossshort, limit=takeprofitshort)
Hi,
Thank you very much for sharing an excellent work.
Will you please help me to conver my “strategy.entry” command in to “alertcondition” with this “stop=low” feature in it ?
my “strategy.entry” command is :-
[strategy.entry (“Short_Entry”, long=false, when=shortEntryCondition_SBPR_EMA_Channel and rsi13, stop=low)]
Thanking you in advance..
Thank you for your work.
I dont know if im stupid, or TradingView has a bug, but why does this stop executes immediately?
strategy.entry( “long”, strategy.long)
strategy.exit( “exit”, “long”, stop=open-5)
I dont get it.
I think I’ve got it now.
When you plot open, close or any other variable it will plot the value of the current bar.
When you reference it in a strategy, like the example above, it will reference the value of the last bar.
This behaviour is very strange.
I dont know if it is documented anywhere.
This has been extremely helpful, thank you. I have one burning, unanswered question, though. How do I test whether or not my stop loss has been hit? The way that I’m doing it now is complicated and error prone, and I figure there must be an easier way.