StockFetcher Forums · General Discussion · Technique: Calc your own returns - for any period<< 1 2 >>Post Follow-up
cegis
235 posts
msg #33296
Ignore cegis
9/17/2004 5:23:18 PM

This is a discussion of a technique that I developed a few months back, and has seemed to be useful regularly in other threads ever since. So, I decided to document it, in the hope it will be of use to others.

This technique allows you to calculate returns for a filter, for any time period, and (almost) any assumed trading "program".

What StockFetcher offers as basic site functionality is a column labeled "Performance" when showing results from a filter that has the "offset" phrase. The performance shown (I think - correct me if I'm wrong) is based on the closing price of the stock on the day that the stock matched the filter (the offset date) and yesterday's close. This means that the period covered by the column changes with the offset. You can also get the 1 day, 2 day, 3 day, 4 day, one week, two week, etc. performance for the filter's results by clicking on the Performance column header, and "browsing through the dates" (assuming there have been that many trading days in the offset period). This is good, but limiting.

The scenarios that SF's Performance functionality does not take into account are things like "performance for a 10 day period, regardless of the start date", "worst case performance" using the day-after-match's high and end-of-period low as entry/exit points, or "trading program rules" such as max loss of 2%. This technique allows such analysis.

The "trick" to this technique is to use the "days ago" phrase instead of the "offset" phrase. This allows you to "see what happened" AFTER the stocks matched your filter. As an example, if you use "offset 10 days", SF will not allow you to retrieve ("see") values from what would be "offset 9 days". However, if you use "10 days ago", you can always analyze what happened "9 days ago".

Let's look at a simple example. We'll assume our starting filter is:

Fetcher[
rsi(2) is less than 1
and price is between 1 and 10
and average volume(25) is greater than 100000
]



If we run this filter on Sat. 9/25/04 (which will be our assumed run date for all of the following discussion), we'll see the results for 9/24 market close. Now, let's assume we want to see how this filter performed based on picks from 10 days ago. If we use

Fetcher[
rsi(2) is less than 1
and price is between 1 and 10
and average volume(25) is greater than 100000
offset 10 days
]



we'll get selections for 9/10, and we'll get the Performance column talked about above (9/10 to 9/24, close to close), as standard SF functionality. However, if we wanted to see what a "worst case" scenario would have been, we would not be able to access the data for 9/11's high or 9/24's low to make this determination. However, if we instead used

Fetcher[
rsi(2) 10 days ago is less than 1
and price 10 days ago is between 1 and 10
and average volume(25) 10 days ago is greater than 100000
]



we'll still get the results of our original filter as of 9/10, but now we can calculate our worst case scenario:

Fetcher[
set{myrtn,low - high 9 days ago}
set{myrtnp,myrtn / high 9 days ago}
set{wcrtnpct,myrtnp * 100}

rsi(2) 10 days ago is less than 1
and price 10 days ago is between 1 and 10
and average volume(25) 10 days ago is greater than 100000

add column high 9 days ago {entry}
add column low {exit}
add column myrtn {profit}
add column wcrtnpct {profit percent}
]



Since all of our conditions have "10 days ago", the first set{} command is "looking into the future" to get the following day's high (high 9 days ago), and the 10th day's low (low [0 days ago]), to calculate the return ($). The other two set{}s just convert the first set{} into a percentage. (I'm using my entry as the basis for the percentage...) The add columns will show us our result.

Note that the return calculated will ALWAYS be a ten-day return. We can continue to backtest this filter by adding an "offset" too!

Fetcher[
set{myrtn,low - high 9 days ago}
set{myrtnp,myrtn / high 9 days ago}
set{wcrtnpct,myrtnp * 100}

rsi(2) 10 days ago is less than 1
and price 10 days ago is between 1 and 10
and average volume(25) 10 days ago is greater than 100000

add column high 9 days ago {entry}
add column low {exit}
add column myrtn {profit}
add column wcrtnpct {profit percent}

offset 1
]



With the offset 1, we'll actually match stocks for 9/9, and show what the 10 day return is through 9/23. You can then alter the offset to continue backtesting other dates. In other words, you do NOT have to keep changing the "days ago" in order to backtest prior dates. Just remember that SF will report "results as of 9/24" (or whatever your offset would suggest) at the top of the results list, and you have to remeber that you used "days ago" to shift the data back an additional 10 days.

Also note that you can calcualte multiple returns for the period covered by the "days ago" offset:

Fetcher[
set{myrtn10,low - high 9 days ago}
set{myrtn10p,myrtn10 / high 9 days ago}
set{wc10rtnpct,myrtn10p * 100}

set{myrtn5,low 5 days ago - high 9 days ago}
set{myrtn5p,myrtn5 / high 9 days ago}
set{wc5rtnpct,myrtn5p * 100}

rsi(2) 10 days ago is less than 1
and price 10 days ago is between 1 and 10
and average volume(25) 10 days ago is greater than 100000

add column wc5rtnpct
add column wc10rtnpct
]



Here, we added a claculation of the 5 day worst case scenario, in addition to the 10 day return.

This can be taken one step further (this is where this gets REALLY interesting!) by using "logical variables" to actually implement a "trading program". (Logical variables are discussed in several other threads, so I'm not gonna go into them here...) Let's say that you want a 2% trailing stop, and you'll hold for a max of 3 days. (I'll discuss this day limit later.) We'll assume we enter at the opening price, and exit at our stop or 3 day close. We'll also assume we only adjust our stop once a day based on the open. This filter will show you how well you would do:

Fetcher[
set{limit1,open 2 days ago * 0.98}
set{in1,count(low 2 days ago is greater than limit1,1)}
set{stopped1,1 - in1}
set{out1,stopped1 * limit1}

set{limit2, open 1 day ago * 0.98}
set{in2,count(low 1 day ago is greater than limit2,1) * in1}
set{stopped2a,1 - in2}
set{stopped2,stopped2a * in1}
set{out2,stopped2 * limit2}

set{limit3, open * 0.98}
set{in3,count(low is greater than limit3,1) * in2}
set{stopped3a,1 - in3}
set{stopped3,stopped3a * in2}
set{out3,stopped3 * limit3}

set{out3c,close * in3}

set{outa,out1 + out2}
set{outb,outa + out3}
set{out,outb + out3c}

add column open 2 days ago {in}
add column out1
add column out2
add column out3
add column out3c

rsi(2) 3 days ago is less than 1
and price 3 days ago is between 1 and 10
and average volume(25) 3 days ago is greater than 100000
]



So here, out1, out2, out3, and out3c will show you your exit price if you exit on the first, second, or third days, or at the close of the third day (respectively). They will be ZERO if you did not exit on that day. Thus, you can see when you would have exited, as well as how well you would have done.

There are two things I would have liked to do in the prior filter, but SF limits how many times you can "nest" set{} statements (that is, base one set{} on the results of another). I would have liked to change the calculation of limit2 and limit3 so that it only goes up in value (a trailing stop), as well as calcaulte the $ and percentage return based on my exit price. I would have also liked to check for "green hold" (high 2 days ago > close 3 day ago) before considering myself "in". However, this filter sits right at the edge of the SF nesting limit, so I was unable to do that. That's also why I chose a three day maximum hold period.

One way to get around this, somewhat, is to show results for every day (instead of figuring out if you would have been stopped), like this:

Fetcher[
set{limit, open 9 days ago * 0.98}

set{profit1, high 9 days ago * 0.98}
set{profit2, high 8 days ago * 0.98}
set{profit3, high 7 days ago * 0.98}
set{profit4, high 6 days ago * 0.98}
set{profit5, high 5 days ago * 0.98}
set{profit6, high 4 days ago * 0.98}
set{profit7, high 3 days ago * 0.98}
set{profit8, high 2 days ago * 0.98}
set{profit9, high 1 days ago * 0.98}

add column open 9 days ago {in}
add column limit
add column low 9 days ago {low1}
add column profit1
add column low 8 days ago {low2}
add column profit2
add column low 7 days ago {low3}
add column profit3
add column low 6 days ago {low4}
add column profit4
add column low 5 days ago {low5}
add column profit5
add column low 4 days ago {low6}
add column profit6
add column low 3 days ago {low7}
add column profit7
add column low 2 days ago {low8}
add column profit8
add column low 1 days ago {low9}
add column profit9

rsi(2) 10 days ago is less than 1
and price 10 days ago is between 1 and 10
and average volume(25) 10 days ago is greater than 100000
]



Now, you can read across the columns, and if you assume you use 2% below the high as the following day's stop, all you have to do is read across the columns to see where a "low" value is less than the prior column (profit or limit) to see where you would have been stopped out.

This isn't perfect, but it can get you some useful information!

Things to Consider
==================
- The number of days you want to use in the "days ago" phrase is the maximum holding period (or days worth of data you need) to calculate your returns. You do not want to use a larger number.

- If your filter compares to prior days (ie, something like "open is greater than close 1 day ago"), you want to ADD the number of days in this calculationally-based days ago to the condition's days ago (in this case, making it something like "open 10 days ago is greater than close 11 days ago").

- Note that the days ago prevents you from running the backtest as of a date "closer" to today than the number of days ago used. I do not consider this a major limiting factor.

- SF will report "Filter results for <day of week> <date>". This will be based on the offset, not your days ago, value. You just need to remember that you have programmed an additional offset into the filter for backtesting purposes.

- Whenever I use this technique, I first copy the filter that I want to backtest, then modify it to do the calculations and use the days ago that I need. You don't really want to use days ago (in the manner described here) in a filter you're gonna actually select stocks from...

- Depending on your trading conditions and return calculations, you may be able to use the count() and sum() functions, and/or the "<n> days high" / "<n> days low", to look at longer time frames than the three days as in the example.

- If you are backtesting a weekly filter, use "weeks ago" instead of "days ago". Also, funky things will happen if your filter mixes weekly and daily indicators. (Basically, the "days ago" and "offset" are not quite equivalent due to the way SF handles weekly values...)

- Remember that you can download the results into a spreadsheet, and then use that to extend the backtest based on any logic you couldn't get into the filter.

Hope this helps,

C


Joules360
30 posts
msg #33300
Ignore Joules360
9/17/2004 11:56:53 PM

Cegis:

Wow!..You definitely know your stuff!...I apreciate your work...Stockfetcher should hire you as a consultant...Thanks for the help on my Le-Sling scan backtest as well.

I haven't had time to read all the threads here..but is there a scan you prefer and have had good success with? I like my Le-Sling...but I am always open to another great scan for finding winners.

My strategy is to find the meat of the run of a documented safe uptrending stock...NO pennies etc. I have my whole retirement that I'm controlling and the pennies just dont do it for me...hehe...

Just looking for 10% hits over 10 days or so to avg with the small gainers and losers to attain that 1% week net gain. Doubling my money every 2 years is my goal. You probably have read the system of buying/selling I am using.

I understand if you dont want to disclose it...but being that you are so proficient in this programming...I figured you must have backtested some that have great potential?

Thanks!
Joules360


cegis
235 posts
msg #33323
Ignore cegis
9/20/2004 8:48:37 AM

Joules360,

> I apreciate your work...

I'm glad somebody does! <GRIN>

> Stockfetcher should hire you as a consultant...

I Agree!!! (I could use the work!)

> Thanks for the help on my Le-Sling scan backtest as well.

Glad I could help...

> I haven't had time to read all the threads here..but is there a scan you prefer and have had good success with?

To be honest, I've yet to find a filter that *consistently* produces more winners than losers. Most that I have backtested seem to follow the general market, more or less. Money management (therefore) is the key to producing an overall gain on a short-term basis (or any other term basis, for that matter<g>).

Good luck!

C


Joules360
30 posts
msg #33328
Ignore Joules360
9/20/2004 2:08:17 PM

Cegis:

I agree the general market is a factor in all of these scans...that is why I developed the opposite Le-Sling scan for shorts as well as well as the Long scan...This way...if the overall daily trend is down...you can go with the short picks...My window trade broker does not allow shorting...so I will have to deal with the longs only.

Joules360


kgriffen
49 posts
msg #33339
Ignore kgriffen
9/21/2004 8:38:11 PM

cegis,

I am currently furiously trying your suggestions. They are great!

How would you re-write this line for ten days ago?
and slow stochastic(14) Fast %K 1-week low was less than 50 within the last 5 days


kgriffen
49 posts
msg #33340
Ignore kgriffen
9/21/2004 9:03:46 PM

Ok, I think I need help. I am getting set{} errors in debug. Maybe for ease I can post my filter here for suggestions:

Here is my Mother of All Filters:

Fetcher[/* Parameters */
close was above open
and close was above ema(14)
and close yesterday was below ema(14) yesterday
and ema(14) one day ago was decreasing for 3 days
and accumulation distribution is greater than accumulation distribution yesterday
and macd(12,26) is increasing for 1 day
and MACD Fast Line(12,26) one day ago was below 0
and MACD Slow Line(12,26) one day ago was below 0
and slow stochastic(14) Fast %K 1-week low was less than 50 within the last 4 days
set {macdtouch,count(MACD Fast Line(12,26) crossed MACD Slow Line(12,26),9)}
and macdtouch is less than 2

/* Chart Options */
and do not draw slow stochastic(14) Fast %K 1-week low
and do not draw macdtouch

/* Column Options */
and add column high
and add column low
and add column close
and add column ema(14)

/* Volume Price Exchange Limiters */
and exchange is sp500
]



Now, that gives me a list of SP500 stocks that have closed above their ema14 today with a white candle, and has Stoch and MACD features that I like.

Tommorrow, I would only purchase the stock if it went at least .05 cents above today's high, confirming what I hope to be the start of a trend. So my one day backtest filter reads as so:
Fetcher[/* Parameters */
close yesterday was above open yesterday
and close yesterday was above ema(14) yesterday
and close two days ago was below ema(14) two days ago
and ema(14) two days ago was decreasing for 3 days
and accumulation distribution yesterday was greater than accumulation distribution two days ago
and macd(12,26) yesterday was increasing for 1 day
and MACD Fast Line(12,26) two days ago was below 0
and MACD Slow Line(12,26) two days ago was below 0
and slow stochastic(14) Fast %K 1-week low was less than 50 within the last 5 days
set {macdtouch,count(MACD Fast Line(12,26) crossed MACD Slow Line(12,26),10)}
and macdtouch is less than 2

/* Chart Options */
and do not draw slow stochastic(14) Fast %K 1-week low
and do not draw macdtouch

/* Column Options */
and add column high
and add column low
and add column close
and add column ema(14)

/* Volume Price Exchange Limiters */
exchange is sp500

/* Purchase Parameters */
set {pricediff,high-high yesterday}
pricediff is above .05
and do not draw pricediff
]




Now, here comes the tricky part. I immediatly set a stop for 3% -OR- .01 below yesterday's low, whichever is less. I also will close the position if the close falls below the 14 day EMA at any time.


With your new advice above, I would like to build a filter that tests backwards 5 days. I am having problems with these two lines:
Fetcher[and slow stochastic(14) Fast %K 1-week low was less than 50 within the last 5 days
set {macdtouch,count(MACD Fast Line(12,26) crossed MACD Slow Line(12,26),10)}
and macdtouch is less than 2
]



and I would also like to see if I got stopped out either by my stop or by the close below ema14. I tried to write it, but my brain is at its own set{} limit.

Any help would be appreciated! I am currently seeing how Le-Sling may help this filter as well. :)

-kg


cegis
235 posts
msg #33343
Ignore cegis
9/22/2004 8:43:53 AM

kg,

First, there's an issue with the way you wrote these two lines in your backtest filter. The way you have it is not a "1 day ago" equivalent to the "normal" filter. What you want for these two lines is

Fetcher[
and slow stochastic(14) Fast %K 1-week low 1 day ago was less than 50 within the last 4 days
set {macdtouch,count(MACD Fast Line(12,26) 1 day ago crossed MACD Slow Line(12,26) 1 day ago,9)}
and macdtouch is less than 2
]



Note that when you are trying to "shift" an expression by a number of days, you need to shift *each indicator*, not the period of time covered by the phrase. Your "slow stoch... ...the last 5 days" and "count(... ..,10)" are both increasing the time period viewed by the filter - the condition has to be true for a longer period of time, and/or you're covering days not covered in the original filter. You want to add "1 day ago" (or "yesterday", as you point out) to the values being inspected, and leave the time period alone.

Is this what you mean by "having problems with these two lines"? (For future reference, it helps *a lot* if you can be *very* specific as to what problems you seem to be having. Just friendly advice...)

Oh, as for your first post's question, I'd write the line as:

and slow stochastic(14) Fast %K 1-week low 10 days ago was less than 50 within the last 5 days

You can test this by adding columns for "slow stochastic(14) Fast %K 1-week low 10 days ago" as well as "slow stochastic(14) Fast %K 10 days ago", 11 days ago, 12 days ago, 13 days ago, and 14 days ago, and manually verify that the 1-week low is the lowest of the individual values.

One last thing: The filter debugger currently only allows 10 set{} statements before it shows an error message. The actual filter execution does not have such a limit, so the "limit exceeded" message can be ignored in this case. You know when you've hit the filter's nesting limit when it will no longer show the variable being set{} as a column in the results. If you are still having trouble, post the filter with the error and I can take a look at it.

HTH,

C


kgriffen
49 posts
msg #33368
Ignore kgriffen
9/23/2004 10:44:00 PM

cegis,

very helpful as always!

thanks,
kg


cegis
235 posts
msg #33370
Ignore cegis
9/24/2004 8:21:35 AM

kgriffen,

Glad I could help!

Good luck!

C


cegis
235 posts
msg #34758
Ignore cegis
1/5/2005 11:57:01 AM

People have been asking about this over the past few days, so I thought I'd bring it to the top of the list of threads...

This allows you to backtest for any period, including some entry/exit strategies.

HTH,

C


StockFetcher Forums · General Discussion · Technique: Calc your own returns - for any period<< 1 2 >>Post Follow-up

*** Disclaimer *** StockFetcher.com does not endorse or suggest any of the securities which are returned in any of the searches or filters. They are provided purely for informational and research purposes. StockFetcher.com does not recommend particular securities. StockFetcher.com, Vestyl Software, L.L.C. and involved content providers shall not be liable for any errors or delays in the content, or for any actions taken based on the content.


Copyright 2016 - Vestyl Software L.L.C.Terms of Service | License | Questions or comments? Contact Us
EOD Data sources: DDFPlus & CSI Data Quotes delayed during active market hours. Delay times are at least 15 mins for NASDAQ, 20 mins for NYSE and Amex. Delayed intraday data provided by DDFPlus