Systems based on Sheldon Knight and William Brower Research

Nov 11, 2022 | EasyLanguage, TradeStation

Systems based on Sheldon Knight and William Brower Research

In this post on Easylanguage, we are going to give our point of view on the famous statistician and trader Sheldon Knight and his K-DATA. In this timeline we will use the following nomenclature:

  • The first Monday of December = 1MonDec
  • The second Friday of April = 2FriApr
  • Third Wednesday of March = 3Wedmar

This enumeration or coding was used to determine whether a week of the month and day of the week were related to the seasonal trend. In the same way, if you know this type of indices, it is because you have ever worked with Witching Days.

Four times a year, stock option, stock index and stock index futures contracts expire on the same day, resulting in higher volumes and higher price volatility. For this reason, the stock market can seem strange and complicated to many people and this is what is known as “triple days of witching”.

The triple witching hour usually occurs on the third Friday of the last month of the term. That means the third Friday of March, June, September, and December. In 2022, the triple witching hours are March 18, June 17, September 16, and December 16 for the reasons previously stated.

Following this same line, there are also other days of some months that have their meaning. As is the case with the first Friday of every month (employment situation), they have even more meaning. In 1996, Bill Brower wrote an excellent article in Technical Analysis of Stocks and Commodities. The title of the article was The S&P 500 Seasonal Day Trade. In this article, Bill came up with 8 very simple daily trading patterns and then filtered them with the day of the week in the month. Here are the eight patterns as he laid them out in the article:

  1. Pattern 1: If tomorrow’s open is less than 30 points from today’s close, buy.
  2. Pattern 2: If it’s 30 points below today’s close, don’t buy.
  3. Pattern 3: If tomorrow’s open minus 30 points is higher than today’s close, then you sell the market.
  4. Pattern 4: if it is greater than 30 points then you should sell short.
  5. Pattern 5: If tomorrow’s open plus 10 is less than today’s low, then buy the market.
  6. Pattern 6: If the open is less than 20 points, then go short on the stop at today’s close.
  7. Pattern 7: If tomorrow’s open is less than 40 points from today’s close, buy at today’s low.
  8. Pattern 8 – If the open is more than 70 points from today’s close, then go short at the high limit today.

This article was written almost 27 years ago when 30 points meant something on the S&P futures contract. This company traded around 600,000. Today, the e-mini is trading around 4,000,000 and has been higher, so talking about 30, 40 or 70 points does not make sense.

For this reason, we have decided to create an ATR percentage, which means that if the range equals 112.00 mangoes and we use 5%, then the base would equal 11200 = 560 points. Using logic, if we use 1% and 5% ATR, we could replace Bill’s point values.

So Bill’s syntax is different from how the patterns have been described and it’s easy to see a gap of more than 30 points, but remember, there’s more than one way to program an idea.

Let’s continue with Bill’s syntax:

  • 10 points = 1 X (Mult) X ATR
  • 20 points = 2 X (Mult) X ATR
  • 30 points = 3 X (Mult) X ATR
  • 40 points = 4 X (Mult) X ATR
  • 50 points = 5 X (Mult) X ATR
  • 70 points = 7 X (Mult) X ATR

We can then play around with Mult to see if we can simulate similar levels from 1996.


// atrMult will be a small percentage like 0.01 or 0.05 atrVal = avgTrueRange(atrLen) * atrMult; //original patterns //use IFF function to either returne a 1 or a 0 //1 pattern is true or 0 it is false patt1 = iff(open of tomorrow - 3 * atrVal > c,1,0); patt2 = iff(open of tomorrow + 3 * atrVal < c,1,0); patt3 = iff(open of tomorrow - 3 * atrVal > c,1,0); patt4 = iff(open of tomorrow + 3 * atrVal < c,1,0); patt5 = iff(open of tomorrow + 1 * atrVal < l,1,0); patt6 = iff(open of tomorrow - 2 * atrVal > h,1,0); patt7 = iff(open of tomorrow - 4 * atrVal > c,1,0); patt8 = iff(open of tomorrow + 7 * atrVal < c,1,0);

 

Nov22Post1

 

Following this same line, the day of the week in a month is represented by a two-digit number. The first digit is the range of the week and the second number is the day of the week. I thought this was very clever so I decided to program it. I approached it from a couple of different angles, and actually coded an encoding method that included week range, weekday, and month (1stWedJan) in my high-res edit. Bill’s version didn’t need to be as sophisticated, and because I decided to use TradeStation’s optimization capabilities, I didn’t need to create a data structure to store any data.

On the other hand, NewMonth is set to false on every bar. If tomorrow’s day of month is less than today’s day of month, then we know we have a new month and newMonth is set to true.

If we have a new month, several things happen: reset the code that counts the number monday, tuesday, wednesday, thursday, and friday to 0 (not used for this application, but can be used later) and set the week count weekCnt to 1. If it is not a new month and tomorrow’s weekday is less than today’s weekday (Monday = 1 and Friday = 5, if tomorrow is less than today (1 < 5)) then we must have a new week at the bar tomorrow. Encoding the day of the week in a month as a two-digit number is quite easy: just multiply the week range (or count) by 10 and add the day of the week (1-Monday, 2-Tuesday,… .) So the third Wednesday would be equal to 3X10+3 or 33.

Use optimization to go through 8 patterns and 25 days of the week in monthly enumerations

Stepping through the 8 patterns is pretty straightforward. However, going through the 25 possible DowInAMonth codes or enumerations is more complex. Many times you can use an equation based on the iterative process of going from 1 to 25. This is a perfect example of how to replace math with computer code.

newMonth = False;
newMonth = dayOfMonth(d of tomorrow) < dayOfMonth(d of today);
atrVal = avgTrueRange(atrLen) * atrMult;
if newMonth then
begin
	startTrading = True;
	monCnt = 0;
	tueCnt = 0;
	wedCnt = 0;
	thuCnt = 0;
	friCnt = 0;
	weekCnt = 1;
end;

if not(newMonth) and dayOfWeek(d of tomorrow) < dayOfWeek(d of today) then
	weekCnt +=1;

dayOfWeekInMonth = weekCnt * 10 + dayOfWeek(d of tomorrow);

Similarly, here we are activating the input (dowInMonthInc). Remember that this value will go from 1 to 25 in increments of 1. What’s really cool about EasyLanguage’s Switch-Case implementation is that it can handle ranges. If dowInMonthInc happens to be 4, it will fall into the first block of cases (case 1 to 5). Here we know that if this value is less than 6 then we are in the first week, so I set the first number in the two-digit representation dayOfWeekInMonth to 1. This is accomplished by setting value3 to 10. Now you need to extract the day of the week from loop 1 to 25. If dowInMonthInc is less than 6, all you need to do is use the modulo function and the value 6.

  • mod(1,6) = 1
  • mod(2,6) = 2
  • mod(3,6) = 3

This works great when the increment value is less than 6. Remember:

  • 1 –> 11 (first Monday)
  • 2 –> 12 (first Tuesday)
  • 3 –> 13 (first Wednesday)
  • 6 –> 21 (second Monday)
  • 7 –> 22 (second Tuesday).

So, you have to be a bit creative with your code. Suppose the iterative value is 8. We need 8 to equal 23 (second Wednesday). This value falls in the second case, so Value3 = 20 the second week of the month. That’s pretty easy. Now we need to extract the day of the week. Remember this is just one solution, I guarantee there are many.

mod(dowInMonthInc – 5, 6) – works?

valor2 = mod(8-5,6) = 3 -> valor3 = valor1 + valor2 -> valor3 = 23. It worked. See the pattern below?

  • case 6 to 10 – mod(dowInMonthInc – 5, 6)
  • case 11 to 15 – mod(dowInMonthInc – 10, 6)
  • case 16 to 20 – mod(dowInMonthInc – 15, 6)
  • case 21 to 25 – mod(dowInMonthInc – 20, 6)

Nov22Post3

Save the optimization report as text and open it with Excel

Nov22Post2

These are the settings I used to create the following report. If you do the math, that’s a total of 200 interactions.

inputs: atrLen(10),atrMult(.05),patternNum(1),dowInMonthInc(1);

vars: patt1(0),patt2(0),patt3(0),patt4(0),
patt5(0),patt6(0),patt7(0),patt8(0);

vars: atrVal(0),dayOfWeekInMonth(0),startTrading(false),newMonth(False);;

vars: monCnt(0),tueCnt(0),wedCnt(0),thuCnt(0),friCnt(0),weekCnt(0);


newMonth = False;
newMonth = dayOfMonth(d of tomorrow) < dayOfMonth(d of today);
atrVal = avgTrueRange(atrLen) * atrMult;
if newMonth then
begin
	startTrading = True;
	monCnt = 0;
	tueCnt = 0;
	wedCnt = 0;
	thuCnt = 0;
	friCnt = 0;
	weekCnt = 1;
end;

if not(newMonth) and dayOfWeek(d of tomorrow) < dayOfWeek(d of today) then
	weekCnt +=1;

dayOfWeekInMonth = weekCnt * 10 + dayOfWeek(d of tomorrow);	


//print(date," ", dayOfMonth(d)," " ,dayOfWeek(d)," ",weekCnt," ",monCnt," ",dayOfWeekInMonth);


//original patterns

patt1 = iff(open of tomorrow - 3 * atrVal > c,1,0);
patt2 = iff(open of tomorrow + 3 * atrVal < c,1,0);
patt3 = iff(open of tomorrow - 3 * atrVal > c,1,0);
patt4 = iff(open of tomorrow + 3 * atrVal < c,1,0);
patt5 = iff(open of tomorrow + 1 * atrVal < l,1,0);
patt6 = iff(open of tomorrow - 2 * atrVal > h,1,0);
patt7 = iff(open of tomorrow - 4 * atrVal > c,1,0);
patt8 = iff(open of tomorrow + 7 * atrVal < c,1,0);


switch(dowInMonthInc)
begin
	case 1 to 5:
		value2 =  mod(dowInMonthInc,6);
		value3 = 10;
	case 6 to 10:
		value2 = mod(dowInMonthInc-5,6);
		value3 = 20;
	case 11 to 15:
		value2 = mod(dowInMonthInc-10,6);
		value3 = 30;
	case 16 to 20:
		value2 = mod(dowInMonthInc-15,6);
		value3 = 40;
	case 21 to 25:
		value2 = mod(dowInMonthInc-20,6);
		value3 = 50;
end;

value1 = value3 + value2 ;

//print(d," ",dowInMonthInc," ",dayOfWeekInMonth," ",value1," ",value2," ",value3," ",mod(dowInMonthInc,value2));

if value1 = dayOfWeekInMonth then
begin
	if patternNum = 1 and patt1 = 1 then buy("Patt1") next bar at open;
	if patternNum = 2 and patt2 = 1 then buy("Patt2") next bar at open;
	if patternNum = 3 and patt3 = 1 then sellShort("Patt3") next bar at open;
	if patternNum = 4 and patt4 = 1 then sellShort("Patt4") next bar at open;
	if patternNum = 5 and patt5 = 1 then buy("Patt5") next bar at low limit;
	if patternNum = 6 and patt6 = 1 then sellShort("Patt6") next bar at close stop;
	if patternNum = 7 and patt7 = 1 then buy("Patt7") next bar at low limit;
	if patternNum = 8 and patt8 = 1 then sellShort("Patt8") next bar at high stop;
end;

setExitOnClose;

In closing, I would like to point out that this subject could be considered in much more depth, however I would like to thank William Brower for his excellent article The S&P Seasonal Day Trade in Stocks and Commodities, August 1996 issue, V.14:7 ( 333-337).

We hope you enjoyed and took advantage of this article on systems based on Sheldon Knight and William Brower Research.

Article written by George Pruitt

Subscribe to our Newsletter

Join our mailing list to receive the latest news and updates from Quantified Models team.

Subscribe to our Newsletter

You have Successfully Subscribed!

Skip to content