Dynamic Moving Average Cross Over EasyLanguage

{Dynamic Moving AVerage System by George Pruitt}
{Use volatility to adapt moving average lenghts}
{in hopes to outperform a static parameter set!}

vars: avg1Ceil(23),avg1Floor(9),avg2Ceil(100),avg2Floor(25);

vars: shortMALen(19),longMALen(29);
vars: x1(0),y1(0),x2(0),y2(0),deltaVol1(0),deltaVol2(0);


If barNumber = 1 then
Begin
	shortMALen = 19;
	longMALen = 29;
end;

x1 = Stddev(close,shortMALen);
y1 = Stddev(close[1],shortMALen);

x2 = Stddev(close,longMALen);
y2 = Stddev(close[1],longMALen);



deltaVol1 = (x1-y1)/y1;
deltaVol2 = (x2-y2)/y2;


shortMALen = (1+deltaVol1)*shortMALen;
shortMALen = MaxList(shortMALen,avg1Floor);
shortMALen = MinList(shortMALen,avg1Ceil);
shortMALen = round(shortMALen,0);

longMALen = (1+deltaVol2)*longMALen;
longMALen = MaxList(longMALen,avg2Floor);
longMALen = MinList(longMALen,avg2Ceil);
longMALen = round(longMALen,0);

If average(c,shortMALen) crosses above average(c,longMALen) then buy next bar at open;
If average(c,shortMALen) crosses below average(c,longMALen) then sellshort next bar at open;

Hash Table In EasyLanguage [Part 2]

Using The Hash Table

Now that we have created an empty Hash Table and the Hash Index it is now time to start filling the table up with the appropriate information.  As I pointed out in my last post, every day of any given year can be represented by a nine character string. If January 1st lands on a Tuesday, you can express this day with the following string, “1stTueJan.” That is if you want to ignore the year and in this case, we do.

Mapping Into the Hash Table

The table has already been prepared as well as the index.  All we have to do is map the current day into the index.  The location of the index value in the Hash Index array will then be used to locate the day’s location in the Hash Table.  We will use a function to convert the current day of the year into a value our Hash Index can interpret.

Here is the code to the function.  Don’t fret too much at the number of lines of code!

inputs: testDate(numericSeries);

vars: testMonth(0),tempStr("");
Array : prefixStrArr[6](""),dayofweekStr[5](""),monthName[12]("");
vars: monCnt(0),tueCnt(0),wedCnt(0),thuCnt(0),friCnt(0),tempDate1(0),tempDate2(0);
vars: freshStart(false),occurString(""),dayString(""),monthString("");
vars: whatOccurOfMonthStr(""),cnt(0),td(0),myCnt(0),daysBack(0);

preFixStrArr[1] = "1st";
preFixStrArr[2] = "2nd";
preFixStrArr[3] = "3rd";
preFixStrArr[4] = "4th";
preFixStrArr[5] = "5th";
preFixStrArr[6] = "6th";

dayOfWeekStr[1] = "Mon";
dayOfWeekStr[2] = "Tue";
dayOfWeekStr[3] = "Wed";
dayofWeekStr[4] = "Thu";
dayOfWeekStr[5] = "Fri";

monthName[1] = "Jan";
monthName[2] = "Feb";
monthName[3] = "Mar";
monthName[4] = "Apr";
monthName[5] = "May";
monthName[6] = "Jun";
monthName[7] = "Jul";
monthName[8] = "Aug";
monthName[9] = "Sep";
monthName[10] = "Oct";
monthName[11] = "Nov";
monthName[12] = "Dec";

tempDate1 = month(testDate[0]);
tempDate2 = month(testDate[1]);
cnt = 0;monCnt = 0;tueCnt=0;wedCnt=0;thuCnt=0;friCnt=0;
While (month(date) = month(date[cnt])) and cnt < 30
Begin
//	print(date," ",date[cnt]," ",cnt);
	cnt = cnt + 1;
end;
daysBack = cnt -1;

If daysBack < 0 then daysBack = 0;

For cnt = daysBack downto 0
begin
	If dayOfWeek(date[cnt]) = 1 then monCnt = monCnt + 1;
	If dayOfWeek(date[cnt]) = 2 then tueCnt = tueCnt + 1;	
	If dayOfWeek(date[cnt]) = 3 then wedCnt = wedCnt + 1;
	If dayOfWeek(date[cnt]) = 4 then thuCnt = thuCnt + 1;
	If dayOfWeek(date[cnt]) = 5 then friCnt = friCnt + 1;
end;
//print("counts: ",monCnt," ",tueCnt," ",wedCnt," ",thuCnt," ",friCnt);

If dayOfWeek(date) = Monday then tempStr = preFixStrArr[monCnt];
If dayOfWeek(date) = Tuesday then tempStr = preFixStrArr[tueCnt];
If dayOfWeek(date) = Wednesday then tempStr = preFixStrArr[wedCnt];
If dayOfWeek(date) = Thursday then tempStr = preFixStrArr[thuCnt];
If dayOfWeek(date) = Friday then tempStr = preFixStrArr[friCnt];

tempStr = tempStr + dayOfWeekStr[dayOfWeek(date)];
tempStr = tempStr + monthName[month(date)];
GetWhichWeekMonth = tempStr;
GetWhichWeekMonth Function

Here is where using an integer representation of the date would reduce the number of lines of code tremendously.  Well, I made my bed I might as well sleep in it.  You will see some duplication between this code and the Hash Table creator function.  I have to store names for the week rank, day of the week, and month in arrays.  There isn’t a simple function that will pull the week rank from any given date.  So I simply take the date and work my way back to the beginning of the month counting each weekday as I go along.

For cnt = daysBack downto 0
begin
	If dayOfWeek(date[cnt]) = 1 then monCnt = monCnt + 1;
	If dayOfWeek(date[cnt]) = 2 then tueCnt = tueCnt + 1;	
	If dayOfWeek(date[cnt]) = 3 then wedCnt = wedCnt + 1;
	If dayOfWeek(date[cnt]) = 4 then thuCnt = thuCnt + 1;
	If dayOfWeek(date[cnt]) = 5 then friCnt = friCnt + 1;
end;

Getting The Hash Index

The number that is stored in the individual counters (monCnt, tueCnt, etc.) determines which week of the month the current day is located.  I build the string through concatenation.  First I get the week rank (“1st”, “2nd”, “3rd”, “4th”, “5th”), add the name of the day and then add the month.  The end result looks like “1stMonJan”.  From here I cross-reference the Hash Index and pull out the location of the of the string (aka index.)  Here is the function GetHashIndex.

input: hashIndex[n](stringArrayRef),hashTableRows(numericSimple),searchString(string);
vars: iCnt(0),done(false);

GetHashIndex = 0;
done = false;

For iCnt = 1 to hashTableRows
Begin
//	print("Looking for: ",searchString," ",hashIndex[iCnt]," ",iCnt);
	If searchString = hashIndex[iCnt] then 
	begin
		done = true;
		GetHashIndex = iCnt;
	end;
	If done then break;
end;
GetHashIndex

As you can see it is a linear search that returns the Hash Index’s Index.  Check out how I prematurely exit the loop by using the keyword Break.  This keyword knocks you out of any loop where it is located.  If you have a nested loop, the break only gets you out of that current loop where it is located.

Hast Table Indicator

Now how can we pull all this together to create a useful trading tool.  I used these tools to create an indicator that plots the average daily change from one day to the next.  So, if today is the “3rdMonJune” and the indicator reads 0.52, this represents that over the last X years the average percentage change is a plus .5%.  Would it make sense to buy the “2ndFriJun” and exit on the close of the “3rdMonJune?”  Maybe.

Here is the code for the Hash Table indicator.

vars: returnValString(""),iCnt(0),jCnt(0); 
array: weekDayMonthIndex[300]("");
array: HashTable[300,100](0);
array: timeLine[300](0);
vars: searchString(""),numYearsCollected(0),hashIndex(0);
vars: yCnt(0),numYears(0);
vars: hashRows(300);
vars: myBarCount(0),maxNumYearsInHash(0),avgDailyChange(0),dailyChangeSum(0);

If barNumber = 1 then  //build the hash index - index form "1stMonJul" "2ndFriDec"
begin
	Value1 = HashIndexCreator(weekDayMonthIndex);
end;

numYearsCollected = HashTableCreator(HashTable,weekDayMonthIndex);  {Build hash table as we go along}

If year(date) <> year(date[1]) then numYears = numYears + 1; 

If numYearsCollected > 3 then  // only pull information if there is at least three years of data
Begin
	searchString = GetWhichWeekMonth(date);	// convert today's date into a compatible Hash Index value
	hashIndex = GetHashIndex(weekDayMonthIndex,hashRows,searchString);  // find the location of today's value in the Hash Index
	dailyChangeSum = 0;;
//	print(d," ",searchString," ",hashIndex);
	For yCnt = 2 to numYearsCollected
	Begin
		dailyChangeSum = dailyChangeSum + HashTable[hashIndex,yCnt];
	end;
	avgDailyChange = dailyChangeSum/numYearsCollected;
	if year(date) = 116 then print(d," ",searchString," ",numYearsCollected," ",avgDailyChange);
	if numYearsCollected > numYears-1  then plot1(avgDailyChange,"AvgChgFromYesterday");
End;
HashTableIndicator

Results of Using the Hash Table

Here is a simple output of the results from the indicator for the year of 2016.  I sorted the data based on highest average daily change and number of years collected.

1160729 5thFriJul 7 0.95
1161031 5thMonOct 5 0.62
1160115 2ndFriJan 16 0.56
1160830 5thTueAug 7 0.55
1160713 2ndWedJul 17 0.52
1160812 2ndFriAug 17 0.52
1160519 3rdThuMay 16 0.43
1161003 1stMonOct 17 0.38
1160112 2ndTueJan 16 0.38
1160223 4thTueFeb 16 0.38
1161122 4thTueNov 16 0.37
1160804 1stThuAug 17 0.35
1160316 3rdWedMar 16 0.35
1160711 1stMonJul 17 0.34
1161121 3rdMonNov 17 0.34
1160225 4thThuFeb 16 0.34
1160517 3rdTueMay 16 0.34
1160610 2ndFriJun 16 0.34
1161215 3rdThuDec 17 0.33

It looks like the buying the close “4thThuJul” is the way to go!  But since there are only seven observations I think would think twice.  But, buying the close on the day prior to “2ndFriJan” might offer that technical advantage you’re looking for.

Hash Table in EasyLanguage [Part 1]

 

This concept may be considered advanced and only used by pure programmers, but that is not the case at all.  A Hash Table is simply a table that is indexed by a function.  The function acts like the post office – it sends the data to the correct slot in the table.  I utilized this data structure because  I wanted to know the closing prices for the past fifteen years for the “1stThuJan” (first Thursday of January.)  This, of course, would require some programming and I could simply store the values in an array.  However, what if I wanted to know the closing prices for the “3rdFriMar?”  I would have to spend more time and re-code, right?   What if I changed my mind again.  Instead, as we programmers often do, I wanted to be able to pull the data for any instance of “Week, Day Of Week, Month.”  This is where a table structure comes in handy.  With this table, I can query it and find out the average yearly closing prices for the “1stMonSep” or the “4thFriJuly” or the “3rdWedApr ” on a rolling year by year basis.  Why would you want this you might ask?  Would it be helpful to know the price  change from the “2ndMonMar” to the subsequent “2ndMonMar” on a rolling basis?  What if the average price change is 10%.  You could use this information to make sure you always buy on this particular day.  That is if you believe in this form of analysis.

Here’s how I created a table that stores the closing prices for the past 15 years for each entry in the table.  Remember each row value only comes up once a year.  You only have one “1stMonJan” in a calendar year.  So the first part of the problem was simple, create a table that can store the closing prices with all the different combinations like the “1stTueJan” for the past fifteen years.  The second part of creating the post office like function that places the correct closing price in the right row was a little more difficult, but not much.  Here’s how I did it.

As I said earlier, a Hash Table is a very simple concept and very useful as well!  For some of you out there, I just want to make sure that you know that I am not talking about a device to keep your cannabis off of the floor;-) All kidding aside, go ahead and take a look at the table below.  Notice how it stores the closing prices of all the possible occurrences of Week, Day Of Week, Month.   Column 1 is the key or Hash Index value.  You will need this key to unlock the data for that particular row.  Column 2 shows the number of years that the data was collected.  Column 3 and on are the closing prices for that particular day across the years.  Once you have the data collected you can do anything your heart desires with it.

Table Index Num. Years Close 1 Close 2 Close 3 Close 4 Close 5 Close 6
1stWedJan 6 603 496 450.25 589 612.75 684.5
1stThuJan 6 606.5 486.25 446 571.75 597.75 683
1stFriJan 6 607.25 491.75 451.5 564.75 597.75 674
1stMonJan 6 606.75 490.75 447 590.25 606.25 679.25
1stTueJan 6 619.25 507 447.25 578.25 612.75 682.5
2ndWedJan 6 617.75 446 412.5 600.75 605.75 688
2ndThuJan 6 615.5 444.75 409.5 612.25 565.75 692.5
2ndFriJan 6 635.5 490.25 400 618.5 553.75 702.5
2ndMonJan 6 652.5 460.25 451 576.75 574.25 717.75

Sounds cool – so let’s do it!

Step 1:  Calculate the size of the table.

Each month consists of 4.25 weeks (52/12).  Because of this, you can have up to five occurrences of any given day of the week inside of a month – five Mondays, Tuesdays, etc.,  So we must build the table big enough to handle five complete weeks for each month.  Since there are 5 days in a week and 5 weeks in a month (not really but plan on it)  and 12 months in a year, the table must contain at least 300 rows ( 5 X 5 X 12.)  Since we don’t know how many years of data that we might want to collect we could make the arrays dynamic, but I want to keep things simple so I will reserve space for 100 years.  Overkill?

Step 2:  Use measurements from Step 1 to construct the container and create an addressing function.

The container is easy just dimension a 2-d array.  A 2-d array is a table whereas a 1-d array is a list.  A spreadsheet is an example of a 2-d array.  Just make the table big enough to hold the data.  Remember the key component to the Hash Table is not what it can hold, but the ability to quickly reference the data.  Just like your home, we need to create a unique address for each of the three hundred rows so the right mail, er data can be delivered or stored.  This is really quite simple –  we know we need a distinctive address and we know we need 300 of them.  Like the table above we can create a unique address in the form of “1stMonJan.”  This is a nine character string.  This  string can easily represent the 300 different addresses.  We start with “1stMonJan” and end with “5thFriDec.”  These values most consist of only nine characters.  I could have done the same thing using an integer value to represent each address.  “1stMonJan” could also be represented with 10101.  The “3rdFriDec” would be 30512.  I liked the string approach because the addresses are instantly recognizable with little or no translation.  So we need to get to typing, right?  Always remember if you are doing something redundant a computer can do the chore and do it quicker.  Just a quick note here.  I  designed the table ahead of time with the values in column 1 already filled in.  I could have done it more dynamically, but creating a data structure and filling in as much information before can save time on the programming side.

Instead of typing each unique address into the table, let’s let the computer do it for us.  Remember, Easylanguage has some cool string manipulation tools and with a little bit of cleverness, you can create the 300 unique addresses in one fell swoop.  The following code creates an array (list) of all of the possible combinations of “Week, Day Of Week, Month.”  There are 100 lines of code here, don’t freak out!  It’s mostly redundant.  I used a Finite State Machine and Easylanguage’s Switch – Case programming structure.  So you are learning about Hash Tables, Hash Indices, Finite State Machines, and Switch-Case programming in one post.  And here, all you want is a winning trading system.  Well, they are hard to come by and you need as many tools at your disposal to unlock the Holy Grail.  This is just one way to come up with the address values.

{Developed and programmed by George Pruitt-copyright 2017 www.georgepruitt.com}
{Just provide credit if you reuse! Or buy my book ;-)}

Input: hashIndex[n](stringArrayRef);
Vars: done(false);
Vars: firstCount(0),secondCount(0),thirdCount(0),fourthCount(0),fifthCount(0);
Vars: state(1),arrCnt(0),tempStr(""),monthCnt(0),returnValString(""),iCnt(0),jCnt(0),numBucket(0);
array: dayString[5](""),monString[12]("");


dayString[1] = "Mon";
dayString[2] = "Tue";
dayString[3] = "Wed";
dayString[4] = "Thu";
dayString[5] = "Fri";

monString[1] = "Jan";
monString[2] = "Feb";
monString[3] = "Mar";
monString[4] = "Apr";
monString[5] = "May";
monString[6] = "Jun";
monString[7] = "Jul";
monString[8] = "Aug";
monString[9] = "Sep";
monString[10] = "Oct";
monString[11] = "Nov";
monString[12] = "Dec";


arrCnt = 0;
monthCnt = 1;
While not(done) and arrCnt<300
begin
	if state < 6 then arrCnt = arrCnt + 1;
	switch (state)
	Begin		   
		case 1:
			firstCount = firstCount + 1;
			tempStr = "1st";
			tempStr = tempStr + dayString[firstCount] + monString[monthCnt];
			hashIndex[arrCnt] = tempStr;
			If firstCount = 5 then 
			begin
				state = 2;
				firstCount = 0;
			end;
		case 2:
			secondCount = secondCount + 1;
			tempStr = "2nd";
			tempStr = tempStr + dayString[secondCount] + monString[monthCnt];
			hashIndex[arrCnt] = tempStr;	
			If secondCount = 5 then 
			begin
				state = 3;
				secondCount = 0;
			end;
		case 3:
			thirdCount = thirdCount + 1;
			tempStr = "3rd";
			tempStr = tempStr + dayString[thirdCount] + monString[monthCnt];
			hashIndex[arrCnt] = tempStr;
			If thirdCount = 5 then 
			begin
				state = 4;
				thirdCount = 0;
			end;
		case 4:
			fourthCount = fourthCount + 1;
			tempStr = "4th";
			tempStr = tempStr + dayString[fourthCount] + monString[monthCnt];
			hashIndex[arrCnt] = tempStr;
			If fourthCount = 5 then 
			begin
				state = 5;
				fourthCount = 0;
			end;
		case 5:
			fifthCount = fifthCount + 1;
			tempStr = "5th";
			tempStr = tempStr + dayString[fifthCount] + monString[monthCnt];
			hashIndex[arrCnt] = tempStr;	
			If fifthCount = 5 then 
			begin
				state = 6;	
				fifthCount = 0;
			end;
		case 6:
			If monthCnt < 12 then
			Begin
				state = 1;
				monthCnt = monthCnt + 1;
			end
			else
			begin
				done = true;
			end;
		end;				
end;
HashIndexCreator = 1;
Hash Index Creator

Here is a brief overview of this code.  The switch statement requires matching case statements.  In this machine, there are 6 different states.  Based on whatever the current state happens to be, the computer executes that block of code.  If the state is 1, then the block of code encapsulated with case(1) is executed.  All other code is ignored.  I start building the array by executing all of the “1st”‘s in January – “1stMonJan, 1stTueJan, 1stWedJan, 1stThuJan, and 1stFriJan.”   The nine character strings are built using concatenation.  In Easylanguage and most other languages you can add strings together:  “Cat” + “Dog” = “CatDog.”  So I take the string “1st” + “Mon” +  “Jan” to form the string “1stMonJan.”  I store the three characters for the day of the week and the three characters for the month in simple arrays.  After the fifth “1st”, I transition to state 2 and start working on all the “2nd”‘s.  Eventually the machine switches into 6th gear, er uh I mean state.  If month count is less than twelve, we gear down all the way back down to state 1 and start the process again for the month of February.  The machine finally turns off after month counter exceeds 12.  The Hash Index is completed; we have a unique address for the 300 rows.  In Part 2 I will show how to map the Hash Index onto the Hash Table and how to store the necessary information.  Finally, we will create an indicator using the data pulled from the table.

Don’t Fool Yourself – Limitations of Back Testing with Daily Data [EasyLanguage]

Which equity curve do you like best? (created with EasyLanguage script) This one…

Or this one?

Obviously the first one.  Even though it had a substantial draw down late in the test.  What if I told you that the exact same system logic generated both curves?  Here is the EasyLanguage code for this simple system.

Buy next bar at open of next bar + .25 *avgTrueRange(10) stop;
Sellshort next bar at open of next bar - .25*avgTrueRange(10) stop;

setStopLoss(500);
setProfitTarget(1000);
Open Range Break Out with Profit and Loss Objective

This algorithm relies heavily on needing to know which occurred first: the high or the low of the day.   The second chart tells the true story because it looks inside the daily bar to see what really happened.  The first chart uses an algorithm to try to determine which happened first and applies this to the trades.  In some instances,  the market looks like it opens then has a slight pull back and then goes up all day.  As a result the system buys and holds the trade through the close and onto the next day, but in reality the market opens, goes up and triggers a long entry, then retraces and you get stopped out.  What was a nice winner turns into a bad loss.  Here is an example of what might have happened during a few trades:

Nice flow – sold, bought, sold, bought, sold again and finally a nice profit.  But this is what really happened:

Sold, bought, reversed short on same day and stopped out on same day.  Then sold and reversed long on same day and finally sold and took profit.   TradeStation’s Look Inside Bar feature helps out when your system needs to know the exact path the market made during the day.  In many cases, simply clicking this feature to on will take care of most of your testing needs.  However, this simple algorithm needs to place or replace orders based on what happens during the course of the day.  With daily bars you are sitting on the close of the prior day spouting off orders.  So once the new day starts all of your orders are set.  You can’t see this initially on the surface, because it seems the algorithm is so simple.   Here is another consequence of day bar testing when the intra-day market movement is paramount:

Here the computer is doing exactly what you told it!  Sell short and then take a profit and sell short 25% of the ATR below the open.  Well once the system exited the short it realized it was well below the sell entry point so it immediately goes short at the exact same price (remember TS doesn’t allow stop limit orders).  You told the computer that you wanted to be short if the market moves a certain amount below the open.  These were the orders that were place on yesterday’s close  This may not be exactly what you wanted, right?  You probably wanted to take the profit and then wait for the next day to enter a new trade.  Even if you did want to still be short after the profit level was obtained you wouldn’t want to exit and then reenter at the same price (practically impossible) and be levied a round-turn slip and commission.   You could fiddle around with the code and try to make it work, but I guarantee you that a system like this can only be tested properly on intra-day data.  Let’s drop down to a lower time frame, program the system and see what the real results look like:

Looks very similar to the daily bar chart with Look Inside Bar turned on.  However, it is different.  If you wan’t to gauge a systems potential with a quick program, then go ahead and test on daily bars with LIB turned on.  If it shows promise, then invest the time and program the intra-day version just to validate your results.  What do you mean spend the time?  Can’t you simply turn your chart from daily bars to five minute bars and be done with it.  Unfortunately no!  You have to switch paradigms and this requires quite a bit more programming.  Here is our simple system now in EasyLanguage:

Vars:stb(0),sts(0),atr(0),icnt(0);
Vars:buysToday(0),sellsToday(0),mp(0);

{Use highD() and XXXXD(0)  functions to capture the highs, lows, and closes for the past 10 days.  
 I could have just used a daily bar as data2.  
 I am looking at five minute bars so we know how the market flows through the day.
}

{This loop kicks out a warning message, but seems to work
 Just do this once at the beginning of the day - faster}

{remember true range is either the higher of todays high
 Or yesterdays close minus the lower of todays low or 
 yesterdays close}

{ tradeStation time stamps at the close of the bar so
 we capture the opening of the open time plus the bar interval - 
 in this case 5 minute - so at 1800 + 5 (1805) I capture the open
 of the day}

if time = sess1StartTime + barInterval then  
begin
	Value1 = 0.0;
	for icnt = 1 to 10
	begin
		Value1 = value1 + maxList(closeD(icnt-1),highD(icnt)) - minList(closeD(icnt-1),lowD(icnt));
	end;
	atr = value1/10.0;
	stb = open + .25* atr;
	sts = open - .25* atr;
	buysToday = 0;
	sellsToday = 0;
end;

mp = marketPosition; {The ole mp trick}

If mp = 1 and mp[1] <> 1 then buysToday = buysToday + 1;
If mp =-1 and mp[1] <> -1 then sellsToday = sellsToday + 1;

if buysToday = 0  and time < sess1EndTime and close <= stb then buy next bar at stb stop;
if sellsToday = 0 and time < sess1EndTime and close >= sts then sellshort next bar at sts stop;

setStopLoss(500);
setProfitTarget(1000);
Open Range Break Out Utilizing Five Minute Bars

Here is a validation that Look Inside Bar does work:

This is the trade from June 1st.  Scroll back up to the second chart where LIB is turned on.

Original Camarilla EasyLanguage Code

Camarilla – A group of confidential, often scheming advisers; a cabal.

 

I wanted to elaborate on the original version of Camarilla.  The one that users have been downloading from this website is pure reversion version.  The Camarilla Equation was by created by Nick Scott, a bond day trader, in 1989.  The equation uses just yesterday’s price action to project eight support/resistance price levels onto today’s trading action.  These levels, or advisers, as the name of the equation suggests provides the necessary overlay to help predict turning points as well as break outs.  Going through many charts with the Camarilla indicator overlay it is surprising how many times the market does in fact turn at one of these eight price levels.  The equations that generate the support/resistance levels are mathematically simple:

 

Resistance #4 = Close + Range * 1.1 / 2;

Resistance #3 = Close + Range * 1.1/4;

Resistance #2 = Close + Range * 1.1/6;

Resistance #1 = Close + Range * 1.1/12;

 

Support #1 = Close – Range * 1.1/12;

Support #2 = Close – Range * 1.1/6;

Support #3 = Close – Range * 1.1/4;

Support #4 = Close – Range * 1.1/2;

 

The core theory behind the equation and levels is that prices have a tendency to revert to the mean.  Day trading the stock indices would be easy if price broke out and continued in that direction throughout the rest of the day.  We all know that “trend days” occur very infrequently on a day trade basis; most of the time the indices just chop around without any general direction.  This is where the Camarilla can be effective.  Take a look at the following chart [ ES 5-minute day session] where the indicator is overlaid. and how the strategy was able to take advantage of the market’s indecisiveness.  This particular example shows the counter-trend nature of the Camarilla.  The original Camarilla looked at where the market opened to make a trading decision.  The chart below is an adapted version of the one I send out when one registers on for the download.   I thought it would be a good idea to show the original that incorporates a break out along with the counter trend mechanism.  I will go over the code in the next post.  You can copy the code below and paste directly into you EasyLanguage editor.

Camarilla at its Best!
Carmarilla in Reversion Mode

Original Camarilla rules:

  • If market opens between R3 and R4 go with the break out of R4.  This is the long break out part of the strategy.
  • If market opens between R3 and S3 then counter trend trade at the R3 level.  In other words, sell short at R3.  If the market moves down, then buy S3.  As you can see this is the mean reversion portion of the strategy.
  • If market open between S3 and S4 go with the break out of S4 – the short break out method.
  • Stops are placed in the following manner:
    • If long from a R4 break-out, then place stop at R3.
    • If short from a S4 break-out, then place stop at S3.
    • If long from a R3 countertrend, then place stop at R4.
    • If short from a S3 countertrend, then place stop at S4.
  • Profit objectives can be placed at opposite resistance/support levels:
    • If short from a R3 countertrend, then take profits at S1, S2, or S3.
    • If long from a S3 countertrend, then take profits at R1, R2, or R3.

Profit objectives for all trades can be a dollar, percent of price of ATR multiple.

Example of Camarilla Break Out Trade from S4
Camarilla Break Out
inputs: endTradetime(1530);
vars:	R1(0),R2(0),R3(0),R4(0),S1(0),S2(0),S3(0),S4(0),pivotPoint(0),myAvg(0);
vars: buyTrig(0),sellTrig(0),waitBar(0),s3Pen(0),r3Pen(0),s4Pen(0),r4Pen(0),s2Pen(0),r2Pen(0);
vars: buysToday(0),sellsToday(0);
vars: s3_s4(0),s2_s3(0),s1_s2(0),s4_s5(0);
vars: r1_r2(0),r2_r3(0),r3_r4(0),r4_r5(0);
vars: r1_s1(0),r3_s3(0),r4_s4(0),r5_s5(0);

if date <> date[1] then
begin
	buyTrig = 0;
	sellTrig = 0;
	waitBar = 0;
	
	s3Pen = 0;
	r3Pen = 0;
	s4Pen = 0;
	r4Pen = 0;
	s2Pen = 0;
	r2Pen = 0;
	
	buysToday = 0;
	sellsToday = 0;
	
	r4_r5 = 0;
	r3_r4 = 0;
	r2_r3 = 0;
	r1_r2 = 0;
	
	r1_s1 = 0;
	r3_s3 = 0;
	r4_s4 = 0;
	r5_s5 = 0;
	
	s1_s2 = 0;
	s2_s3 = 0;
	s3_s4 = 0;
	s4_s5 = 0;

end;

waitBar = waitBar + 1;

R4 = CloseD(1)+(HighD(1)-LowD(1)) * 1.1 / 2;
R3 = CloseD(1)+(HighD(1)-LowD(1)) * 1.1/4;
R2 = CloseD(1)+(HighD(1)-LowD(1)) * 1.1/6;
R1 = CloseD(1)+(HighD(1)-LowD(1)) * 1.1/12;
S1 = CloseD(1)-(HighD(1)-LowD(1)) * 1.1/12;
S2 = CloseD(1)-(HighD(1)-LowD(1)) * 1.1/6;
S3 = CloseD(1)-(HighD(1)-LowD(1)) * 1.1/4;
S4 = CloseD(1)-(HighD(1)-LowD(1)) * 1.1/2;

if openD(0)<= s4 then s4_s5 = 1;

If openD(0)> s4 and openD(0) <= s3 then s3_s4 = 1;
If openD(0)> s3 and openD(0) <= s2 then s2_s3 = 1;
If openD(0)> s2 and openD(0) <= s1 then s1_s2 = 1;

If openD(0)> s1 and openD(0) <= r1 then r1_s1 = 1;

If openD(0)> r1 and openD(0) <= r2 then r1_r2 = 1;
If openD(0)> r2 and openD(0) <= r3 then r2_r3 = 1;
If openD(0)> r3 and openD(0) <= r4 then r3_r4 = 1;

If openD(0)> r4 then r4_r5 = 1;

if openD(0) < r3 and openD(0) > s3 then r3_s3 = 1;
If openD(0) < r4 and openD(0) > s4 then r4_s4 = 1;


if time < endTradeTime and time > 930 then
begin
	
	
	if r3_r4 = 1 and entriesToday(date) < 3 and  c < r4 then buy("R4-BrkOut") next bar at r4 stop;
	if s3_s4 = 1 and entriesToday(date) < 3 and  c > s4 then sellShort("S4-BrkOut") next bar at s4 stop;
	
	if c > r2 then r2Pen = 1;
	if c > r3 then r3Pen = 1;
	if c > r4 then r4Pen = 1;
	
	if r3_s3 = 1 and r3Pen = 1 and c > r3 and entriesToday(date) < 3 
		 then sellShort("R3Sell") next bar at r3 stop;
		 
	if r4Pen = 1 and c < r4Pen then r4Pen = 0;	
	if r3Pen = 1 and c < r3Pen then r3Pen = 0;	
	if r2Pen = 1 and c < r2Pen then r2Pen = 0;

	if c < r1 then 
	begin
		r2Pen = 0;
		r3Pen = 0;
		r4Pen = 0;
	end;
	if c > s1 then
	begin
		s2Pen = 0;
		s3Pen = 0;
		s4Pen = 0;
	end;

	if c < s2 then s2Pen = 1;
	if c < s3 then s3Pen = 1;
	if c < s4 then s4Pen = 1;

	if r3_s3 = 1 and s3Pen = 1 and c < s3 and entriesToday(date) < 3 then 
		buy("S3Buy") next bar at s3 stop;
		
	if s4Pen = 1 and c > s4Pen then s4Pen = 0;	
	if s3Pen = 1 and c > s3Pen then s3Pen = 0;
	if s2Pen = 1 and c > s2Pen then s2Pen = 0;
		
	if marketPosition = 1 then
	begin
		sell from entry("S3Buy") next bar at s4 stop;
		sell from entry("R4-BrkOut") next bar at r3 stop;
	end;
	
	if marketPosition = -1 then
	begin
		buyToCover from entry("R3Sell") next bar at r4 stop;
		buyToCover from entry("S4-BrkOut") next bar at s3 stop;
	end;


end;
 
setExitOnClose;
Camarilla Strategy EasyLanguage Source

Don’t Forget To Make Copies of your EasyLanguage in Text/ASCII

Well it happened again.  I had four analysis techniques open in my TradeStation Development Environment and my TS crashed and when I went back to work after restarting they were all garbage.  The easiest way to back up your code is to have NotePad open and while working on your EasyLanguage copy and past your code into a NotePad file.  Do this in stages and if you like you can keep different revisions.   If you don’t want to keep revisions just select all and replace with the code you are copying from the TDE.  Make sure you copy your final changes so that you can archive this in a separate folder and store it on your computer, flash drive or the cloud.  Remember TradeStation stores your analysis techniques in one big library glob.  If that glob gets corrupted and you don’t back up your TradeStation periodically, then you run  a chance of losing all of your hard work.  So definitely back up TradeStation automatically using their software and also back up your coding in a NotePad file.

VBA for Excel Code to Open Multiple Text Files

For all of you who want to open multiple text files from within VBA here is the code that will do just that. Here I open a Open File Dialog and enable multiple file selection. I then parse the return string to gather the path and individual file names. The data is then processed – I am looking for a file with a date and a value separated by a comma.  You can basically do anything you want with VBA and you also have the EXCEL functions right at your fingertips.  Plus output to a worksheet is ultra simple.

 Set fd = Application.FileDialog(msoFileDialogOpen)
    With fd
        Set ffs = .Filters
        With ffs
            .Clear
            .Add "CSV Data", "*.csv; *.txt"
        End With
        .AllowMultiSelect = True
        If .Show = False Then Exit Sub
        fcnt = 0
        maxDate = DateValue("01/01/1900")
        minDate = DateValue("12/31/2200")
        prevSymbol = "**"
        dateNewestToOldest = False
        For Each it In fd.SelectedItems
			fcnt = fcnt + 1
            filePath(fcnt) = it
            fileName(fcnt) = Right(filePath(fcnt), Len(filePath(fcnt)) - InStrRev(filePath(fcnt), "\"))
            symbol(fcnt) = Left(fileName(fcnt), 2)
            Set DataHolder = New DataClass
                        
            If symbol(fcnt) <> prevSymbol Then symbolHolder.Add symbol(fcnt)
            prevSymbol = symbol(fcnt)
            dataCnt = 0
            Open filePath(fcnt) For Input As #1
            rowNumber = 1
			Do Until EOF(1)
				Line Input #1, lineFromFile
                dataCnt = dataCnt + 1
                If dataCnt > skipLines Then
					lineFromFile = Replace(lineFromFile, " ", "")
                    lineItems = Split(lineFromFile, ",")
                    numItems = UBound(lineItems) - LBound(lineItems) + 1
                    isDash1 = InStr(lineItems(0), "-")
                    isSlash1 = InStr(lineItems(0), "/")
                    If isDash1 > 0 Or isSlash1 > 0 Then
                        inputDate = CDate(lineItems(0))
                    Else
                        myYear = Left(lineItems(0), 4)
                        myMonth = Mid(lineItems(0), 5, 2)
                        myDay = Right(lineItems(0), 2)
                        inputDate = DateSerial(Left(lineItems(0), 4), Mid(lineItems(0), 5, 2), Right(lineItems(0), 2))
                    End If
                	If dataCnt > 1 Then
                    	If inputDate < tempDate Then dateNewestToOldest = True
                	End If
                	If inputDate > maxDate Then maxDate = inputDate
                	If inputDate < minDate Then minDate = inputDate
                	Set DataHolder = New DataClass
                	DataHolder.SymbolName = symbol(fcnt)
                	DataHolder.SymbolNum = fcnt
                	DataHolder.EquDate = inputDate
                	DataHolder.EquVal = lineItems(1)
                	myEquityCollection.Add DataHolder
                	tempDate = inputDate              
				End If
            Loop
        Close #1
        Next it
    End With
VBA code to open multiple text files

Day Of Week Analysis using a Method In EasyLanguage

One metric that seems to be missing from TradeStation is a Day Of Week analysis.  It would be nice, but I don’t know how helpful, to know the $P/L breakdown on a weekday basis; does your system make all of its money on Mondays and Fridays?  I created the code that will print out to the print log using an EasyLanguage method.  A method is a subroutine that can be included in the main code (strategy, indicator, etc.,.)  The global parameters to the main program can be seen inside the method.  You can localize variable scope to the method by declaring the variable within the method’s body.  A method is a great way to modularize your programming, but it is not the best way to reuse software; the method is accessible only to the main program.  This EasyLanguage also utilizes an array and shows a neat piece of code to access the array elements and align them with the day of the week.  The dayOfWeek() function returns [1..5] depending on what day of the week the trading day falls on.  Monday = 1 and Friday = 5.  The array has five elements and each element accumulates the $P/L for each of the five days based on when the trade was initiated.

vars: mp(0);
array: weekArray[5](0);

method void dayOfWeekAnalysis()   {method definition}
var: double tradeProfit;
begin
	If mp = 1 and mp[1] = -1 then tradeProfit = (entryPrice(1) - entryPrice(0))*bigPointValue;
	If mp = -1 and mp[1] = 1 then tradeProfit = (entryPrice(0) - entryPrice(1))*bigPointValue;
	weekArray[dayOfWeek(entryDate(1))] = weekArray[dayOfWeek(entryDate(1))] + tradeProfit;
end; 
Buy next bar at highest(high,9)[1] stop;
Sellshort next bar at lowest(low,9)[1] stop;

mp = marketPosition;

if mp <> mp[1] then dayOfWeekAnalysis();

If lastBarOnChart then
Begin
	print("Monday ",weekArray[1]);
	print("Tuesday ",weekArray[2]);
	print("Wednesday ",weekArray[3]);
	print("Thursday ",weekArray[4]);
	print("Friday ",weekArray[5]);
end;
Code using METHOD and ARRAY manipulation

Here is an example of the print out created by running this simple EasyLanguage strategy.  Maybe don’t trade on Monday?  Or is that curve fitting.  This is an interesting tool and might carry more weight if applied to day trade algorithm.  Maybe!

  • Monday -8612.50
  • Tuesday 6350.00
  • Wednesday 2612.50
  • Thursday -937.50
  • Friday -1987.50

Connors and Raschke (Momentum Pinball)™ EasyLanguage

In the Connors Raschke book, “Street Smarts – High Probability Short-Term Trading Strategies” published by M. Gordon Publishing Group 1995, the authors describe a strategy that incorporates several ideas and functions that can be difficult to program into TradeStation.  I highly suggest purchasing the book as it has some very interesting ideas and comes from the minds of two very accomplished traders.

The strategy is conceptually simple and uses the Taylor Buy/Sell day concept.   On buy days, buy on the penetration of the first hour high and use the extreme of the first hour as the stop.  If you get stopped out the system will allow you to re-enter once more on a re-penetration of the first hour high.  If the trade is profitable at the close of the day session then exit on the open of the following day.  Selling short is just the opposite.  The Buy Day is defined when the three day RSI of the one day ROC (close[0] – close[1]) is below 30 and the Sell Day  is defined when this indicator rises above 70.  As you can see, pretty simply.

The first problem when programming this strategy is the mixing of the 5 minute bars (for trade execution) and daily bars (calculating the indicator.)  How do you calculate an RSI(c[0] – c[1],3) indicator value when looking at a 5 minute bar chart.   This turns out to be pretty simple since TradeStation allows the mixing of different time frames on the same chart.  First plot a 5 minute bar of the @ES.D as data 1 and then insert a daily bar of the @ES.D as data 2.   The first step in programming this strategy is setting up the indicator.

//EasyLanguage

Vars: rsiVal(0,data2);

rsiVal = rsi(close of data2 - close[1] of data2,3);
Setting up the nested indicator.

Notice how I declared the variable rsiVal?  I initially set it to zero and tie it to data2.  This tying or aliasing limits the variable from being updated on each five minute bar.  Remember we want the RSI calculation done on the daily bars.  The RSI function allows the embedding of calculations as well as different prices.  Here we are telling the computer to look at the momentum of today’s close versus yesterday’s  and apply the RSI  3 – period calculation.

In their book Connors and Raschke determined that it would be better to buy after an RSI reading of below 30 and sell after an RSI above 70.  Once we have this set, then the fun really begins with this type of programming.  If you can do this type of programming, there’s very little you can’t do.  This is because we are solving a few limitations of EasyLanguage by logically addressing the what we need from language. In this example, we have to capture the range of the first hour.  There really isn’t a built-in way to do this or at least not an easy one.  When testing intra-day, I always like using five minute bars.  This time frame is small enough that not much usually happens during this t time and it isn’t much of a resource hog – like say minute or smaller bars. Since there are 12 five minute bars in an hour, I wait until the 12th bar of the day is completed to determine the first hour’s range and its extremes.  I set the dayBarCount to zero on the first bar of the day and then increment the variable on each bar.

If date <> date[1] then
begin
	dayBarCount = 0;
	buysToday = 0;
	sellsToday = 0;
end;

dayBarCount = dayBarCount + 1;

If dayBarCount = 12 then
Begin
	buyLevel = highD(0) + minMove/priceScale;
	sellLevel = lowD(0) - minMove/priceScale;
end;
Tracking the number of bars and capturing first hour.

TradeStation has made this process simpler by providing the highD and lowD functions.  When you call these functions the current daily bar extremes are returned at the point in the day.  So the functions are called on the 12th bar and they return the high and low of the first hour.   The functions are not called again until the next day.  Oh yeah – passing a zero as the function argument informs EasyLanguage to return today’s values.  A 1 would return yesterday’s values.  These are nifty functions if you aren’t incorporating a secondary daily data stream.  Even if you are, like in our example, they still come in handy.

mp = marketPosition;

If mp = 1 and mp[1] <> 1 then buysToday = buysToday + 1;
If mp = -1 and mp[1] <> -1 then sellsToday = sellsToday + 1;

If dayBarCount > 12 and time < 1430 then
Begin
	if rsiVal < 30 and mp <> 1 and buysToday < 2 then buy next bar at buyLevel Stop;
	If rsiVal > 70 and mp <> -1 and sellsToday < 2 then sellShort next bar at sellLevel stop;
end;
Order placement directives and the use of mp.

I also keep track of the number of buys and shorts for the day.  The books states that a re-entry is possible, so I allow up to two trade entries.  I look and the current MP value and its prior value to see if  the system went from one state to another.  If current MP is long and the prior reading is not long, then a long entry was undoubtedly entered.  You can use this logic (I did for illustrative purposes) or use the built-in functions entriesShortToday/entriesLongToday to determine the number of trades for the day.  If the rsiValue < 30 and less than two long entries have occurred, then you can buy on a breakout of the first hourly bar.  Entering short is the opposite, but with the RSI reading of greater than 70.

If mp = 1 then
Begin
	Sell next bar at sellLevel stop;
end;

If mp= -1 then
Begin
	Buytocover next bar at buyLevel stop;
end;

If time = 1510 and openPositionProfit < 0 then setExitOnClose;
If time = 1515 then
Begin
	If mp = 1 then sell next bar at open;
	If mp =-1 then buyToCover next bar at open;
end;
Initial stop and if winning trade exit next morning else get out on close.

The buy/sell levels are the first hourly bar’s extreme +/- min. tick.  These levels are used as protective stops as well as entry levels.  If long then the first hourly bar’s low is the protective stop.  Notice how I check at 1510 if the trade is in a profit by examining the current openPositionProfit.  I am checking five minutes prior to the close because I need to execute setExitOnClose if in a loss.  I don’t think I can check at 15:15 (closing time) and then exit, but I will check and let you know.

The last little trick is to execute on the open tick the next morning.  I have explained in my books the different trading paradigms where you are either sitting on the prior bar when you place orders or sitting on the open of the current bar.  EasyLanguage assumes the former so you always have to place an order for the next bar.  If you wait until the open, then the earliest you can exit is 8:35.  So you have to issue the order on the last bar of the prior day to get the open tick, hence the test to see if the time stamp is 15:15.

This sample of EasyLanguage is a good example of some easy fixes to things that usually leave beginning EasyLanguage coders scratching their heads and groaning out loud.

Trade Entry/Exit Functions in PSB

Since Python doesn’t  allow for a GOTO program flow structure I changed the inline trade entry/exit logic into function calls.  This allows for the call of these functions to be non-sequential.  The original PSB order placement was sequential and would examine the entry/exit logic in TOP-DOWN fashion.  Meaning that if you put the Long Entry Logic first, the program would evaluate that logic first on every bar of data.  This was fine for the majority of trading systems out there.  However, systems that could enter multiple signals on the same bar require the orders to be placed in order of whichever was closest to the current market price.  Let’s say you have a trading algorithm that issues a stop to exit at a loss and a stop to reverse your current position.  If you examine the reversal logic prior to the stop loss and the stop loss is actually closer, then you will execute the wrong signal first.   By encapsulating the entry/exit logic into functions you can use decision constructs to flow through the correct logic in the correct order.  Here are the trade signals programmed as function calls:

entryexitfuncs

As you can see there are six modules – Long Entry, Long Loss, Long Profit, Short Entry, Short Loss, and Short Profit (note non-necessary code was collapsed).  I used the Sublime text editor to collapse the unnecessary lines of code.  You can download a trail version from their website.  I am no longer using the IDLE as my go to IDE – I have fallen deeply in love with PyScripter.  I will delve into this subject later.

Now that you have all your trade signals programmed as functions you can utilize if-thens to determine what order they are called.  Here is the code that calls these functions

functionflow

If you are flat and you can buy or sell on the same day then you call both functions.  This back tester will allow you to buy and sell on the same day – the only problem is it doesn’t know which occurred first: the buy or the sell.  This can cause a problem because you need to know the correct position by the end of the day.  Since we can peak into the future (be very, very careful) we can look at the close of the day and compare it with the long and short entry prices.  Here is some code that might make your entries more accurate on those occasions where both orders could be filled.

if myHigh[D0] >= buyLevel and myLow[D0] <= sellLevel:
	closeToBuyDiff = myClose[D0] -  buyLevel
	closeToSellDiff = sellLevel - myClose[D0]
    	if closeToBuyDiff < closeToSellDiff:
    		se = sellEntry(sellLevel)
	    	le = longEntry(buyLevel)
      	else:
    		le = longEntry(buyLevel)
    		se = sellEntry(sellLevel)
					
Increasing Trade Entry/Exit Accuracy - Maybe?

This code will execute the long entry logic after the short entry logic whenever the close is closer to the long entry price than the short entry price.  If both orders are filled on the same bar and the close happens to fall closer to the long entry price, then the software assumes the short entry was entered earlier in the day and the long entry later in the day making the position at end of day long.  Is this 100% accurate?  No but it logically stands to reason that a close near the high would indicate they high was made last.  Without intraday data we simply do not know what happened first.

Backtesting with [Trade Station,Python,AmiBroker, Excel]

Follow

Get every new post delivered to your Inbox

Join other followers: