Momentum Rotation Strategies and Data - Part 3

In this post we will look at the results for a Momentum Rotation strategy that ranks funds based on the sum of two rate-of-change (ROC) values.  We will use the same portfolio of ETFs discussed in Part 1 and Part 2 of this series.

So far this series has started a number of good conversations, trying to find an explanation for my results.  I appreciate all of your comments, and would be delighted if you could find an alternate explanation for my results!  At the end of this post I will include the AFL code I used in AmiBroker to generate the results in Part 2.  The primary AFL indicator that I am using for rotation ranking is ROC.

The strategy discussed in this post works as follows.  On the second to last trading day of the month, the strategy calculates the 60 day ROC and 120 day ROC for each fund in the portfolio based on closing prices on that day.  It adds these two values together, and if the sum is negative it reassigns the sum a value of 0.  It then ranks all 10 funds, selecting the fund with the highest rank.  If all funds have a rank of 0, the system will move to cash.  On the last trading day of the month, it executes the buy and sell orders at the close - "market on close" orders in live trading.

In the diagram below, three equity curves are displayed for a 60 day / 120 day dual momentum Rotation System.  

60 Day / 120 Day Momentum Rotation Strategy Equity Curves
(click to enlarge)
The blue curve labeled "Adjusted" is the equity curve generated from our momentum system using dividend and split adjusted data.  The signals and P&L are derived only from the adjusted data time series.  The red curve labeled "Actual" is the equity curve generated from our momentum system using only split adjusted data, that has not been adjusted for dividends.  It is lower, as expected, since dividends are not included.  The green curve labeled "Hybrid" is the equity curve generated from our momentum system using the "Actual" time series for signal generation and the "Adjusted" time series for the P&L calculation.  This is a two step process...first the system is run across the "Actual" time series data to derive the trade dates and ETFs selected.  The second step uses the dates and ETFs selected in step 1 to to calculate P&L from the "Adjusted" time series data.

What we can see in the next two images are the signals, or ETFs selected, by the strategy from the "Actual" data set and the "Adjusted" data set.  The chart below shows the ETFs selected (and held) by the strategy by date using the "Actual" time series data.

(click to enlarge)

The chart below shows the ETFs selected (and held) by the strategy by date using the "Adjusted" time series data.  Similar to the 60 day momentum strategy, the signals do not match.

(click to enlarge)

As I mentioned to some readers today, I am using Yahoo adjusted data in my "Adjusted" data database, and Yahoo non-adjusted data in my "Actual" data database.  Here are two links from Yahoo that discuss their data source for historical data (it's CSI), and their approach for dividend adjusting data:

Also, here is the AmiBroker AFL code that was used in Part 2 of this series:

SetBacktestMode( backtestRotational );

SetOption("InitialEquity", 100000);
SetOption("MinShares", 1);
SetOption("MinPosValue", 0);
SetOption("FuturesMode", False);
SetOption("AllowPositionShrinking", False);
SetOption("ActivateStopsImmediately", False);
SetOption("ReverseSignalForcesExit", False);
SetOption("AllowSameBarExit", False);
RoundLotSize = 0;
TickSize = 0;
MarginDeposit = 0;
PointValue = 1;
SetOption("CommissionMode", 2);
SetOption("CommissionAmount", 7.95);
SetOption("InterestRate", 0);
SetOption("AccountMargin", 100);
SetOption("MarginRequirement", 100);

BuyPrice = SellPrice = ShortPrice = CoverPrice = Close;
SetTradeDelays( 1, 1, 1, 1);

//SetOption("MaxOpenPositions", 1);
// check the box to "Add artificial future bar..."
// Limit trade size as % - use 10 for live trading
// check the box to "Disable trade size limit..."
SetOption("UsePrevBarEquityForPosSizing", False);
SetOption("UseCustomBacktestProc", False);

//SetOption("WorstRankHeld", 1);

Totalpositions = 1;
SetOption("WorstRankHeld", 1);
SetOption("MaxOpenPositions", Totalpositions );
PositionSize = -100 / Totalpositions ;

LastDayOfMonth = IIf( (Month() == Ref( Month(), 1) AND (Month() != Ref( Month(), 2)) ), 1, 0);
TradeDay = LastDayOfMonth ;

Score = ROC(Close, 60);
PositionScore = IIf(Score < 0, 0, Score ); // Long only
PositionScore = IIf(TradeDay , PositionScore , scoreNoRotate);

Filter = 1;
AddColumn(Score ,"Score",1.1);
AddColumn(PositionScore ,"PositionScore ",1.1);
AddColumn(PositionSize ,"Position Size",1.1);

You can download the AFL code above from my Google Drive:

If you don't want to miss my new blog posts, follow my blog either by email or by RSS feed.  Both options are free, and are available on the top of the right hand navigation column under the headings "Follow By Email" and "Subscribe To RSS Feed".  I follow blogs by RSS using Feedly, but any RSS reader will work.

Berkomentarlah dengan kata-kata yang sopan :-)