Days above the ATR Trailing Stop


Category:
0
0

Hi Pete,

I would like to run a scan for stocks that have been above their ATRts for at least the last 10 days.

Marked as spam
Posted by (Questions: 2, Answers: 21)
Asked on February 28, 2017 11:37 am
875 views
1
Private answer

To create this scan you would copy and paste the code included in the built in version on Thinkorswim, named “ATRTrailingStop”.

  1. Delete the last several lines of code beginning with plot TrailingStop = trail;
  2. Add the following lines of code to the bottom of what you pasted in step 1

def aboveTrailingStop = state == state.long;
plot scan = Lowest(aboveTrailingStop, 10) > 0;

Marked as spam
Posted by (Questions: 37, Answers: 4118)
Answered on February 28, 2017 12:32 pm
0
Private answer

Great, that’s working.  Perusing through the scan results, I’ve noticed that some stocks have spent more time above the ATRts than others.  Is there a way to count how many days they have spent above the ATRts since crossing, so that the results can be sorted on the scan screen?

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on February 28, 2017 1:21 pm
0

Yes, we can do that with a custom column in a watchlist technique. The columns in the scan results can take custom script just like watchlists and the MarketWatch screen.
Using the same code from the built in ATRTrailingStop, we delete the same lines as before and then we need to add a few new ones.
def changeOfState = state[1] == state.short and state == state.long;
rec longStateCounter = CompoundValue(1, if changeOfState then 1 else longStateCounter[1] +1, 0);
plot data = longStateCounter;

( at February 28, 2017 1:58 pm)
0
Private answer

The scan column is producing results, but the numbers are way off compared to the bars on the chart that are over the ATRts.  Your thoughts.

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on February 28, 2017 2:43 pm
0

Check the aggregation period and make sure each is set to the same. I just tested it on Daily and spot checked a few results. The counts are an exact match.

( at February 28, 2017 5:22 pm)
0
Private answer

I’ve obviously overlooked something.  The aggregation period is un-checked.  For example, AAL shows up in the list with a value of 28 and it is not even above the ATRts, rather it has been below for over 20 bars (daily).

I set up the scan and the custom column with this script. http://tos.mx/ibLgve

 

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on February 28, 2017 8:13 pm
0

Unfortunately the custom column you included in your scan results did not carry over into the shared item.
”The aggregation period is un-checked”… definitely something amiss there. What do you mean by un-checked? The only time aggregation period displays a checkbox is when it is set to intraday. The checkbox is used to determine whether to include extended hours data in the custom column or scan. The aggregation for both the scan and the custom column should be set to ”D”.

( at February 28, 2017 9:34 pm)
0
Private answer

When you referenced the aggregation period, I thought you were referring to the chart > settings > equities > start aggregation at market open, which is un-checked on the daily chart.  The scan aggregation is set to “D”.  There is a difference in the ATR Period, the ATR Factor and AverageType (EXPONENTIAL instead of WILDERS) from the TOS version, but that shouldn’t cause the issue I’m having.  This is how the script looks for the custom column and the scan that I’m using.
input trailType = {default modified, unmodified};
input ATRPeriod = 9;
input ATRFactor = 2.9;
input firstTrade = {default long, short};
input averageType = AverageType.EXPONENTIAL;

Assert(ATRFactor > 0, “‘atr factor’ must be positive: ” + ATRFactor);

def HiLo = Min(high – low, 1.5 * Average(high – low, ATRPeriod));
def HRef = if low <= high[1]
then high – close[1]
else (high – close[1]) – 0.5 * (low – high[1]);
def LRef = if high >= low[1]
then close[1] – low
else (close[1] – low) – 0.5 * (low[1] – high);

def trueRange;
switch (trailType) {
case modified:
trueRange = Max(HiLo, Max(HRef, LRef));
case unmodified:
trueRange = TrueRange(high, close, low);
}
def loss = ATRFactor * MovingAverage(averageType, trueRange, ATRPeriod);

def state = {default init, long, short};
def trail;
switch (state[1]) {
case init:
if (!IsNaN(loss)) {
switch (firstTrade) {
case long:
state = state.long;
trail = close – loss;
case short:
state = state.short;
trail = close + loss;
}
} else {
state = state.init;
trail = Double.NaN;
}
case long:
if (close > trail[1]) {
state = state.long;
trail = Max(trail[1], close – loss);
} else {
state = state.short;
trail = close + loss;
}
case short:
if (close < trail[1]) {
state = state.short;
trail = Min(trail[1], close + loss);
} else {
state = state.long;
trail = close – loss;
}
}

def BuySignal = Crosses(state == state.long, 0, CrossingDirection.ABOVE);
def SellSignal = Crosses(state == state.short, 0, CrossingDirection.ABOVE);

def changeOfState = state[1] == state.short and state == state.long;
rec longStateCounter = CompoundValue(1, if changeOfState then 1 else longStateCounter[1] +1, 0);
plot data = longStateCounter;

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on February 28, 2017 10:14 pm
0
Private answer

See screenshot below. This shows the custom column in the scan results. You can see it is set to daily aggregation period and matches the aggregation period used for the scan. After spot checking several stock tickers on a daily chart I found the counts were an exact match.

Attachments:
Marked as spam
Posted by (Questions: 37, Answers: 4118)
Answered on February 28, 2017 10:23 pm
0
Private answer

First of all, my bad.  I overlooked the aggregation for the custom column and fixed that so it is set to “D”.  Now that everything is correct, just for grins and giggles, I ran the scan on the S&P 500 and it came back showing that 505 stocks meet the scan criteria.  That can’t be right, so I started down through the symbols to look at their charts.  The second symbol (AAL) shows it has been above the ATRts for 23 bars (my settings are slightly different than yours) and when I looked at the chart, AAL has been under the ATRts for 21 bars.  It shows BBY being above for 156 bars, the last time it was above the ATRts was 12/20/16. The ATRts settings for the chart are identical to the scan settings.  I am beginning to think there is something wrong with my computer or TOS.

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 1, 2017 7:21 am
0

Only thing I can offer is that I am using default settings on everything and everything is matching. All I did was copy the code directly from the chart study, then pasted it straight into the scan and custom column. I only deleted the lines and added the lines as I described in my answer. I suggest you start from scratch, with clean code straight from the built in version of the study that comes with Thinkorswim. Assuming that matches, then proceed to change ONE setting at a time, checking for accuracy after each change. I just checked the scan that you shared in your earlier post. It is set to use Wilders and NOT Exponential. That is likely causing the discrepancy. But be sure to go through the trouble shooting steps if you still have issues.

( at March 1, 2017 8:21 am)
0
Private answer

I followed your suggestion from scratch and I’m getting the same results I had before.  The scan is returning all of the stocks in the S&P 500, for example and the numbers don’t make sense.  This morning I also noticed that one of my other scans (in a watchlist) that has been working for months, is only partially working.  It is on a daily time-frame and some of the others on different time-frames are working just fine, so I called TOS support.  They installed a clean version and we still had the same issues. Aha…want to know something interesting?  The ‘ol pea sized brain just had an idea.  Daily appears to have a problem so I changed the ATRts (scan and custom column) to run on an hourly and everything is spot on.  Guess I’m going to have to contact TOS and let them know what I found.  Will keep you informed of what I find out.

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 1, 2017 10:21 am
0

K, best of luck to you!

( at March 1, 2017 11:18 am)
0
Private answer

Ok Pete, here is the reply I received this afternoon from the TOS development team.  I understand what he is saying about the script counting everything, which is why it’s counting when stocks are under the ATRts.  Is there a way to stop the counter when the stock crosses under the ATRts so that only the longs are displayed?

Hello Bob,

Thank you for the message.  There are a couple things you may want to look at to correct these studies.  The issue with this study is occurring in the last several lines of the script.  Below is a piece of the code you have emailed that is calculating a little differently than what you need.

def BuySignal = Crosses(state == state.long, 0, CrossingDirection.ABOVE);

def SellSignal = Crosses(state == state.short, 0, CrossingDirection.ABOVE);

def changeOfState = state[1] == state.short and state == state.long;

rec longStateCounter = CompoundValue(1, if changeOfState then 1 else longStateCounter[1] +1, 0);

plot data = longStateCounter;

This section of the first script uses the change of state variable you have defined to reset the counter back to 1.  The issue here is that there is nothing set to reset the counter when the ATR flips to a short indicator.  So the counter will count even while the ATR has flipped and then reset when it becomes a long indicator once more.  You will want to define a condition to reset the counter when the ATR is giving a short indicator as well.

I hope this helps.  If you have additional questions please let us know.

Garrett Kelly

Thinkscript

 

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 7, 2017 6:00 pm
0

I think you may be mixing two separate solutions. In the first, we devised a scan that picks up a list of stocks meeting your criteria ”10 days or more above the ATR Trailing Stop”. The second request was for a custom column that would contain the actual number of bars a stock has been above the ATR Trailing Stop. So that you could sort the results list by that column.
You did not request to count the bars under the ATR Trailing Stop. Nor did you request to reset the count when prices crossed below. I provided that along with a screenshot showing it at work. If you used the original solution for the scan, and the second solution for the custom column you should get a perfect match.
It seems you may be trying to use the second solution in the scan. Which is never going to work. Let me state that another way. ”plot data = longStateCounter;” is meant for a custom column and NOT for a scan. The scan must use the first solution: ”plot scan = Lowest(aboveTrailingStop, 10) > 0;”
I hope this helps because I’m starting to get lost myself at this point.

( at March 7, 2017 6:45 pm)
0
Private answer

Please accept my apologies for any confusion I may have caused.  After reading your recent post, I realized I misread how to utilize the scans.  I went back and reset everything per your suggestions and all is working as you stated it should.  Again, thank you for your patience, persistence and experience.

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 8, 2017 8:13 am
0

Awesome! So glad we preserved and got this working for you!

( at March 8, 2017 10:04 am)
0
Private answer

Now that everything is working in the long direction, I thought it would be simple to reverse the scan in the short direction, however it’s not.  Here’s what I changed and it is not showing just those stocks that have been under the ATRts for 10 days or more, instead it is showing all of them.  How do I get the scan to obey your command?

def BuySignal = Crosses(state == state.long, 0, CrossingDirection.ABOVE);
def SellSignal = Crosses(state == state.short, 0, CrossingDirection.ABOVE);
def belowTrailingStop = state == state.short;
plot scan = Highest(belowTrailingStop, 10) > 0;

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 21, 2017 2:08 pm
0

I forgot to also mention, how is the counter set to count those occurrences below the ATRts? The long counter works perfect, so I tried altering it to show the short counter and it didn’t work.

( at March 21, 2017 2:16 pm)
0

plot scan = Highest(belowTrailingStop, 10) > 0;

This part is not correct. I understand it’s intuitive to think you simply reverse everything to reverse the signal. But this still needs to using the Lowest() function, not the Highest() function.

Why? Because the variable you are using, belowTrailingStop, is a true/false condition. It is either a 1 or a zero. So we need to check if that variable has been greater than zero for the last 10 bars. In plain english: The lowest value of condition in previous 10 bars is greater than zero. Which means it must be 1 (true) for the previous ten bars.
As for the short side counter. You did not provide the code you tried to use but this is the code that should work. (but not if you are combining a short and long counter in the same code)

def changeOfState = state[1] == state.long and state == state.short;
rec shortStateCounter = CompoundValue(1, if changeOfState then 1 else shortStateCounter[1] +1, 0);
plot data = shortStateCounter;

( at March 21, 2017 3:57 pm)
0
Private answer

Now that I think about it, that scan logic makes sense.  Thanks for that.

The counter is exactly what I was using, but didn’t include it because I thought there was something wrong with the scan.  Now that the scan is fixed, the counter is working just fine.

On my scan results screen I have a column for short counts and a column for long counts, now if I run a scan for just the longs, the results populate as expected and numbers for the short count are also appearing, but they are completely wrong.  If I run a long scan for example, shouldn’t the short column just be blank?  The long column doesn’t even mention the short counter.  This is how the long counter appears:

def BuySignal = Crosses(state == state.long, 0, CrossingDirection.ABOVE);
def SellSignal = Crosses(state == state.short, 0, CrossingDirection.ABOVE);

def changeOfState = state[1] == state.short and state == state.long;
rec longStateCounter = CompoundValue(1, if changeOfState then 1 else longStateCounter[1] +1, 0);
plot data = longStateCounter;

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 21, 2017 4:45 pm
0

The long and short counters were designed as single taskers. That’s what we end up with when we don’t begin with the full specifications up front. Most often it is easiest to code the complete specification up front rather than having to go back and mash things together. Things that were not designed to work together. But I understand when you are trying to learn something it’s easier to consider one side at a time. I recommend you use it as is for now. Then come back to it in a week or two. See if you can understand the code enough to mash them together.

( at March 21, 2017 5:58 pm)
0
Private answer

Will give it a try. Thanks.

Marked as spam
Posted by (Questions: 2, Answers: 21)
Answered on March 21, 2017 6:45 pm