Rotation of 5 Momentum stocks (like Clenow)

Hello everybody,

I like Amibroker because of its unlimited possibilities and the helpful and skilled community here. But I am quite new with Aminbroker.

I want to set up a rotational stock trading system, according to the rules of the book from Andreas Clenow ("Stocks on the move", 2015).
Clenow selects the top momentum stocks from the S&P500. He buys the stocks with the highest momentum (within the last 90 days). He sells the stocks when they loose momentum, which he gets from the rank (=PositionScore). He changes positions once per week. He has more complex rules (position sizing, linear regression instead of simple RSL etc. since he claimes to trade it for a fund) but I want to start very simple first.

Here are the buy/sell rules of my Clenow-system which I want to build:

Entry: Buy the top 5 stocks from the S&P500, with the highest Momentum = RSL(90)
Exit: Sell a stock and replace it with a new one (top 5), when it falls out of the top 20% in the list (=less than rank 100 in the S&P500).

Rotation should take place weekly.

So here is my starting code for the entry. The 20% exit is still missing (since I am not a good programmer, but I want to learn it).

SetOption("InitialEquity", 1000000); 
SetBacktestMode(backtestRotational); 
SetOption("MaxOpenPositions",5);

//The number of issues to hold at a time
NumberHeld = 5;
//Optimize("NumberHeld",1,1,10,2);


//Allocate funds equally among all issues (das war alt, nun fixed amount 21.10.2017; so einfach wie möglich)
PositionSize =-100/(NumberHeld);

LookBack =90;//Optimize("LookBack ",1,1,200,5);

Score = exp(ROC(C,LookBack)); // make sure it is positive ("by adding big constant") ; better is exp function here

PositionScore = Score; //THIS IS REQUIRED for rotational mode. In the rotational mode the trades are driven by the values of the PositionScore variable alone!

My first problem is that I do not get 5 positions at a time. It starts with 5 positions but soon there are only 4. See the following screenshot (detailed log).

4 Open Positions_weekly

Probably some components in my code are missing?! Which?

Your help is much appreciated. Thanks, Michael

1 Like

Dear all,

sorry, the picture was not readable. Here you find the text of the detailed log.

I don’t understand this : 846900 not entered because of insufficient funds or wrong position size/value , see below the beginning of the “Detailed log”

Date Information
07.01.2000 00:00:00
Scores:B55750=1.#INF, D23610=1.#INF, D50000=3.145718e+023, D16460=9.392492e+016, PSM777=7.506841e+013,
Enter Long, B55750, Price: 64.74, Shares: 2019.138, Commission: 0, Rank: 1.#INF, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
Enter Long, D23610, Price: 76.02927, Shares: 1719.324, Commission: 0, Rank: 1.#INF, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
Enter Long, D50000, Price: 33.55, Shares: 3896.243, Commission: 0, Rank: 3.145718e+023, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
Enter Long, D16460, Price: 53.328, Shares: 2451.226, Commission: 0, Rank: 9.392492e+016, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
PSM777 not entered because of insufficient funds or wrong position size/value (reqEntryPrice: 23.715, reqEntryPosSize: 20% of equity (value = 200000), reqLotSize: 0)
4 Open Positions: , B55750 (+2019.14), , D23610 (+1719.32), , D50000 (+3896.24), , D16460 (+2451.23), Market Value: 800000.02, Equity: 1000000.00, Cash: 199999.98, Margin: 0.00, Net Cash Balance: 199999.98,
14.01.2000 00:00:00
Scores:B55750=1.#INF, D23610=1.#INF, PSM777=9.185512e+028, D16460=9.950977e+027, E43002=2.952216e+019,
Exit Long, B55750, Price: 71.3, (Avg. exit pr. 71.3), Shares: 2019.138, Commission: 0, (Total comm.: 0), Profit: 20265.68 (10.13 %), Entry rank:1.#INF, Equity: 1025617, Fx rate: 1.53
Exit Long, D23610, Price: 87.69375, (Avg. exit pr. 87.69375), Shares: 1719.324, Commission: 0, (Total comm.: 0), Profit: 30683.97 (15.34 %), Entry rank:1.#INF, Equity: 1025617, Fx rate: 1.53
Exit Long, D50000, Price: 30.8, (Avg. exit pr. 30.8), Shares: 3896.243, Commission: 0, (Total comm.: 0), Profit: -16393.44 (-8.20 %), Entry rank:3.145718e+023, Equity: 1025617, Fx rate: 1.53
Enter Long, B55750, Price: 71.3, Shares: 1919.694, Commission: 0, Rank: 1.#INF, Equity 1047087, Margin Loan: 0, Fx rate: 1.53
Enter Long, D23610, Price: 87.69375, Shares: 1560.82, Commission: 0, Rank: 1.#INF, Equity 1047087, Margin Loan: 0, Fx rate: 1.53
Enter Long, PSM777, Price: 18.75, Shares: 7299.956, Commission: 0, Rank: 9.185512e+028, Equity 1047087, Margin Loan: 0, Fx rate: 1.53
E43002 not entered because of insufficient funds or wrong position size/value (reqEntryPrice: 448.9239, reqEntryPosSize: 20% of equity (value = 209417.5), reqLotSize: 0)
4 Open Positions: , D16460 (+2451.23), , B55750 (+1919.69), , D23610 (+1560.82), , PSM777 (+7299.96), Market Value: 859502.44, Equity: 1065806.21, Cash: 206303.77, Margin: 0.00, Net Cash Balance: 206303.77,
21.01.2000 00:00:00

@Trader It may help you to learn about
SetOptions(“AllowPositionShrinking”)
SetOptions(“WorstRankHeld”)

A worthwhile read,

http://www.amibroker.com/kb/2014/11/10/troubleshooting-procedure-when-backtest-shows-no-trades/

The book and coding it were discussed a few times on the old forum so there is some useful information there for you.

https://groups.yahoo.com/neo/groups/amibroker/conversations/topics/195057

3 Likes

Dear portfoliobuilder,

thank you for your reply and the useful links! I will look into them carefully within the next days.

About your suggestions:

  1. SetOptions(“AllowPositionShrinking”)
    Yes, this controls the size of your investments after you loose money. But I have from the start only 4 positions, or one week later. Yes, maybe that is the point but I think rather unlikely? What do you think?

  2. SetOptions(“WorstRankHeld”)
    My problem is that I would need a function (“BestRankHeld”), but it does not exist unfortunately?

And to use Worst rank held in a long only system when always looking at the best rank brings problems, see here:

I made PositionScore = exp(ROC(C,LookBack)); // make sure it is positive.

If I make instead **-**exp(ROC(C,LookBack));// make sure it is NEGATIVE
and then use SetOptions(“WorstRankHeld”)
then I get short positions?! (because PositionScore is negative)???

In any case, many thanks for your tips- much appreciated! (I need some days to check and will let you know if this already helps).

1 Like

@Trader Firstly, before I criticize you I want to complement what you are doing well. You bought AmiBroker! You read Clenow's 2nd book, which is both highly entertaining and informative. Most importantly you clicked on the :heart: to thank someone for posting a helpful post! A very good start!

I am not an expert but you have so many mistakes in your last post I don't know where to begin.

Take your time, read up on the things I told you to investigate. Read the articles in the Official Knowledge Base. Try implementing the suggestions and then come back.

Let us start with simple forum suggestion. Use the @portfoliobuilder (by that I mean @ and user name) to create a notification for anyone in your post that they are being referred to.

Next, you dismiss SetOptions(“AllowPositionShrinking”). It must be used anyway, so try it and see if it makes a difference.

Next, your completely misunderstood what SetOptions(“WorstRankHeld”) does. Did you read up on it? Your original post asked for help with dealing with

Well guess what. That's how you do it.
http://www.amibroker.com/guide/afl/enablerotationaltrading.html

"Exits are generated automatically when security's rank drops below "worst rank held".

Next, you are trying to be little bit too clever by using the exponent of your ROC to generate a ranking. AmiBroker creator / lead programmer / and most knowledgeable about how to use this software has listed examples where he simply adds a large number to avoid the negative number and short selling. Might he have done this for a reason? Just a lazy day on TJ's part?

To debug your codes you will run through various steps. One useful method of finding out if the code is actually doing what you intended is to look at the "Detailed log" of your trades (like you have done and posted. Another plus for you btw :wink: ) But let us look at your output and see how helpful it will be in debugging.
image

Mikey! How helpful are those scores in determining that your code is properly following your plan?

Now let us take a more simple approach by adding 1000 to the ROC thus avoiding the negative numbers.
image

Looks a lot more useful to me.

Keep at it and don't necessarily follow my suggestions as I am not an expert. Good luck and welcome to the AmiBroker family.

7 Likes

@portfoliobuilder

Many thanks! (you opened my eyes- I had completely misunderstood this function “worst rank held”). I will check everything (thank you for the links and your detailed reply!) and let you know here with an update of my code as soon as possible.

1 Like

@portfoliobuilder

I cannot login to the old forum unfortunately (although I opened an account to login on yahoo) https://groups.yahoo.com/neo/groups/amibroker/conversations/topics/195057

@trader and in another thread @nick you need to ask @Tomasz about access to the old forum I guess. In the past I believe you needed to register through AmiBroker as well as with Yahoo!

2 Likes

@Tomasz

I cannot login to the old forum unfortunately (although I opened an account to login on yahoo)https://groups.yahoo.com/neo/group

Could you help me please and give me access?
(It is not time critical, needs no hurry).

Here is my revised rotational system- thanks again @portfoliobuilder

I want the rotation to take place once per week (I set this in the Backtester settings : Periodicity: weekly). I want to buy the top 5 of the Position score and exit when the stock drops out of the top 20% (top 100 stocks in the S&P500). So here is my code:

//First the Inputs

Top20Proz=100;    // Top-Proz. For Exit according to Clenow; set for S&P500 = 100 (20% of 500 Stocks)
TopPositionen=5; // buy the Top 5 Positions of the PositionScore (RSI or whatever you use here)
LookBack =18;    //I want a weekly system; rotation once per week: 18 WEEKS = 90 DAYS

// Now the Settings; Equity, Rotational etc.

SetOption("InitialEquity", 1000000); 
SetBacktestMode(backtestRotational); //instead of EnableRotationalTrading()
SetOption("MaxOpenPositions",TopPositionen);

SetOption("AllowPositionShrinking", True ); //**Allow position size shrinking** "If you mark this box AmiBroker will shrink down positions if available equity is less than requested position size (via PositionSize variable). If this box is unmarked positions will not be entered in such case." 
SetOption("WorstRankHeld",Top20Proz);

//Allocate funds equally among all issues 
PositionSize =-100/(NumberHeld);


Score = 1000 + ROC(C,LookBack); // make sure it is positive;to avoid short entries, by adding big constant

PositionScore = Score; //THIS IS REQUIRED for rotational mode. In the rotational mode the trades are driven by the values of the PositionScore variable alone!

On the 30 stocks of the DAX it seems to work, but on the bigger universe of the 500 stocks of the
S&P500 some strange things happen concerning some dates.

One time all positions are exited in the mid of the week and re-entered three days later. Really strange…
Maybe I missed again an important setting…

I will post this here later in
order to solve it somehow. Many thanks to all who helped me so far!
I try to continue this project until the result is something useful.

In your code NumberHeld is undefined, so it throws an error when it’s run. Perhaps it should be TopPositionen?

To figure out why you’re getting unexpected results, try using the Detailed Log, as suggested earlier by @portfoliobuilder. From there use the other tips in How do I debug my formula?

If you get stuck, post back with what you’ve tried and an example that illustrates the issue you’re having.

2 Likes

@HelixTrader

Many thanks! Sorry- you are absolutely right, this was a copy/ paste error. This one line was missing in my code.

NumberHeld = TopPositionen; //The number of issues to hold at a time

(Detailed Log: Of course I am using this…)

  1. DAX: 30 stocks: entry / exit dates ok

So here are the results (detailed log) from my first two weekly tests. First the test on 30 DAX stocks, here, it seems to work well. 5 Positions are taken week per week: 01.09.2017/ 08.09.2017/ 15.09.2017 etc.

Date	Information	
01.09.2017 00:00:00
	Scores:D03712=1038.047, E23212=1037.374, D61440=1034.54, CBK100=1016.342, D50000=1016.198, 01ML7J=1006.574, B55200=1006.47, C23100=1004.342, B00340=1003.535, E40400=1003.518, 
	Enter Long, D03712, Price: 20.99, Shares: 6227.678, Commission: 0, Rank: 1038.047, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
	Enter Long, E23212, Price: 21.76, Shares: 6007.305, Commission: 0, Rank: 1037.374, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
	Enter Long, D61440, Price: 9.629, Shares: 13575.55, Commission: 0, Rank: 1034.54, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
	Enter Long, CBK100, Price: 10.465, Shares: 12491.06, Commission: 0, Rank: 1016.342, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
	Enter Long, D50000, Price: 25.395, Shares: 5147.429, Commission: 0, Rank: 1016.198, Equity 1000000, Margin Loan: 0, Fx rate: 1.53
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , D50000 (+5147.43), Market Value: 1000000.00, Equity: 1000000.00, Cash: 0.00, Margin: 0.00, Net Cash Balance: 0.00, 
08.09.2017 00:00:00
	Scores:D03712=1038.719, D61440=1035.593, E23212=1035.336, D50000=1015.567, C23100=1007.481, B55200=1006.423, 01ML7J=1006.19, CBK100=1005.938, B00340=1005.865, E40400=1003.594, 
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , D50000 (+5147.43), Market Value: 1004515.86, Equity: 1004515.86, Cash: 0.00, Margin: 0.00, Net Cash Balance: 0.00, 
15.09.2017 00:00:00
	Scores:E23212=1033.651, D03712=1029.336, D61440=1027.032, D50000=1021.021, CBK100=1013.014, B55200=1012.558, B00340=1012.316, C23100=1007.694, E40400=1007.031, 01ML7J=1001.349, 
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , D50000 (+5147.43), Market Value: 996741.83, Equity: 996741.83, Cash: 0.00, Margin: 0.00, Net Cash Balance: 0.00, 
22.09.2017 00:00:00
	Scores:E23212=1040.38, D61440=1025.936, D03712=1019.21, B55200=1017.931, CBK100=1017.629, B00340=1011.449, E40400=1009.953, C23100=1008.437, D50000=1008.359, B43900=1005.302, 
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , D50000 (+5147.43), Market Value: 1008109.94, Equity: 1008109.94, Cash: 0.00, Margin: 0.00, Net Cash Balance: 0.00, 
29.09.2017 00:00:00
	Scores:E23212=1037.566, D61440=1027.036, CBK100=1022.695, B55200=1019.25, B00340=1011.182, E40400=1011.049, D03712=1009.578, C23100=1008.134, B43900=1007.806, B15100=1007.369, 
	Exit Long, D50000, Price: 25.075, (Avg. exit pr. 25.075), Shares: 5147.429, Commission: 0, (Total comm.: 0), Profit: -2520.181 (-1.26 %), Entry rank:1016.198, Equity: 1001894, Fx rate: 1.53
	Enter Long, B55200, Price: 37.665, Shares: 3426.836, Commission: 0, Rank: 1019.25, Equity 1003075, Margin Loan: 0, Fx rate: 1.53
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , B55200 (+3426.84), Market Value: 1015569.70, Equity: 1015569.70, Cash: -0.01, Margin: 0.00, Net Cash Balance: -0.01, 
06.10.2017 00:00:00
	Scores:E23212=1034.945, CBK100=1023.744, D61440=1020.005, B55200=1014.08, B00340=1012.245, E40400=1011.826, C23100=1007.738, B43900=1006.782, D03712=1006.746, B15100=1005.462, 
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , B55200 (+3426.84), Market Value: 1016951.44, Equity: 1016951.43, Cash: -0.01, Margin: 0.00, Net Cash Balance: -0.01, 
13.10.2017 00:00:00
	Scores:E23212=1034.624, CBK100=1023.326, B55200=1019.727, B00340=1013.588, D61440=1013.561, E40400=1012.482, D66403=1008.71, B43900=1008.379, C23100=1006.917, D03712=1006.234, 
	5 Open Positions: , D03712 (+6227.68), , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , B55200 (+3426.84), Market Value: 1061177.40, Equity: 1061177.39, Cash: -0.01, Margin: 0.00, Net Cash Balance: -0.01, 
20.10.2017 00:00:00
	Scores:E23212=1038.317, CBK100=1022.91, B55200=1018.018, C23100=1013.632, D61440=1013.575, E40400=1012.926, B43900=1008.266, D66403=1007.889, B00340=1007.584, E43002=1006.684, 
	Exit Long, D03712, Price: 21.45, (Avg. exit pr. 21.45), Shares: 6227.678, Commission: 0, (Total comm.: 0), Profit: 4383.04 (2.19 %), Entry rank:1038.047, Equity: 1062069, Fx rate: 1.53
	Enter Long, C23100, Price: 22.465, Shares: 5946.303, Commission: 0, Rank: 1013.632, Equity 1066929, Margin Loan: 0, Fx rate: 1.53
	5 Open Positions: , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , B55200 (+3426.84), , C23100 (+5946.3), Market Value: 1071684.56, Equity: 1071684.55, Cash: -0.02, Margin: 0.00, Net Cash Balance: -0.02, 
25.10.2017 00:00:00
	Scores:E23212=1045.706, CBK100=1031.077, B55200=1017.649, C23100=1015.234, E40400=1012.967, B00340=1010.594, D61440=1009.994, B43900=1008.206, D03712=1008.128, D66403=1008.118, 
	5 Open Positions: , E23212 (+6007.3), , D61440 (+13575.5), , CBK100 (+12491.1), , B55200 (+3426.84), , C23100 (+5946.3), Market Value: 1091889.89, Equity: 1091889.87, Cash: -0.02, Margin: 0.00, Net Cash Balance: -0.02,
  1. S&P500: 500 stocks: exit on 12.09, re-entry 15.09, why???

1.9.2017/ 8.9.2017 (ok) /12.9.2017 (???)/ 15.9.2017 (ok)/ … rest is ok

Date	Information	
01.09.2017 00:00:00
	Scores:NVDA=1063.432, NRG=1046.805, VRTX=1037.143, ALGN=1031.021, BA=1030.028, REGN=1029.733, PYPL=1028.793, ANDV=1028.227, ADSK=1026.246, EA=1025.828, 
	Enter Long, NVDA, Price: 170.46, Shares: 1173.296, Commission: 0, Rank: 1063.432, Equity 1000000, Margin Loan: 0, Fx rate: 1
	Enter Long, NRG, Price: 24.81, Shares: 8061.266, Commission: 0, Rank: 1046.805, Equity 1000000, Margin Loan: 0, Fx rate: 1
	Enter Long, VRTX, Price: 162.24, Shares: 1232.742, Commission: 0, Rank: 1037.143, Equity 1000000, Margin Loan: 0, Fx rate: 1
	Enter Long, ALGN, Price: 176.38, Shares: 1133.915, Commission: 0, Rank: 1031.021, Equity 1000000, Margin Loan: 0, Fx rate: 1
	Enter Long, BA, Price: 240.33, Shares: 832.1891, Commission: 0, Rank: 1030.028, Equity 1000000, Margin Loan: 0, Fx rate: 1
	5 Open Positions: , NVDA (+1173.3), , NRG (+8061.27), , VRTX (+1232.74), , ALGN (+1133.92), , BA (+832.189), Market Value: 1000000.00, Equity: 1000000.00, Cash: 0.00, Margin: 0.00, Net Cash Balance: 0.00, 
08.09.2017 00:00:00
	Scores:NRG=1059.148, NVDA=1057.606, VRTX=1038.305, ALGN=1031.479, BA=1029.063, ABBV=1027.392, COL=1025.841, ANDV=1025.612, GILD=1025.516, CBOE=1025, 
	5 Open Positions: , NVDA (+1173.3), , NRG (+8061.27), , VRTX (+1232.74), , ALGN (+1133.92), , BA (+832.189), Market Value: 985171.69, Equity: 985171.69, Cash: 0.00, Margin: 0.00, Net Cash Balance: 0.00, 
12.09.2017 00:00:00
	Scores:SPLS=1010.811, 
	Exit Long, NVDA, Price: 163.69, (Avg. exit pr. 163.69), Shares: 1173.296, Commission: 0, (Total comm.: 0), Profit: -7943.212 (-3.97 %), Entry rank:1063.432, Equity: 985171.7, Fx rate: 1
	Exit Long, NRG, Price: 23.92, (Avg. exit pr. 23.92), Shares: 8061.266, Commission: 0, (Total comm.: 0), Profit: -7174.526 (-3.59 %), Entry rank:1046.805, Equity: 985171.7, Fx rate: 1
	Exit Long, VRTX, Price: 158.83, (Avg. exit pr. 158.83), Shares: 1232.742, Commission: 0, (Total comm.: 0), Profit: -4203.649 (-2.10 %), Entry rank:1037.143, Equity: 985171.7, Fx rate: 1
	Exit Long, ALGN, Price: 181.48, (Avg. exit pr. 181.48), Shares: 1133.915, Commission: 0, (Total comm.: 0), Profit: 5782.969 (2.89 %), Entry rank:1031.021, Equity: 985171.7, Fx rate: 1
	Exit Long, BA, Price: 238.78, (Avg. exit pr. 238.78), Shares: 832.1891, Commission: 0, (Total comm.: 0), Profit: -1289.893 (-0.64 %), Entry rank:1030.028, Equity: 985171.7, Fx rate: 1
	Enter Long, SPLS, Price: 10.25, Shares: 19222.86, Commission: 0, Rank: 1010.811, Equity 985171.7, Margin Loan: 0, Fx rate: 1
	1 Open Positions: , SPLS (+19222.9), Market Value: 197034.33, Equity: 985171.69, Cash: 788137.36, Margin: 0.00, Net Cash Balance: 788137.36, 
15.09.2017 00:00:00
	Scores:NRG=1052.157, NVDA=1040.832, BA=1035.88, VRTX=1034.04, ALGN=1033.05, ABBV=1032.259, PVH=1027.857, COL=1026.89, CNC=1026.84, ANDV=1026.663, 
	Exit Long, SPLS, Price: 10.25, (Avg. exit pr. 10.25), Shares: 19222.86, Commission: 0, (Total comm.: 0), Profit: 0 (0.00 %), Entry rank:1010.811, Equity: 985171.7, Fx rate: 1
	Enter Long, NRG, Price: 22.93, Shares: 8592.862, Commission: 0, Rank: 1052.157, Equity 985171.7, Margin Loan: 0, Fx rate: 1
	Enter Long, NVDA, Price: 180.11, Shares: 1093.967, Commission: 0, Rank: 1040.832, Equity 985171.7, Margin Loan: 0, Fx rate: 1
	Enter Long, BA, Price: 249, Shares: 791.3026, Commission: 0, Rank: 1035.88, Equity 985171.7, Margin Loan: 0, Fx rate: 1
	Enter Long, VRTX, Price: 153.61, Shares: 1282.692, Commission: 0, Rank: 1034.04, Equity 985171.7, Margin Loan: 0, Fx rate: 1
	Enter Long, ALGN, Price: 184.5, Shares: 1067.937, Commission: 0, Rank: 1033.05, Equity 985171.7, Margin Loan: 0, Fx rate: 1
	5 Open Positions: , NRG (+8592.86), , NVDA (+1093.97), , BA (+791.303), , VRTX (+1282.69), , ALGN (+1067.94), Market Value: 985171.67, Equity: 985171.69, Cash: 0.02, Margin: 0.00, Net Cash Balance: 0.02, 
22.09.2017 00:00:00
	Scores:NRG=1052.387, BA=1041.873, ALGN=1035.809, ABBV=1033.394, PYPL=1031.768, NVDA=1031.618, MU=1029.748, GILD=1029.643, VRTX=1028.165, RL=1027.609, 
	5 Open Positions: , NRG (+8592.86), , NVDA (+1093.97), , BA (+791.303), , VRTX (+1282.69), , ALGN (+1067.94), Market Value: 999254.82, Equity: 999254.83, Cash: 0.02, Margin: 0.00, Net Cash Balance: 0.02, 
29.09.2017 00:00:00
	Scores:ISRG=1244.694, NRG=1055.373, ALXN=1043.593, BA=1036.24, SIG=1034.963, ABBV=1034.514, RL=1033.55, MU=1032.157, GPS=1031.713, JBHT=1031.382, 
	Exit Long, NVDA, Price: 178.77, (Avg. exit pr. 178.77), Shares: 1093.967, Commission: 0, (Total comm.: 0), Profit: -1465.915 (-0.74 %), Entry rank:1040.832, Equity: 997912, Fx rate: 1
	Exit Long, VRTX, Price: 152.04, (Avg. exit pr. 152.04), Shares: 1282.692, Commission: 0, (Total comm.: 0), Profit: -2013.827 (-1.02 %), Entry rank:1034.04, Equity: 997912, Fx rate: 1
	Exit Long, ALGN, Price: 186.27, (Avg. exit pr. 186.27), Shares: 1067.937, Commission: 0, (Total comm.: 0), Profit: 1890.248 (0.96 %), Entry rank:1033.05, Equity: 997912, Fx rate: 1
	Enter Long, ISRG, Price: 1045.88, Shares: 191.0183, Commission: 0, Rank: 1244.694, Equity 998911.4, Margin Loan: 0, Fx rate: 1
	Enter Long, ALXN, Price: 140.29, Shares: 1424.067, Commission: 0, Rank: 1043.593, Equity 998911.4, Margin Loan: 0, Fx rate: 1
	Enter Long, SIG, Price: 66.55, Shares: 2854.23, Commission: 0, Rank: 1034.963, Equity 998911.4, Margin Loan: 0, Fx rate: 1
	5 Open Positions: , NRG (+8592.86), , BA (+791.303), , ISRG (+191.018), , ALXN (+1424.07), , SIG (+2854.23), Market Value: 1010561.90, Equity: 1010561.89, Cash: -0.01, Margin: 0.00, Net Cash Balance: -0.01, 
06.10.2017 00:00:00
	Scores:NRG=1050.65, KORS=1043.661, ALXN=1038.761, BA=1035.93, ABBV=1034.558, URI=1031.109, SNI=1030.437, GM=1030.421, CNC=1030.35, GPS=1030.275, 
	Exit Long, ISRG, Price: 361.47, (Avg. exit pr. 361.47), Shares: 191.0183, Commission: 0, (Total comm.: 0), Profit: -130734.9 (-65.44 %), Entry rank:1244.694, Equity: 1011883, Fx rate: 1
	Exit Long, SIG, Price: 67.32, (Avg. exit pr. 67.32), Shares: 2854.23, Commission: 0, (Total comm.: 0), Profit: 2197.757 (1.16 %), Entry rank:1034.963, Equity: 1011883, Fx rate: 1
	Enter Long, KORS, Price: 47.48, Shares: 3715.442, Commission: 0, Rank: 1043.661, Equity 882045.9, Margin Loan: 0, Fx rate: 1
	Enter Long, ABBV, Price: 90.49, Shares: 936.9543, Commission: 0, Rank: 1034.558, Equity 882045.9, Margin Loan: 0, Fx rate: 1
	5 Open Positions: , NRG (+8592.86), , BA (+791.303), , ALXN (+1424.07), , KORS (+3715.44), , ABBV (+936.954), Market Value: 886061.81, Equity: 886061.81, Cash: -0.00, Margin: 0.00, Net Cash Balance: -0.00, 
13.10.2017 00:00:00
	Scores:NRG=1056.141, BA=1037.21, RHT=1036.297, CF=1035.046, KORS=1033.81, GM=1033.605, ALXN=1033.431, MU=1032.026, URI=1030.838, ABBV=1030.142, 
	5 Open Positions: , NRG (+8592.86), , BA (+791.303), , ALXN (+1424.07), , KORS (+3715.44), , ABBV (+936.954), Market Value: 892932.48, Equity: 892932.48, Cash: -0.00, Margin: 0.00, Net Cash Balance: -0.00, 
20.10.2017 00:00:00
	Scores:NRG=1050.207, KORS=1044.364, MU=1038.982, RHT=1038.645, LRCX=1038.547, ALGN=1038.29, URI=1037.262, PYPL=1036.955, ABBV=1035.257, BA=1034.774, 
	Exit Long, ALXN, Price: 139.02, (Avg. exit pr. 139.02), Shares: 1424.067, Commission: 0, (Total comm.: 0), Profit: -1808.564 (-0.91 %), Entry rank:1043.593, Equity: 893292.4, Fx rate: 1
	Enter Long, MU, Price: 41.5, Shares: 4292.121, Commission: 0, Rank: 1038.982, Equity 890615.1, Margin Loan: 0, Fx rate: 1
	5 Open Positions: , NRG (+8592.86), , BA (+791.303), , KORS (+3715.44), , ABBV (+936.954), , MU (+4292.12), Market Value: 881506.69, Equity: 901357.37, Cash: 19850.68, Margin: 0.00, Net Cash Balance: 19850.68, 
25.10.2017 00:00:00
	Scores:NRG=1043.301, KORS=1040.719, DLTR=1037.553, CF=1037.268, LRCX=1033.792, FLIR=1032.8, URI=1032.484, ALB=1032.122, GM=1031.93, CAT=1031.438, 
	Exit Long, BA, Price: 258.42, (Avg. exit pr. 258.42), Shares: 791.3026, Commission: 0, (Total comm.: 0), Profit: 7454.07 (3.78 %), Entry rank:1035.88, Equity: 903430.7, Fx rate: 1
	Exit Long, ABBV, Price: 91.77, (Avg. exit pr. 91.77), Shares: 936.9543, Commission: 0, (Total comm.: 0), Profit: 1199.301 (1.41 %), Entry rank:1034.558, Equity: 903430.7, Fx rate: 1
	Exit Long, MU, Price: 41.06, (Avg. exit pr. 41.06), Shares: 4292.121, Commission: 0, (Total comm.: 0), Profit: -1888.533 (-1.06 %), Entry rank:1038.982, Equity: 903430.7, Fx rate: 1
	Enter Long, DLTR, Price: 91.61, Shares: 1944.837, Commission: 0, Rank: 1037.553, Equity 890832.5, Margin Loan: 0, Fx rate: 1
	Enter Long, CF, Price: 37.68, Shares: 4728.411, Commission: 0, Rank: 1037.268, Equity 890832.5, Margin Loan: 0, Fx rate: 1
	Enter Long, LRCX, Price: 203.07, Shares: 641.2805, Commission: 0, Rank: 1033.792, Equity 890832.5, Margin Loan: 0, Fx rate: 1
	5 Open Positions: , NRG (+8592.86), , KORS (+3715.44), , DLTR (+1944.84), , CF (+4728.41), , LRCX (+641.281), Market Value: 885460.91, Equity: 885460.91, Cash: 0.01, Margin: 0.00, Net Cash Balance: 0.01,

Moderator comment: for long texts copy-pasted from Analysis use code tags as it gives proper formatting. I already changed it here.

Are you using Weekly periodicity?

Can you save your backtest as an APX file and link to it here?

1 Like

@HelixTrader:

Yes, In the backtester setting I set "weekly" (periodicity)

Here you find the backtest as APX-file- I hope it is right?

Rotation of 5 Stocks_SP500_27Okt17.apx (10.2 KB)

Thank you for looking into it.

Your unexpected rotations are due to price data for some stocks producing a weekly bar with a date mid-week, because that's when they started or stopped trading. The solution is to Pad & Align your data to the index.

image

6 Likes

@HelixTrader Many Thanks!!! You are brilliant- I would never find this…
It solved the date problem at the S&P500 stocks

1 Like

Hi everyone,
I’m also interested in simulating this strategy. I am eagerly waiting for Norgate Data to release their new data tool, they closed their beta trial and keep postponing their release date which was supposed to be Q3, now it’s more Q4 if we’re lucky.

I found a couple of code fragments at the old YAHOO forum. They are a headstart at best, none of them covers some details to my liking.

Here is the snippet 1, obviously created by some Spanish guy:

/ ranking

lnc=log(C);
pendiente= LinRegSlope( lnc, 90 );
rentanual=100*(((exp(pendiente))^250)-1);
r2=(Correlation( BarIndex()+1, lnc, 90 ))^2;
rank= r2*rentanual;
rank= IIf (IsNull(rank),-10000,rank);
//gap del 15%

gap15= IIf(C>1.15*Ref(C,-1) OR C<0.85*Ref(C,-1),1,0);
filtrogap15=IIf(Sum(gap15,90)>0,1,0);
filtrogap15= IIf (IsNull(filtrogap15),10000,filtrogap15);
//filtro media 100dias

mm100=MA(C,100);
filtromedia100=IIf(C<mm100,1,0);
filtromedia100= IIf (IsNull(filtromedia100),10000,filtromedia100);
//filtro para Vender

filtroventa= (filtromedia100 OR filtrogap15);

//formula momentum

momentum = rank-10000*(filtrogap15+filtromedia100);
momentum = IIf (IsNull(momentum ),-1000000,momentum );
// Filtro Miercoles
miercoles= IIf(DayOfWeek() == 3 ,1,0);

//Filtro MM200 SP500

SP500 = Foreign("^GSPC","C");
SMA200SP500 = MA (SP500 , 200);
FSP= IIf((SP500>SMA200SP500),1,0);
FSP = IIf (IsNull(FSP),-10000,FSP);

numeroacciones = IIf(filtroventa,0,Cuenta*0.001/ATR(20));
numeroacciones =IIf(IsNull(numeroacciones),0,numeroacciones );
montante=numeroacciones *Close;




Filter = (momentum > 0) OR (momentum <= 0);
AddTextColumn( FullName(), "Nombre de la empresa",1.2, colorRed );
AddColumn(momentum , "momentum ",1.2,IIf(momentum ==1000000, colorRed,colorGreen));
AddColumn(miercoles, "miercoles",1.2,colorGreen );
AddColumn(FSP, "FSP",1.2,IIf(FSP==10000, colorRed,colorGreen) );
AddColumn(miercoles AND FSP, "miercoles y FSP",1.2,colorGreen );
AddColumn(filtroventa, "filtroventa",1.2,IIf(filtroventa, colorRed,colorGreen));
AddColumn(filtrogap15, "filtrogap15",1.2,IIf(filtrogap15==10000, colorRed,colorGreen));
AddColumn(filtromedia100, "filtromedia100",1.2,IIf(filtromedia100==10000, colorRed,colorGreen));
AddColumn(ATR(20), "ATR(20)",1.2,colorGreen );
AddColumn(numeroacciones , "numeroacciones ",1.2,colorGreen );
AddColumn(montante, "montante",1.2,colorGreen );
if ( Status( "action" ) == actionExplore)
SetSortColumns( -5 ,2,-4);

Then, there is another snippet which lends itself better to backtesting:

SetOption("WorstRankHeld", 250); //top 25%

BuyPrice = O;
SellPrice = O;

//Calculate purchase size
lbp = 30; //one month
RiskPerShare =  3 * ATR(lbp);
PositionRisk = 1.5;     //PosRisk risk 1.5% of equity
PctSize =  PositionRisk * BuyPrice / RiskPerShare;


SPY = Foreign("SPY","C");
SPY200 = EMA(SPY,200);

//sell if SPY below 200day ema
bMacro_SellStocks =     (SPY < SPY200) ;      //Liquidate holdings


lbp = 90;   
AnnualizedSlope = (exp(LinRegSlope(ln(C),lbp))^252) - 1;
Rsq =  (Correlation(Cum(1), C, lbp))^2; 
ExpectedGain = AnnualizedSlope * Rsq; 


Score = 100 + ExpectedGain;  //to avoid short selling
Score = IIf(bMacro_SellStock, 0, Score); //no buy if market in downtrend

PositionScore = IIf(DayOfWeek() == 5,Score, scoreNoRotate); //trade once a week

There is always something missing…

Lastly some other code that I found which does funny things resetting the timeframe from daily to weekly, seems to me it uses unecessary system resources by providing little value added.

/ Setting the day of the week for trading
function firstTradingDay( timeframe )  // this finds the first trading day of the timeframe
{
    TimeFrameSet( timeframe ) ;  // 'timeframe' I would commonly use are inDaily, inWeekly, inMonthly
    lastday = 1;
    TimeFrameRestore( timeframe );
    lastday = TimeFrameExpand( lastday , timeframe , expandpoint ) ;
    // expands time-compressed array from 'timeframe' (inWeekly) to base timeframe (daily)
    // expandPoint - the resulting array gets not empty values only for the last bar within given period (all remaining bars are Null (empty))
    // so this is finding the last day in the week (or selected 'timeframe') and setting it to lastday variable
    return  Ref( lastday , -1 );
}
 
Rotate = firstTradingDay( inweekly ) ;
Rotate = Nz( Rotate ); // Converts Null/Nan/Infinity values to zero
 
 
//Credit to fxshrat for this second one.
function firstdayofweek() {
    
global dow;
    
dow = DayOfWeek();
    
SOW = dow < Ref( dow, -1 );
    
return SOW;
}

flag =
Nz(firstdayofweek());
/*
And I personally like the idea of rotation every 2nd week, so my code modification, (also see http://amibrokerforum.proboards.com/thread/75/start-period-recognition)
 */
/////////////////////////////////////////////////
// Designed with a bi-weekly rotation system in mind
// First trading day of every other week identifier
/////////////////////////////////////////////////

function
firstdayofweek() {
global dow;
dow = DayOfWeek();
SOW = dow < Ref(
dow, -1 );
return SOW;
}

flag = Nz(firstdayofweek());

countflag=Cum(flag);
// count firstdayofweek

// rotate only on
firstdayofweek, every 2nd one

rotation=flag AND
(countflag%2==0);//using the modulus we find days with remainder zero

// to explore the
results
Filter = 1;// 

AddColumn( C,"Price", 1.2, -1, -1 ); 
AddColumn(flag,"flag");
AddColumn( dow,"Weekday", 1, colorGrey50, IIf( flag, colorDarkGreen, colorDefault) );
AddColumn(countflag,"countflag");
AddColumn(rotation,"Rotation Day",1,colorBlue, IIf(rotation,colorYellow,colorDefault));
// rotate only on firstdayofweek, every 2nd one

rotation=flag AND (countflag%2==0);//using the modulus we find days with remainder zero

// to explore the results
Filter = 1;// 

AddColumn( C,"Price", 1.2, -1, -1 ); 
AddColumn(flag,"flag");
AddColumn( dow,"Weekday", 1, colorGrey50, IIf( flag, colorDarkGreen, colorDefault) );
AddColumn(countflag,"countflag");
AddColumn(rotation,"Rotation Day",1,colorBlue, IIf(rotation,colorYellow,colorDefault));

I’ll start with the PSEUDO-CODE of the book in the next post. Strictly the title of this thread is also misleading because there is no position limit like 5 momentum stocks in the original as the title suggests…

Thanks,
Dio

/* System Description:
As described by Clenow, Stocks on the Move, 2015
A very fine read, go n get it.

  1. Market regime filter:
    Snp500Index > SMA(C,200)

  2. Ranking stocks:
    2.1 Exponential regression slope over the past 90 days, annualized to 250 trading days - Unit [%]
    2.2 Ensure smoothness of slope by calculating the coefficient of determination, R2 (“R Squared”) - Value Range [0: worst fit … 1: best fit]
    2.3 Gauge for momentum: 2.1 * 2.2

  3. Stock volatility consideration: ATR(20)

  4. Additional Filter 1: Individual stock needs to trade above the 100 day SMA - C > SMA(C,100)

  5. Additional Filter 2: Avoid stocks with gaps - no move larger than 15% over the past 90 days

  6. Position Sizing: Risk Parity Approach - #ofshares = (Equity * RiskFactor) / ATR(20)
    Suggested 0.1% daily impact on total equity: Equity * 0.001 / ATR(20)

  7. Open position Rebalancing: Adjust position size to current ATR(20) if threshold “thr” exceeds a value. Rebalance 1 or 2 times per month.

8.1 Exits - Portfolio Rebalancing: Close below 100 day SMA - C<SMA(C,100)
8.2 Exits - Portfolio Rebalancing: Stock no longer in the top 20% ranking (SnP500 = 500 stocks, top 20% = drops out of the best performing 100 stocks)
8.3 Exits - Portfolio Rebalancing: Stock dropped out of index

Logic:

  • Buy as many stocks as your Equity allows, each pos has a suggested daily impact of 0.1% of portfolio

  • prefer the highest ranked until out of cash

  • no buy: GAP >15%

  • no buy: C<SMA(C,100)

  • no buy: SnP500, close < SMA(SnP500,close, 200)

  • rebalance position 1 or 2 times per month

  • rebalance portfolio on Wednesday: (i.e. exit or open new pos) based on top20%, close below 100day SMA, or gap larger than 15%, or drop out of index

*/

5 Likes