Category Archives: Must Know EasyLanguage

Highly Illogical – Best Guess Doesn’t Match Reality

An ES Break-Out System with Unexpected Parameters

I was recently testing the idea of a short term VBO strategy on the ES utilizing very tight stops.  I wanted to see if using a tight ATR stop in concert with the entry day’s low (for buys) would cut down on losses after a break out.  In other words, if the break out doesn’t go as anticipated get out and wait for the next signal.  With the benefit of hindsight in writing this post, I certainly felt like my exit mechanism was what was going to make or break this system.  In turns out that all pre conceived notions should be thrown out when volatility enters the picture.

System Description

  • If 14 ADX < 20 get ready to trade
  • Buy 1 ATR above the midPoint of the past 4 closing prices
  • Place an initial stop at 1 ATR and a Profit Objective of 1 ATR
  • Trail the stop up to the prior day’s low if it is greater than entryPrice – 1 ATR initially, and then trail if a higher low is established
  • Wait 3 bars to Re-Enter after going flat – Reversals allowed

That’s it.  Basically wait for a trendless period and buy on the bulge and then get it out if it doesn’t materialize.  I knew I could improve the system by optimizing the parameters but I felt I was in the ball park.  My hypothesis was that the system would fail because of the tight stops.  I felt the ADX trigger was OK and the BO level would get in on a short burst.  Just from past experience I knew that using the prior day’s price extremes as a stop usually doesn’t fair that well.

Without commission the initial test was a loser: -$1K and -$20K draw down over the past ten years.  I thought I would test my hypothesis by optimizing a majority of the parameters:

  • ADX Len
  • ADX Trigger Value
  • ATR Len
  • ATR BO multiplier
  • ATR Multiplier for Trade Risk
  • ATR Multiplier for Profit Objective
  • Number of bars to trail the stop – used lowest lows for longs

Results

As you can probably figure, I  had to use the Genetic Optimizer to get the job done.  Over a billion different permutations.  In the end here is what the computer pushed out using the best set of parameters.

No Commission or Slippage – Genetic Optimized Parameter Selection

Optimization Report – The Best of the Best

Top Parameters – notice the Wide Stop Initially and the Trailing Stop Look-Back and also the Profit Multiplier – but what really sticks out is the ADX inputs

ADX – Does it Really Matter?

Take a look at the chart – the ADX is mostly in Trigger territory – does it really matter?

A Chart is Worth a 1000 Words

What does this chart tell us?

70% of Profit was made in last 40 trades

Was the parameter selection biased by the heightened level of volatility?  The system has performed on the parameter set very well over the past two or three years.  But should you use this parameter set going into the future – volatility will eventually settle down.

Now using my experience in trading I would have selected a different parameter set.   Here are my biased results going into the initial programming.  I would use a wider stop for sure, but I would have used the generic ADX values.

George’s More Common Sense Parameter Selection – wow big difference

I would have used 14 ADX Len with a 20 trigger and risk 1 to make 3 and use a wider trailing stop.  With trend neutral break out algorithms, it seems you have to be in the game all of the time.  The ADX was supposed to capture zones that predicated break out moves, but the ADX didn’t help out at all.  Wider stops helped but it was the ADX values that really changed the complexion of the system.  Also the number of bars to wait after going flat had a large impact as well.  During low volatility you can be somewhat picky with trades but when volatility increases you gots to be in the game. – no ADX filtering and no delay in re-Entry.  Surprise, surprise!

Alogorithm Code

Here is the code – some neat stuff here if you are just learning EL.  Notice how I anchor some of the indicator based variables by indexing them by barsSinceEntry.  Drop me a note if you see something wrong or want a little further explanation.

Inputs: adxLen(14),adxTrig(25),atrLen(10),atrBOMult(1),atrRiskMult(1),atrProfMult(2),midPtNumBar(3),posMovTrailNumBars(2),reEntryDelay(3);
vars: mp(0),trailLongStop(0),trailShortStop(0),BSE(999),entryBar(0),tradeRisk(0),tradeProf(0);
vars: BBO(0),SBO(0),ATR(0),totTrades(0);

mp = marketPosition;
totTrades = totalTrades;
BSE = barsSinceExit(1);
If totTrades <> totTrades[1] then BSE = 0;
If totalTrades = 0 then BSE = 99;


ATR = avgTrueRange(atrLen);

SBO = midPoint(c,midPtNumBar) - ATR * atrBOMult;
BBO = midPoint(c,midPtNumBar) + ATR * atrBOMult;

tradeRisk = ATR * atrRiskMult;
tradeProf = ATR * atrProfMult;

If mp <> 1 and adx(adxLen) < adxTrig and BSE > reEntryDelay and open of next bar < BBO then buy next bar at BBO stop;
If mp <>-1 and adx(adxLen) < adxTrig AND BSE > reEntryDelay AND open of next bar > SBO then sellshort next bar at SBO stop;

If mp = 1 and mp[1] <> 1 then
Begin
	trailLongStop = entryPrice - tradeRisk;
end;

If mp = -1 and mp[1] <> -1 then
Begin
	trailShortStop = entryPrice + tradeRisk;
end;
	
if mp = 1 then sell("L-init-loss") next bar at entryPrice - tradeRisk[barsSinceEntry] stop;
if mp = -1 then buyToCover("S-init-loss") next bar at entryPrice + tradeRisk[barsSinceEntry] stop;


if mp = 1 then 
begin
	sell("L-ATR-prof") next bar at entryPrice + tradeProf[barsSinceEntry] limit;
	trailLongStop = maxList(trailLongStop,lowest(l,posMovTrailNumBars));
	sell("L-TL-Stop") next bar at trailLongStop stop;
end; 
if mp =-1 then 
begin
	buyToCover("S-ATR-prof") next bar at entryPrice -tradeProf[barsSinceEntry] limit;
	trailShortStop = minList(trailShortStop,highest(h,posMovTrailNumBars));
//	print(d, " Short and trailStop is : ",trailShortStop);
	buyToCover("S-TL-Stop") next bar at trailShortStop stop;
end;
Please follow and like us:

Converting A String Date To A Number – String Manipulation in EasyLanguage

EasyLanguage Includes a Powerful String Manipulation Library

I thought I would share this function.  I needed to convert a date string (not a number per se) like “20010115” or “2001/01/15” or “01/15/2001” or “2001-01-15” into a date that TradeStation would understand.  The function had to be flexible enough to accept the four different formats listed above.

String Functions

Most programming languages have functions that operate strictly on strings and so does EasyLanguage.  The most popular are:

  • Right String (rightStr) – returns N characters from the right side of the string.
  • Left String (leftStr) – returns N character starting from the left side of the string
  • Mid String (midStr) – returns the middle portion of a string starting at a specific place in the string and advance N characters
  • String Length (strLen) – returns the number of characters in the string
  • String To Number (strToNum) – converts the string to a numeric representation.  If the string has a character, this function will return 0
  • In String (inStr) – returns location of a sub string inside a larger string ( a substring can be just one character long)

Unpack the String

If the format is YYYYMMDD format then all you need to do is remove the dashes or slashes (if there are any) and then convert what is left over to a number.   But if the format is MM/DD/YYYY format then we are talking about a different animal.  So how can you determine if the date string is in this format?  First off you need to find out if the month/day/year separator is a slash or a dash.  This is how you do this:

whereIsAslash = inStr(dateString,”/”);
whereIsAdash = inStr(dateString,”-“);

If either is a non zero then you know there is a separator.  The next thing to do is locate the first “dash or slash” (the search character or string).  If it is located within the first four characters of the date string then you know its not a four digit year.  But, lets pretend the format is “12/14/2001” so if the first dash/slash is the 3rd character you can extract the month string by doing this:

firstSrchStrLoc = inStr(dateString,srchStr);
mnStr= leftStr(dateString,firstSrchStrLoc-1);

So if firstSrchStrLoc = 3 then we want to leftStr the date string and extract the first two characters and store them in mnStr.  We then store what’s left of the date string in tempStr by using rightStr:

strLength = strLen(dateString);

tempStr = rightStr(dateString,strLength-firstSrchStrLoc);

Here I pass dateString and the strLength-firstSrchStrLoc – so if the dateString is 10 characters long and the firstSrchStrLoc is 3, then we can create a tempstring by taking [10 -3  = 7 ] characters from right side of the string:

“12/14/2001” becomes “14/2001” – once that is done we can pull the first two characters from the tempStr and store those into the dyStr [day string.]  I do this by searching for the “/” and storing its location in srchStrLoc.  Once I have that location I can use that information and leftStr to get the value I need.   All that is left now is to use the srchStrLoc and the rightStr function.

srchStrLoc = inStr(tempStr,srchStr);
dyStr = leftStr(tempStr,srchStrLoc-1);
yrStr = rightStr(tempStr,strLen(tempStr)-srchStrLoc);

Now convert the strings to numbers and multiply their values accordingly.

DateSTrToYYYMMDD = strToNum(yrStr) X 10000-19000000 + strToNum(mnStr) X 100 + strToNum(dyStr)

To get the date into TS format I have to subtract 19000000 from the year.  Remember TS represents the date in YYYMMDD  format.

Now what do  you do if the date is in the right format but simply includes the dash or slash separators.  All you need to do here is loop through the string and copy all non dash or slash characters to a new string and then convert to a number.  Here is the loop:

        tempStr = "";
        iCnt = 1;
        While iCnt <= strLength
        Begin
            If midStr(dateString,iCnt,1) <> srchStr then
            	tempStr += midStr(dateString,iCnt,1);
            	iCnt+=1;
        end;
        tempDate = strToNum(tempStr);
        DateStrToYYYMMDD = tempDate-19000000;

Here I use midStr to step through each character in the string.  MidStr requires a string and the starting point and how many characters you want returned from the string.  Notice I step through the string with iCnt and only ask for 1 character at a time.  If the character is not a dash or slash I concatenate tempStr with the non dash/slash character.  At the end of the While loop I simply strToNum the string and subtract 19000000.  That’s it!  Remember EasyLanguage is basically a full blown programming language with a unique set of functions that relate directly to trading.

Here is the function and testFunc caller.

STRINGFUNCANDFUNCCALLER

 

Please follow and like us:

Ratio Adjusted versus Point Adjusted Contracts in TradeStation Part 2

Thomas Stridsman quote from  his “Trading Systems That Work Book”

The benefits of the RAD contract also become evident when you want to put together a multimarket portfolio…For now we only state that the percentage based calculations do not take into consideration how many contracts you’re trading and, therefore, give each market an equal weighting in the portfolio.

The Stridsman Function I presented in the last post can be used to help normalize a portfolio of different markets.  Here is a two market portfolio (SP – 250price and JY -125Kprice contract sizes) on a PAD contract.

1-Contract SP and JY on PAD data

Here is the performance of the same portfolio on a RAD contract.

Equal rating of SP and JY on RAD data

 

The curve shapes are similar but look at the total profit and the nearly $125K draw down.  I was trying to replicate Thomas’ research so this data is from Jan. 1990 to Dec. 1999.  A time period where the price of the SP increased 3 FOLD!  Initially you would start trading 1 JY to 2 SP but by the time it was over you would be trading nearly 3 JY to 1 SP.  Had you traded at this allocation the PAD numbers would be nearly $240K in profit.  Now this change occurred through time so the percentage approach is applied continuously.  Also the RAD data allows for a somewhat “unrealistic” reinvestment or compounding mechanism.  Its unrealistic because you can’t trade a partial futures contract.  But it does give you a glimpse of the potential.  The PAD test does not show reinvestment of profit.  I have code for that if you want to research that a little bit more.  Remember everything is in terms of Dec. 31 1999 dollars.  That is another beauty of the RAD contract.

Another Stridsman Quote

Now, wait a minute, you say, those results are purely hypothetical.  How can I place all the trades in the same market at presumably the same point in time?  Well, you can’t, so that is a good and valid question; but let me ask you, can you place any of these trades for real, no matter, how you do it?  No, of course not.  They all represent foregone opportunities.  Isn’t it better then to at least place them hypothetically in today’s marketplace to get a feel for what might happen today, rather in a ten-year-old market situation to get a feel for how the situation was back then?  I think wall can agree that it is better to know what might happen today, rather than what happened ten years ago.

That is a very good point.  However, convenience and time is import and when developing an algorithm.  And most platforms, including my TS-18, are geared toward PAD data.  However TS-18 can look at the entire portfolio balance and all the market data for each market up to that point in time and can adjust/normalize based on portfolio and data metrics.  However, I will add a percentage module a little later, but I would definitely use the StridsmanFunc that I presented in the last post to validate/verify your algorithm in today’s market place if using TradeStation.

Email me if you want the ELD of the function.

Please follow and like us:

Ratio Adjusted versus Pointed Adjusted Contracts in TradeStation – Part 1

If you play around with TradeStation’s custom futures capabilities you will discover you can create different adjusted continuous contracts.  Take a look at this picture:

Panama vs Ratio Adjusted

Both charts look the same and the trades enter and exit at the same locations in relation to the respective price charts.   However, take a look at the Price Scale on the right and the following pictures.

As you can see from the P/L from each trade list there is a big difference.  The top list is using RAD and the second is the generally accepted Panama Adjusted Data (PAD.)  Ratio adjusted data takes the percentage difference between the expiring contract and the new contract and propagates the value throughout the entire back history.  This is different than the PAD we have all used, where the actual point difference is propagated.  These two forms of adjustment have their own pros and cons but many industry leaders prefer the RAD.  I will go over a little bit of the theory in my next post, but in the mean time I will direct you to Thomas Stridsman’s excellent work on the subject in his book, “Trading Systems That Work – Building and Evaluating Effective Trading Systems”.

Here is another look at draw down metrics between the two formats.

RAD V PAD DrawDown

Who would want to trade a system on 1 contract of crude and have a $174K draw down?  Well you can’t look at it like that.  Back in 2008 crude was trading at $100 X 1000 = $100,000 a contract.  Today May 11th 2020 it is around $25,000.  So a drawdown of $44K back then would be more like $176K in today’s terms.  In my next post I will go over the theory of  RAD, but for right now you just need to basically ignore TradeStation’s built in performance metrics and use this function that I developed in most part by looking at Thomas’ book.

//Name this function StridsmanFunc1

Vars: FName(""), offset(1),TotTr(0), Prof(0), CumProf(1), ETop(1), TopBar(0), Toplnt(0),BotBar(0), Botlnt(0), EBot(1), EDraw(1), TradeStr2( "" );
Arrays: tradesPerArr[1000](0),drawDownArr[1000](0);
Vars: myEntryPrice(0),myEntryDate(0),myMarketPosition(0),myExitDate(0),myExitPrice(0);
If CurrentBar = 1 Then 
Begin
	FName = "C:\Temp\" + LeftStr(GetSymbolName, 3) + ".csv";
	FileDelete(FName);
	TradeStr2 = "E Date" + "," + "Position" + "," + "E Price" + "," + "X Date" +"," + "X Price" + "," + "Profit" + "," + "Cum. prof." + "," + "E-Top" + "," +"E-Bottom" + "," + "Flat time" + "," + "Run up" + "," + "Drawdown" +NewLine;
	FileAppend(FName, TradeStr2);
End;
TotTr = TotalTrades;
If TotTr > TotTr[1] or (lastBarOnChart and marketPosition <> 0) Then 
Begin

	if TotTr > TotTr[1] then
	begin
		if (EntryPrice(1) <> 0) then Prof = 1 + PositionProfit(1)/(EntryPrice(1) * BigPointValue); 
	End
	else
	begin
		Value99 = iff(marketPosition = 1,c - entryPrice, entryPrice - c);
		Prof = 1 + (Value99*bigPointValue) /(Entryprice *BigPointValue);
//		print(d," StridsmanFunc1 ",Value99," ",Prof," ",(Value99*bigPointValue) /(Entryprice *BigPointValue):5:4);
		TotTr = totTr + 1;
	end;
	tradesPerArr[TotTr] = Prof - 1;
	CumProf = CumProf * Prof;
	ETop = MaxList(ETop, CumProf);
	If ETop > ETop[1] Then 
	Begin
		TopBar = CurrentBar;
		EBot = ETop;
	End;
	
	EBot = MinList(EBot, CumProf);
	
	If EBot<EBot[1] Then BotBar = CurrentBar;
	
	Toplnt = CurrentBar - TopBar;
	
	Botlnt = CurrentBar - BotBar;
	
	if ETop <> 0 then EDraw = CumProf / ETop;
	
	drawDownArr[TotTr] = (EDraw - 1);
	
	myEntryDate = EntryDate(1);
	myMarketPosition = MarketPosition(1);
	myEntryPrice = EntryPrice(1);
	myExitDate = ExitDate(1);
	myExitPrice = ExitPrice(1);
	If lastBarOnChart and marketPosition <> 0 then
	Begin
		myEntryDate = EntryDate(0);
		myMarketPosition = MarketPosition(0);
		myEntryPrice = EntryPrice(0);
		myExitDate = d;
		myExitPrice =c;
	end;
	TradeStr2 = NumToStr(myEntryDate, 0) + "," +NumToStr(myMarketPosition, 0) + "," + NumToStr(myEntryPrice, 2) + "," 
	+ NumToStr(myExitDate, 0) + "," + NumToStr(myExitPrice, 2) + ","+ NumToStr((Prof - 1) * 100, 2) + "," + NumToStr((CumProf - 1) *100, 2) + "," 
	+ NumToStr((ETop - 1) * 100, 2) + "," + NumToStr((EBot - 1) * 100, 2) + "," + NumToStr(Toplnt, 0) + "," + NumToStr(Botlnt, 0) + "," + NumToStr((EDraw - 1) * 100, 2) +
	NewLine;
	
	FileAppend(FName, TradeStr2);
End;
vars: tradeStr3(""),
	  ii(0),avgTrade(0),avgTrade$(0),cumProf$(0),trdSum(0),
	  stdDevTrade(0),stdDevTrade$(0),
	  profFactor(0),winTrades(0),lossTrades(0),perWins(0),perLosers(0),
	  largestWin(0),largestWin$(0),largestLoss(0),largestLoss$(0),avgProf(0),avgProf$(0),
      winSum(0),lossSum(0),avgWin(0),avgWin$(0),avgLoss(0),avgLoss$(0),maxDD(0),maxDD$(0),cumProfit(0),cumProfit$(0);
      
If lastBarOnChart then
begin
    stdDevTrade = standardDevArray(tradesPerArr,TotTr,1);
    stdDevTrade$ = stdDevTrade*c*bigPointValue;
    For ii = 1 to TotTr
    Begin
    	trdSum = trdSum + tradesPerArr[ii];	
//    	print(d," ",ii," ",tradesPerArr[ii]);
    	If tradesPerArr[ii] > 0 then 
    	begin
    		winTrades = winTrades + 1;
    		winSum = winSum + tradesPerArr[ii];
    	end;
    	If tradesPerArr[ii] <=0 then 
    	begin
    		lossTrades = lossTrades + 1;
    		lossSum = lossSum + tradesPerArr[ii];
    	end;
    	If tradesPerArr[ii] > largestWin then 
    	begin
    		largestWin = tradesPerArr[ii];
 //   		print("LargestWin Found ",largestWin);
    	end;
    	If tradesPerArr[ii] < largestLoss then largestLoss = tradesPerArr[ii];
    	If drawDownArr[ii] < maxDD then maxDD = drawDownArr[ii];
    end;
 //   print("TradeSum: ",trdSum);
    if TotTr <> 0 then avgTrade = trdSum/TotTr;
	avgTrade$ = avgTrade*c*bigPointValue;
    largestWin = largestWin;
    largestLoss = largestLoss;
    largestWin$ = largestWin*c*bigPointValue;
    largestLoss$ = largestLoss*c*bigPointValue;
    if TotTr <> 0 then perWins = winTrades/TotTr;
    if TotTr <> 0 then perLosers = lossTrades/TotTr;
    If winTrades <> 0 then avgWin = winSum / winTrades;
    avgWin$ = avgWin*c*bigPointValue;
    if lossTrades <> 0 then avgLoss= lossSum / lossTrades;
    avgLoss$ = avgLoss*c*bigPointValue;
    maxDD$ = maxDD *c*bigPointValue;
    if lossTrades <>0 and avgLoss$ <> 0 then profFactor = (winTrades*avgWin$)/(lossTrades*avgLoss$);
    CumProf = cumProf - 1;
    CumProf$ = cumProf*c*bigPointValue;
    
    TradeStr3 = "Total Trades,,"+NumToStr(TotTr,0)+",Num. Winners,"+NumToStr(winTrades,0)+","+NumToStr(perWins,3)+", Num. Losses,"+NumToStr(lossTrades,0)+","+NumToStr(perLosers,3)+NewLine+
                "Profit Factor,,"+NumToStr(profFactor,3)+",Largest Win ,"+NumToStr(largestWin,3)+","+NumToStr(largestWin$,0)+",Largest Loss,"+NumToStr(largestLoss,3)+","+NumToStr(largestLoss$,0)+NewLine+
                "Avg Profit,"+NumToStr(avgTrade,3)+","+NumToSTr(avgTrade$,0)+",Avg Win,"+NumToStr(avgWin,3)+","+NumToStr(avgWin$,0)+",Avg Loss,"+NumToStr(avgLoss,3)+","+NumToStr(avgLoss$,0)+NewLine+
                "Std. Dev,"+NumToStr(stdDevTrade,3)+","+NumToStr(stdDevTrade$,0)+",Cum Profit,"+NumToStr(cumProf,3)+","+NumToStr(cumProf$,3)+",Draw Down,"+numToStr(maxDD,3)+","+numToStr(maxDD$,0)+NewLine;
 	FileAppend(FName, TradeStr3);
 {  Print("Total Trades  ",totalTrades," Num. Winners ",winTrades," ",perWins," Num. Losses    ",lossTrades," ",perLosers);
    Print("Profit Factor ",profFactor," Largest Win   ",largestWin:5:2," ",largestWin$," Largest Loss ",largestLoss:5:2," ",largestLoss$);
    Print("Avg Profit ",avgTrade," ",avgTrade$," Avg Win ",avgWin," ",avgWin$," Avg Loss ",avgLoss," ",avgLoss$);
    Print("St Dev ",stdDevTrade," ",stdDevTrade$," Cum Profit ",cumProf," ",cumProf$," Drawdown ",maxDD," ",maxDD$);}
    
    
 end;   
    
    

StridsmanFunc1 = 1;
Conversion of Performance Metrics to Percentages Instead of $Dollars

This function will out put a file that looks like this.  Go ahead and play with the code – all you have to do is call the function from within an existing strategy that you are working with.  In part two I will go over the code and explain what its doing and how arrays and strings were used to archive the trade history and print out this nifty table.

Stridsman Function Output.

In this output if you treat the return from each trade as a function of the entry price and accumulate the returns you can convert the value to today’s current market price of the underlying.  In this case a 15 -year test going through the end of last year, ended up making almost $70K.

RAD TradeStation Metrics:

Profit $350K – Draw Down $140K

PAD TradeStation Metrics:

Profit $89K – Draw Down $45K

Stridsman Func on RAD:

Profit $70K – Draw Down $48K

At this point you can definitely determine that the typical RAD/TS metrics are not all that usuable.  The PAD/TS results look very similar to RAD/StridsmanFunc results.  Stay tuned for my next post and I will hopefully explain why RAD/StridsmanFunc is probably the most accurate performance metrics of the three.

Please follow and like us:

Using a Dictionary to Create a Trading System

Dictionary Recap

Last month’s post on using the elcollections dictionary was a little thin so I wanted to elaborate on it and also develop a trading system around the best patterns that are stored in the dictionary.  The concept of the dictionary exists in most programming languages and almost all the time uses the (key)–>value model.  Just like a regular dictionary a word or a key has a unique definition or value.  In the last post, we stored the cumulative 3-day rate of return in keys that looked like “+ + – –” or “+ – – +“.  We will build off this and create a trading system that finds the best pattern historically based on average return.  Since its rather difficult to store serial data in a Dictionary I chose to use Wilder’s smoothing average function.

Ideally, I Would Have Liked to Use a Nested Dictionary

Initially, I played around with the idea of the pattern key pointing to another dictionary that contained not only the cumulative return but also the frequency that each pattern hit up.  A dictionary is designed to have  unique key–> to one value paradigm.  Remember the keys are strings.  I wanted to have unique key–> to multiple values. And you can do this but it’s rather complicated.  If someone wants to do this and share, that would be great.  AndroidMarvin has written an excellent manual on OOEL and it can be found on the TradeStation forums.  

Ended Up Using A Dictionary With 2*Keys Plus an Array

So I didn’t want to take the time to figure out the nested dictionary approach or a vector of dictionaries – it gets deep quick.  So following the dictionary paradigm I came up with the idea that words have synonyms and those definitions are related to the original word.  So in addition to having keys like “+ + – -” or “- – + -” I added keys like “0”, “1” or “15”.  For every  + or – key string there exists a parallel key like “0” or “15”.  Here is what it looks like:

–  –  –  –  = “0”
– – – + = “1”
– – + – = “2”

You can probably see the pattern here.  Every “+” represents a 1 and every “0” represent 0 in a binary-based numbering system.  In the + or – key I store the last value of Wilders average and in the numeric string equivalent, I store the frequency of the pattern.

Converting String Keys to Numbers [Back and Forth]

To use this pattern mapping I had to be able to convert the “++–” to a number and then to a string.  I used the numeric string representation as a dictionary key and the number as an index into an array that store the pattern frequency.  Here is the method I used for this conversion.  Remember a method is just a function local to the analysis technique it is written.

//Lets convert the string to unique number
method int convertPatternString2Num(string pattString) 
Vars: int pattLen, int idx, int pattNumber;
begin
	pattLen = strLen(pattString);
	pattNumber = 0;
	For idx = pattLen-1 downto 0 
	Begin
		If MidStr(pattString,pattLen-idx,1) = "+" then pattNumber = pattNumber + power(2,idx);
	end;
	Return (pattNumber);
end;
String Pattern to Number

This is a simple method that parses the string from left to right and if there is a “+” it is raised to the power(2,idx) where idx is the location of “+” in the string.  So “+  +  –  –  ” turns out to be 8 + 4 + 0 + 0 or 12.

Once I retrieve the number I used it to index into my array and increment the frequency count by one.  And then store the frequency count in the correct slot in the dictionary.

patternNumber = convertPatternString2Num(patternString); 
//Keep track of pattern hits
patternCountArray[patternNumber] = patternCountArray[patternNumber] + 1;
//Convert pattern number to a string do use as a Dictionary Key
patternStringNum = numToStr(patternNumber,2);
//Populate the pattern number string key with the number of hits
patternDict[patternStringNum] = patternCountArray[patternNumber] astype double;
Store Value In Array and Dictionary

Calculating Wilder’s Average Return and Storing in Dictionary

Once I have stored an instance of each pattern [16] and the frequency of each pattern[16] I calculate the average return of each pattern and store that in the dictionary as well.

//Calculate the percentage change after the displaced pattern hits
Value1 =  (c - c[2])/c[2]*100;
//Populate the dictionary with 4 ("++--") day pattern and the percent change
if patternDict.Contains(patternString) then
Begin
	patternDict[patternString] = (patternDict[patternString] astype double * 
	(patternDict[patternStringNum] astype double - 1.00) + Value1) / patternDict[patternStringNum] astype double;
end
Else
begin
	patternDict[patternString] = value1;
//	print("Initiating: ",patternDict[patternString] astype double);
end;
(pAvg * (N-1) + return) / N

When you extract a value from a collection you must us an identifier to expresses its data type or you will get an error message : patternDict[patternString] holds a double value {a real number}  as well as patternDict[patternStringNum] – so I have to use the keyword asType.  Once I do my calculation I ram the new value right back into the dictionary in the exact same slot.  If the pattern string is not in the dictionary (first time), then the Else statement inserts the initial three-day rate of return.

Sort Through All of The Patterns and Find the Best!

The values in a dictionary are stored in alphabetic order and the string patterns are arranged in the first 16 keys.  So I loop through those first sixteen keys and extract the highest return value as the “best pattern.”

//  get the best pattern that produces the best average 3 bar return
vars: hiPattRet(0),bestPattString("");
If patternDict.Count > 29 then
Begin
	index = patternDict.Keys;
	values = patternDict.Values; 
	hiPattRet = 0;
	For iCnt = 0 to 15
	Begin
		If values[iCnt] astype double > hiPattRet then
		Begin
			hiPattRet = values[iCnt] astype double ;
			bestPattString = index[iCnt] astype string;
		end;
	end;
//	print(Date," BestPattString ",bestPattString," ",hiPattRet:8:4," CurrPattString ",currPattString);
end;
Extract Best Pattern From All History

If Today’s Pattern Matches the Best Then Take the Trade

// if the current pattern matches the best pattern then bar next bar at open
If currPattString = BestPattString then buy next bar at open;
// cover in three days
If barsSinceEntry > 2 then sell next bar at open;
Does Today Match the Best Pattern?

If today matches the best pattern then buy and cover after the second day.

Conclusion

I didn’t know if this code was worth proffering up but I decided to posit it because it contained a plethora of programming concepts: dictionary, method, string manipulation, and array.  I am sure there is a much better way to write this code but at least this gets the point across.

Contents of Dictionary at End of Run

++++    0.06
+++-   -0.08
++-+    0.12
++--   -0.18
+-++    0.08
+-+-    0.40
+--+   -0.46
+---    0.34
-+++    0.20
-++-    0.10
-+-+    0.23
-+--    0.31
--++    0.02
--+-    0.07
---+    0.22
----    0.46
0.00  103.00
1.00  128.00
10.00  167.00
11.00  182.00
12.00  146.00
13.00  168.00
14.00  163.00
15.00  212.00
2.00  157.00
3.00  133.00
4.00  143.00
5.00  181.00
6.00  151.00
7.00  163.00
8.00  128.00
9.00  161.00
Contents of Dictionary

Example of Trades

Pattern Dictionary System

 

Code in Universum

//Dictionary based trading sytem
//Store pattern return
//Store pattern frequency
// by George Pruitt
Using elsystem.collections; 

vars: string keystring("");
vars: dictionary patternDict(NULL),vector index(null), vector values(null);
array: patternCountArray[100](0);

input: patternTests(8);
  
var: patternTest(""),tempString(""),patternString(""),patternStringNum("");
var: patternNumber(0);
var: iCnt(0),jCnt(0);
//Lets convert the string to unique number
method int convertPatternString2Num(string pattString) 
Vars: int pattLen, int idx, int pattNumber;
begin
	pattLen = strLen(pattString);
	pattNumber = 0;
	For idx = pattLen-1 downto 0 
	Begin
		If MidStr(pattString,pattLen-idx,1) = "+" then pattNumber = pattNumber + power(2,idx);
	end;
	Return (pattNumber);
end;


once begin 
   clearprintlog; 
   patternDict = new dictionary; 
   index = new vector;
   values = new vector;
end;   

//Convert 4 day pattern displaced by 2 days
patternString = ""; 
for iCnt = 5 downto 2
begin
    if(close[iCnt]> close[iCnt+1]) then
    begin
        patternString = patternString + "+";
    end
    else
    begin
        patternString = patternString + "-";
    end;
end;

//What is the current 4 day pattern
vars: currPattString("");
currPattString = "";

for iCnt = 3 downto 0
begin
    if(close[iCnt]> close[iCnt+1]) then
    begin
        currPattString = currPattString + "+";
    end
    else
    begin
        currPattString = currPattString + "-";
    end;
end;

//Get displaced pattern number
patternNumber = convertPatternString2Num(patternString); 
//Keep track of pattern hits
patternCountArray[patternNumber] = patternCountArray[patternNumber] + 1;
//Convert pattern number to a string do use as a Dictionary Key
patternStringNum = numToStr(patternNumber,2);
//Populate the pattern number string key with the number of hits
patternDict[patternStringNum] = patternCountArray[patternNumber] astype double;
//Calculate the percentage change after the displaced pattern hits
Value1 =  (c - c[2])/c[2]*100;
//Populate the dictionary with 4 ("++--") day pattern and the percent change
if patternDict.Contains(patternString) then
Begin
	patternDict[patternString] = (patternDict[patternString] astype double * 
	(patternDict[patternStringNum] astype double - 1.00) + Value1) / patternDict[patternStringNum] astype double;
end
Else
begin
	patternDict[patternString] = value1;
//	print("Initiating: ",patternDict[patternString] astype double);
end;
//  get the best pattern that produces the best average 3 bar return
vars: hiPattRet(0),bestPattString("");
If patternDict.Count > 29 then
Begin
	index = patternDict.Keys;
	values = patternDict.Values; 
	hiPattRet = 0;
	For iCnt = 0 to 15
	Begin
		If values[iCnt] astype double > hiPattRet then
		Begin
			hiPattRet = values[iCnt] astype double ;
			bestPattString = index[iCnt] astype string;
		end;
	end;
//	print(Date," BestPattString ",bestPattString," ",hiPattRet:8:4," CurrPattString ",currPattString);
end;
// if the current pattern matches the best pattern then bar next bar at open
If currPattString = BestPattString then buy next bar at open;
// cover in three days
If barsSinceEntry > 2 then sell next bar at open;
Pattern Dictionary Part II

 

 

 

Please follow and like us:

Multi-Time Frame – Using Built-in Indicators and Multi Data Charts

A reader of this blog wanted to be able to use different time frames and some built-in indicators and output the information in a similar fashion as I did in the original MTF post.  There are numerous ways to program this but the two easiest are to use data structures such as arrays or vectors or use TradeStation’s own multi data inputs.  The more complicated of the two would be to use arrays and stay compliant with Multicharts.  Or in that same vein use vectors and not stay compliant with Multicharts.  I chose, for this post, the down and dirty yet compliant method.  [NOTE HERE! When I started this post I didn’t realize it was going to take the turn I ended up with.  Read thoroughly before playing around with the code to see that it is what you are really, really looking for.]  I created a multi data chart with five-time frames: 5,10,15,30 and 60 minutes.  I then hid data2 thru data5.  I created an MTF indicator that plots the relationship of the five time frames applied to the ADX indicator with length 14.  If the ADX > 20 then the plot will be green else it will be red.  If all plots align, then the composite plot will reflect the alignment color.

Using the MTF indicator with ADX
{EasyLanguage MultiTime Frame Indicator)
 written by George Pruitt - copyright 2019 by George Pruitt
 }


Inputs:adxLen(14),adxTrendVall(20);

vars: adxData1(0),adxData2(0),adxData3(0),adxData4(0),adxData5(0);


If barNumber > 1 then
Begin
	
	adxData1 = adx(adxLen) of data1;
	adxData2 = adx(adxLen) of data2;
	adxData3 = adx(adxLen) of data3;
	adxData4 = adx(adxLen) of data4;
	adxData5 = adx(adxLen) of data5;
	
	Condition10 = adxData1 > adxTrendVall;
	Condition11 = adxData2 > adxTrendVall;
	Condition12 = adxData3 > adxTrendVall;
	Condition13 = adxData4 > adxTrendVall;
	Condition14 = adxData5 > adxTrendVall;
	 
	If condition10 then setPlotColor(1,Green) else SetPlotColor(1,Red);
	If condition11 then setPlotColor(2,Green) else SetPlotColor(2,Red);
	If condition12 then setPlotColor(3,Green) else SetPlotColor(3,Red);
	If condition13 then setPlotColor(4,Green) else SetPlotColor(4,Red);
	If condition14 then setPlotColor(5,Green) else SetPlotColor(5,Red);
	
	condition6 = condition10 and condition11 and condition12 and condition13 and condition14;
	Condition7 = not(condition10) and not(condition11) and not(condition12) and not(condition13) and not(condition14);

	If condition6 then setPlotColor(7,Green);
	If condition7 then setPlotColor(7,Red);
	
	If condition6 or condition7 then plot7(7,"trend");

	Plot6(5,"line");	
	Plot1(4,"t1");
	Plot2(3,"t2");
	Plot3(2,"t3");
	Plot4(1,"t4");
	Plot5(0,"t5"); 

end;
MTF with 5 data streams and ADX

This code is very similar to the original MTF indicator, but here I simply pass a pointer to the different time frames to the ADX function.  Since the ADX function only requires a length input I had assumed I could use the following format to get the result for each individual time frame:

adxData1 = adx(14) of data1;

adxData2 = adx(14) of data2;

This assumption worked out.

But are we really getting what we really, really want?  I might be putting too much thought into this but of the five-time frame indicator dots, only the 5-minute will change on a 5-minute basis.  The 10-min dot will stay the same for two 5-min bars.  The dots will reflect the closing of the PRIOR time frame and the current 5-min bar is ignored in the calculation.  This may be what you want, I will leave that up to you.  Here is an illustration of the delay in the different time frames.

So when you look at each dot color remember to say to yourself – this is the result of the prior respective time frame’s closing price.  You can say to yourself, “Okay this is the ADX of the current 5-minute bar and this is the ADX of the prior 10-minute close and this is the ADX of the prior 15 minutes close and so on and so on.   We all know that the last 5 minutes will change all of the time frames closing tick, but it may or may not change the price extremes of those larger time frames.   I will show you how to do this in the next post.   If you want to see the impact of the last 5- minutes, then you must build your bars internally and dynamically.

 

Please follow and like us:

Calculating Position Size with Optimal F

I had a reader of the blog ask how to use Optimal F.  That was really a great question.  A few posts back I provided the OptimalFGeo function but didn’t demonstrate on how to use it for allocation purposes.  In this post, I will do just that.

I Have Optimal F – Now What?

From Ralph Vince’s book, “Portfolio Management Formulas”, he states: “Once the highest f is found, it can readily be turned into a dollar amount by dividing the biggest loss by the negative optimal f.  For example, if our biggest loss is $100 and our optimal f is 0.25, then -$100/ 0.25 = $400.  In other words, we should bet 1 unit for every $400 we have in our stake.”

Convert Optimal F to dollars and then to number of shares

In my example strategy, I start out with an initial capital of $50,000 and allow reinvestment of profit or loss.  The protective stop is set as 3 X ATR(10).  A fixed $2000 profit objective is also utilized.  The conversion form Optimal F to position size is illustrated by the following lines of code:

//keep track of biggest loss
biggestLoss = minList(positionProfit(1),biggestLoss);
//calculate the Optimal F with last 10 trades.
OptF = OptimalFGeo(10);
//reinvest profit or loss
risk$ = initCapital$ + netProfit;
//convert Optimal F to $$$
if OptF <> 0 then numShares = risk$ / (biggestLoss / (-1*OptF));
Code snippet - Optimal F to Position Size
  1. Keep track of biggest loss
  2. Calculate optimal F with OptimalFGeo function – minimum 10 trades
  3. Calculate Risk$ by adding InitCapital to current NetProfit (Easylanguage keyword)
  4. Calculate position size by dividing Risk$  by the quotient of biggest loss and (-1) Optimal F

I applied the Optimal F position sizing to a simple mean reversion algorithm where you buy on a break out in the direction of the 50-day moving average after a lower low occurs.

Code listing:

vars: numShares(0),initCapital$(50000),biggestLoss(0),OptF(0),risk$(0);


//keep track of biggest loss
biggestLoss = minList(positionProfit(1),biggestLoss);
//calculate the Optimal F with last 10 trades.
OptF = OptimalFGeo(10);
//reinvest profit or loss
risk$ = initCapital$ + netProfit;
//convert Optimal F to $$$
if OptF <> 0 then numShares = risk$ / (biggestLoss / (-1*OptF));
numShares =  maxList(1,numShares);
//if Optf <> 0 then print(d," ",t," ",risk$ / (biggestLoss / (-1*OptF))," ",biggestLoss," ",optF);

if c > average(c,50) and low < low[1] then Buy numShares shares next bar at open + .25* range stop;

setStopPosition;
setProfitTarget(2000);

setStopLoss(3*avgTrueRange(10)*bigPointValue);
Strategy Using Optimal F

I have included the results below.  At one time during the testing the number of contracts jumped up to 23.  That is 23 mini Nasdaq futures ($20 * 7,300) * 23.  That’s a lot of leverage and risk.  Optimal doesn’t  always mean the best risk mitigation.  Please let me know if you find any errors in the code or in the logic.

 

Here is the ELD that incorporates the Strategy and the Function.USINGOPTIMALF

 

Please follow and like us:

What’s Our Vector Victor – Tiptoeing in the EL Collections

Tired of Manipulating Arrays – Try a Vector and a Queue

Vectors:

An array like structure but are dynamic and have a plethora of tools at your disposal.  Arrays are cool and can be multi-dimensional and can be easily manipulated.  But they require a lot of forethought as to how much size to reserve for their implementation.  Now don’t think this is going to be an advanced EasyLanguage tutorial, because it’s really not.  Most of us TRS-80, Ti-99/4A, Vic-20 and Commodore 64 trained programmers of the early ’80s have not welcomed objects with open arms and that is really a mistake.  In this sense we are like cavemen – we have all of the rudimentary tools at our disposal and can create some really cool stuff and we can really understand what we are doing.  With time and effort, we can get to the same place as object-oriented programmers.  We just don’t like the concept of using other’s tools as much as we like using ours.  So if you aren’t classically trained in programming you may have an advantage when tieing into the objects of a programming language.  This little tutorial is a very brief glimpse into a whole different world of programming.  The beauty is you can combine “old school” programming with objects – even if you don’t understand how the objects are truly constructed.    I want to introduce the concept of the Vector and the Queue-  truly cool Swiss Army knives.  First the vector.  Let’s just jump into some of the code – it really is simple.

Object Instantiation – a long word for declaring variable:
Using elsystem.collections;

Vars: Vector opVector(NULL),
      Vector hiVector(Null),
      Vector loVector(Null),
      Vector clVector(Null),
      Vector barVector(Null),
      Queue timeStampQue(Null);
Once
Begin
	barVector = new Vector;
	opVector = new Vector;
	hiVector = new Vector;
	loVector = new Vector;
	clVector = new Vector;
	timeStampQue = new Queue;
end;
Instantiating and Declaring Vectors and Queue

You have to tell EasyLanguage you want to use some of the tools in the elsystem.collections.  You do this by simply tell it you are Using elsystem.collections.  The word collections is a catch-all for a bunch of different types of data structures.  Remember data structures are just programming constructs used to hold data – like an array.  All the variables that you declare in EasyLanguage are arrays – you just aren’t really aware of it.   When you index into them to get prior values then you become slightly aware of it.  In this portion of code, I create five vectors and one queue and assign them the Null or an empty value.  I just finished a programming gig where I had to build dynamically sized bars from the base data.  Kind of like creating 15, 30, 60-minute bars from a 5-minute bar chart or stream.   I did this using arrays because I wanted to be able to index into them to go back in time and I didn’t how far I wanted to go back.  So I declared some arrays with large dimensions to be safe.  This really takes a bite out of your resources which costs space and time.  I had played with Vector like objects in Python, so I thought I would post about them here and show how cool they are.  Remember this is a rudimentary program and could be streamlined and cleaned up.  Each vector will store their respective time, open, high, low and close values of the combined bar.  In a later post, I would like to do this with a Dictionary.  So the opVector will hold the open price, the hiVector will hold the high price and so on.

Build a Queue – why the extra ue?

I want to build 15-minute bars from 5-minute bars so I need to know when to sample the data to properly collect the corresponding data.  If I start at 9:30 then I want to sample the data at 9:45 and look back three bars to get the open and the highest high and the lowest low.  The close will simply be the close of the 9:45 bar.  I want to do this at 9:45, 10:00. 10:15 and so on.  I could manipulate the time and use the modulus function to see if the minutes are multiples of 15 and I tried this but it didn’t work too well.  So I thought since I was already in the collections why not build a list or a queue with all the timestamps I would need.  This is how I did it.

vars: hrs(0),mins(0),barMult(3),combBarTimeInterval(0),totBarsInHour(0),startTime(930),endTime(1615),cnt(0);

Once
Begin
	mins = fracPortion(t/100);
	combBarTimeInterval = barInterval*barMult;
	While value1 < endTime
	Begin
		cnt = cnt + 1;
		Value1 = calcTime(startTime,cnt*combBarTimeInterval);
//		print("Inside queue : ",Value1," ",cnt*combBarTimeInterval);
		timeStampQue.Enqueue(Value1);
	end;	
end;
Populating A Queue With Time Stamps

I simply use the CalcTime function to add 15-minute intervals to the start time and then I add them to the queue:  timeStampQue.Enqueue(Value1);  You access the methods or tools to a class by using the dot (” . “) notation.  Once I instantiated or created the timeStampQue I gained access to all the tools that belong to that object.  The Enqueue method simply appends the list the value that you pass it.  I would have preferred the method to be labeled simply add.  How did I figure out the right method name you ask?  I accessed the Dictionary from the View menu in the TDE.  Here is a picture to help:

Dictionary:

I use the keyword Once to just execute the code one time.  You could have said if BarNumber = 1, but why not use the tools at your disposal,   I figured out the combBarTimeInterval by using the 5-minute bar multiplier (3).  I then looped from startTime to endTime in 15-minute intervals and stored the timeStamps in the queue.  So every time stamp I need is in the timeStampQue.  All I need now is to compare the time of the 5-minute bar to the time stamps inside the queue.  This is where using object really come in handy.

Queue Methods:

Old school would have looped through all of the elements in the list and compared them to the value I was seeking and if found it would return true.  In the object world, I can simply ask the object itself to see if the value is in it:

condition1 = timeStampQue.Contains(t);

Cool!  If condition1 is true then I know I am sitting on the 5-minute bar that shares the same timestamp as a 15-minute bar.  If the time stamps are the same then I can start building the large timeframe from the lower timeframe.  You add elements to a vector by using the insert method.  I simply looked it up in the dictionary.   I had to specify where to insert the value in the vector.  I simply inserted each value into the [0] location.  Remember we are inserting so everything else in the vector is moved down.

Vector Methods:

 

If condition1 then
Begin
	barVector.insert(0,t);
	opVector.insert(0,open[2]);
	hiVector.insert(0,highest(h[0],3)); 
	loVector.insert(0,lowest(l[0],3));
	clVector.insert(0,close[0]);
end;
Inserting Values at Vector Location 0

I only need to keep track of the last 10 15-minute bars, so once the vector count exceeded 10, I simply popped off the value at the back end – pop_back().  I figured this out by looking at Martin Whittaker’s awesome website – www.markplex.com. 

 

If opVector.Count > 10 then 
begin
    barVector.pop_back();
	opVector.pop_back();
	hiVector.pop_back();
	loVector.pop_back(); 
	clVector.pop_back();
end;
Popping the Back-End

To check my work I printed the 15-minute bars on each 5-minute bar to make sure the bars were being built properly.  These data structures expect an object to be inserted, added, popped so when you print out one of their values you have to tell the print statement what the object should be translated as.  Here the keyword asType comes into play.  Take a look at my code, and you will see what I mean.  I hope this gets you excited about objects because the collections class can save you a ton of time and is really cool.  Use it and you can brag that you are an OOP programmer at your next cocktail party.

Code Listing:
Using elsystem.collections;

Vars: Vector opVector(NULL),
      Vector hiVector(Null),
      Vector loVector(Null),
      Vector clVector(Null),
      Vector barVector(Null),
      Queue timeStampQue(Null);
Once
Begin
	barVector = new Vector;
	opVector = new Vector;
	hiVector = new Vector;
	loVector = new Vector;
	clVector = new Vector;
	timeStampQue = new Queue;
end;

vars: hrs(0),mins(0),barMult(3),combBarTimeInterval(0),totBarsInHour(0),startTime(930),endTime(1615),cnt(0);

Once
Begin
	mins = fracPortion(t/100);
	combBarTimeInterval = barInterval*barMult;
	While value1 < endTime
	Begin
		cnt = cnt + 1;
		Value1 = calcTime(startTime,cnt*combBarTimeInterval);
//		print("Inside queue : ",Value1," ",cnt*combBarTimeInterval);
		timeStampQue.Enqueue(Value1);
	end;	
end;	
	
	
	
condition1 = timeStampQue.Contains(t);

Print(d," ",t," ",condition1);

If condition1 then
Begin
	barVector.insert(0,t);
	opVector.insert(0,open[2]);
	hiVector.insert(0,highest(h[0],3)); 
	loVector.insert(0,lowest(l[0],3));
	clVector.insert(0,close[0]);
end;
 
If opVector.Count > 10 then 
begin
    barVector.pop_back();
	opVector.pop_back();
	hiVector.pop_back();
	loVector.pop_back(); 
	clVector.pop_back();
end;

vars:vectCnt(0);
print(d," ",t);
If opVector.Count > 9 then
Begin
	For vectCnt = 0 to 9 
	begin
		print(vectCnt," ",barVector.at(vectCnt) astype int," ",opVector.at(vectCnt) astype double," ",hiVector.at(vectCnt) astype double," ",loVector.at(vectCnt) astype double," ",clVector.at(vectCnt) astype double);
	end;
end;
Program in its Entirety

 

Please follow and like us:

Python Script To Import List of Trades into TradeStation’s EasyLanguage – Sort of

Converting A List of Trades, Dates and Prices Into EasyLanguage Arrays:

As the old saying goes “a picture is worth a thousand words!”  Have you ever been given a list of trades like this:

Sell Short,20010622,1178.50 
Buy to Cover,20010626,1159.75 
Sell Short,20010801,1150.00 
Buy to Cover,20010807,1139.75 
Sell Short,20010814,1129.00 
Buy to Cover,20010816,1117.25 
Sell Short,20011001,976.75 
Buy to Cover,20011004,1016.75 
Sell Short,20011107,1053.00 
Buy to Cover,20011123,1069.50 
Sell Short,20011219,1076.25 
Buy to Cover,20020102,1075.00 
Sell Short,20020129,1067.25 
Buy to Cover,20020131,1046.75 
Sell Short,20020131,1046.75 
Buy to Cover,20020205,1026.75 
Sell Short,20020520,1033.25 
Buy to Cover,20020522,1011.50 
Sell Short,20020731,832.00 
Buy to Cover,20020805,792.50 
Sell Short,20020812,834.00 
Buy to Cover,20020814,811.75 
Sell Short,20020911,838.50 
Buy to Cover,20020913,816.75 
List of Trades : Order, Date, Price

But really wanted to see this:

I have created a small Python script that will take a list of trades like those listed in table above and create the following EasyLanguage:

arrays: DateArr[500](0),TradeArr[500](""),PriceArr[500](0);
DateArr[0]=1010622;TradeArr[0]="SS";PriceArr[0]=1178.5;
DateArr[1]=1010626;TradeArr[1]="SX";PriceArr[1]=1159.75;
DateArr[2]=1010801;TradeArr[2]="SS";PriceArr[2]=1150.0;
DateArr[3]=1010807;TradeArr[3]="SX";PriceArr[3]=1139.75;
DateArr[4]=1010814;TradeArr[4]="SS";PriceArr[4]=1129.0;
DateArr[5]=1010816;TradeArr[5]="SX";PriceArr[5]=1117.25;
DateArr[6]=1011001;TradeArr[6]="SS";PriceArr[6]=976.75;
DateArr[7]=1011004;TradeArr[7]="SX";PriceArr[7]=1016.75;
DateArr[8]=1011107;TradeArr[8]="SS";PriceArr[8]=1053.0;
DateArr[9]=1011123;TradeArr[9]="SX";PriceArr[9]=1069.5;
DateArr[10]=1011219;TradeArr[10]="SS";PriceArr[10]=1076.25;
DateArr[11]=1020102;TradeArr[11]="SX";PriceArr[11]=1075.0;
DateArr[12]=1020129;TradeArr[12]="SS";PriceArr[12]=1067.25;
DateArr[13]=1020131;TradeArr[13]="SX";PriceArr[13]=1046.75;
DateArr[14]=1020131;TradeArr[14]="SS";PriceArr[14]=1046.75;
DateArr[15]=1020205;TradeArr[15]="SX";PriceArr[15]=1026.75;
DateArr[16]=1020520;TradeArr[16]="SS";PriceArr[16]=1033.25;
DateArr[17]=1020522;TradeArr[17]="SX";PriceArr[17]=1011.5;
Converting list of trades to EasyLanguage

This just creates the arrays that you can use to graph the trades on a chart.  If you are using exact prices you got to make sure your data aligns with the prices in the list of trades.  If you are only entering on the open or the close of the bar then the price array isn’t necessary.

The following Python script will also be helpful if you want to learn how to open a file in csv format, read it into lists, convert it and then save the output to a file.

#-------------------------------------------------------------------------------
# Name:        Read csv file via askOpen and save txt file via askSave
# Purpose:     Read the trade metrics from a TradeStation csv format
#              and build arrays from the information to display on charts in
#              TradeStation
# Author:      georg
#
# Created:     29/08/2018
# Copyright:   (c) georg 2018
#-------------------------------------------------------------------------------
import csv
import tkinter as tk
import os.path
from tkinter.filedialog import askopenfilenames
from tkinter.filedialog import asksaveasfilename

tradeType = list()
tradeDate = list()
tradePrice = list()

def main():
    root = tk.Tk()
    root.withdraw()
    files = askopenfilenames(filetypes=(('CSV files', '*.csv'),
                                       ('TXT files', '*.txt')),
                                       title='Select CSV format only!')
    fileList = root.tk.splitlist(files)
    fileListLen = len(fileList)


# make sure you know the format ahead of time
# I know "Buy",20180828,2745.75
#
    cnt = 0
    for files in range(0,fileListLen):
        head,tail = os.path.split(fileList[files])
        with open(fileList[files]) as f:
            f_csv = csv.reader(f)
            for row in f_csv:
                numCols = len(row)
                tradeType.append(row[0])
                tradeDate.append(int(row[1]))
                tradePrice.append(float(row[2]))
                cnt += 1
        f.close


    filename = asksaveasfilename(title="Will Save File with '.txt'",defaultextension=".txt")
#    filename = filename + '.txt'
    target1 = open(filename,'w')
    outString = 'arrays: DateArr[500](0),TradeArr[500](0),PriceArr[500](0);\n'
    target1.write(outString)
    for x in range(0,cnt):
        if tradeType[x] == "Sell Short": tradeType[x] = "SS"
        if tradeType[x] == "Buy": tradeType[x] = "B"
        if tradeType[x] == "Buy to Cover": tradeType[x] = "SX"
        if tradeType[x] == "Sell": tradeType[x] = "LX"
        outString = 'DateArr['+str(x)+']='+str(tradeDate[x]-19000000)+';TradeArr['+str(x)+']="'+tradeType[x]+'";PriceArr['+str(x)+']='+str(tradePrice[x])+';\n'
        target1.write(outString)
    target1.close


if __name__ == '__main__':
    main()
Python Script Open, Read, Convert and Write A File Using TK Dialogs

And here is the EasyLanguage code that will step through the arrays and place the trades accordingly.  I noticed that sometimes two trades could occur on the same bar, but only two and you will notice in the code where I programmed this occurrence.

vars: cnt(0);

If date of tomorrow = DateArr[cnt] then
Begin
	print("Inside: ",d," ",dateArr[cnt]);
	If tradeArr[cnt] = "B" then
	begin
		buy next bar at PriceArr[cnt] stop;
	end;
	If tradeArr[cnt] = "LX" then
	begin
		sell next bar at PriceArr[cnt] stop;
	end;
		If tradeArr[cnt] = "SS" then
	begin
		sellShort next bar at PriceArr[cnt] stop;
	end;
	If tradeArr[cnt] = "SX" then
	begin
		buyToCover next bar at PriceArr[cnt] stop;
	end;
	cnt = cnt + 1;
	If DateArr[cnt] = DateArr[cnt-1] then
	Begin
		print("two trades same day ",d," ",dateArr[cnt]);
		If tradeArr[cnt] = "B" then
		begin
			buy next bar at PriceArr[cnt] stop;
		end;
		If tradeArr[cnt] = "LX" then
		begin
			sell next bar at PriceArr[cnt] stop;
		end;
		If tradeArr[cnt] = "SS" then
		begin
	    	print("looking to go short at ",PriceArr[cnt]);
			sellShort next bar at PriceArr[cnt] stop;
		end;
		If tradeArr[cnt] = "SX" then
		begin
			buyToCover next bar at PriceArr[cnt] stop;
		end;
		cnt = cnt + 1;
	end;	
end;
EasyLanguage Snippet To Execute Trades Stored in Arrays

 

 

Please follow and like us:

Updated Pattern Smasher in EasyLanguage

Update To Original Pattern Smasher

What will you learn : string manipulation, for-loops, optimization

Before proceeding I would suggest reading my original post on this subject.    If you believe the relationship of the last few bars of data can help determine future market direction, then this post will be in you wheel house.  Another added benefit is that you will also learn some cool EasyLanguage.

Original post was limited to four day patterns!

This version is limitless (well not really, but pretty close).  Let’s stick with the original string pattern nomenclature (+ + – – : two up closes followed by two down closes.)  Let’s also stick with our binary pattern representation:

Pattern # 2^3 2^2 2^1 1
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 1

Remember a 0 represents a down close and a 1 represents an up close.  We will deviate from the original post by doing away with the array and stick with only strings (which are really just arrays of characters.)  This way we won’t have to worry about array manipulation.

How to create a dynamic length string pattern

This was the difficult part of the programming.  I wanted to be able to optimize 3, 4 and 5 day patterns and I wanted to control this with using just inputs.  I discovered that pattern three is different in a three day pattern than it is in a four day pattern: in a three day pattern it is 011 or – + + and in a four day pattern it is 0011 or – – + +.  Since I am counting 0’s as down closes, pattern #3 depends on the ultimate size of the pattern string.  No worries I will have eventually have another version where I utilize a different value for down closes and we can then have holes in our string patterns.  But I digress – so to differentiate the patterns based on the pattern length I included a maxPatternLen input.  So if maxPatternLen is three and we are trying to match pattern #3 then we will be looking for 011 and not 0011.  That was an easy fix.  But then I wanted to build a string pattern based on this input and the pattern number dynamically.  Here is some psuedo code on how I figured it out.


{Psuedo code to translate pattern number into binary number}
patternNumber = 3
maxPatternLen = 3

numBits = 0    						// stick with binary representation
testValue = 0						// temporary test value
numBits = maxPatternLen-1  			// how many bits will it take to get to the
									// center of - or numBits to represent max
									// number of patterns or 2^numBits
currentBit =numBits					// start wit current bit as total numBits

value1 = patternOptTest				// value1 represents current pattern number
testString = ""  					// build test string from ground up


for icnt = numBits downto 0			//building string from left to right
begin       						// notice keyword downto
	if power(2,currentBit) > value1 then  // must use power function in EL
	begin							// if the very far left bit value > 
		testString = testString + "-"	  // patten number then plug in a "-"
	end
	else
	begin							// else plug in a "+" and deccrement by
		testString = testString + "+"	 // that bits value - if its the 3rd bit
	value1 = value1 - power(2,currentBit)// then decrement by 8
	end;
	currentBit = currentBit - 1		// move onto the next bit to the right
end;
Pseudocode for Binary Representation of Pattern #

Now if you want to optimize then you must make sure your pattern number search space or range can be contained within maxPatternLen.  For example, if you want to test all the different combinations of a four day pattern, then your maxPatternLen would naturally be four and you would optimize the pattern number from 0 to 15.  Don’t use 1-16 as I use zero as the base.  A five day pattern would include the search space from 0 – 31.  The rest of the code was basically hacked from my original post.   Here is the rest of the code to do optimizations on different length pattern strings.  Notice how I use strings, for-loops and comparisons.

input: buyPattern("+++-"),sellPattern("---+"),patternOptimize(True),patternOptTest(7),maxPatternLen(3),patternOptBuySell(1),
	   stopLoss$(2000),profitTarg$(2000),holdDays(5);
vars: buyPatternString(""),sellPatternString(""),buyPatternMatch(""),sellPatternMatch(""),numBits(0),testValue(0),currentBit(0),
      remainder(0),value(0),icnt(0),testString(""),numCharsInBuyPattern(0),numCharsInSellPattern(0);
vars:okToBuy(false),okToSell(false);

buyPatternMatch = buyPattern;
sellPatternMatch = sellPattern;
numCharsInBuyPattern = strLen(buyPatternMatch);
numCharsInSellPattern = strLen(sellPatternMatch);

If patternOptimize then
begin
	numBits = 0;
    testValue = 0;
    value = maxPatternLen;
    numBits = maxPatternLen-1;  
    currentBit =numBits;
    remainder = patternOptTest;
    testString = "";
    for icnt = numBits downto 0
    begin       
        if power(2,currentBit) > value1 then
        begin
            testString = testString + "-";
        end
        else
        begin
            testString = testString + "+";
            remainder = remainder - power(2,currentBit);
        end;
        currentBit = currentBit - 1;
	end;
	numCharsInBuyPattern = maxPatternLen;
	numCharsInSellPattern = maxPatternLen;
	if patternOptBuySell = 1 then
	Begin
		buyPatternMatch = testString;
		sellPatternMatch = "0";
	end;
	If patternOptBuySell = 2 then
	Begin
		buyPatternMatch = "0";
		sellPatternMatch = testString;
	end;
end;
	

buyPatternString = "";
sellPatternString = "";

For icnt = numCharsInBuyPattern-1 downto 0
Begin
	If close[icnt] >= close[icnt+1] then buyPatternString = buyPatternString + "+";
	If close[icnt] < close[icnt+1] then buyPatternString = buyPatternString + "-";
end;
For icnt = numCharsInSellPattern-1 downto 0
Begin
	If close[icnt] >= close[icnt+1] then sellPatternString = sellPatternString + "+";
	If close[icnt] < close[icnt+1] then sellPatternString = sellPatternString + "-";
end;


okToBuy = false;
okToSell = false;

if buyPatternMatch <> "" then
	If buyPatternString = buyPatternMatch then okToBuy = true;
If buyPatternMatch = "" then
	okToBuy = true;
If sellPattern <> "" then
	If sellPatternString = sellPatternMatch then okToSell = true;
If sellPatternMatch = "" then
	okToSell = true;
	
If okToBuy then buy next bar at open;
If okToSell then sellshort next bar at open;

If marketPosition = 1 and barsSinceEntry > holdDays then sell next bar at open;
If marketPosition = -1 and barsSinceEntry > holdDays then buytocover next bar at open;

setStopLoss(stopLoss$);
setProfitTarget(profitTarg$);

If lastBarOnChart then print(d," ",buyPatternMatch);
Final Version of New Pattern Smasher

Also see how I incorporate a profit target and protective stop.  I use the built in BarsSinceEntry function to count the number of days I am in a trade so I can utilize a time based exit.  Here is an interesting equity curve I developed using a two day pattern ( – –) to go long.

Register on the website and I will email you an ELD of the improved Pattern Smasher.  Or just shoot me an email.

 

 

Please follow and like us: