Tutorial: Trailing Stop Strategies for Think or Swim

I have received lots of requests for making a trailing stop strategy work in Think or Swim. Unfortunately, due to the way that ToS currently works with strategies, there is no way to make your entry and exit strategies “talk” to each other automatically. But it is possible to get trailing stop strategies to work! It just takes some extra elbow grease.

In general, any intelligent trailing stop must have knowledge of the entry point, so it knows where to start trailing. You can use “dumb” stops, like my experimental “N bar trailing stop” indicator, that just takes the low / high of N bars back and plots the value. This kind of a number is always on, so to speak. But if you want to trail a certain number of points behind your entry, for example, you can’t use the “dumb” stop method. You have to know where the entry took place. So how do you do it?

The way to make trailing stop strategies work is similar to the way I made my “Volatility-based trailing stop” indicator. The key is to reproduce the entry logic in your stop strategy routine. In that indicator, I had the logic for the long side and the short side together in the same set of code. This is needed for the calculation of the new stop loss value once we switch direction. Along with this, you need to define recursive functions that perform the trailing action. In the Volatility Stop, I’m actually always calculating the values of the long side stops and the short side stops according to the ATR. There is logic to check whether that value is outside the current trailed value (ignore it) or inside the current trailed value (update the trailing stop to the tighter level). This is done for both sides at the same time. There is one more set of logic to decide if we are in the long direction or the short direction, and that tells the indicator which one to plot.

Sound complicated? Actually, it’s HARDER to make the study indicator work than it is to make the strategy work! The strategy will automatically decide if you are long or short based on the criteria you give it for entries and exits. All you have to worry about is reproducing the entry logic in your exit, and the trailing logic.

Time for an example. Say you want to trail a stop N-points below your entry price, and then move it up if “low-N” is greater than the prior trailing stop value.

(Tangent–this is different than a true trailing stop like you might have with your broker, but you have to do it this way if you want to get any backtested results out of the indicator. This is because you will get falsely stopped out if your bar range is greater than your trailing N-points value if you track from the highest high, as a true trailing stop order would. If you could plot this on the tape, tick by tick, then it would work, but for any aggregated candles you have to do it this way. Here’s an explanatory picture:

TrailStopTangent

Confusing, I know; comment if you need more explanation.)

The following strategy skeleton, when fleshed out and paired with an entry strategy, will create a simple “N-points below the low” trailing stop:

declare LONG_EXIT;
# input the number of points to trail under the entry:
input trailstop=2.0;
# Reproduce Your Entry Code Below:
#################################

def trigger=if XXXXXX then 1 else 0; # check for entry condition
def orderprice=YYYYYYY; # What price? close, low, high, etc
#################################
# Now, initialize Trailing Stop
rec ts=if trigger then orderprice-trailstop else if low-trailstop>ts[1] then low-trailstop else ts[1];
# Send LONG_EXIT order if the low touches the trailing stop (ignore the entry bar):
def stopout=if trigger then 0 else if low<=ts then 1 else 0;
addorder(stopout,ts);

The part about reproducing your entry code should be self explanatory. You put the same code in that you are using for your entry. The strategy skeleton above uses two other lines:

def trigger=if XXXXXX then 1 else 0; # check for entry condition
def orderprice=YYYYYYY; # What price? close, low, high, etc

“Tigger” is the final check for your entry condition. “Orderprice” is where you put the price that you want the trade to be taken at, or the price where you would submit the order.

Now, the part with the recursive trailing logic is trickier, and is done in two parts, like this:

# Now, initialize Trailing Stop
rec ts=if trigger then orderprice-trailstop else if low-trailstop>ts[1] then low-trailstop else ts[1];

In english: If this is the entry bar then set the trailing stop N points below the order price. If it’s not the entry bar, then check if the value of low-N points > the last trailing stop value, and if it is, then that’s the new trailing stop value, but if it’s not, then keep the old one.

Then, you check if you actually get stopped out:

# Send LONG_EXIT order if the low touches the trailing stop (ignore the entry bar):
def stopout=if trigger then 0 else if low<=ts then 1 else 0;

In english: If this is the entry bar, then do nothing. If it’s not the entry bar, then if the low of the bar hit the trailing stop (or went below) then stop out, but if not, then do nothing.

The final step is in the code that actually adds the order:

addorder(stopout,ts);

In english: Add the order if we got a stop out. Use the value of the trailing stop as the order execution price.

This acts like we had an actual stop loss order in the market, and if it was hit we’d get out of the trade. So to wrap it all up, here’s an example set of files I made using a simple EMA Crossover as the entry condition. There are two studies for visualization, and two strategies–one for the entry and one for the exit:

Download Example Files

If you add all of this to a chart, it looks something like this:

TrailingStopExample51309

Trailing Stop Strategy in Think or Swim. Q.E.D.

You could reverse all of this logic to get the short side strategies as well. Now you should know enough to make your own trailing stop strategies. Unfortunately, I can’t create a standalone trailing stop strategy that you can just drop on a chart. There has to be some editing and coding done each time that you want to use a trailing stop with different strategy of your own. Feel free to ask questions if you need a pointer or two while making your own, I’m glad to help out!

Alternatively, if you would like me to do the work for you and create a custom trailing stop strategy to go along with an entry strategy you already have, contact me and I’ll code it for you for a flat $20 donation. You can choose any trailing style you like–trail N-points, ATR/volatility-based, percentage-based, etc. I do have to ask for new donations even from past donors for this specific offering due to the additional time I have to put in for each request, rather than just giving access to the products of my brain, as it were. The $20 pricing is good if you already have the entry strategy file to send me. If you need the entry strategy developed too, then it becomes a custom development request, and I’ll have to give you a quote on the number of hours it will take in order to determine the pricing. Either way, if you need some work done, send me an email.

Tags: , , ,

27 Responses to “Tutorial: Trailing Stop Strategies for Think or Swim”

  1. manatrader Says:

    Cool-o Prospectus, will try to wrap my head around the false stopouts issue with aggregate bars. Be well :)

  2. Prospectus Says:

    I’ll try to make a picture or two explaining what I mean.

  3. thinkscripter Says:

    Very nice!

  4. adamgreen Says:

    Nicely done. Have you published the code for this “EMA Cross” strategy?

    Also, do you use entry/exit pairs or just long/short?

    I’ve written some uncomplicated long/short pairs where the exit simply “rethinks” the exit, but I’m yet to convince the script to “rethink” the “StopLossLimit” as predictably as I’d like. It’s useful on 15m and greater samples, but in 133tk samples, it’s erratic.

    Adam

  5. RappidFyre Says:

    Fantastic work on the tutorial Prospectus.

    You are a fine crafter of precision tools as well as a great conveyor of education.

    It is all much appreciated.

    Keep up the great work and the great spirit in what you do.

    All your efforts will soon be greatly rewarded.

    Peace-

  6. Prospectus Says:

    Thanks for the nice feedback, everybody.

    Adam, I have published the EMA cross strategies in my first strategy
    tutorial, and I inlcuded the strategy code for the long entry in this
    post as well in the example files download, so I’m not sure what else
    you’re looking for? Maybe I need to make the download links more
    visible.

    Also, I don’t trade this EMA Cross method. I use it for tutorials and
    examples because it is easy to understand and to visualize.

    In some strategy methods that I have looked at seriously for intraday
    trading, I have about 8 strategies active at once, such as:

    Long entry
    Long profit target exit
    Long stop loss exit
    Long exit at close of day
    Short entry
    Short profit target exit
    Short stop loss exit
    Short exit at close of day

    And Think Desktop decides which ones happen in what order based on
    when the rules for each are met. If you are long and trigger an exit
    strategy, you go flat. If you are long and you hit the short entry
    trigger, you go flat and reverse. Think Desktop handles each case
    automatically as long as you provide the strategy files with the
    triggering conditions.

  7. fiki Says:

    great work. very helpfull…

    after starting to scripting my own studies I now try to learn about strategies…

    is it possible instead of trailing stops just make a strategy that after an entry eighter exits on a fixed target like 5 ES point or SL 2 points?

    thanx

  8. Prospectus Says:

    Thanks, glad you found it helpful!

    Yes, a fixed exit is easy. You have to reproduce the entry code in your exit strategy, and then you do something like this for a long exit :

    input limit=5;

    rec entryprice = if **stick entry condition here** then **stick entry price here** else entryprice[1];

    def target = if close>=entryprice+limit then 1 else 0;

    addorder(target,entryprice+limit);

    Hope that helps a bit!

  9. fiki Says:

    Thanx a lot… I somehow managed to make a (not so easy :D ) script for fixed exits yesterday using your trailing example… Really cool having the abillity baktesting ideas… To bad intraday tickchats only have historic data for 5 days in TOS.

    But I do belive I just found a new favorite blog to follow!

    Have a great day!

  10. Nihil Says:

    Very helpful indeed. Thank you Prospectus for the detailed explanations.

    However I must say that I tried a great deal to get stops and limits to work in strategies well but not too satisfied at the end of the day. So I decided to make a study that works like a strategy so that entries and exits can better talk to each other and so that the order-price can be more easily captured. So here, instead of a report, we get to look at the cumulative profit/loss chart in the study itself. I think this allows for easier implementation of trailing stops too. Do check out the page-link above for some additional write up and a screenshot.

    http://sites.google.com/site/badkrash/

  11. Prospectus Says:

    Nihil,

    That is a very interesting idea! It looks like it works well for visualization purposes. My only gripe would be that I would want to see the entries and exits annotated on the base chart, but that’s just me.

    Hopefully one day Think Desktop will get better strategy backtesting integration so all these workarounds aren’t needed. Thanks for sharing!

  12. Darrell Martin Says:

    I am stuck can anyone help me figure this out:

    I am trying to simply find a way to reference the highest/lowest close since a crossover

    the basic highest(close, xxx) or lowest(close, xxx) function in think script requires xxx to be a constant but I need it to be a variable

    i.e.

    a 50 day moving average

    if a bar has been above the 50 day moving average for 10, 40, 100, 349 etc… days and then closes below the 50 day ma

    then i need to be able to reference the highest close since it last crossed over from below to above the 50 day ma

    likewise after it has crossed below it – i need to be able to reference the lowest close since it crossed below the 50 day ma
    and going forward be able to do this (reference highest when closes above 50 day ma and reference lowest when closes below 50 day ma)

    i need this in order to be able to calculate my formula

    Step 1: start long
    start 6 bars in
    average of high-low for last 5 days x 1.5 = volmultiplier
    since long – subtract volmultiplier from highest close since beginning of chart
    plot this line on every close
    Step 2 IF price closes below plotted line then add volmultiplier to lowest close since crossover
    if price closes above plotted line then subtract volmultiplier to highest close since crossover
    Step 3 repeat step 2 infinitely

    I am running this on other platforms with no issues but am trying to get all my formulas over to TOS

    i tried if/then statements but thinkscript only allows for a certain size script so if it has been 100 bars since a crossover or 200 bars since a crossover r more it will not work

    i have everything but how to look a highest/lowest close since last crossover??

  13. Prospectus Says:

    You use a recursive statement. Assuming you have a definition of your crossover like this:

    def crossover = if [crossover logic] then 1 else 0;

    Then here is the logic you use:

    rec highestclose = if crossover then close else if close>highestclose[1] then close else highestclose[1];

    Similar for a crossunder. That logic is doing this, in english:

    If we are crossing over, then set the value to this bar’s close. If not, then if thus close is higher than the highest close since crossing over, then update it. If not, then just keep the highest close value from the last bar.

  14. guy H Says:

    I am loving using some of your tools on strategies,,

    Was wondering if you were ever able to use partial exits from positions, ..

    the addorder( condition , price , trade size ) for some reason seems to ignore the trade size . I tried to long entry with say trade size of 100 ,, and then use say in the long exit a tradesize of 50 ,, but the tos seems to always exit with the same quabtity as the entry ..
    What am I soing wrong ,,, any comments suggestions or smaple of something that works .

    Thanks

  15. guy H Says:

    I am loving using some of your tools on strategies,,

    Was wondering if you were ever able to use partial exits from positions, ..
    Resubmitted with a bit more care for spelling

    the addorder( condition , price , trade size ) for some reason seems to ignore the trade size . I tried to long entry with say trade size of 100 ,, and then use say in the long exit a tradesize of 50 ,, but the tos seems to always exit with the same quantity as the entry ..
    What am I soing wrong ,,, any comments suggestions or sample of something that works .

    Thanks

  16. Prospectus Says:

    If it’s not working, try entering two positions and only exiting one at a time. Also report the bug to TOS.

  17. Brad Says:

    hello,

    Id first like to say i appreciate resources like this that support learning. I am trying to write a short exit strategy that exits the short position once a +3 limit is hit. For some reason, I cannot get it to work. Here is what i have:

    #
    def trigger = if smasignal and isopen and adxsignal and macdsignal and rsisignal then 1 else 0;
    def orderprice = close;
    #

    input limit=3;

    rec entryprice = if trigger then orderprice-limit else orderprice[1];

    def target = if close<=orderprice-limit then 1 else 0;

    addorder(target,orderprice-limit);

    What am I doing wrong?

  18. Prospectus Says:

    Do you have smasignal and isopen and adxsignal and macdsignal and rsisignal all defined? What’s your error message? Do you also have the strategy defined as a short_exit?

  19. Brad Says:

    i do. have them all defined. they are my conditions for the entry. I do not get an error message, instead it just does not show entries or exits. They go away when I put this piece into the source for the exit. here is all of it

    # EMA Cross Short Entry
    # By Prospectus @ http://readtheprospectus.wordpress.com
    #
    # Input Declarations:
    #
    declare SHORT_exit;

    input opentime = 0930;
    input closetime = 1600;
    input SMA_F = 5;
    input SMA_S = 20;
    input dmilength = 14;
    input adxcrossingType = {default above, below};
    input adxthreshold = 20;
    input fastLength = 12;
    input slowLength = 26;
    input MACDLength = 9;
    input AverageType = {default EMA, SMA};
    input crossingType = {default “Positive to Negative”, “Negative to Positive”};
    input rsilength = 14;
    input rsicrossingType = {default below, above};
    input rsithreshold = 50;

    # enter during market hours only
    def AP = getAggregationPeriod();
    def daily = if AP >= aggregationPeriod.DAY then 1 else 0;
    def isopen = if daily then 1 else if secondsFromTime(opentime) >= 0 and secondsTillTime(closetime) >= 0 then 1 else 0;

    # SMA crossunder
    def smaunder = if simplemovingavg(close, SMA_S)> simplemovingavg(close, SMA_F) then 1 else 0;
    def smacross = if simplemovingAvg(close, SMA_S)[1] = simplemovingavg(close, SMA_F) then 1 else 0;
    def smasignal = if smaunder or smacross then 1 else 0;

    # adx crossover 20 or adx above 20 and increasing
    def adxincreasing = if DMI(dmilength).ADX[1] = adxthreshold then 1 else 0;
    def adxsignal = if Crossover(adxcrossingType == adxCrossingType.above, DMI(dmilength).ADX > adxthreshold) or adxincreasing then 1 else 0;

    # macd positive to negative cross or macd negative and decreasing

    def Diff = MACD(fastLength, slowLength, MACDLength, AverageType).Diff;

    def macddecreasing = if macd(fastlength, slowlength, macdlength, averagetype).diff macd(fastlength, slowlength, macdlength, averagetype).diff then 1 else 0;
    def macdsignal = if Crossover(crossingType == CrossingType.”Negative to Positive”, Diff > 0) or macddecreasing then 1 else 0;

    #rsi cross below 50 or rsi below 50 and decreasing
    def rsidecreasing = if rsiwilder(length = rsilength).RSI[1] > rsiwilder(length = rsilength).rsi and rsiwilder(length = rsilength).RSI rsithreshold) or rsidecreasing;

    #
    def trigger = if smasignal and isopen and adxsignal and macdsignal and rsisignal then 1 else 0;
    def orderprice = close;
    #
    # This code triggers the strategy on the chart.
    # Arg. 1 adds order if true, nothing if false
    # Arg. 2 is the entry price
    input limit=3;

    rec entryprice = if trigger then orderprice-limit else orderprice[1];

    def target = if close<=orderprice-limit then 1 else 0;

    addorder(target,orderprice-limit);

    # Formatting:
    #
    SetColor(color.green);

  20. Wolfetrust Says:

    Prospectus,
    I’m fairly new to TOS and found your site via google. I have a strategy that I’ve worked on that suits me, but have little experience with TOS scripting. I’d like to forward you the laymans terminology to get your opinion on what you might be able to do to reflect it within a script to make conditions for entry/exit both long and short appear clearly on a chart. Happy to donate for your efforts. Please email me if interested.

    Best,
    Paul

  21. Sean Says:

    Prospectus. I am trying to recreate TOS’s ATRTrailing Stop Indicator into C#. I have Visual Basic Files and Think Script Files for the indicator. But C# is the end goal. Any advice? Will donate for help.
    Thanks

  22. Prospectus Says:

    I can help you next week. Email me

  23. Tom Jameson Says:

    This may be a stupid request, but can you tell me the custom criteria for
    thinkorswim for the following?
    Simply, the “last 15 minutes of trading”

  24. Raf Says:

    Hi, can you please offer additional details regarding your “N bar trailing stop” indicator? I am trying to understand what the criteria is for the lines breaking? Is it because the candlestick is unable to take out the previous “x” number of highs, but succeeds in taking out the previous “x” number of lows (in an uptrend)? …in other words, using your default value of “2″ what criteria determines the beginning/end of the trailing stop? I have created a screen cap to explain this question further: http://oi44.tinypic.com/xlwmeb.jpg
    Regards.

  25. Prospectus Says:

    The trailing stop shown is simply the high or the low of N bars ago. It is always updating on each bar. The lines on your chart are breaking because the current bar has broken the high or low of 2 bars in the past. That can happen even if the current bar doesn’t move very far if a wide range bar drops off the back end and is replaced by the next bar. In that sense, it’s not a true trailing stop.

  26. Raf Says:

    Hey, thanks for the explanation. I think I get it now.

  27. Robots Says:

    Robots…

    [...]Tutorial: Trailing Stop Strategies for Think or Swim « Read the Prospectus[...]…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

Join 106 other followers

%d bloggers like this: