Category Archives: EasyLanguage equivalent to cool code

Super Combo Day Tradng System A 2020 Redo!

If you have some time on your hands and you want to build your own Frankenstein monster from a parts bin, here is your chance.  The Super Combo Day Trading System was originally published in my “Building Winning Trading Systems” book back in 2001.  I designed it to be more of a tutorial than a pure trading system.    You should be able to get the spare parts you need to create your own day trading system.  Back in 2001, I wanted to show how to control and monitor different entry and exit techniques in one complete algorithm.  The system was designed to day-trade the big SP and the results at the time looked promising.  Since the transition to the ES and the higher levels of volatility that we have seen over the years and the adoption of overnight trading,  the system hasn’t fared that well, but the concepts are still viable as an instructional tool today as they were 20 years ago.  EasyLanguage has been improved over this time period so the coding for the Super Combo can definitely take advantage of the new enhancements.

Here are the main premises of the logic:

  • take advantage of a buyEasier and shortEasier pattern setup
  • incorporate daily and 5-minute time frames in one chart
  • include a breakOut, failedBreakOut and reverseOnLiquidation trade entry techniques
  • monitor which signal is currently online and apply the correct exit signal
  • monitor profit and incorporate a break even stop
  • monitor time and incorporate a trailing stop
  • provide an interface into the logic via inputs

Okay here we go – there is quite a bit of code here so let’s divide an conquer by examining just one module at a time.  This first module includes the inputs and variables section plus once per day calculations.

[LegacyColorValue = true]; 

{Super Combo by George Pruitt - redo 2020
 This intra-day trading system will illustrate the multiple data
 handling capabilities of TradeStation.  All pertinent buy and sell
 calculations will be based on daily bars and actual trades will be
 executed on 5-min bars.  I have made most of the parameters input
 variables}

 Inputs:waitPeriodMins(30),initTradesEndTime(1430),liqRevEndTime(1200),
 	thrustPrcnt1(0.30),thrustPrcnt2(0.60),breakOutPrcnt(0.25),
	failedBreakOutPrcnt(0.25),protStopPrcnt1(0.30),protStopPrcnt2(0.20),
	protStopAmt(3.00),breakEvenPrcnt(0.50),avgRngLength(10);
	
Variables:averageRange(0),canTrade(0),buyEasierDay(FALSE),
	sellEasierDay(FALSE),buyBOPoint(0),sellBOPoint(0),longBreakPt(0),
	shortBreakPt(0),longFBOPoint(0),shortFBOPoint(0),barCount(0),buysToday(0),
	sellsToday(0),mp(0),longLiqPoint(0),shortLiqPoint(0),
	longLiqPoint1(0),shortLiqPoint1(0),intraTradeHigh(0),intraTradeLow(999999);


{Just like we did in the psuedo code -- let's start out with the daily 
 bar calculations.  If Date <> Date[1] -- first bar of day}
if(Date <> Date[1]) then {save time by doing these calculations once per day}
begin
 	averageRange = Average(Range,10) of Data2; {Data 2 points to daily bars}
 	
	canTrade = 0;
    if range of data2 < averageRange then canTrade = 1;

	{use close of data2 - seems to be more accurate than CloseD(1)
	buyEasierDay =Close of Data2 >= Close[1] of Data2;
	sellEasierDay = Close of Data2 <  Close[1] of Data2;

	buyBOPoint = Open + thrustPrcnt1*averageRange;
	sellBOPoint= Open - thrustPrcnt2*averageRange;
	
	if(sellEasierDay) then
	begin
		sellBOPoint= Open - thrustPrcnt1*averageRange;
		buyBOPoint = Open + thrustPrcnt2*averageRange; 
	end;

	longBreakPt = HighD(1) + breakOutPrcnt*averageRange;
	shortBreakPt=  LowD(1) - breakOutPrcnt*averageRange;

	shortFBOPoint = HighD(1) - failedBreakOutPrcnt*averageRange;
	longFBOPoint=  LowD(1) + failedBreakOutPrcnt*averageRange;

{Go ahead and initialize any variables that we may need later on in the day}
	
	barCount = 0;
	buysToday = 0;sellsToday = 0;{You can put multiple statements on one line}	
end;
First Modules of SuperCombo 2020

Here I am just setting up the inputs and variables that I will need to execute the algorithm.  If you are using .D data then the code

if date <> date[1] then

is a valid test for the first bar of the day.  A new date will represent the beginning of the next day.  The code controlled by this if-then construct is only executed one time per day.  So if you can put the lion’s share of daily calculations here, then it should speed stuff up.  The first thing I do is calculate the average range of the last 10 daily bars.  I access this date from data2.  Can you build a loop and accumulate the difference between the HighD and LowD function calls?

  1. for i = 1 to 10 begin
  2.      sum = sum + (HighD(i) – LowD(i));
  3. end;

The HighD() and LowD() functions are EasyLanguage enhancements that can help eliminate the need for a multi-data chart.  However, if you do this, you will get an warning message that its not a good idea.  I have done this and it seems to work, but to be safe just use Data2.    Next I determine if there has been a narrow range or range compression by comparing yesterday’s range to the averageRange.  If so, then I allow trading.  This is an old filter that looks for range expansion after compression.  The concept of a buyDay and sellDay was originated in the 1930s by George W. Cole (correct me if I am wrong here).  I use this idea by comparing the prior two bars closing relationships.  If there has been an up close, then I consider the next day to be a buyEasier day.  If the opposite is true, then its a sellEasier day.   This system isn’t unidirectional and does allow buying  and shorting in the same session – hence the word easier.   Continuing I calculate the levels that if the market reaches will hopefully trigger a short term trend in that direction.  This is the once highly respected open range break out or ORBO.  This methodology has lost its luster over the last 10 years or so due to overnight trading and allowing pent up buying and selling to be expressed in the overnight sessions.  Twenty years ago it was still viable.  The next bit of code creates the break out levels based on the buyEasier or sellEasier days.   The thrust is calculated by multiplying the range by thrustPrcnt1 and thrustPrcnt2.

So that is method 1 – break out.  Hope the market breaks out and continues to the close.  I wish it were this easy.  Since its not, the second methodolgy, FailedBreakOut, is calculated.  This is also known as the “ClearOut” trade.   The market is pushed to take out all the buy stops and then pulled back for the professionals to feast on the amateurs.  SuperCombo tries to take advantage of this by calculating the two points to determine a failed break out.  On the long side, it is the two points the market rises up to and then falls back to.  If the market breaches the longBreakPt, then look to sellShort at the shortFBOPoint.    Here is the next module

{Now lets trade and manage on 5-min bars}

barCount = barCount + 1; {count the number of bars of intraday data}
if(barCount >= waitPeriodMins/BarInterval and canTrade = 1) then {have we waited long enough}
begin
	if(MarketPosition = 1) then buysToday = 1;
	if(MarketPosition =-1) then sellsToday= 1;
	
	if(buysToday = 0 and Time < initTradesEndTime) then
		Buy("LBreakOut") next bar at buyBOPoint stop;
		
	if(sellsToday= 0 and Time < initTradesEndTime) then 
		SellShort("SBreakout") next bar at sellBOPoint stop;
				
	if(highD(0) > longBreakPt and sellsToday = 0 and Time < initTradesEndTime) then
		SellShort("SfailedBO") next bar at shortFBOPoint stop;
	if(lowD(0) < shortBreakPt and buysToday = 0 and Time < initTradesEndTime) then
		Buy("BfailedBO") next bar at longFBOPoint stop;
		
Monitor Market Action and Place Trades Accordingly

 

if(barCount>= waitPeriodMins/BarInterval and canTrade = 1) then

Forces the logic to flow only if canTrade is 1 and we have waited for amateur hour to be completed – well 30 minutes to be accurate.  Is the first hour really amateur hour?  I don’t think this applies, but if you think it does this is how you control trading prior to the completion of this period.  By dividing by BarInterval and counting each bar you can generalize this code for any time resolution.   If MarketPosition is 1 then you know you entered a long position and the opposite is true for short positions.  Only place the break out orders if time is less than initTradesEndTime.  If the market penetrates the long and shortBreakPts, then prepare to take advantage of a failed breakout.  Only go short if a short position has not already been entered – same for longs.  So, this logic places the breakOut and failedBreakOut orders.  Now for the last module.

{The next module keeps track of positions and places protective stops}

	mp = marketPosition;
	if(MarketPosition = 1) then
	begin
		longLiqPoint = EntryPrice-protStopPrcnt1*averageRange;
		longLiqPoint = MinList(longLiqPoint,EntryPrice - protStopAmt);
		longLiqPoint1 = EntryPrice - protStopPrcnt2*averageRange;
		longLiqPoint1 = MinList(longLiqPoint1,EntryPrice - protStopAmt);
		if Maxpositionprofit >= breakEvenPrcnt*averageRange*bigPointValue then
		begin
			longLiqPoint = EntryPrice;  {Breakeven trade}
			longLiqPoint1 = EntryPrice;  {Breakeven trade}
		end;
		if(Time >= initTradesEndTime) then
		begin
			longLiqPoint = MaxList(longLiqPoint,Lowest(Low,3)); {Trailing stop}
			longLiqPoint1 = MaxList(longLiqPoint1,Lowest(Low,3)); {Trailing stop}
		end;
		if(Time < liqRevEndTime and sellsToday = 0 and 
		longLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then
			SellShort("LongLiqRev") next bar at longLiqPoint stop;
			 
		Sell("LongLiq-BO") from entry("LBreakOut") next bar at longLiqPoint stop;
		Sell("LongLiq-FBO") from entry("BFailedBO") next bar at longLiqPoint stop;
		Sell("LongLiq-RLoss") from entry("ShortLiqRev") next bar at longLiqPoint1 stop;
	end;
	if(MarketPosition =-1) then
	begin	
		shortLiqPoint = EntryPrice+protStopPrcnt1*averageRange;
		shortLiqPoint = MaxList(shortLiqPoint,EntryPrice + protStopAmt);
		shortLiqPoint1 = EntryPrice + protStopPrcnt2*averageRange;
		shortLiqPoint1 = MaxList(shortLiqPoint1,EntryPrice + protStopAmt);
		if maxPositionProfit >= breakEvenPrcnt*averageRange*bigPointValue then
		begin
			shortLiqPoint = EntryPrice;  {Breakeven trade}
			shortLiqPoint1 = EntryPrice;
		end;
		if(Time >= initTradesEndTime) then
		begin
			shortLiqPoint = MinList(shortLiqPoint,Highest(High,3)); {Trailing stop}
			shortLiqPoint1 = MinList(shortLiqPoint1,Highest(High,3)); {Trailing stop}
	    end;
		if(Time < liqRevEndTime and buysToday = 0 and 
		shortLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then
			Buy("ShortLiqRev") next bar at shortLiqPoint stop;
			
		BuyToCover("ShortLiq-BO") from entry("SBreakOut") next bar at shortLiqPoint stop;
		BuyToCover("ShortLiq-FBO") from entry("SFailedBO") next bar at shortLiqPoint stop;
		BuyToCover("ShortLiq-RLoss") from entry("LongLiqRev") next bar at shortLiqPoint1 stop;			
	end;
end;
SetExitOnClose;
TradeManagement (Enter on Stop Loss or Not?)

This code looks a little hairy, but its not.  Let’s just look at the long side logic to save time here.  First let’s calculate the LongLiqPoints (1 and 2.)  Twenty years ago I thought it would be better to have a smaller stop for entries that occurred on a LiquidationReversal.  Oh yeah that code is in here to.  Back in the day I wanted to make sure the stop was at least 3 handles – ha, ha, ha – no really I am serious.  Really.  Stop laughing!! That code could be eliminated.  After calculating these two points I start to monitor profit and if it reaches a predetermined level I pull the the longLiqPoints toa  BreakEven stop.  If you are fortunate to still be in a trade after initTradesEndTime, then I start trailing the stop by the lowest low of the last 3 five minute bars – I don’t want to turn a small winner into a loser.  Now this is the fun stuff.

  1. if(Time < liqRevEndTime and sellsToday = 0 and
    longLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then
  2.      SellShort(“LongLiqRev”) next bar at longLiqPoint stop;

If time is less than liqRevEndTime and BarsSinceEntry, then reverse and go short at the longLiqPoint stop.  Do this instead of liquidating.  I thought if the market reversed course quickly, then I wanted to take advantage of this counter trend move.  Eliminating this to see if it has any impact would be where I would start to play around with the template.  Okay now the liquidations based on from Entry take place next.  If I am long from a “ShortLiqRev“, then I use longLiqPoint1 instead of longLiqPoint.  Okay that last part was the kitchen sink.  Now you have enough code to make your own day trading system – really too much code, but you should be able to hobble something together from these parts.  Let me know if you can create your own Frankenstein monster.  I will update the parameters to see if there is any hope to the system as a whole.  Keep checking back for updated performance metrics.  Best to all and be safe!

 

 

Please follow and like us:

A Quant’s ToolBox: Beautiful Soup, Python, Excel and EasyLanguage

Many Times It Takes Multiple Tools to Get the Job Done

Just like a mechanic, a Quant needs tools to accomplish many programming tasks.  In this post, I use a toolbox to construct an EasyLanguage function that will test a date and determine if it is considered a Holiday in the eyes of the NYSE.

Why a Holiday Function?

TradeStation will pump holiday data into a chart and then later go back and take it out of the database.  Many times the data will only be removed from the daily database, but still persist in the intraday database.  Many mechanical day traders don’t want to trade on a shortened holiday session or use the data for indicator/signal calculations.  Here is an example of a gold chart reflecting President’s Day data in the intra-day data and not in the daily.

Holiday Data Throws A Monkey Wrench Into the Works

This affects many stock index day traders.  Especially if automation is turned on.  At the end of this post I provide a link to my youTube channel for a complete tutorial on the use of these tools to accomplish this task.  It goes along with this post.

First Get The Data

I searched the web for a list of historical holiday dates and came across this:

Historic List of Holidays and Their Dates

You might be able to find this in a easier to use format, but this was perfect for this post.

Extract Data with Beautiful Soup

Here is where Python and the plethora of its libraries come in handy.  I used pip to install the requests and the bs4 libraries.  If this sounds like Latin to you drop me an email and I will shoot you some instructions on how to install these libraries.  If you have Python, then you have the download/install tool known as pip.

Here is the Python code.  Don’t worry it is quite short.

# Created:     24/02/2020
# Copyright:   (c) George 2020
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import requests
from bs4 import BeautifulSoup

url = 'http://www.market-holidays.com/'
page = requests.get(url)
soup = BeautifulSoup(page.text,'html.parser')
print(soup.title.text)
all_tables = soup.findAll('table')
#print (all_tables)
print (len(all_tables))
#print (all_tables[0])
print("***")
a = list()
b = list()
c = list()
#print(all_tables[0].find_all('tr')[0].text)
for numTables in range(len(all_tables)-1):
    for rows in all_tables[numTables].find_all('tr'):
        a.append(rows.find_all('td')[0].text)
        b.append(rows.find_all('td')[1].text)

for j in range(len(a)-1):
    print(a[j],"-",b[j])
Using Beautiful Soup to Extract Table Data

As you can see this is very simple code.  First I set the variable url to the website where the holidays are located.  I Googled on how to do this – another cool thing about Python – tons of users.  I pulled the data from the website and stuffed it into the page object.  The page object has several attributes (properties) and one of them  is a text representation of the entire page.  I pass this text to the BeautifulSoup library and inform it to parse it with the html.parser.  In other words, prepare to extract certain values based on html tags.  All_tables contains all of the tables that were parsed from the text file using Soup.  Don’t worry how this works, as its not important, just use it as a tool.  In my younger days as a programmer I would have delved into how this works, but it wouldn’t be worth the time because I just need the data to carry out my objective; this is one of the reasons classically trained programmers never pick up the object concept.  Now that I have all the tables in a list I can loop through each row in each table.  It looked liker there were 9 rows and 2 columns in the different sections of the website, but I didn’t know for sure so I just let the library figure this out for me.  So I played around with the code and found out that the first two columns of the table contained the name of the holiday and the date of the holiday.  So, I simply stuffed the text values of these columns in two lists:  a and b.  Finally I print out the contents of the two lists, separated by a hyphen, into the Interpreter window.  At this point I could simply carry on with Python and create the EasyLanguage statements and fill in the data I need.  But I wanted to play around with Excel in case readers didn’t want to go the Python route.  I could have used a powerful editor such as NotePad++ to extract the data from the website in place of Python.  GREP could have done this.  GREP is an editor tool to find and replace expressions in a text file.

Use Excel to Create Actual EasyLanguage – Really!

I created a new spreadsheet.  I used Excel, but you could use any spreadsheet software.   I first created a prototype of the code I would need to encapsulate the data into array structures.  Here is what I want the code to look like:

Arrays: holidayName[300](""),holidayDate[300](0);

holidayName[1]="New Year's Day ";	holidayDate[1]=19900101;
Code Prototype

This is just the first few lines of the function prototype.  But you can notice a repetitive pattern.  The array names stay the same – the only values that change are the array elements and the array indices.  Computers love repetitiveness.  I can use this information a build a spreadsheet – take a look.

Type EasyLanguage Into the Columns and Fill Down!

I haven’t copied the data that I got out of Python just yet.  That will be step 2.  Column A has the first array name holidayName (notice I put the left square [ bracket in the column as well).  Column B will contain the array index and this is a formula.  Column C contains ]=”.  Column D will contain the actual holiday name and Column E contains theThese columns will build the holidayName array.  Columns G throuh K will build the holidayDates array.    Notice column  H  equals column B.  So whatever we do to column B (Index) will be reflected in Column H (Index).  So we have basically put all the parts of the EasyLanguage into  Columns A thru K. 

Excel provides tools for manipulating strings and text.  I will use the Concat function to build my EasyLanguageBut before I can use Concat all the stuff I want to string together must be in a string or text format.  The only column in the first five that is not a string is Column B.  So the first thing I have to do is convert it to text.  First copy the column and paste special as values.  Then go to your Data Tab and select Text To Columns. 

Text To Columns

It will ask if fixed width or delimited – I don’t think it matters which you pick.  On step 3 select text.

Text To Columns – A Powerful Tool

The Text To Columns button will solve 90% of your formatting issues in Excel.    Once you do this you will notice the numbers will be left justified – this signifies a text format.  Now lets select another sheet in the workbook and past the holiday data.

Copy Holiday Data Into Another Spreadsheet

New Year's Day - January 1, 2021
Martin Luther King, Jr. Day - January 18, 2021
Washington's Birthday (Presidents' Day) - February 15, 2021
Good Friday - April 2, 2021
Memorial Day - May 31, 2021
Independence Day - July 5, 2021
Labor Day - September 6, 2021
Thanksgiving - November 25, 2021
Christmas - December 24, 2021
New Year's Day - January 1, 2020
Martin Luther King, Jr. Day - January 20, 2020
Washington's Birthday (Presidents' Day) - February 17, 2020
Good Friday - April 10, 2020
Memorial Day - May 25, 2020
Holiday Output

 

Data Is In Column A

Text To Columns to the rescue.  Here I will separate the data with the “-” as delimiter and tell Excel to import the second column in Date format as MDY.  

Text To Columns with “-” as the delimiter and MDY as Column B Format

Now once the data is split accordingly into two columns with the correct format – we need to convert the date column into a string.

Convert Date to a String

Now the last couple of steps are really easy.  Once you have converted the date to a string, copy Column A and past into Column D from the first spreadsheet.  Since this is text, you can simply copy and then paste.  Now go back to Sheet 2 and copy Column C and paste special [values] in Column J on Sheet 1.  All we need to do now is concatenate the strings in Columns A thru E for the EasyLanguage for the holidayName array.  Columns G thru K will be concatenated for the holidayDate array.  Take a look.

Concatenate all the strings to create the EasyLanguage

Now create a function in the EasyLanguage editor and name it IsHoliday and have it return a boolean value.  Then all you need to do is copy/paste Columns F and L and the data from the website will now be available for you use.   Here is a portion of the function code.  Notice I declare the holidayNameStr as a stringRef?  I did this so I could change the variable in the function and pass it back to the calling routine.

Inputs : testDate(numericSeries),holidayNameStr(stringRef);

Arrays: holidayName[300](""),holidayDate[300](0);

holidayNameStr = "";

holidayName[1]="New Year's Day ";	holidayDate[1]=19900101;
holidayName[2]="Martin Luther King, Jr. Day ";	holidayDate[2]=19900115;
holidayName[3]="Washington's Birthday (Presidents' Day) ";	holidayDate[3]=19900219;
holidayName[4]="Good Friday ";	holidayDate[4]=19900413;
holidayName[5]="Memorial Day ";	holidayDate[5]=19900528;
holidayName[6]="Independence Day ";	holidayDate[6]=19900704;
holidayName[7]="Labor Day ";	holidayDate[7]=19900903;
holidayName[8]="Thanksgiving ";	holidayDate[8]=19901122;
holidayName[9]="New Year's Day ";	holidayDate[9]=19910101;
holidayName[10]="Martin Luther King, Jr. Day ";	holidayDate[10]=19910121;
holidayName[11]="Washington's Birthday (Presidents' Day) ";	holidayDate[11]=19910218;

// There are 287 holiays in the database.
// Here is the looping mechanism to compare the data that is passed
// to the database

vars: j(0);
IsHoliday = False;
For j=1 to 287
Begin
	If testDate = holidayDate[j] - 19000000 then
	Begin
		holidayNameStr = holidayName[j] + " " + numToStr(holidayDate[j],0);
		IsHoliday = True;
	end;
end;
A Snippet Of The Function - Including Header and Looping Mechanism

This was a pretty long tutorial and might be difficult to follow along.  If you want to watch my video, then go to this link.

I created this post to demonstrate the need to have several tools at your disposal if you really want to become a Quant programmer.  How you use those tools is up to you.  Also you will be able to take bits and pieces out of this post and use in other ways to get the data you really need.  I could have skipped the entire Excel portion of the post and just did everything in Python.  But I know a lot of Quants that just love spreadsheets.  You have to continually hone your craft in this business.   And you can’t let one software application limit your creativity.  If you have a problem always be on the lookout for alternative platforms and/or languages to help you solve it.

 

 

Please follow and like us:

Testing Keith Fitschen’s Bar Scoring with Pattern Smasher

Keith’s Book

Thanks to MJ for planting the seed for this post.  If you were one of the lucky ones to get Keith’s “Building Reliable Trading SystemsTradable Strategies that Perform as They Backtest and Meet Your Risk-Reward Goals”  book by John Wiley 2013 at the list price of $75 count yourself lucky.  The book sells for a multiple of that on Amazon.com.  Is there anything earth shattering in the book you might ask?  I wouldn’t necessarily say that, but there are some very well thought out and researched topics that most traders would find of interest.

Bar Scoring

In his book Keith discusses the concept of bar-scoring.  In Keith’s words, “Bar-scoring is an objective way to classify an instrument’s movement potential every bar.  The two parts of the bar-scoring are the criterion and the resultant profit X days hence.”  Keith provides several bar scoring techniques, but I highlight just one.

Keith broke these patterns down into the relationship of the close to the open, and close in the upper half of the range; close greater than the open and close in the lower half of the range.  He extended the total number of types to 8 by adding the relationship of the close of the bar to yesterdays bar.

The PatternSmasher code can run through a binary representation

for each pattern and test holding the position for an optimizable number of days.  It can also check for long and short positions.  The original Pattern Smasher code used a for-loop to create patterns that were then compared to the real life facsimile.  In this code it was easier to just manually define the patterns and assign them the binary string.

if c[0]> c[1] and c[0] > o[0] and c[0] > (h[0] + l[0])/2  then patternString = "----";
if c[0]> c[1] and c[0] > o[0] and c[0] < (h[0] + l[0])/2  then patternString = "---+";
if c[0]> c[1] and c[0] < o[0] and c[0] > (h[0] + l[0])/2  then patternString = "--+-";
if c[0]> c[1] and c[0] < o[0] and c[0] < (h[0] + l[0])/2  then patternString = "--++";
if c[0]< c[1] and c[0] > o[0] and c[0] > (h[0] + l[0])/2  then patternString = "-+--";
if c[0]< c[1] and c[0] > o[0] and c[0] < (h[0] + l[0])/2  then patternString = "-+-+";
if c[0]< c[1] and c[0] < o[0] and c[0] > (h[0] + l[0])/2  then patternString = "-++-";
if c[0]< c[1] and c[0] < o[0] and c[0] < (h[0] + l[0])/2  then patternString = "-+++";
Manual Pattern Designations

Please check my code for any errors.  Here I go through the 8 different relationships and assign them to a Patter String.  “-+++”  represents pattern number (7 ) or type (7 + 1 = 8 – my strings start out at 0).  You can then optimize the test pattern and if the test pattern matches the actual pattern, then the Pattern Smasher takes the trade  on the opening of the next bar and holds it for the number of days you specify.  You an also designate long and short positions in the code.  Here I optimized the 8 patterns going long and short and holding from 1-4 days.

Here is the equity curve!  Remember these are Hypothetical Results with $0 commission/slippage and historic performance is not necessarily indicative of future results.  Educational purposes only!  This is tested on ES.D

Play around with the code and let me know if you find any errors or any improvements.

input: patternTests(8),orbAmount(0.20),LorS(1),holdDays(0),atrAvgLen(10),enterNextBarAtOpen(true);
  
var: patternTest(""),patternString(""),tempString("");
var: iCnt(0),jCnt(0);
array: patternBitChanger[4](0);
   
{written by George Pruitt -- copyright 2019 by George Pruitt
 This will test a 4 day pattern based on the open to close
 relationship.  A plus represents a close greater than its
 open, whereas a minus represents a close less than its open.
 The default pattern is set to pattern 14 +++- (1110 binary).
 You can optimize the different patterns by optimizing the
 patternTests input from 1 to 16 and the orbAmount from .01 to
 whatever you like.  Same goes for the hold days, but in this
 case you optimize start at zero.  The LorS input can be
 optimized from 1 to 2 with 1 being buy and 2 being sellshort.}
  
patternString = "";
patternTest = "";
 
patternBitChanger[0] = 0;
patternBitChanger[1] = 0;
patternBitChanger[2] = 0;
patternBitChanger[3] = 0;
 
value1 = patternTests - 1;
 
 
//example patternTests = 0 -- > 0000
//example patternTests = 1 -- > 0001
//example patternTests = 2 -- > 0010
//example patternTests = 3 -- > 0011
//example patternTests = 4 -- > 0100
//example patternTests = 5 -- > 0101
//example patternTests = 6 -- > 0110
//example patternTests = 7 -- > 0111

if(value1 >= 0) then
begin
 
    if(mod(value1,2) = 1) or value1 = 1 then patternBitChanger[0] = 1;
    value2 = value1 - patternBitChanger[0] * 1;
  
    if(value2 >= 7) then begin
        patternBitChanger[3] = 1;
        value2 = value2 - 8;
    end;
 
    if(value2 >= 4) then begin
        patternBitChanger[2] = 1;
        value2 = value2 - 4;
    end;
    if(value2 = 2) then patternBitChanger[1] = 1;
end;

for iCnt = 3 downto 0  begin
    if(patternBitChanger[iCnt] = 1) then
    begin
        patternTest = patternTest + "+";
    end
    else
    begin
        patternTest = patternTest + "-";    
    end;
end;
 
 patternString = "";
  
if c[0]> c[1] and c[0] > o[0] and c[0] > (h[0] + l[0])/2  then patternString = "----";
if c[0]> c[1] and c[0] > o[0] and c[0] < (h[0] + l[0])/2  then patternString = "---+";
if c[0]> c[1] and c[0] < o[0] and c[0] > (h[0] + l[0])/2  then patternString = "--+-";
if c[0]> c[1] and c[0] < o[0] and c[0] < (h[0] + l[0])/2  then patternString = "--++";
if c[0]< c[1] and c[0] > o[0] and c[0] > (h[0] + l[0])/2  then patternString = "-+--";
if c[0]< c[1] and c[0] > o[0] and c[0] < (h[0] + l[0])/2  then patternString = "-+-+";
if c[0]< c[1] and c[0] < o[0] and c[0] > (h[0] + l[0])/2  then patternString = "-++-";
if c[0]< c[1] and c[0] < o[0] and c[0] < (h[0] + l[0])/2  then patternString = "-+++";

 
if(barNumber = 1) then print(elDateToString(date)," pattern ",patternTest," ",patternTests-1);
if(patternString = patternTest) then
 begin
 
//   print(date," ",patternString," ",patternTest); //uncomment this and you can print out the pattern
	if (enterNextBarAtOpen) then
	begin
		if(LorS = 2) then SellShort("PatternSell") next bar on open;
		if(LorS = 1) then buy("PatternBuy") next bar at open;
	end
	else
	begin
		if(LorS = 2) then SellShort("PatternSellBO") next bar at open of tomorrow - avgTrueRange(atrAvgLen) * orbAmount stop;
    	if(LorS = 1) then buy("PatternBuyBO") next bar at open of tomorrow + avgTrueRange(atrAvgLen) * orbAmount stop;
    end;
	

end;
 
if(holdDays = 0 ) then setExitonClose;
if(holdDays > 0) then
begin
    if(barsSinceEntry = holdDays and LorS = 2) then BuyToCover("xbarLExit") next bar at open;
    if(barsSinceEntry = holdDays and LorS = 1) then Sell("xbarSExit") next bar at open;
end;
Bar Scoring Testing Template
Please follow and like us:

George’s EasyLanguage BarsSince Function – How Many Bars Since?

BarsSince Function in EasyLanguage

Have you ever wondered how many bars have transpired since a certain condition was met?  Some platforms provide this capability:

If ExitFlag and (c crosses above average within 3 bars) then

TradeStation provides the MRO (Most Recent Occurrence) function that provides a very similar capability.  The only problem with this function is that it returns a -1 if the criteria are not met within the user provided lookback window.  If you say:

myBarsSinceCond = MRO(c crosses average(c,200),20,1) < 3

And c hasn’t crossed the 200-day moving average within the past twenty days the condition is still set to true because the function returns a -1.

I have created a function named BarsSince and you can set the false value to any value you wish.  In the aforementioned example, you would want the function to return a large number so the function would provide the correct solution.  Here’s how I did it:

inputs: 
	Test( truefalseseries ), 
	Length( numericsimple ), 
	Instance( numericsimple ) , { 0 < Instance <= Length}
	FalseReturnValue(numericsimple); {Return value if not found in length window}
	 
value1 = RecentOcc( Test, Length, Instance, 1 ) ;
If value1 = -1 then 
	BarsSince = FalseReturnValue
Else
	BarsSince = value1;
BarsSince Function Source Code

And here’s a strategy that uses the function:

inputs: profTarg$(2000),protStop$(1000),
rsiOBVal(60),rsiOSVal(40),slowAvgLen(100),
fastAvgLen(9),rsiLen(14),barsSinceMax(3);

Value1 = BarsSince(rsi(c,rsiLen) crosses above rsiOSVal,rsiLen,1,999);
Value2 = BarsSince(rsi(c,rsiLen) crosses below rsiOBVal,rsiLen,1,999);

If c > average(c, slowAvgLen) and c < average(c,fastAvgLen) and Value1 <barsSinceMax then buy next bar at open;

If c < average(c, slowAvgLen) and c > average(c,fastAvgLen) and Value2 <barsSinceMax then sellshort next bar at open;

setStopLoss(protStop$);
setProfitTarget(profTarg$)
Strategy Utilizing BarsSince Function

The function requires four arguments:

  1. The condition that is being tested [e.g.  rsi > crosses above 30]
  2. The lookback window [rsiLen – 14 bars in this case]
  3. Which occurrence [1 – most recent; 2- next most recent; etc…]
  4. False return value [999 in this case; if condition is not met in time]

A Simple Mean Reversion Using the Function:

Here are the results of this simple system utilizing the function.

Optimization Results:

I came up with this curve through a Genetic Optimization:

The BarsSince function adds flexibility or fuzziness when you want to test a condition but want to allow it to have a day (bar) or two tolerance.  In a more in-depth analysis, the best results very rarely occurred on the day the RSI crossed a boundary.   Email me with questions of course.

 

 

Please follow and like us:

Multiple Ouput function in EasyLanguage

In the Pascal programming language you have Procedures and Functions.  Procedures are used when you want to modify multiple variables within a sub-program.  A function is a sub-program that returns a single value after it has been modified by say a formula.  EasyLanguage combines procedures and functions into one sub-program called a function.  Functions and procedures both have a formal parameter definition –  a list that describes the type of parameters that are being received by the calling program.  In Pascal procedures, you pass the address of the value that you want changed.  By modifying the contents of the address you can pass the value back and forth or in and out of the procedure.  In functions you pass by value.   Remember the parameter in a normal function call is used to instruct something within the body of the function and is not altered (e.g. the number 19 in value1 = average(c,19)).  This value doesn’t need to be modified it’s just used.  Look at the following code:

Here I am modifying mav1, mav2 and mav3 within the function and then passing the values back to the calling strategy/indicator/paintbar.  All functions must return a value so I simply assign the value 1 to the function name.  The key here is the keyword numericRef, once I change the values located in the addresses of mav1, mav2 and mav3 (address are provided by the keyword numericRef), they will be made available to the calling program.  This code allows the function to return more than just one value.

Please follow and like us:

EasyLanguage Code for Pyramiding a Day-Trading System w/video [PART-2]

 

Check out the latest video on Pyramiding.

Here is the finalized tutorial on building the pyramiding ES-day-trade system that was presented in the last post.

I will admit this video should be half as long as the end result.  I get a bit long-winded.  However, I think there are some good pointers that should save you some time when programming a similar system.

EasyLanguage Source:

Here is the final code from the video:

vars: mp(0),lastTradePrice(0),canSell(true);

mp = marketPosition * currentContracts;

if date[0] <> date[1] then
begin
	canSell = true;  // canSell on every day
end;

if mp = -1 then canSell = false; // one trade on - no more
if time > 1430 then canSell = false; //no entries afte 230 central

if mp = 0 and canSell = true then sellShort next bar at OpenD(0) - 3 stop;

if mp = -1 then sellShort next bar at OpenD(0) - 6 stop; //add 1
if mp = -2 then sellShort next bar at OpenD(0) - 9 stop; //add 2

if mp = -1 then lastTradePrice = OpenD(0) - 3; //keep track of entryPrice
if mp = -2 then lastTradePrice = OpenD(0) - 6;
if mp = -3 then lastTradePrice = OpenD(0) - 9;


if mp <> 0 then buyToCover next bar at lastTradePrice + 3 stop; // 3 handle risk on last trade

// next line provides a threshold prior to engaging trailing stop
if mp = -3 and barsSinceEntry > 0 and lowD(0) < lastTradePrice - 3 then buyToCover next bar at lowD(0) + 3 stop;

setExitOnClose;
EasyLanguage for Pyramiding and Day-Trading ES

What we learned here:

  • can’t use entriesToday(date) to determine last entry price
  • must use logic to not issue an order to execute on the first bar of the next day
  • mp = marketPosition * currentContracts is powerful stuff!

In the next few days, I will publish the long side version of this code and also a more eloquent approach to the programming that will allow for future modifications and flexibility.

Let me know how it works out for you.

Take this code and add some filters to prevent trading every day or a filter to only allow long entries!

Please follow and like us:

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.

Please follow and like us: