A Super Easy Way to Work With Dates for Monthly Rotation Strategies in AmiBroker
(Scroll down to video at bottom of the page for presentation. The Presentation starts at the 8:02 Timestamp mark).
* Click Here to Download the Slide Deck to this Presentation.
- Note that this is the first Session of two (2) Presentations.
- The slides for this presentation are from 1-18.
- If you are interested in Session 2: AmiBroker Strategy Development and AutoTrading "From Soup to Nuts", click here.
Pre-Amble
I recently developed a Monthly Momentum Rotation Strategy for members of the Systematic Investors Group community called the Fab Tech Rotation Strategy. (If that link no longer works, then it's been moved out of Beta as a Free Strategy and into our Pro Membership section which requires that you are a Systematic Investing Game Plan member).
One of the major challenge points in completing the Strategy inspired me to share the information in this blog post.
(Special thanks to Gary B of Mountain Trading Group for his help in providing the solution!)
The Challenge: Dealing With Dates for Monthly Rotation Strategies
Although it was relatively easy to code dates for backtesting in order to verify this Monthly Momentum Rotation Strategy, the backtesting code was not suitable for generating signals in real time.
The code required that the new (next) month opened before being able to recognize that the previous month was closed, and be able to look back N days from the end of the month. The reason, is that the Strategy calculates it's rotation list of stock three (3) trading days before the end of each month (EOM). It then trades the next day, or two (2) trading days before the EOM.
Here's what the initial code for dealing with rotation day for backtesting looked like:
SetOption( “AllowSameBarExit”, False);
BEDays = 3; // Before EOM # of days
m = Month();
FirstOfMonth = m != Ref( m, -1 ); // new month
BuyDate = Ref ( FirstOfMonth, BEDays );
SellDate = Ref ( FirstOfMonth, BEDays + 1);
So the big challenge was figuring out how to code the Strategy for both backtesting accuracy, and, ability to generate signals in real time in order to take real trades on the rotation day. Both the backtest and real live trading signals needed to line up perfectly. Implementing a solution that was different for both would introduce inconsistencies and errors.
The Date Challenge
Gregorian Calendar dates pose so many complexities for Rotational Systematic Trading because of weekends, holidays, different number of days in different months, different number of days in the same months (approximately every 4 years, Feb has 29 days instead of 28 in a leap year), etc.
Soooo, how do you easily define a rotation day in order to generate both live rotation dates (and back test dates) when you don’t exactly know which date it falls on or which day is the end of the month in advance?
A Valiant Attempt!
The above code could not look forward and know how many days before the beginning of a new month. I'm sure there is a solution to figuring out the proper calendar day to trade based on N days before the end of the month, but I could not find it easily enough.
Here's one attempt using some code I found online in the official AmiBroker Forum and modifying it (considering I didn't understand the calendar math needed too clearly)...
// Pick Proper Days to Rotate from EOM - this code doesn't work exactly
BEDays = 3;
Rotate = 0;
BEDays1 = 0;
Daysinmonth = IIf( Month() == 1 OR Month() == 3 OR Month() == 5 OR Month() == 7 OR Month() == 8 OR Month() == 10 OR Month() == 12, 31, 30 );
Daysinmonthfeb = IIf( Year() % 4 == 0 AND Year() % 100 != 0, 29, 28 );
Daysinmonthfinal = IIf( Month() == 2, Daysinmonthfeb, Daysinmonth );
BEDays1 = IIf( Daysinmonthfinal - Day() == BEDays AND( DayOfWeek() == 1 OR DayOfWeek() == 2 OR DayOfWeek() == 3 OR DayOfWeek() == 4 OR DayOfWeek() == 5 ), BEDays1 = BEDays,
IIf( Daysinmonthfinal - Day() == BEDays AND DayOfWeek() == 6, BEDays1 = BEDays - 1,
IIf( Daysinmonthfinal - Day() == BEDays AND DayOfWeek() == 0, BEDays1 = BEDays - 2, BEDays1 ) ) );
Rotate = IIf( Daysinmonthfinal - Day() == BEDays1, 1, 0 );
Although the code works "sometimes", it didn't provide a consistent working solution.
The Solution
As mentioned earlier, Gary provided me with a .txt file that he created with the help of a program that he created in C# to generate a date file. The date file included all calendar dates from 1992 to 2040, and had a column for each date that showed:
1. The number of trading days that date represents from the beginning of the month (what Gary refers to as the positive offset), and,
2. The number of trading days that date represents from the end of the month (Gary refers to as the negative offset).
Given this, the solution was then quite simple...
The Solution Steps
1. I modified the text file Gary gave me for use with AmiBroker – adding a ticker symbol column (~~~TradeBarsOffset) and a few other steps then saved as a CSV file.
2. Import the CSV file into AmiBroker’s current database as a ticker symbol. The data essentially shows up when charting the symbol ~~~TradeBarsOffset
3. Reference the ticker symbol ~~~TradeBarsOffset directly in the strategy code using a Foreign statement and the Open or Close value, and use that value as the “signal” for the rotation date. Open will represent the positive offset, and Close will represent the negative offset.
Here’s how this looks…
Import Settings
Once the data is imported into AmiBroker (I imported into Group 255), you should be able to "chart" the ticker symbol using ~~~TradeBarsOffset as the symbol.
Solution: The Rotation Date Code
// # of days before EOM (End of Month)
BEDaysDflt = Param( "Number of Days before EOM", 3, 1, 23, 1 );
BEDays = Optimize( "Number of Days before EOM", BEDaysDflt, 1, 23, 1 );
// Entry & exit timing - the N trading day before the end of the month.
Rotate = Foreign( "~~~TradeBarsOffset", “Close", 1 ); // use “Open” for Pos. Offset
BuyDate = SellDate = Rotate == -BEDays; // use “-” for Neg. Offset
//BuyConditions
BuyCondition = BuyDate AND _____ AND _______ ;
Buy = BuyCondition;
// Sell Conditions
Sell = SellDate OR ______ OR ________ ;
Caveat - a note from Gary about Dates
You may want to mention a caveat about future dates that they're predicted based on the calendar and don't take into account "exceptional" events. Here are the list of exceptional events currently incorporated into the historical dates:
// 9/11
// 9/11/2001
// 9/12/2001
// 9/13/2001
// 9/14/2001// day of mourning for ronald reagan
// 6/11/2004// national day of mourning for president gerald ford
// 1/2/2007// hurricane sandy
// 10/29/2012
// 10/30/2012// george bush's death
// 12/5/2018So only 9 over the last 25 years but good to be aware of.
Regards,
Gary
Download the Date File CSV
* Click Here to Download the Date File CSV.
I hope this helps should you want to develop your own Monthly Momentum Rotation Strategy.
Thanks again to Gary B for sharing this solution, and, allowing me to share the idea and the CSV file!
This solution was implemented for our Fab Tech Rotation Strategy, the Strategy Signals (Strategy Scans) will be available in the Free Strategy section while it remains in BETA testing, after which it will be moved over to our Pro Strategy Member's Only area.
(Speaking of which, try out our full service and strategies with a no-risk 2-week trial of the Systematic Investing Game Plan membership today for only $14.99 using Coupon Code: secret15)
Here's the Video Presentation (Session 1)...
Session 1 in the video starts at 8:02 timestamp.
Update: Jan 12 2021
Here's an edited version of the above presentation...
Enjoy,
Dave
Systematic Investors Group Team