Category Archives: Free EasyLanguage

The Complete Turtle EasyLanguage [Well About as Close as You Can Get]

The Complete Turtle EasyLanguage – Almost!

I have seen a plethora of posts on the Turtle trading strategies where the rules and code are provided.  The codes range from a mere Donchian breakout to a fairly close representation.  Without dynamic portfolio feedback its rather impossible to program the portfolio constraints as explained by Curtis Faith in his well received “Way Of The Turtle.”  But the other components can be programmed rather closely to Curtis’ descriptions.   I wanted to provide this code in a concise manner to illustrate some of EasyLanguage’s esoteric constructs and some neat shortcuts.  First of all let’s take a look at how the system has performed on Crude for the past 15 years.

Turtle Performance on Crude past 15 years

If a market trends, the Turtle will catch it.  Look how the market rallied in 2007 and immediately snapped back in 2008, and look at how the Turtle caught the moves – impressive.  But see how the system stops working in congestion.  It did take a small portion of the 2014 move down and has done a great job of catching the pandemic collapse and bounce.  In my last post, I programmed the LTL (Last Trader Loser) function to determine the success/failure of the Turtle System 1 entry.  I modified it slightly for this post and used it in concert with Turtle System 2 Entry and the 1/2N AddOn pyramid trade to get as close as possible to the core of the Turtle Entry/Exit logic.

Can Your Program This – sure you CAN!

Can You Program This?

I will provide the ELD so you can review at your leisure, but here are the important pieces of the code that you might not be able to derive without a lot of programming experience.

If mp[1] <> mp and mp <> 0 then 
begin
	if mp = 1 then
	begin	
		origEntry = entryPrice;
		origEntryName = "Sys1Long";
		If ltl = False and h >= lep1[1] then origEntryName = "Sys2Long";
	end;
	if mp =-1 then
	begin	
		origEntry = entryPrice;
		origEntryName = "Sys1Short";
		If ltl = False and l <= sep1[1] then origEntryName = "Sys2Short";
	end;
end;
Keeping Track Of Last Entry Signal Price and Name

This code determines if the current market position is not flat and is different than the prior bar’s market position.  If this is the case then a new trade has been executed.  This information is needed so that you know which exit to apply without having to forcibly tie them together using EasyLanguage’s from Entry keywords.  Here I just need to know the name of the entry.  The entryPrice is the entryPrice.  Here I know if the LTL is false, and the entryPrice is equal to or greater/less  than (based on current market position) than System 2 entry levels, then I know that System 2 got us into the trade.

If mp = 1 and origEntryName = "Sys1Long" then Sell currentShares shares next bar at lxp stop;
If mp =-1 and origEntryName = "Sys1Short" then buyToCover currentShares shares next bar at sxp stop;

//55 bar component - no contingency here
If mp = 0 and ltl = False then buy("55BBO") next bar at lep1 stop;
If mp = 1 and origEntryName = "Sys2Long" then sell("55BBO-Lx") currentShares shares next bar at lxp1 stop;

If mp = 0 and ltl = False then sellShort("55SBO") next bar at sep1 stop;
If mp =-1 and origEntryName = "Sys2Short" then buyToCover("55SBO-Sx") currentShares shares next bar at sxp1 stop;
Entries and Exits

The key to this logic is the keywords currentShares shares.  This code tells TradeStation to liquidate all the current shares or contracts at the stop levels.  You could use currentContracts contracts if you are more comfortable with futures vernacular.

AddOn Pyramiding Signal Logic

Before you can pyramid you must turn it on in the Strategy Properties.

Turn Pyramiding ON
If mp = 1 and currentShares < 4 then buy("AddonBuy") next bar at entryPrice + (currentShares * .5*NValue) stop;
If mp =-1 and currentShares < 4 then sellShort("AddonShort") next bar at entryPrice - (currentShares * .5*NValue) stop;

This logic adds positions on from the original entryPrice in increments of 1/2N.  The description for this logic is a little fuzzy.  Is the N value the ATR reading when the first contract was put on or is it dynamically recalculated?  I erred on the side of caution and used the N when the first contract was put on.  So to calculate the AddOn long entries you simply take the original entryPrice and add the currentShares * .5N.  So if currentShares is 1, then the next pyramid level would be entryPrice + 1* .5N.  If currentShares is 2 ,then entryPrice + 2* .5N and so on an so forth.  The 2N stop trails from the latest entryPrice.  So if you put on 4 contracts (specified in Curtis’ book), then the trailing exit would be 2N from where you added the 4th contract.  Here is the code for that.

Liquidate All Contracts at Last Entry –  2N

vars: lastEntryPrice(0);
If cs <= 1 then lastEntryPrice = entryPrice;
If cs > 1 and cs > cs[1] and mp = 1 then lastEntryPrice = entryPrice + ((currentShares-1) * .5*NValue);
If cs > 1 and cs > cs[1] and mp =-1 then lastEntryPrice = entryPrice - ((currentShares-1) * .5*NValue);

//If mp = -1 then print(d," ",lastEntryPrice," ",NValue);

If mp = 1 then sell("2NLongLoss") currentShares shares next bar at lastEntryPrice-2*NValue stop;
If mp =-1 then buyToCover("2NShrtLoss") currentShares shares next bar at lastEntryPrice+2*NValue Stop;
Calculate Last EntryPrice and Go From There

I introduce a new variable here: cs.  CS stands for currentShares and I keep track of it from bar to bar.  If currentShares or cs is less than or equal to1 I know that the last entryPrice was the original entryPrice.  Things get a little more complicated when you start adding positions – initially I couldn’t remember if EasyLanguage’s entryPrice contained the last entryPrice or the original – turns out it is the original – good to know.  So, if currentShares is greater than one and the current bar’s currentShares is greater than the prior bar’s currentShares, then I know I added on another contract and therefore must update lastEntryPrice.  LastEntryPrice is calculated by taking the original entryPrice and adding (currentShares-1) * .5N.  Now this is the theoretical entryPrice, because I don’t take into consideration slippage on entry.  You could make this adjustment.  So, once I know the lastEntryPrice I can determine 2N from that price.

Getting Out At 2N Trailing Stop

If mp = 1 then sell("2NLongLoss") currentShares shares next bar at lastEntryPrice-2*NValue stop;
If mp =-1 then buyToCover("2NShrtLoss") currentShares shares next bar at lastEntryPrice+2*NValue Stop;
Get Out At LastEntryPrice +/-2N

That’s all of the nifty code.  Below is the function and ELD for my implementation of the Turtle dual entry system.   You will see some rather sophisticated code when it comes to System 1 Entry and this is because of these scenarios:

  • What if you are theoretically short and are theoretically stopped out for a true loser and you can enter on the same bar into a long trade.
  • What if you are theoretically short and the reversal point would result in a losing trade.  You wouldn’t  record the loser in time to enter the long position at the reversal point.
  • What if you are really short and the reversal point would results in a true loser, then you would want to allow the reversal at that point

There are probably some other scenarios, but I think I covered all bases.  Just let me know if that’s not the case.  What I did to validate the entries was I programmed a 20/10 day breakout/failure with a 2N stop and then went through the list and deleted the trades that followed a non 2N loss (10 bar exit for a loss or a win.)  Then I made sure those trades were not in the output of the complete system.  There was quite a bit of trial and error.  If you see a mistake, like I said, just let me know.

Remember I published the results of different permutations of this strategy incorporating dynamic portfolio feedback at my other site www.trendfollowingsystems.com.  These results reflect the a fairly close portfolio that Curtis suggests in his book.

TURTLELTLFUNCTEST

 

Please follow and like us:

Implementing Finite State Machine Functionality with EasyLanguage (Last Trade Was Loser Filter a la Turtle)

Last Trade Was a Loser Filter – To Use or Not To Use

Premise

A major component of the Turtle algorithm was to skip the subsequent 20-day break out if the prior was a winner.  I guess Dennis believed the success/failure of a trade had an impact on the outcome of the subsequent trade.  I have written on how you can implement this in EasyLanguage in prior posts, but I have been getting some questions on implementing FSM in trading and thought this post could kill two birds with one stone: 1) provide a template that can be adapted to any LTL mechanism and 2) provide the code/structure of setting up a FSM using EasyLanguage’s Switch/Case structure.

Turtle Specific LTL Logic

The Turtle LTL logic states that a trade is a loser if a 2N loss occurs after entry.  N is basically an exponential-like moving average of TrueRange.  So if the market moves 2N against a long or short position and stops you out, you have a losing trade.  What makes the Turtle algorithm a little more difficult is that you can also exit on a new 10-day low/high depending on your position.  The 10-day trailing exit does not signify a loss.  Well at least in this post it doesn’t.  I have code that says any loss is a loss, but for this explanation let’s just stick to a 2N loss to determine a trade’s failure.

How To Monitor Trades When Skipping Some Of Them

This is another added layer of complexity.  You have to do your own trade accounting behind the scenes to determine if a losing trade occurs.  Because if you have a winning trade you skip the next trade and if you skip it how do you know if it would have been a winner or a loser.  You have to run a theoretical system in parallel with the actual system code.

Okay let’s start out assuming the last trade was a winner.  So we turn real trading off.  As the bars go by we look for a 20-Day high or low penetration.  Assume a new 20-Day high is put in and a long position is established at the prior 20-Day high.  At this point you calculate a 2N amount and subtract if from the theoretical entry price to obtain the theoretical exit price.  So you have a theoMP (marketPosition) and a theoEX (exit price.)  This task seems pretty simple, so you mov on and start looking for a day that either puts in a new 10-Day low or crosses below your theoEX price.  If a new 10-Day low is put in then you continue on looking for a new entry and a subsequent 2N loss.  If a 2N loss occurs, then you turn trading back on and continue monitoring the trades – turning trading off and then back on when necessary.  In the following code I use these variables:

  • state – 0: looking for an entry or 1: looking for an exit
  • lep – long entry price
  • sep– short entry price
  • seekLong – I am seeking a long position
  • seekShort – I am seeking a short position
  • theoMP – theoretical market position
  • theoEX – theoretical exit price
  • lxp – long exit price
  • sxp – short exit price

Let’s jump into the Switch/Case structure when state = 0:

	Switch(state)
	Begin
		Case 0:
			lep = highest(h[1],20) + minMove/priceScale;
			sep = lowest(l[1],20) - minMove/priceScale;
			If seekLong and h >= lep then 
			begin
				theoMP = 1;
				theoEX = maxList(lep,o) - 2 * atr; 
//				print(d," entered long >> exit at ",theoEX," ",atr);
			end;
			If seekShort and l <= sep then 
			begin
				theoMP = -1;
				theoEX = minList(sep,o) + 2 * atr;
			end;
			If theoMP <> 0 then 
			begin
				state = 1;
				cantExitToday = True;
			end;
State 0 (Finite State Set Up)

The Switch/Case is a must have structure in any programming language.  What really blows my mind is that Python doesn’t have it.  They claim its redundant to an if-then structure and it is but its so much easier to read and implement.  Basically you use the Switch statement and a variable name and based on the value of the variable it will flow to whatever case the variable equates to.  Here we are looking at state 0.  In the CASE: 0  structure the computer calculates the lep and sep values – long and short entry levels.  If you are flat then you are seeking a long or a short position.  If the high or low of the bar penetrates it respective trigger levels then theoMP is set to 1 for long or -1 for short.  TheoEX is then calculated based on the atr value on the day of entry.  If theoMP is set to either a 1 or -1, then we know a trade has just been triggered.  The Finite State Machine then switches gears to State 1.  Since State = 1 the next Case statement is immediately evaluated.  I don’t want to exit on the same bar as I entered (wide bars can enter and exit during volatile times) I use a variable cantExitToday.  This variable delays the Case 1: evaluation by one bar.

State = 1 code:

		Case 1:
			If not(cantExitToday) then
			begin
				lxp = maxList(theoEX,lowest(l[1],10)-minMove/priceScale);
				sxp = minList(theoEX,highest(h[1],10)+minMove/priceScale);	
				If theoMP = 1 and l <= lxp then
				begin
					theoMP = 0;
					seekLong = False;
					if lxp <= theoEX then 
						ltl = True
					Else 
						ltl = False;
				end;
				If theoMP =-1 and h >= sxp then
				begin
					theoMP = 0;
					seekShort = False;
					if sxp >= theoEX then 
						ltl = True
					else 
						ltl = False;
				end;
				If theoMP = 0 then state = 0;
			end;
			cantExitToday = False;	
	end;
State = 1 (Switching Gears)

Once we have a theoretical position, then we only examine the code in the Case 1: module.  On the subsequent bar after entry, the lxp and sxp (long exit and short exit prices) are calculated.  Notice these values use maxList or minList to determine whichever is closer to the current market action – the 2N stop or the lowest/highest low/high for the past 10-daysLxp and sxp are assigned whichever is closer.  Each bar’s high or low is compared to these values.  If theoMP = 1 then the low is compared to lxp.  If the low crosses below lxp, then things are set into motion.  The theoMP is immediately set to  0 and seekLong is turned to False.  If lxp <= a 2N loss then ltl (last trade loser) is set to true.  If not, then ltl is set to False.   If theoMP = 0 then we assume a flat position and switch the FSM back to State 0 and start looking for a new trade.  The ltl variable is then used in the code to allow a real trade to occur.

Strategy Incorporates Our FSM Output

vars:N(0),mp(0),NLossAmt(0);
If barNumber = 1 then n = avgTrueRange(20);
if barNumber > 1 then n = (n*19 + trueRange)/20;

If useLTLFilter then
Begin
	if ltl then buy next bar at highest(h,20) + minMove/priceScale stop;
	if ltl then sellShort next bar at lowest(l,20) -minMove/priceScale stop;
end
Else
Begin
	buy next bar at highest(h,20) + minMove/priceScale stop;
	sellShort next bar at lowest(l,20) -minMove/priceScale stop;
end;

mp = marketPosition;

If mp <> 0 and mp[1] <> mp then NLossAmt = 2 * n;

If mp = 1 then
Begin
	Sell("LL10-LX") next bar at lowest(l,10) - minMove/priceScale stop;
	Sell("2NLS-LX") next bar at entryPrice - NLossAmt stop;
end;
If mp =-1 then
Begin
	buyToCover("HH10-SX") next bar at highest(h,10) + minMove/priceScale stop;
	buyToCover("2NLS-SX") next bar at entryPrice + NLossAmt stop;
end;
Strategy Code Using ltl filter

This code basically replicates what we did in the FSM, but places real orders based on the fact that the Last Trade Was A Loser (ltl.)

Does It Work – Only Trade After a 2N-Loss

Last Trade Loser In Action

Without Filter on the last 10-years in Crude Oil

With Filter on the last 10-years in Crude Oil

I have programmed this into my TradingSimula-18 software and will show a portfolio performance with this filter a little later at www.trendfollowingsystems.com.

I had to do some fancy footwork with some of the code due to the fact you can exit and then re-enter on the same bar.  In the next post on this blog I will so you those machinations .  With this template you should be able to recreate any last trade was a loser mechanism and see if it can help out with your own trading algorithms.  Shoot me an email with any questions.

 

 

 

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:

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:

State of Trend Following – Part 1

Clenow’s Trend Following System

Its a new decade! Time to see what’s up with Trend Following.

I am a huge fan of Andreas Clenow’s books, and how he demonstrated that a typical trader could replicate the performance of most large Trend Following CTAs and not pay the 2% / 20% management/incentive combo fees.  So. I felt the system that he described in his book would be a great representation of The State of Trend Following.  At the same time I am going to demonstrate TradingSimula18 (the software included in my latest book).

System Description

Take a look at my last post.  I provide the EasyLanguage and a pretty good description of Clenow’s strategy.

TradingSimula18 Code [Python]

#---------------------------------------------------------------------------------------------------
#  Start programming your great trading ideas below here - don't touch stuff above
#---------------------------------------------------------------------------------------------------
#  Define Long, Short, ExitLong and ExitShort Levels - mind your indentations
            ATR = sAverage(myTrueRange,30,curBar,1)
            posSize = 2000/(ATR*myBPV)
            posSize = max(int(posSize),1)
            posSize = min(posSize,20)
            avg1 = xAverage(myClose,marketVal5[curMarket],50,curBar,1)
            avg2 = xAverage(myClose,marketVal6[curMarket],100,curBar,1)
            marketVal5[curMarket] = avg1
            marketVal6[curMarket] = avg2
            donchHi = highest(myHigh,50,curBar,1)
            donchLo = lowest(myLow,50,curBar,1)

            if mp == 1 : marketVal1[curMarket] = max(marketVal1[curMarket],myHigh[curBar-1]- 3 * ATR)
            if mp ==-1 : marketVal2[curMarket] = min(marketVal2[curMarket],myLow[curBar-1]+ 3 * ATR)
#  Long Entry
            if avg1 > avg2 and myHigh[curBar-1] == donchHi and mp !=1:
                price = myOpen[curBar]
                tradeName = "TFClenowB";numShares = posSize
                marketVal1[curMarket] = price - 3 * ATR
                if mp <= -1:
                profit,curShares,trades = bookTrade(entry,buy,price,myDate[curBar],tradeName,numShares)
                barsSinceEntry = 1
                marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
#  Long Exit
            if mp == 1 and myClose[curBar-1] <= marketVal1[curMarket] and barsSinceEntry > 1:
                price = myOpen[curBar]
                tradeName = "Lxit";numShares = curShares
                profit,curShares,trades = bookTrade(exit,ignore,price,myDate[curBar],tradeName,numShares)
                todaysCTE = profit;barsSinceEntry = 0
                marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
#  Short Entry
            if avg1 < avg2 and myLow[curBar-1] == donchLo and mp !=-1:
                price = myOpen[curBar];numShares = posSize
                marketVal2[curMarket] = price + 3 * ATR
                if mp >= 1:
                tradeName = "TFClenowS"
                profit,curShares,trades = bookTrade(entry,sell,price,myDate[curBar],tradeName,numShares)
                barsSinceEntry = 1
                marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
#  Short Exit
            if mp == -1 and myClose[curBar-1] >= marketVal2[curMarket] and barsSinceEntry > 1:
                price = myOpen[curBar]
                tradeName = "Sxit"; numShares = curShares
                profit,curShares,trades = bookTrade(exit,ignore,price,myDate[curBar],tradeName,numShares)
                todaysCTE = profit;barsSinceEntry = 0
                marketMonitorList[curMarket].setSysMarkTrackingInfo(tradeName,cumuProfit,mp,barsSinceEntry,curShares,trades)
#----------------------------------------------------------------------------------------------------------------------------
# - Do not change code below - trade, portfolio accounting - our great idea should stop here
#----------------------------------------------------------------------------------------------------------------------------
 
TradingSimula18 Python System Testing Environment

I am going to go over this very briefly.   I know that many of the readers of my blog have attempted to use Python and the various packages out there and have given up on it.  Quantopia and QuantConnect are great websites, but I feel they approach back-testing with a programmer in mind.  This was the main reason I created TS-18 – don’t get me wrong its not a walk in the park either, but it doesn’t rely on external libraries to get the job done.  All the reports I show here are generated from the data created solely by TS-18.  Plus it is very modular – Step 1 leads to Step2 and on and on.   Referring to the code I calculate the ATR (average true range) by calling the simple average function sAverage.  I pass it myTrueRanges, 30, curBar and 1.   I am looking for the average true range over the last 30 days.  I then move onto my position sizing – posSize = $2,000 / ATR in $s.  PosSize must fit between 1 and 20 contracts.  The ATR calculation can get rather small for some markets and the posSize can get rather large.  Avg1 and Avg2 are exponential moving averages of length 50 and 100DonchHi and donchLo are the highest high and lowest low of the past 50 days.   If mp == 1 (long position) then a trailing stop (marketVal1) is set to whichever is higher – the current marketVal1 or the yesterday’s High – 3 X ATR;  the trailing stop tracks new intra-trade highs.  The trailing stop for the short side, marketVal2 is calculated in a similar manner, but low prices are used as well as a positive offset of 3 X ATR.  

Now the next section of code is quite a bit different than say EasyLanguage, but parallels some of the online Python paradigms. Here you must test the current bar’s extremes against the donchHi if you are flat and marketVal1 (the trailing stop variable) if you are long.  If flat you also test the low of the bar against donchLo.  The relationship between avg1 and avg2 are also examined.  If the testing criteria is true, then its up to you to assign the correct price, posSize and tradeName.  So you have four independent if-then constructs:

  • Long Entry – if flat test to see if a long position should be initiated
  • Long Exit – if Long then test to see if a liquidation should be initiated
  • Short Entry – if flat test to see if a short position should be initiated
  • Short Exit – if Short then test to see if a liquidation should be initiated

That’s it – all of the other things are handled by TS-18.  Now that I have completely bored you out of your mind, let’s move onto some results.

Results from 2000 – risking $2,000 per trade:

Roller Coaster Ride for most CTAs, Last one out turn off the lights!

Sector Performance from 2000

Sector Performance from 2000

From this chart it doesn’t make much sense to trade MEATS, SOFTS or GRAINS with a Trend Following approach or does it?

In the next post, I will go over the results with more in depth and possibly propose some ideas that might or might not help.  Stay Tuned!

 

Please follow and like us:

Free Trend Following System with Indicator Tracker

Free Trend Following System

Here is a free Trend Following System that I read about on Andreas Clenow’s www.followthetrend.com website and from his book.  This is my interpretation of the rules as they were explained.  However the main impetus behind this post wasn’t to provide a free trading system, but to show how you can program a simple system with a complete input interface and program a tracking indicator.   You might be asking what is a “tracking indicator?”  We use a tracking indicator to help provide insight to what the strategy is doing and what it might do in the near future.  The indicator can let you know that a new signal is imminent and also what the risk is in a graphical form.  The indicator can also plot the indicators that are used in the strategy itself.

Step 1:  Program the Strategy

This system is very simple.  Trade on a 50 day Donchian in the direction of the trend and use a 3 X ATR trailing stop.  So the trend is defined as bullish when the 50-day exponential moving average is greater than the 100-day exponential moving average.  A bearish trend is defined when the 50-day is below the 100-day.  Long positions are initiated on the following day when a new 50 day high has been established and the trend is bullish.  Selling short occurs when the trend is bearish and a new 50 day low is establish.  The initial stop  is set to 3 X ATR below the high of the day of entry.  I tested using a 3 X ATR stop initially from the entryPrice for protection on the day of entry, but it made very little difference.  As the trade moves more into your favor, the trailing stop ratchets up and tracks the higher intra-trade extremes.  Eventually once the market reverses you get stopped out of a long position 3 X ATR from the highest high since you entered the long trade.  Hopefully, with a big winner.   The Clenow model also uses a position sizing equation that uses ATR to determine market risk and $2000 for the allocated amount to risk.  Size= 2000 / ATR – this equation will normalize size across a portfolio of markets.

Here is the code.

//Based on Andreas Clenow's description from www.followingthetrend.com
//This is my interpretation and may or may not be what Andreas intended
//Check his books out at amazon.com
//
inputs: xAvgShortLen(50),xAvgLongLen(100),hhllLen(50),buyTrigPrice(h),shortTrigPrice(l),risk$Alloc(2000);
inputs: atrLen(30),trailATRMult(3);
vars: avg1(0),avg2(0),lXit(0),sXit(0),posSize(0),atr(0);

avg1  = xaverage(c,xAvgShortLen);
avg2  = xaverage(c,xAvgLongLen);

atr = avgTrueRange(atrLen);
posSize = maxList(1,intPortion(risk$Alloc/(atr*bigPointValue)));

If marketPosition <> 1 and avg1 > avg2 and buyTrigPrice = highest(buyTrigPrice,hhllLen) then buy posSize contracts next bar at open;
If marketPosition <> -1 and avg1 < avg2 and shortTrigPrice = lowest(shortTrigPrice,hhllLen) then sellshort posSize contracts next bar at open;

If marketPosition = 0 then
Begin
	lXit = o - trailATRMult * atr ;
	sXit = o + trailATRMult * atr; 
//	if c < lXit then Sell currentcontracts contracts next bar at open;
//	If c > sXit then buyToCover currentcontracts contracts next bar at open;
end;

If marketPosition = 1 then 
begin
	lXit = maxList(lXit,h - trailATRMult * atr);
	If c < lXit then sell currentContracts contracts next bar at open;
end;

If marketPosition = -1 then 
begin
	sXit = minList(sXit,l + trailATRMult * atr);
	If c > sXit then buyToCover currentContracts contracts next bar at open;
end;
Cleanow Simple Trend Following System

What I like about this code is how you can use it as a template for any trend following approach.  All the variables that could be optimized are included as inputs.  Many may not know that you can actually change the data series that you want to use as your signal generator right in the input.  Here I have provided two inputs : buyTrigPrice(H), shortTrigPrice(L).  If you want to use the closing price, then all you need to do is change the H and L to C.  The next lines of code performs the calculations needed to calculate the trend.  PosSize is then calculated next.  Here I am dividing the variable risk$Alloc by atr*bigPointValue.  Basically I am taking $2000 and dividing the average true range over the past 30 days multiplied by the point value of the market being tested.  Always remember when doing calculations with $s you have to convert whatever else you are using into dollars as well.  The ATR is expressed in the form of a price difference.  You can’t divide dollars by a price component, hence the multiplication by bigPointValue.  So now we have the trend calcuation and the position sizing taken care of and all we need now is the trend direction and the entry levels.  If avg1 > avg2 then the market is in a bullish posture, and if today’s High = highest(High,50) days back then initiate a long position with posSize contracts at the next bar’s openNotice how I used the keyword contracts after posSize.  This let’s TS know that I want to trade more than one contract.  If the current position is flat I set the lXit and sXit price levels to the open -/+ 3 X ATR.  Once a position (long or short) is initiated then I start ratcheting the trailing stop up or down.  Assuming a long position, I compare the current lXit and the current bar’s HIGH- 3 X ATR and take the larger of the two valuesSo lXit always moves up and never down.  Notice if the close is less than lXit I used the keyword currentContracts and contracts in the directive to exit a long trade.  CurrentContracts contains the current number of contracts currently long and contracts informs TS that more than one contract is being liquidated.  Getting out of a short position is exactly the same but in a different direction.

Step 2: Program the System Tracking Indicator

Now you can take the exact code and eliminate all the order directives and use it to create a tracking indicator.  Take a look at this code:

//Based on Andreas Clenow's description from www.followingthetrend.com
//This is my interpretation and may or may not be what Andreas intended
//Check his books out at amazon.com
//
inputs: xAvgShortLen(50),xAvgLongLen(100),hhllLen(50),buyTrigPrice(h),shortTrigPrice(l);
inputs: atrLen(30),trailATRMult(3);
vars: avg1(0),avg2(0),lXit(0),sXit(0),posSize(0),atr(0),mp(0);

avg1  = xaverage(c,xAvgShortLen);
avg2  = xaverage(c,xAvgLongLen);

atr = avgTrueRange(atrLen);

plot1(avg1,"stXavg");
plot2(avg2,"ltXavg");

If avg1[1] > avg2[1] and buyTrigPrice[1] = highest(buyTrigPrice[1],hhllLen) then mp = 1;
If avg1[1] < avg2[1] and shortTrigPrice[1] = lowest(shortTrigPrice[1],hhllLen) then mp = -1;

If mp = 0 then
Begin
	lXit = o - trailATRMult * atr ;
	sXit = o + trailATRMult * atr;
end;

If mp = 1 then 
begin
	lXit = maxList(lXit,h - trailATRMult * atr);
	plot3(lXit,"LongTrail");
	If c < lXit then mp = 0;
end;

If mp = -1 then 
begin
	sXit = minList(sXit,l + trailATRMult * atr);
	plot4(sXit,"ShortTrail");
	If c > sXit then mp = 0;
end;

However, you do need to keep track if the underlying strategy is long or short and you can do this by pretending you are the computer and using the mp variable.  You know if yesterdays avg1 > avg2 and HIGH[1] = highestHigh(HIGH[1],50), then a long position should have been initiated.  If this happens just set mp to 1You set mp to -1 by checking the trend and lowestLow(LOW[1],50).  Once you know the mp or implied market position then you can calculate the lXit and sXit.  You will always plot the moving averages to help determine trend direction, but you only plot the lXit and sXit when a position is on.  So plot3 and plot4 should only be plotted when a position is long or short.

Here is a screenshot of the strategy and tracking indicator.

Notice how the Yellow and Cyan plots follow the correct market position.  You will need to tell TS not to connect these plot lines when they are not designed to be plotted.

Turn-Off Auto Plot Line Connection

Do this for Plot3 and Plot4 and you will be good to go.

I hope you found this post useful.  Also don’t forget to check out my new book at Amazon.com.  If you really want to learn programming that will help across different platforms I think it would be a great learning experience.

 

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:

MULTI-TIME FRAME – KEEPING TRACK OF DISCRETE TIME FRAMES

Just a quick post here.  I was asked how to keep track of the opening price for each time frame from our original Multi-Time Frame indicator and I was answering the question when I thought about modifying the indicator.  This version keeps track of each discrete time frame.  The original simply looked back a multiple of the base chart to gather the highest highs and lowest lows and then would do a simple calculation to determine the trend.  So let’s say its 1430 on a five-minute bar and you are looking back at time frame 2.  All I did was get the highest high and lowest low two bars back and stored that information as the high and low of time frame 2.  Time frame 3 simply looked back three bars to gather that information.  However if you tried to compare these values to a 10-minute or 15-minute chart they would not match.

In this version, I use the modulus function to determine the demarcation of each time frame.  If I hit the border of the time frame I reset the open, high, low and carry that value over until I hit the next demarcation.  All the while collecting the highest highs and lowest lows.  In this model, I am working my way from left to right instead of right to left.  And in doing so each time frame is discrete.

Let me know which version you like best.

 

Inputs:tf1Mult(2),tf2Mult(3),tf3Mult(4),tf4Mult(5);



vars: mtf1h(0),mtf1l(0),mtf1o(0),mtf1c(0),mtf1pvt(0),diff1(0),
      mtf2h(0),mtf2l(0),mtf2o(0),mtf2c(0),mtf2pvt(0),diff2(0),
      mtf3h(0),mtf3l(0),mtf3o(0),mtf3c(0),mtf3pvt(0),diff3(0),
      mtf4h(0),mtf4l(0),mtf4o(0),mtf4c(0),mtf4pvt(0),diff4(0),
      mtf0pvt(0),diff0(0);

If barNumber = 1 then
Begin
	mtf1o = o;
	mtf2o = o;
	mtf3o = o;
	mtf4o = o;
end;


If barNumber > 1 then
Begin

	Condition1 =  mod((barNumber+1),tf1Mult) = 0;
	Condition2 =  mod((barNumber+1),tf2Mult) = 0;
	Condition3 =  mod((barNumber+1),tf3Mult) = 0;
	Condition4 =  mod((barNumber+1),tf4Mult) = 0;
	
	mtf1h = iff(not(condition1[1]),maxList(high,mtf1h[1]),high);
	mtf1l = iff(not(condition1[1]),minList(low,mtf1l[1]),low);
	mtf1o = iff(condition1[1],open,mtf1o[1]);
	mtf1c = close;

	
	mtf0pvt = (close + high + low) / 3;
	diff0 = close - mtf0pvt;
	
	mtf2h = iff(not(condition2[1]),maxList(high,mtf2h[1]),high);
	mtf2l = iff(not(condition2[1]),minList(low,mtf2l[1]),low);
	mtf2o = iff(condition2[1],open,mtf2o[1]);
	mtf2c = close;
	
	
	mtf1pvt = (mtf1h+mtf1l+mtf1c) / 3;
	diff1 = mtf1c - mtf1pvt;
		
	mtf2pvt = (mtf2h+mtf2l+mtf2c) / 3;
	diff2 = mtf2c - mtf2pvt;
		
	mtf3h = iff(not(condition3[1]),maxList(high,mtf3h[1]),high);
	mtf3l = iff(not(condition3[1]),minList(low,mtf3l[1]),low);
	mtf3o = iff(condition3[1],open,mtf3o[1]);
	mtf3c = close;
	
	mtf3pvt = (mtf3h+mtf3l+mtf3c) / 3;
	diff3 = mtf3c - mtf3pvt;
	
	mtf4h = iff(not(condition4[1]),maxList(high,mtf4h[1]),high);
	mtf4l = iff(not(condition4[1]),minList(low,mtf4l[1]),low);
	mtf4o = iff(condition4[1],open,mtf4o[1]);
	mtf4c = close;

	mtf4pvt = (mtf4h+mtf4l+mtf4c) / 3;
	diff4 = mtf4c - mtf4pvt;
	
	
	Condition10 = diff0 > 0;
	Condition11 = diff1 > 0;
	Condition12 = diff2 > 0;
	Condition13 = diff3 > 0;
	Condition14 = diff4 > 0;
	 
	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;
Multi-Time Frame with Discrete Time Frames
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: