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.
(click to enlarge) |
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 ); // 1 ###### BACKTESTER SETTINGS - 1. GENERAL TAB 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); // 2 ###### BACKTESTER SETTINGS - 2. TRADES TAB BuyPrice = SellPrice = ShortPrice = CoverPrice = Close; SetTradeDelays( 1, 1, 1, 1); // 5 ###### BACKTESTER SETTINGS - 5. PORTFOLIO TAB //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); // 6 ###### BACKTESTER SETTINGS - 6. WALK FORWARD TAB //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); //Exploration 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: 00_60DayMomentum.afl
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.
6 comments:
Many thanks for the article.
However Line no 43 and 44 which are responsible for giving us the last trading day of the month are not working for me. Can you please help. I would like to stress on the point that I am using the above two lines as a stand alone code and not as a part of the whole program, as my requirement is only to findout the last trading day of the month. Some help from your side would be highly appreciated.
Thanks & Regards,
Neha
Sir,
This is in response to my previous post some time back. I think I figured out the answer to my own question.
Check the following code:
SecondLastDayOfMonth = IIf( (Month() == Ref( Month(), 1) AND (Month() != Ref( Month(), 2)) ), 1, 0);
LastDayOfMonth = BarsSince(SecondLastDayOfMonth) + 1;
LastDayOfMonth= ExRem(LastDayOfMonth, SecondLastDayOfMonth);
PlotShapes(IIf(LastDayOfMonth, shapeUpArrow, shapeNone), colorYellow, 0, yposition = Low);
Let me know and sorry for the trouble 🙂
Regards,
Neha
Hi Neha,
You are correct, the formula is checking for the second to last day of the month.
The AFL script has trade delays set to 1, which means that the actual trade occurs on the last day of the month, using the signal from the second to last day of the month. This is how I actually use the system, and is why the backtest AFL is structured in this way.
For example, let's look at July, 2016. The last day of the month was Friday, July 29. I would calculate the signals after the close on Thursday, July 28. These signals would determine which product I would trade on Friday, July 29. On the morning of July, 29 I would enter a market-on-close (MOC) order using the signals from Thursday.
Thanks,
Dave
Sir,
Thank you so much for the explanation. I get one more way of attending to my problem. Thanks once again.
Regards,
Neha
Post a Comment