How To Test and Optimize Turn of the Month Seasonality

Historical evidence suggests a potential seasonal pattern around the end of the month in the markets.

If you have been involved with the markets for even a short period of time, you have heard about this trade.  Buy N days prior to the end of the month and then exit M days after the end of the month.  This is a simple test to perform if you have a way to determine the N and the M in the algorithm.  You could always buy on the 24th of the month, but the 24th of the month may not equal N days prior to the end of the month. 

Simple approach that doesn’t always work – buy the 24th of the month and exit the 5th of the following month.

if dayOfMonth(d) = 24 then buy next bar at open;

if marketPosition = 1 and dayOfMonth(d) = 5 then sell next bar at open;

Before we get into a little better coding of this algorithm, let’s see the numbers.  The first graph is trading one contract of the ES futures once a month – no execution fees were applied.  The same goes for the US bond futures chart that follows.  Before reading further please read this.

CFTC-required risk disclosure for hypothetical results:

Hypothetical performance results have many inherent limitations, some of which are described below. No representation is being made that any account will or is likely to achieve profits or losses similar to those shown. in fact, there are frequently sharp differences between hypothetical performance results and the actual results subsequently achieved by any particular trading program.

One of the limitations of hypothetical performance results is that they are generally prepared with the benefit of hindsight. In addition, hypothetical trading does not involve financial risk, and no hypothetical trading record can completely account for the impact of financial risk in actual trading. For example, the ability to withstand losses or to adhere to a particular trading program in spite of trading losses are material points which can also adversely affect actual trading results. There are numerous other factors related to the markets in general or to the implementation of any specific trading program which cannot be fully accounted for in the preparation of hypothetical performance results and all of which can adversely affect actual trading results.
Buying N days before EOM and Selling M days after EOM
Ditto!

No Pain No Gain

Looking into the maw of draw down and seeing the jagged and long teeth.

Draw down as a percentage of account value.
Ditto2

The bonds had more frequent draw down but not so deep.  These teeth can cause a lot of pain.

Well, George what is the N and M?

I should have done M days before and N days after to maintain alphabetic order, but here you go.

ES: N = 6 AND M = 6

US: N =10 AND M = 1

How did you do this?

Some testing platforms have built-in seasonality tools, but, and I could be wrong I didn’t find what I needed in the TradeStation function library.  So, I built my own.

A TradingDaysLeftInMonth function had to be created.  This function is a broad swipe at attempting to determining this value.  It’s not very smart because it doesn’t take HOLIDAYS into consideration.  But for a quick analysis it is fine.  How does one design such a function?  First off, what do we know to help provide information that might be useful?  We know how many days are in each month (again this function isn’t smart enough to take into consideration leap years) and we know what day of the week each trading day belongs to.  We have this function DayOfWeek(Date) already in EasyLanguage.  And we know the DayOfMonth(Date) (built-in too!) With these three tidbits of information, we should be able to come up with a useful function.   Not to mention a little programming knowledge.  I was working on a Python project when I was thinking of this function, so I decided to prototype it there.  No worries, the algorithm can be easily translated to EasyLanguage. And yes, I could have used my concept of a Sandbox to prototype in EasyLanguage as wellRemember a sandbox is a playground where you can quickly test a snippet of code.  Using the ONCE keyword, you can quickly throw some generic EasyLanguage together sans trade directives and mate it to a chart and get to the nuts and bolts quickly.  I personally like having an indicator and a strategy sandbox.  Here is a generic snippet of code where we assume the day of month is the 16th and it is a Monday ( 2 – 1 for Sunday thru 7 for Saturday) and there are 31 days in whatever month.

currentDayOfWeek = 2;
currentDayOfMonth = 16;
loopDOW = currentDayOfWeek;
daysInMonth = 31
#create the calender for the remaining month
tdToEOM=0; #total days to EOM
for j in range(currentDayOfMonth,daysInMonth+1):
if loopDOW != 1 and loopDOW != 7:
tdToEOM +=1;
print(j," ",loopDOW," ",tdToEOM)
loopDOW +=1;
if loopDOW > 7: loopDOW = 1; #start back on Monday
Create a synthetic calendar from the current day of month

I just absolutely love the simplicity of Python.  When I am prototyping for EasyLanguage, I put a semicolon at the end of each line.  Python doesn’t care.  Here is the output from this snippet of code.

Cur>Day    DOWDay  DaysLeftAccum.
-----------------------------------
16 2 1 Monday
17 3 2 Tuesday
18 4 3 Wednesday
19 5 4 Thursday
20 6 5 Friday
21 7 5 Saturday
22 1 5 Sunday
23 2 6 Monday
24 3 7 Tuesday
25 4 8 Wednesday
26 5 9 Thursday
27 6 10 Friday
28 7 10 Saturday
28 1 10 Sunday
30 2 11 Monday
31 3 12 Tuesday

On Monday 16th there were 12 Trading Days Left In Month Inclusive
Output of Python Snippet - use in EZLang.

I start out with the current day of the month, 16 and loop through the rest of the days of the month.  Whenever I encounter a Sunday (1) or a Saturday (7) I do not increment tdToEOM, else I do increment.  

Here is how the function works on a chart.  Remember in TradeStation I am placing a market order for the NEXT BAR.

Counting the days until the EOM

This snippet of code is the heart of the function, but you must make in generic for any day of any month.  Here it is in EasyLanguage – you will see the similarity between the Python snippet and its corresponding EasyLanguage.

array: monthDays[12](0);

monthDays[1] = 31;
monthDays[2] = 28;
monthDays[3] = 31;
monthDays[4] = 30;
monthDays[5] = 31;
monthDays[6] = 30;
monthDays[7] = 31;
monthDays[8] = 31;
monthDays[9] = 30;
monthDays[10] = 31;
monthDays[11] = 30;
monthDays[12] = 31;

vars: curDayOfMonth(0),curDayOfWeek(0),loopDOW(0),tdToEOM(0),j(0);

curDayOfWeek = dayOfWeek(d);
curDayOfMonth = dayOfMonth(d);

{Python prototype
tdToEOM=0;
for j in range(currentDayOfMonth,daysInMonth+1):
if loopDOW != 1 and loopDOW != 7:
tdToEOM +=1;
print(j," ",loopDOW," ",tdToEOM)
loopDOW +=1;
if loopDOW > 7: loopDOW = 1;
}

loopDOW = curDayOfWeek+1;
tdToEOM=0;

for j = curDayOfMonth to monthDays[month(d)]
begin
if loopDOW <> 1 and loopDOW <> 7 then
tdToEOM +=1; // tdToEOM = tdToEOM + 1;
loopDOW +=1;
if loopDOW > 7 then loopDOW = 1;
end;
TradingDaysLeftInMonth = tdToEOM;
EasyLanguage Function : TradingDaysLeftInMonth

I used arrays to store the number of days in each month.  You might find a better method.  Once I get the day of the month and the day of the week I get to work.  EasyLanguage uses a 0 for Sunday so to be compliant with the Python function I add a 1 to it.  I then loop from the current day of month through monthDays[month(d)].  Remember month(d) returns the month number [1…12].  A perfect index into my array.  That is all there is to it.  The code is simple, but the concept requires a little thinking.  Okay, now that we have the tools for data mining, let’s do some.  I did this by creating the following strategy (the same strategy that create the original equity curves.)

inputs: numDaysBeforeEOM(8),numDaysAfterEOM(10),movingAvgLen(100);
inputs: stopLossAmount(1500),profitTargetAmount(4000);

vars: TDLM(0),TDIM(0);

TDLM = tradingDaysLeftInMonth;
TDIM = tradingDayOfMonth;

if c >= average(c,movingAvgLen) and TDLM = numDaysBeforeEOM then
begin
buy("Buy B4 EOM") next bar at open;
end;

if marketPosition = 1 and barsSinceEntry > 3 then
begin
if TDIM = numDaysAfterEOM then
begin
sell("Sell TDOM") next bar at open;
end;
end;
setStopLoss(stopLossAmount);
setProfitTarget(profitTargetAmount);
EasyLanguage function driver in form of Strategy

A complete strategy has trade management and an entry and an exit.  In this case, I added an additional feature – a trend detector in the form of a longer-term moving average.  Let’s see if we can improve the trading system.  Thank goodness for Genetic Optimization.  Here is the @ES market.

Get your Pick ready to mine!

Smoothed the equity curve – took the big draw down out.

Genetically MODIFIED – Data Mining at its best!

Here are the parameters:

Did not like the moving average. Wide stop and wide profit objective. Days to EOM and after EOM stayed the same.

Bond System:

Bond market results.

If you like this type of programming check out my books at Amazon.com.  I have books on Python and of course EasyLanguage.  I quickly assembled a YouTube video discussing this post here.

Conclusion – there is something here, no doubt.  But it can be a risky proposition.  It definitely could provide fodder for the basis of a more complete trading system.

George’s Amazon Page

 


Discover more from George Pruitt

Subscribe to get the latest posts sent to your email.

Leave a Reply