# Happy New Year!  My First Post of 2021!

In this post I simply wanted to convert the intraday ratcheting stop mechanism that I previously posted into a daily bar mechanism.  Well that got me thinking of how many different values could be used as the amount to ratchet.  I came up with three:

I have had requests for the EasyLanguage in an ELD – so here it is – just click on the link and unZip.

RATCHETINGSTOPWSWITCH

## Ratcheting Schemes

• ATR of N days
• Fixed \$ Amount
• Percentage of Standard Deviation of 20 Days

So this was going to be a great start to a post, because I was going to incorporate one of my favorite programming constructs : Switch-Case.  After doing the program I thought wouldn’t it be really cool to be able to optimize over each scheme the ratchet and trail multiplier as well as the values that might go into each scheme.

In scheme one I wanted to optimize the N days for the ATR calculation.  In scheme two I wanted to optimize the \$ amount and the scheme three the percentage of a 20 day standard deviation.  I could do a stepwise optimization and run three independent optimizations – one for each scheme.  Why not just do one global optimization you might ask?  You could but it would be a waste of computer time and then you would have to sift through the results.  Huh?  Why?  Here is a typical optimization loop:

 Scheme Ratchet Mult Trigger Mult Parameter 1 1 : ATR 1 1 ATR (2) 2 : \$ Amt 1 1 ATR (2) 3 : % of Dev. Amt 1 1 ATR (2) 1 : ATR 2 1 ATR (2) 2 : \$ Amt 2 1 ATR (2)

Notice when we switch schemes the Parameter 1 doesn’t make sense.  When we switch to \$ Amt we want to use a \$ Value as Parameter 1 and not ATR.  So we could do a bunch of optimizations across non sensical values, but that wouldn’t really make a lot of sense.  Why not do a conditional optimization?  In other words, optimize only across a certain parameter range based on which scheme is currently being used.  I knew there wasn’t an overlay available to use using standard EasyLanguage but I thought maybe OOP,  and there is an optimization API that is quite powerful.  The only problem is that it was very complicated and I don’t know if I could get it to work exactly the way I wanted.

EasyLanguage is almost a full blown programming language.  So should I not be able to distill this conditional optimization down to something that I could do with such a powerful programming language?  And the answer is yes and its not that complicated.  Well at least for me it wasn’t but for beginners probably.  But to become a successful programmer you have to step outside your comfort zones, so I am going to not only explain the Switch/Case construct (I have done this in earlier posts)  but incorporate some array stuff.

When performing conditional optimization there are really just a few things you have to predefine:

1. Scheme Based Optimization Parameters
2. Exact Same Number of Iterations for each Scheme [starting point and increment value]
3. Complete Search Space
4. Total Number of Iterations
5. Staying inside the bounds of your Search Space

Here are the optimization range per scheme:

• Scheme #1 – optimize number of days in ATR calculation – starting at 10 days and incrementing by 2 days
• Scheme #2 – optimize \$ amounts – starting at \$250 and incrementing by \$100
• Scheme #3 – optimize percent of 20 Bar standard deviation – starting at 0,25 and incrementing by 0.25

I also wanted to optimize the ratchet and target multiplier.  Here is the base code for the daily bar ratcheting system with three different schemes.  Entries are based on penetration of 20 bar highest/lowest close.

``inputs: ratchetMult(2),trailMult(2),volBase(True),volCalcLen(20),dollarBase(False),dollarAmt(250),devBase(False),devAmt(0.25);vars:longMult(0),shortMult(0);vars:ratchetAmt(0),trailAmt(0);vars:stb(0),sts(0),mp(0);vars:lep(0),sep(0);if volBase then begin	ratchetAmt = avgTrueRange(volCalcLen) * ratchetMult;	trailAmt = avgTrueRange(volCalcLen) * trailMult;end;if dollarBase then begin	ratchetAmt =dollarAmt/bigPointValue * ratchetMult;	trailAmt = dollarAmt/bigPointValue * trailMult;end;if devBase then begin	ratchetAmt = stddev(c,20) * devAmt * ratchetMult;	trailAmt = stddev(c,20) * devAmt * trailMult;end;if c crosses over highest(c[1],20) then buy next bar at open;if c crosses under lowest(c[1],20) then sellshort next bar at open;mp = marketPosition;if mp <> 0 and mp[1] <> mp thenbegin	longMult = 0;	shortMult = 0;end;If mp = 1 then lep = entryPrice;If mp =-1 then sep = entryPrice;// Okay initially you want a X point stop and then pull the stop up// or down once price exceeds a multiple of Y points// longMult keeps track of the number of Y point multiples of profit// always key off of lep(LONG ENTRY POINT)// notice how I used + 1 to determine profit// and -  1 to determine stop levelIf mp = 1 then Begin	If h >= lep + (longMult + 1) * ratchetAmt then	longMult = longMult + 1;	Sell("LongTrail") next bar at (lep + (longMult - 1) *  trailAmt) stop;end;If mp = -1 then Begin	If l <= sep - (shortMult + 1) * ratchetAmt then	shortMult = shortMult + 1;	buyToCover("ShortTrail") next bar (sep - (shortMult - 1) *  trailAmt) stop;end;``
Daily Bar Ratchet System

This code is fairly simple.  The intriguing inputs are:

• volBase [True of False] and  volCalcLen [numeric Value]
• dollarBase [True of False] and  dollarAmt [numeric Value]
• devBase [True of False] and devAmt [numeric Value]

If volBase is true then you use the parameters that go along with that scheme.  The same goes for the other schemes.  So when you run this you would turn one scheme on at a time and set the parameters accordingly.  if I wanted to use dollarBase(True) then I would set the dollarAmt to a \$ value.  The ratcheting mechanism is the same as it was in the prior post so I refer you back to that one for further explanation.

So this was a pretty straightforward strategy.  Let us plan out our optimization search space based on the different ranges for each scheme.  Since each scheme uses a different calculation we can’t simply optimize across all of the different ranges – one is days, and the other two are dollars and percentages.

## Enumerate

We know how to make TradeStation loop based on the range of a value.  If you want to optimize from \$250 to \$1000 in steps of \$250, you know this involves [\$1000 – \$250] / \$250 + 1 or 3 + 1 or 4 interations.   Four loops will cover this entire search space.  Let’s examine the search space for each scheme:

• ATR Scheme: start at 10 bars and end at 40 by steps of 2 or [40-10]/2 + 1 = 16
• \$ Amount Scheme: start at \$250 and since we have to have 16 iterations [remember # of iterations have to be the same for each scheme] what can we do to use this information?  Well if we start \$250 and step by \$100 we cover the search space \$250, \$350, \$450, \$550…\$1,750.  \$250 + 15 x 250.  15 because \$250 is iteration 1.
• Percentage StdDev Scheme:  start at 0.25 and end at 0.25 + 15 x 0.25  = 4

So we enumerate 16 iterations to a different value.  The easiest way to do this is to create a map.  I know this seems to be getting hairy but it really isn’t.  The map will be defined as an array with 16 elements.  The array will be filled with the search space based on which scheme is currently being tested.  Take a look at this code where I show how to define an array of 16 elements and introduce my Switch/Case construct.

``array: optVals[16](0);switch(switchMode)begin	case 1:		startPoint = 10; // vol based		increment = 2;	case 2:		startPoint = 250/bigPointValue; // \$ based		increment = 100/bigPointValue;	case 3:		startPoint = 0.25; //standard dev		increment = 0.25*minMove/priceScale;	default:		startPoint = 1;		increment = 1;end;vars: cnt(0),loopCnt(0);once begin	for cnt = 1 to 16 	begin		optVals[cnt] = startPoint + (cnt-1) * increment;	end;end``
Set Up Complete Search Space for all Three Schemes

This code creates a 16 element array, optVals, and assigns 0 to each element.  SwitchMode goes from 1 to 3.

• if switchMode is 1: ATR scheme [case: 1] the startPoint is set to 10 and increment is set to 2
• if switchMode is 2: \$ Amt scheme [case: 2] the startPoint is set to \$250 and increment is set to \$100
• if switchMode is 3: Percentage of StdDev [case: 3] the startPoint is set to 0.25 and the increment is set to 0.25

Once these two values are set the following 15 values can be spawned by the these two.  A for loop is great for populating our search space.  Notice I wrap this code with ONCE – remember ONCE  is only executed at the very beginning of each iteration or run.

once
begin
for cnt = 1 to 16
begin
optVals[cnt] = startPoint + (cnt-1) * increment;
end;
end

Based on startPoint and increment the entire search space is filled out.  Now all you have to do is extract this information stored in the array based on the iteration number.

``Switch(switchMode) Begin	Case 1:		ratchetAmt = avgTrueRange(optVals[optLoops]) * ratchetMult;		trailAmt = avgTrueRange(optVals[optLoops]) * trailMult;		Case 2:		ratchetAmt =optVals[optLoops] * ratchetMult;		trailAmt = optVals[optLoops] * trailMult;	Case 3: 		ratchetAmt =stddev(c,20) * optVals[optLoops] * ratchetMult;		trailAmt = stddev(c,20) * optVals[optLoops] * trailMult;	Default:		ratchetAmt = avgTrueRange(optVals[optLoops]) * ratchetMult;		trailAmt = avgTrueRange(optVals[optLoops]) * trailMult;end;if c crosses over highest(c[1],20) then buy next bar at open;if c crosses under lowest(c[1],20) then sellshort next bar at open;mp = marketPosition;if mp <> 0 and mp[1] <> mp thenbegin	longMult = 0;	shortMult = 0;end;If mp = 1 then lep = entryPrice;If mp =-1 then sep = entryPrice;// Okay initially you want a X point stop and then pull the stop up// or down once price exceeds a multiple of Y points// longMult keeps track of the number of Y point multiples of profit// always key off of lep(LONG ENTRY POINT)// notice how I used + 1 to determine profit// and -  1 to determine stop levelIf mp = 1 then Begin	If h >= lep + (longMult + 1) * ratchetAmt then longMult = longMult + 1;	Sell("LongTrail") next bar at (lep + (longMult - 1) *  trailAmt) stop;end;If mp = -1 then Begin	If l <= sep - (shortMult + 1) * ratchetAmt then	shortMult = shortMult + 1;	buyToCover("ShortTrail") next bar (sep - (shortMult - 1) *  trailAmt) stop;end;``
Extract Search Space Values and Rest of Code

Switch(switchMode)
Begin
Case 1:
ratchetAmt = avgTrueRange(optVals[optLoops])ratchetMult;
trailAmt = avgTrueRange(optVals[optLoops]) trailMult;
Case 2:
ratchetAmt =optVals[optLoops] * ratchetMult;
trailAmt = optVals[optLoops] * trailMult;
Case 3:
ratchetAmt =stddev(c,20)optVals[optLoops]
ratchetMult;
trailAmt = stddev(c,20) * optVals[optLoops] * trailMult;

Notice how the optVals are indexed by optLoops.  So the only variable that is optimized is the optLoops and it spans 1 through 16.  This is the power of enumerations – each number represents a different thing and this is how you can control which variables are optimized in terms of another optimized variable.   Here is my optimization specifications:

And here are the results:

The best combination was scheme 1 [N-day ATR Calculation] using a 2 Mult Ratchet and 1 Mult Trail Trigger.  The best N-day was optVals[2] for this scheme.  What in the world is this value?  Well you will need to back engineer a little bit here.  The starting point for this scheme was 10 and the increment was 2 so if optVals[1] =10 then optVals[2] = 12 or ATR(12).    You can also print out a map of the search spaces.

``vars: cnt(0),loopCnt(0);once begin	loopCnt = loopCnt + 1;//	print(switchMode," : ",d," ",startPoint);//	print("  ",loopCnt:2:0,"  --------------------");	for cnt = 1 to 16 	begin		optVals[cnt] = startPoint + (cnt-1) * increment;//		print(cnt," ",optVals[cnt]," ",cnt-1);	end;end;	``
``  Scheme 1  --------------------   1.00   10.00    0.00 10 days   2.00   12.00    1.00   3.00   14.00    2.00   4.00   16.00    3.00   5.00   18.00    4.00   6.00   20.00    5.00   7.00   22.00    6.00   8.00   24.00    7.00   9.00   26.00    8.00  10.00   28.00    9.00  11.00   30.00   10.00  12.00   32.00   11.00  13.00   34.00   12.00  14.00   36.00   13.00  15.00   38.00   14.00  16.00   40.00   15.00   Scheme2  --------------------   1.00    5.00    0.00 \$ 250   2.00    7.00    1.00 \$ 350   3.00    9.00    2.00 \$ 400   4.00   11.00    3.00 \$ ---   5.00   13.00    4.00   6.00   15.00    5.00   7.00   17.00    6.00   8.00   19.00    7.00   9.00   21.00    8.00  10.00   23.00    9.00  11.00   25.00   10.00  12.00   27.00   11.00  13.00   29.00   12.00  14.00   31.00   13.00  15.00   33.00   14.00  16.00   35.00   15.00 \$1750  Scheme 3  --------------------   1.00    0.25    0.00 25 % stdDev   2.00    0.50    1.00   3.00    0.75    2.00   4.00    1.00    3.00   5.00    1.25    4.00   6.00    1.50    5.00   7.00    1.75    6.00   8.00    2.00    7.00   9.00    2.25    8.00  10.00    2.50    9.00  11.00    2.75   10.00  12.00    3.00   11.00  13.00    3.25   12.00  14.00    3.50   13.00  15.00    3.75   14.00  16.00    4.00   15.00``

This was a elaborate post so please email me with questions.  I wanted to demonstrate that we can accomplish very sophisticated things with just the pure and raw EasyLanguage which is a programming language itself.

# The System

This system has been around for several years.  Its based on the belief that fund managers start pouring money into the market near the end of the month and this creates momentum that lasts for just a few days.  The original system states to enter the market on the close of the last bar of the day if the its above a certain moving average value.  In the Jaekle and Tomasini book, the authors describe such a trading system.  Its quite simple, enter on the close of the month if its greater than X-Day moving average and exit either 4 days later or if during the trade the closing price drops below the X-Day moving average.

## EasyLanguage or Multi-Charts Version

Determining the end of the month should be quite easy -right?  Well if you want to use EasyLanguage on TradeStation and I think on Multi-Charts you can’t sneak a peek at the next bar’s open to determine if the current bar is the last bar of the month.  You can try, but you will receive an error message that you can’t mix this bar on close with next bar.  In other words you can’t take action on today’s close if tomorrow’s bar is the first day of the month.  This is designed, I think, to prevent from future leak or cheating.  In TradeStation the shift from backtesting to trading is designed to be a no brainer, but this does provide some obstacles when you only want to do a backtest.

### LDOM function – last day of month for past 15 years or so

So I had to create a LastDayOfMonth function.  At first I thought if the day of the month is the 31st then it is definitely the last bar of the month.  And this is the case no matter what.  And if its the 30th then its the last day of the month too if the month is April, June, Sept, and November.  But what happens if the last day of the month falls on a weekend.  Then if its the 28th and its a Friday and the month is blah, blah, blah.  What about February?  To save time here is the code:

``Inputs: movAvgPeriods(50);vars: endOfMonth(false),theDayOfWeek(0),theMonth(0),theDayOfMonth(0),isLeapYear(False);endOfMonth = false;theDayOfWeek = dayOfWeek(date);theMonth = month(date);theDayOfMonth = dayOfMonth(date);isLeapYear = mod(year(d),4) = 0;// 29th of the month and a Fridayif theDayOfMonth = 29 and theDayOfWeek = 5 then 	endOfMonth = True;// 30th of the month and a Fridayif theDayOfMonth = 30 and theDayOfWeek = 5 then 	endOfMonth = True;// 31st of the month 	if theDayOfMonth = 31 then 	endOfMonth = True;// 30th of the month and April, June, Sept, or Novif theDayOfMonth = 30 and (theMonth=4 or theMonth=6 or theMonth=9 or theMonth=11) then 	endOfMonth = True;// 28th of the month and February and not leap yearif theDayOfMonth = 28 and theMonth = 2 and not(isLeapYear)  then 	endOfMonth = True;// 29th of the month and February and a leap year or 28th, 27th and a Friday	if theMonth = 2 and isLeapYear thenBegin	If theDayOfMonth = 29 or ((theDayOfMonth = 28 or theDayOfMonth = 27) and theDayOfWeek = 5) then 	endOfMonth = True;	end;// 28th of the month and Friday and April, June, Sept, or Novif theDayOfMonth = 28 and (theMonth = 4 or theMonth = 6 or 	theMonth = 9 or theMonth =11) and theDayOfWeek = 5 then	endOfMonth = True;// 27th, 28th of Feb and Friday	if theMonth = 2 and theDayOfWeek = 5 and theDayOfMonth = 27 then	endOfMonth = True;// 26th of Feb and Friday and not LeapYearif theMonth = 2 and theDayOfWeek = 5 and theDayOfMonth = 26 and not(isLeapYear) then	endOfMonth = True;	// Memorial day adjustmentIf theMonth = 5 and theDayOfWeek = 5 and theDayOfMonth = 28 then	endOfMonth = True;//Easter 2013 adjustmentIf theMonth = 3 and year(d) = 113 and theDayOfMonth = 28 then	endOfMonth = True;//Easter 2018 adjustmentIf theMonth = 3 and year(d) = 118 and theDayOfMonth = 29 then	endOfMonth = True;	if endOfMonth and c > average(c,movAvgPeriods) then		Buy("BuyDay") this bar on close;If C <average(c,movAvgPeriods) then 	Sell("MovAvgExit") this bar on close;If BarsSinceEntry=4 then 	Sell("4days") this bar on close;``
Last Day Of Month Function and Strategy

All the code is generic except for the hard code for days that are a consequence of Good Friday.

All this code because I couldn’t sneak a peek at the date of tomorrow.  Here are the results of trading the ES futures sans execution costs for the past 15 years.

What if it did the easy way and executed the open of the first bar of the month.

``If c > average(c,50) and month(d) <> month(d of tomorrow) then 	buy next bar at open;If  barsSinceEntry >=3 then 	sell next bar at open;If marketPosition = 1 and c < average(c,50) then 	sell next bar at open;``

The results aren’t as good but it sure was easier to program.