The last book in the Easing Into EasyLanguage Serieshas finally been put to bed. Unlike the first two books in the series, where the major focus and objective was to introduce basic programming ideas to help get new EasyLanguages users up to speed, this edition introduces more Advanced topics and the code to develop and program them.

Buy this book to learn how to overcome the obstacles that may be holding you back from developing your ideal Analysis Technique. This book could be thousands of pages long because the number of topics could be infinite. The subjects covered in this edition provide a great cross-section of knowledge that can be used further down the road. The tutorials will cover subjects such as:

Arrays – single and multiple dimensions

Functions – creation and communicating via Passed by Value and Passed by Reference

Finite State Machine – implemented via the Switch-Case programming construct

String Manipulation – construction and deconstruction of strings using EasyLanguage functions

Hash Table and Hash Index – a data structure(s) that contains unique addresses of bins that can contain N records

Using Hash Tables – accessing and storing data related to unique Tokens

Token Generation – an individual instance of a type of symbol

Seasonality – in depth analysis of the Ruggiero/Barna and Sheldon Knight Universal Seasonal data

File Manipulation – creating, deleting and writing to external files

Using Projects – organizing Analysis Techniques by grouping support functions and code into a single entity

Text Graphic Objects – extracting text from a chart and storing the object information in arrays for later development into a strategy

Commitment of Traders Report – TradeStation only (not MultiChart compatible) code. Converting the COT indicator and using the FundValue functionality to develop a trading strategy

Multiple Time Frame based indicator – use five discrete time frames and pump the data into a single indicator – “traffic stop light” feel

Once you become a programmer, of any language, you must continually work on honing your craft. This book shows you how to use your knowledge as building blocks to complete some really cool and advanced topics.

Well it’s been a year, this month, that Murray passed away. I was fortunate to work with him on many of his projects and learned quite a bit about inter-market convergence and divergence. Honestly, I wasn’t that into it, but you couldn’t argue with his results. A strategy that he developed in the 1990s that compared the Bond market with silver really did stand the test of time. He monitored this relationship over the years and watched in wane. Murray replaced silver with $UTY.

The PHLX Utility Sector Index (UTY) is a market capitalization-weighted index composed of geographically diverse public utility stocks.

He wrote an article for EasyLanguage Mastery by Jeff Swanson where he discussed this relationship and the development of inter-market strategies and through statistical analysis proved that these relationships added real value.

I am currently writing Advanced Topics, the final book in my Easing Into EasyLanguage trilogy, and have been working with Murray’s research. I am fortunate to have a complete collection of his Futures Magazine articles from the mid 1990s to the mid 2000s. There is a quite a bit of inter-market stuff in his articles. I wanted, as a tribute and to proffer up some neat code, to show the performance and code of his Bond and $UTY inter-market algorithm.

Here is a version that he published a few years ago updated through June 30, 2022 – no commission/slippage.

Not a bad equity curve. To be fair to Murray he did notice the connection between $UTY and the bonds was changing over the past couple of year. And this simple stop and reverse system doesn’t have a protective stop. But it wouldn’t look much different with one, because the system looks at momentum of the primary data and momentum of the secondary data and if they are in synch (either positively or negatively correlated – selected by the algo) an order is fired off. If you simply just add a protective stop, and the momentum of the data are in synch, the strategy will just re-enter on the next bar. However, the equity curve just made a new high recently. It has got on the wrong side of the Fed raising rates. One could argue that this invisible hand has toppled the apple cart and this inter-market relationship has been rendered meaningless.

Murray had evolved his inter-market analysis to include state transitions. He not only looked at the current momentum, but also at where the momentum had been. He assigned the transitions of the momentum for the primary and secondary markets a value from one to four and he felt this state transition helped overcome some of the coupling/decoupling of the inter-market relationship.

However, I wanted to test Murray’s simple strategy with a fixed $ stop and force the primary market to move from positive to negative or negative to positive territory while the secondary market is in the correct relationship. Here is an updated equity curve.

This equity curve was developed by using a $4500 stop loss. Because I changed the order triggers, I reoptimized the length of the momentum calculations for the primary and secondary markets. This curve is only better in the category of maximum draw down. Shouldn’t we give Murray a chance and reoptimize his momentum length calculations too! You bet.

These metrics were sorted by Max Intraday Draw down. The numbers did improve, but look at the Max Losing Trade value. Murray’s later technology, his State Systems, were a great improvement over this basic system. Here is my optimization using a slightly different entry technique and a $4500 protective stop.

This system, using Murray’s overall research, achieved a better Max Draw Down and a much better Max Losing Trade. Here is my code using the template that Murray provided in his articles in Futures Magazine and EasyLanguage Mastery.

// Code by Murray Ruggiero
// adapted by George Pruitt
Inputs: InterSet(2),LSB(0),Type(1),LenTr(4),LenInt(4),Relate(0);
Vars: MarkInd(0),InterInd(0);
If Type=0 Then
Begin
InterInd=Close of Data(InterSet)-CLose[LenInt] of Data(InterSet);
MarkInd=CLose-CLose[LenTr];
end;
If Type=1 Then
Begin
InterInd=Close of Data(InterSet)-Average(CLose of Data(InterSet),LenInt);
MarkInd=CLose-Average(CLose,LenTr);
end;
if Relate=1 then
begin
If InterInd > 0 and MarkInd CROSSES BELOW 0 and LSB>=0 then
Buy("GO--Long") Next Bar at open;
If InterInd < 0 and MarkInd CROSSES ABOVE 0 and LSB<=0 then
Sell Short("GO--Shrt") Next Bar at open;
end;
if Relate=0 then begin
If InterInd<0 and MarkInd CROSSES BELOW 0 and LSB>=0 then
Buy Next Bar at open;
If InterInd>0 and MarkInd CROSSES ABOVE 0 and LSB<=0 then
Sell Short Next Bar at open;
end;

Here the user can actually include more than two data streams on the chart. The InterSet input allows the user to choose or optimize the secondary market data stream. Momentum is defined by two types:

Type 0: Intermarket or secondary momentum simply calculated by close of data(2) – close[LenInt] of date(2) and primary momentum calculated by close – close[LenTr]

Type 1: Intermarket or secondary momentum calculated by close of data(2) – average( close of data2, LenInt) and primary momentum calculated by close – average(close, LenTr)

The user can also input what type of Relationship: 1 for positive correlation and 0 for negative correlation. This template can be used to dig deeper into other market relationships.

George’s Modification

I simply forced the primary market to CROSS below/above 0 to initiate a new trade as long the secondary market was pointing in the right direction.

If InterInd > 0 and MarkInd CROSSES BELOW 0 and LSB>=0 then
Buy("GO--Long") Next Bar at open;
If InterInd < 0 and MarkInd CROSSES ABOVE 0 and LSB<=0 then
Sell Short("GO--Shrt") Next Bar at open;

Using the keyword CROSSES

This was a one STATE transition and also allowed a protective stop to be used without the strategy automatically re-entering the trade in the same direction.

Thank You Murray – we sure do miss you!

Murray loved to share his research and would want us to carry on with it. I will write one or two blogs a year in tribute to Murray and his invaluable research.

SuperTrend is a trading strategy and indicator all built into one entity. There are a couple of versions floating around out there. MultiCharts and Sierra Chart both have slightly different flavors of this combo approach.

Ratcheting Trailing Stop Paradigm

This indic/strat falls into this category of algorithm. The indicator never moves away from your current position like a parabolic stop or chandelier exit. I used the code that was disclosed on Futures.io or formerly known as BigMikesTrading blog. This version differs from the original SuperTrend which used average true range. I like Big Mike’s version so it will discussed here.

Big Mike’s Math

The math for this indicator utilizes volatility in the terms of the distance the market has travelled over the past N days. This is determined by calculating the highest high of the last N days/bars and then subtracting the lowest low of last Ndays/bars. Let’s call this the highLowRange. The next calculation is an exponential moving average of the highLowRange. This value will define the market volatility. Exponential moving averages of the last strength days/bars highs and lows are then calculated and divided by two – giving a midpoint. The volatility measure (multiplied my mult) is then added to this midpoint to calculate an upper band. A lower band is formed by subtracting the volatility measure X mult from the midpoint.

Upper or Lower Channel?

If the closing price penetrates the upper channel and the close is also above the highest high of strength days/bars back (offset by one of course) then the trend will flip to UP. When the trend is UP, then the Lower Channel is plotted. Once the trend flips to DN, the upper channel will be plotted. If the trend is UP the lower channel will either rise with the market or stay put. The same goes for a DN trend – hence the ratcheting. Here is a graphic of the indicator on CL.

If you plan on using an customized indicator in a strategy it is always best to build the calculations inside a function. The function then can be used in either an indicator or a strategy.

Function Name: SuperTrend_BM

Function Type: Series – we will need to access prior variable values

SuperTrend_BM Function Code:

//SuperTrend from Big Mike now futures.io
inputs:
length(NumericSimple), mult(NumericSimple), strength(NumericSimple), STrend(NumericRef);
vars:
highLowRange(0),
xAvgRng(0),
xAvg(0),
dn(0),
up(0),
trend(1),
trendDN(False),
trendUP(False),
ST(0);
highLowRange = Highest(high, length) - Lowest(low, length);
xAvgRng = XAverage(highLowRange, length);
xAvg = (XAverage(high, Strength) + XAverage(low, Strength))/2;
up = xAvg + mult * xAvgRng;
dn = xAvg - mult * xAvgRng;
if c > up[1] and c > Highest(High, strength)[1] then
trend = 1
else
if c < dn[1] and c < Lowest(Low, Strength)[1] then
trend = -1;
//did trend flip?
trendDN = False;
trendUP = False;
if trend < 0 and trend[1] > 0 then
trendDN = True;
if trend > 0 and trend[1] < 0 then
trendUP = True;
//ratcheting mechanism
if trend > 0 then dn = maxList(dn,dn[1]);
if trend < 0 then up = minList(up,up[1]);
// if trend dir. changes then assign
// up and down appropriately
if trendUP then
up = xAvg + mult * xAvgRng;
if trendDN then
dn = xAvg - mult * xAvgRng;
if trend = 1 then
ST = dn
else
ST = up;
STrend = trend;
SuperTrend_BM = ST;

SuperTrend ala Big Mike

The Inputs to the Function

The original SuperTrend did include the Strength input. This input is a Donchian like signal. Not only does the price need to close above/below the upper/lower channel but also the close must be above/below the appropriate Donchian Channels to flip the trend, Also notice we are using a numericRef as the type for STrend. This is done because we need the function to return two values: trend direction and the upper or lower channel value. The appropriate channel value is assigned to the function name and STrend contains the Trend Direction.

A Function Driver in the Form of an Indicator

A function is a sub-program and must be called to be utilized. Here is the indicator code that will plot the values of the function using: length(9), mult(1), strength(9).

// SuperTrend indicator
// March 25 2010
// Big Mike https://www.bigmiketrading.com
inputs:
length(9), mult(1), strength(9);
vars:
strend(0),
st(0);
st = SuperTrend_BM(length, mult,strength,strend);
if strend = 1 then Plot1(st,"SuperTrendUP");
if strend = -1 then Plot2(st,"SuperTrendDN");

Function Drive in the form of an Indicator

This should be a fun indicator to play with in the development of a trend following approach. My version of Big Mike’s code is a little different as I wanted the variable names to be a little more descriptive.

Update Feb 28 2022

I forgot to mention that you will need to make sure your plot lines don’t automatically connect.

Can You Do This with Just One Plot1?

An astute reader brought it to my attention that we could get away with a single plot and he was right. The reason I initially used two plot was to enable the user to chose his/her own plot colors by using the Format dialog.

//if strend = 1 then Plot1(st,"SuperTrendUP");
//if strend = -1 then Plot2(st,"SuperTrendDN");
if strend = 1 then SetPlotColor(1,red);
if strend = -1 then SetPlotColor(1,green);
Plot1(st,"SuperTrend_BM");

The theory behind the code is quite interesting and I haven’t gotten into it thoroughly, but will do so in the next few days. The code was derived from Trade-Signal’s Equilla Programming Language. I looked at the website and it seems to leans heavily on an EasyLanguage like syntax, but unlike EZLang allows you to incorporate indicators right in the Strategy. It also allows you, and I might be wrong, to move forward in time from a point in the past quite easily. The code basically was fed a signal (+1,0,-1) and based on this value progressively moved forward one bar at a time (over a certain time period) and calculated the MAE and MFE (max. adverse/favorable excursion for each bar. The cumulative MAE/MFE were then stored in a BIN for each bar. At the end of the data, a chart of the ratio between the MAE and MFE was plotted.

EasyLanguage Version

I tried to replicate the code to the best of my ability by going back in time and recording a trading signal and then moving Back to The Future thirty bars, in this case, to calculated and store the MAE/MFE in the BINS.

Simple Moving Average Cross Over Test

After 100 bars, I looked back 30 bars to determine if the price was either greater than or less than the 21 day moving average. Let’s assume the close was greater than the 21 day moving average 30 days ago, I then kept going backward until this was not the case. In other words I found the bar that crossed the moving average. It could have been 5 or 18 or whatever bars further back. I stored that close and then started moving forward calculating the MAE/MFE by keeping track of the Highest Close and Lowest Close made during 30 bar holding period. You will see the calculation in the code. Every time I got a signal I accumulated the results of the calculations for each bar in the walk forward period. At the end of the chart or test I divided each bars MFE by its MAE and plotted the results. A table was also created in the Print Log. This code is barely beta, so let me know if you see any apparent errors in logic or calculations.

inputs: ilb(30); //ilb - initial lookback
vars: lb(0),signal(0),btf(0),mf(0),ma(0),hh(0),ll(99999999),arrCnt(0),numSigs(0);
arrays : mfe[40](0),mae[40](0);
lb = ilb;
if barNumber > 100 then
begin
signal = iff(c[ilb] > average(c[ilb],21),1,-1);
// print(d," signal ",signal," ",ilb);
if signal <> signal[1] then
begin
numSigs = numSigs + 1; // keep track of number of signals
// print("Inside loop ", date[ilb]," ",c[ilb]," ",average(c[ilb],21));
if signal = 1 then // loop further back to get cross over
begin
// print("Inside signal = 1 ",date[lb]," ",c[lb]," ",average(c[lb],21));
while c[lb] > average(c[lb],21)
begin
lb = lb + 1;
end;
// print("lb = ",lb);
end;
if signal = -1 then // loop further back to get cross over
begin
// print("Inside signal = -1 ",date[lb]," ",c[lb]," ",average(c[lb],21));
while c[lb] < average(c[lb],21)
begin
lb = lb + 1;
end;
end;
lb = lb - 1;
hh = 0;
ll = 999999999;
arrCnt = 0;
for btf = lb downto (lb - ilb) //btf BACK TO FUTURE INDEX
begin
mf=0;
ma=0;
hh=maxList(c[btf],hh);
// print("inside inner loop ",btf," hh ",hh," **arrCnt ",arrCnt);
ll=minList(c[btf],ll);
if signal>0 then
begin
mf=iff(hh>c[lb],(hh-c[lb])/c[lb],0); // mf long signal
ma=iff(ll<c[lb],(c[lb]-ll)/c[lb],0); // ma long signal
end;
if signal<0 then begin
ma=iff(hh>c[lb],(hh-c[lb])/c[lb],0); // ma after short signal
mf=iff(ll<c[lb],(c[lb]-ll)/c[lb],0); // mf after short signal
end;
// print(btf," signal ",signal," mf ",mf:0:5," ma ",ma:0:5," hh ",hh," ll ",ll," close[lb] ",c[lb]);
mfe[arrCnt]=mfe[arrCnt]+absValue(signal)*mf;
mae[arrCnt]=mae[arrCnt]+absValue(signal)*ma;
arrCnt = arrCnt + 1;
end;
end;
end;
if lastBarOnChart then
begin
print(" ** MFE / MAE ** ");
for arrCnt = 1 to 30
begin
print("Bar # ",arrCnt:1:0," mfe / mae ",(mfe[arrCnt]/mae[arrCnt]):0:5);
end;
for arrCnt = 30 downto 1
begin
plot1[arrCnt](mfe[31-arrCnt]/mae[31-arrCnt]," mfe/mae ");
end;
end;

Back to The Future - going backward then forward

Here is an output at the end of a test on Crude Oil

** MFE / MAE **
Bar # 1 mfe / mae 0.79828
Bar # 2 mfe / mae 0.81267
Bar # 3 mfe / mae 0.82771
Bar # 4 mfe / mae 0.86606
Bar # 5 mfe / mae 0.87927
Bar # 6 mfe / mae 0.90274
Bar # 7 mfe / mae 0.93169
Bar # 8 mfe / mae 0.97254
Bar # 9 mfe / mae 1.01002
Bar # 10 mfe / mae 1.03290
Bar # 11 mfe / mae 1.01329
Bar # 12 mfe / mae 1.01195
Bar # 13 mfe / mae 0.99963
Bar # 14 mfe / mae 1.01301
Bar # 15 mfe / mae 1.00513
Bar # 16 mfe / mae 1.00576
Bar # 17 mfe / mae 1.00814
Bar # 18 mfe / mae 1.00958
Bar # 19 mfe / mae 1.02738
Bar # 20 mfe / mae 1.01948
Bar # 21 mfe / mae 1.01208
Bar # 22 mfe / mae 1.02229
Bar # 23 mfe / mae 1.02481
Bar # 24 mfe / mae 1.00820
Bar # 25 mfe / mae 1.00119
Bar # 26 mfe / mae 0.99822
Bar # 27 mfe / mae 1.01343
Bar # 28 mfe / mae 1.00919
Bar # 29 mfe / mae 0.99960
Bar # 30 mfe / mae 0.99915

Ratio Values over 30 Bins

Using Arrays for Bins

When newcomers start to program EasyLanguage and encounter arrays it sometimes scares them away. They are really easy and in many cases necessary to complete a project. In this code I used two 40 element or bins arrays MFE and MAE. I only use the first 30 of the bins to store my information. You can change this to 30 if you like, and when you start using a fixed array it is best to define them with the exact number you need, so that TradeStation will tell you if you step out of bounds (assign value to a bin outside the length of the array). To learn more about arrays just search my blog. The cool thing about arrays is you control what data goes in and what you do with that data afterwards. Anyways play with the code, and I will be back with a more thorough explanation of the theory behind it.

I didn’t say a very good Free System! This code is really cool so I thought I would share with you. Take a look at this rather cool picture.

Thanks to a reader of this blog (AG), I got this idea and programmed a very simple day trading system that incorporated a volatility trailing stop. I wanted to make sure that I had it programmed correctly and always wanted to draw a box on the chart – thanks to (TJ) from MC forums for getting me going on the graphic aspect of the project.

Since I have run out of time for today – need to get a haircut. I will have to wait till tomorrow to explain the code. But real quickly the system.

Buy x% above first y bar high and then set up a trailing stop z% of y bar average range – move to break-even when profits exceed $w. Opposite goes for the short side. One long and one short only allowed during the day and exit all at the close.

What the heck here is the code for the Strategy.

inputs: startTradeTime(930),startTradeBars(6),endTradeTime(1530),
breakOutVolPer(0.5),trailVolPer(.25),breakEven$(500);
vars: longsToday(0),shortsToday(0),
longStop(0),shortStop(0),
longTrail(0),shortTrail(0),
trailVolAmt(0),
barCount(0),highToday(0),lowToday(0),
volAmt(0),mp(0);
if t = startTradeTime + barinterval then
begin
longsToday = 0;
shortsToday = 0;
longStop = 0;
shortStop = 0;
longTrail = 0;
shortTrail = 99999999;
barCount = 0;
highToday = 0;
lowToday = 999999999;
end;
highToday = maxList(h,highToday);
lowToday = minList(l,lowToday);
mp = marketPosition;
barCount +=1;
if barCount >= startTradeBars then
begin
volAmt = average(range,startTradeBars);
if barCount = startTradeBars then
begin
longStop = highToday + breakOutVolPer * volAmt;
shortStop = lowToday - breakOutVolPer * volAmt;
end;
if t < endTradeTime then
begin
if longsToday = 0 then buy("volOrboL") next bar at longStop stop;
if shortsToday = 0 then sellShort("volOrboS") next bar shortStop stop;
end;
trailVolAmt = volAmt * trailVolPer;
if mp = 1 then
begin
longsToday +=1;
if c > entryPrice + breakEven$/bigPointValue then
longTrail = maxList(entryPrice,longTrail);
longTrail = maxList(c - trailVolAmt,longTrail);
sell("L-TrlX") next bar at longTrail stop;
end;
if mp = -1 then
begin
shortsToday +=1;
if c < entryPrice - breakEven$/bigPointValue then
shortTrail = minList(entryPrice,shortTrail);
shortTrail = minList(c + trailVolAmt,shortTrail);
buyToCover("S-TrlX") next bar at shortTrail stop;
end;
end;
setExitOnClose;

I will comment in a later post!

And the code for the Strategy Tracking Indicator.

inputs: startTradeTime(930),startTradeBars(6),endTradeTime(1530),
breakOutVolPer(0.5),trailVolPer(.25),breakEven$(500);
vars: longsToday(0),shortsToday(0),
longStop(0),shortStop(0),
longTrail(0),shortTrail(0),
trailVolAmt(0),
barCount(0),highToday(0),lowToday(0),
volAmt(0),mp(0);
if t = startTradeTime + barinterval then
begin
longsToday = 0;
shortsToday = 0;
longStop = 0;
shortStop = 0;
longTrail = 0;
shortTrail = 99999999;
barCount = 0;
highToday = 0;
lowToday = 999999999;
mp = 0;
end;
highToday = maxList(h,highToday);
lowToday = minList(l,lowToday);
barCount +=1;
vars: iCnt(0),mEntryPrice(0),myColor(0);
if barCount >= startTradeBars then
begin
volAmt = average(range,startTradeBars);
if barCount = startTradeBars then
begin
longStop = highToday + breakOutVolPer * volAmt;
shortStop = lowToday - breakOutVolPer * volAmt;
for iCnt = 0 to startTradeBars-1
begin
plot1[iCnt](longStop,"BuyBO",default,default,default);
plot2[iCnt](shortStop,"ShrtBo",default,default,default);
end;
end;
if t < endTradeTime then
begin
if longsToday = 0 and h >= longStop then
begin
mp = 1;
mEntryPrice = maxList(o,longStop);
longsToday += 1;
end;
if shortsToday = 0 and l <= shortStop then
begin
mp = -1;
mEntryPrice = minList(o,shortStop);
shortsToday +=1;
end;
plot3(longStop,"BuyBOXTND",default,default,default);
plot4(shortStop,"ShrtBOXTND",default,default,default);
end;
trailVolAmt = volAmt * trailVolPer;
if mp = 1 then
begin
if c > mEntryPrice + breakEven$/bigPointValue then
longTrail = maxList(mEntryPrice,longTrail);
longTrail = maxList(c - trailVolAmt,longTrail);
plot5(longTrail,"LongTrail",default,default,default);
end;
if mp = -1 then
begin
if c < mEntryPrice - breakEven$/bigPointValue then
shortTrail = minList(mEntryPrice,shortTrail);
shortTrail = minList(c + trailVolAmt,shortTrail);
plot6(shortTrail,"ShortTrail",default,default,default);
end;
end;

Cool code for the indicator

Very Important To Set Indicator Defaults Like This

For the BO Box use these settings – its the first 4 plots:

The box is created by drawing thick semi-transparent lines from the BuyBo and BuyBOXTND down to ShrtBo and ShrtBOXTND. So the Buy components of the 4 first plots should be Bar High and the Shrt components should be Bar Low. I didn’t specify this the first time I posted. Thanks to one of my readers for point this out!

Also I used different colors for the BuyBo/ShrtBo and the BuyBOXTND/ShrtBOXTND. Here is that setting:

The darker colored line on the last bar of the break out is caused by the overlap of the two sets of plots.

Since this is part 1 we are just going to go over a very simple system: SAR (stop and reverse) at highest/lowest high/low for past 20 days.

A 2D Array in EasyLanguage is Immutable

Meaning that once you create an array all of the data types must be the same. In a Python list you can have integers, strings, objects whatever. In C and its derivatives you also have a a data structure (a thing that stores related data) know as a Structure or Struct. We can mimic a structure in EL by using a 2 dimensional array. An array is just a list of values that can be referenced by an index.

array[1] = 3.14

array[2] = 42

array[3] = 2.71828

A 2 day array is similar but it looks like a table

array[1,1], array[1,2], array[1,3]

array[2,1], array[2,2], array[2,3]

The first number in the pair is the row and the second is the column. So a 2D array can be very large table with many rows and columns. The column can also be referred to as a field in the table. To help use a table you can actually give your fields names. Here is a table structure that I created to store trade information.

trdEntryPrice (0) – column zero – yes we can have a 0 col. and row

trdEntryDate(1)

trdExitPrice (2)

trdExitDate(3)

trdID(4)

trdPos(5)

trdProfit(6)

trdCumuProfit(7)

So when I refer to tradeStruct[0, trdEntryPrice] I am referring to the first column in the first row.

This how you define a 2D array and its associate fields.

In EasyLanguage You are Poised at the Close of a Yesterday’s Bar

This paradigm allows you to sneak a peek at tomorrow’s open tick but that is it. You can’t really cheat, but it also limits your creativity and makes things more difficult to program when all you want is an accurate backtest. I will go into detail, if I haven’t already in an earlier post, the difference of sitting on Yesterday’s close verus sitting on Today’s close with retroactive trading powers. Since we are only storing trade information when can use hindsight to gather the information we need.

Buy tomorrow at highest(h,20) stop;

SellShort tomorrow at lowest(l,20) stop;

These are the order directives that we will be using to execute our strategy. We can also run a Shadow System, with the benefit of hindsight, to see where we entered long/short and at what prices. I call it a Shadow because its all the trades reflected back one bar. All we need to do is offset the highest and lowest calculations by 1 and compare the values to today’s highs and lows to determine trade entry. We must also test the open if a gap occurred and we would have been filled at the open. Now this code gets a bit hairy, but stick with it.

Shadow System

stb = highest(h,20);
sts = lowest(l,20);
stb1 = highest(h[1],20);
sts1 = lowest(l[1],20);
buy("Sys-L") 1 contract next bar at stb stop;
sellShort("Sys-S") 1 contract next bar at sts stop;
mp = marketPosition*currentContracts;
totTrds = totalTrades;
if mPos <> 1 then
begin
if h >= stb1 then
begin
if mPos < 0 then // close existing short position
begin
mEntryPrice = tradeStruct[numTrades,trdEntryPrice];
mExitPrice = maxList(o,stb1);
tradeStruct[numTrades,trdExitPrice] = mExitPrice;
tradeStruct[numTrades,trdExitDate] = date;
mProfit = (mEntryPrice - mExitPrice) * bigPointValue - mCommSlipp;
cumuProfit += mProfit;
tradeStruct[numTrades,trdCumuProfit] = cumuProfit;
tradeStruct[numTrades,trdProfit] = mProfit;
print(d+19000000:8:0," shrtExit ",mEntryPrice:4:5," ",mExitPrice:4:5," ",mProfit:6:0," ",cumuProfit:7:0);
print("-------------------------------------------------------------------------");
end;
numTrades +=1;
mEntryPrice = maxList(o,stb1);
tradeStruct[numTrades,trdID] = 1;
tradeStruct[numTrades,trdPOS] = 1;
tradeStruct[numTrades,trdEntryPrice] = mEntryPrice;
tradeStruct[numTrades,trdEntryDate] = date;
mPos = 1;
print(d+19000000:8:0," longEntry ",mEntryPrice:4:5);
end;
end;
if mPos <>-1 then
begin
if l <= sts1 then
begin
if mPos > 0 then // close existing long position
begin
mEntryPrice = tradeStruct[numTrades,trdEntryPrice];
mExitPrice = minList(o,sts1);
tradeStruct[numTrades,trdExitPrice] = mExitPrice;
tradeStruct[numTrades,trdExitDate] = date;
mProfit = (mExitPrice - mEntryPrice ) * bigPointValue - mCommSlipp;
cumuProfit += mProfit;
tradeStruct[numTrades,trdCumuProfit] = cumuProfit;
tradeStruct[numTrades,trdProfit] = mProfit;
print(d+19000000:8:0," longExit ",mEntryPrice:4:5," ",mExitPrice:4:5," ",mProfit:6:0," ",cumuProfit:7:0);
print("---------------------------------------------------------------------");
end;
numTrades +=1;
mEntryPrice =minList(o,sts1);
tradeStruct[numTrades,trdID] = 2;
tradeStruct[numTrades,trdPOS] =-1;
tradeStruct[numTrades,trdEntryPrice] = mEntryPrice;
tradeStruct[numTrades,trdEntryDate] = date;
mPos = -1;
print(d+19000000:8:0," ShortEntry ",mEntryPrice:4:5);
end;
end;

Shadow System - Generic forany SAR System

Notice I have stb and stb1. The only difference between the two calculations is one is displaced a day. I use the stb and sts in the EL trade directives. I use stb1 and sts1 in the Shadow System code. I guarantee this snippet of code is in every backtesting platform out there.

All the variables that start with the letter m, such as mEntryPrice, mExitPrice deal with the Shadow System. Theyare not derived from TradeStation’s back testing engine only our logic. Lets look at the first part of just one side of the Shadow System:

if mPos <> 1 then
begin
if h >= stb1 then
begin
if mPos < 0 then // close existing short position
begin
mEntryPrice = tradeStruct[numTrades,trdEntryPrice];
mExitPrice = maxList(o,stb1);
tradeStruct[numTrades,trdExitPrice] = mExitPrice;
tradeStruct[numTrades,trdExitDate] = date;
mProfit = (mEntryPrice - mExitPrice) * bigPointValue - mCommSlipp;
cumuProfit += mProfit;
tradeStruct[numTrades,trdCumuProfit] = cumuProfit;
tradeStruct[numTrades,trdProfit] = mProfit;
print(d+19000000:8:0," shrtExit ",mEntryPrice:4:5," ",mExitPrice:4:5," ",mProfit:6:0," ",cumuProfit:7:0);
print("-------------------------------------------------------------------------");
end;

mPos and mEntryPrice and mExitPrice belong to the Shadow System

if mPos <> 1 then the Shadow Systems [SS] is not long. So we test today’s high against stb1 and if its greater then we know a long position was put on. But what if mPos = -1 [short], then we need to calculate the exit and the trade profit and the cumulative trade profit. If mPos = -1 then we know a short position is on and we can access its particulars from the tradeStruct 2D array. mEntryPrice = tradeStruct[numTrades,trdEntryPrice]. We can gather the other necessary information from the tradeStruct [remember this is just a table with fields spelled out for us.] Once we get the information we need we then need to stuff our calculations back into the Structure or table so we can regurgitate later. We stuff date in to the following fields trdExitPrice, trdExitDate, trdProfit and trdCumuProfit in the table.

Formatted Print: mEntryPrice:4:5

Notice in the code how I follow the print out of variables with :8:0 or :4:5? I am telling TradeStation to use either 0 or 5 decimal places. The date doesn’t need decimals but prices do. So I format that so that they will line up really pretty like.

Now that I take care of liquidating an existing position all I need to do is increment the number of trades and stuff the new trade information into the Structure.

The same goes for the short entry and long exit side of things. Just review the code. I print out the trades as we go along through the history of crude. All the while stuffing the table.

If LastBarOnChart -> Regurgitate

On the last bar of the chart we know exactly how many trades have been executed because we were keeping track of them in the Shadow System. So it is very easy to loop from 0 to numTrades.

if lastBarOnChart then
begin
print("Trade History");
for arrIndx = 1 to numTrades
begin
value20 = tradeStruct[arrIndx,trdEntryDate];
value21 = tradeStruct[arrIndx,trdEntryPrice];
value22 = tradeStruct[arrIndx,trdExitDate];
value23 = tradeStruct[arrIndx,trdExitPrice];
value24 = tradeStruct[arrIndx,trdID];
value25 = tradeStruct[arrIndx,trdProfit];
value26 = tradeStruct[arrIndx,trdCumuProfit];
print("---------------------------------------------------------------------");
if value24 = 1 then
begin
string1 = buyStr;
string2 = sellStr;
end;
if value24 = 2 then
begin
string1 = shortStr;
string2 = coverStr;
end;
print(value20+19000000:8:0,string1,value21:4:5," ",value22+19000000:8:0,string2,
value23:4:5," ",value25:6:0," ",value26:7:0);
end;
end;

Add 19000000 to Dates for easy Translation

Since all trade information is stored in the Structure or Table then pulling the information out using our Field Descriptors is very easy. Notice I used EL built-in valueXX to store table information. I did this to make the print statements a lot shorter. I could have just used tradeStruct[arrIndx, trdEntry] or whatever was needed to provide the right information, but the lines would be hard to read. To translate EL date to a normal looking data just add 19,000,000 [without commas].

If you format your PrintLog to a monospaced font your out put should look like this.

Why Would We Want to Save Trade Information?

The answer to this question will be answered in Part 2. Email me with any other questions…..

A reader of this blog wanted a conversion from my Ratchet Trailing Stop indicator into a Strategy. You will notice a very close similarity with the indicator code as the code for this strategy. This is a simple N-Bar [Hi/Lo] break out with inputs for the RatchetAmt and TrailAmt. Remember RatchetAmt is how far the market must move in your favor before the stop is pulldown the TrailAmt. So if the RatchetAmt is 12 and the TrailAmt is 6, the market would need to move 12 handles in your favor and the Trail Stop would move to break even. If it moves another 12 handles then the stop would be moved up/down by 6 handles. Let me know if you have any questions – this system is similar to the one I just posted.

inputs: ratchetAmt(6),trailAmt(6);
vars:longMult(0),shortMult(0),myBarCount(0);
vars:stb(0),sts(0),buysToday(0),shortsToday(0),mp(0);
vars:lep(0),sep(0);
If d <> d[1] then
Begin
longMult = 0;
shortMult = 0;
myBarCount = 0;
mp = 0;
lep = 0;
sep = 0;
buysToday = 0;
shortsToday = 0;
end;
myBarCount = myBarCount + 1;
If myBarCount = 6 then // six 5 min bars = 30 minutes
Begin
stb = highD(0); //get the high of the day
sts = lowD(0); //get low of the day
end;
If myBarCount >=6 and t < calcTime(sess1Endtime,-3*barInterval) then
Begin
if buysToday = 0 then buy("NBar-Range-B") next bar stb stop;
if shortsToday = 0 then sellShort("NBar-Range-S") next bar sts stop;
end;
mp = marketPosition;
If mp = 1 then
begin
lep = entryPrice;
buysToday = 1;
end;
If mp =-1 then
begin
sep = entryPrice;
shortsToday = 1;
end;
// 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 multipes of profit
// always key off of lep(LONG ENTRY POINT)
// notice how I used + 1 to determine profit
// and - 1 to determine stop level
If 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;
setExitOnClose;

I Used my Ratchet Indicator for the Basis of this Strategy

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.

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!

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 currentContractscontracts 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.

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.

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-days. Lxp 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

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.

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?

for i = 1 to 10 begin

sum = sum + (HighD(i) – LowD(i));

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.

if(Time < liqRevEndTime and sellsToday = 0 and longLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then

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!

Advanced Topics Edition just added to Easing Into EasyLanguage Series

Hello to All! The Easing Into EasyLanguage Series is now complete with the publication of the Advanced Topics Edition. This series includes three educational editions. Start out with the Foundation Edition. It is designed for the new user of EasyLanguage or for those you would like to have a refresher course. There are 13 tutorials ranging from creating Strategies to PaintBars. Learn how to create your own functions or apply stops and profit objectives. Ever wanted to know how to find an inside day that is also a Narrow Range 7 (NR7?) Now you can, and the best part is you get over 4 HOURS OF VIDEO INSTRUCTION – one for each tutorial. All source code is available too, and if you have TradeStation, so are the workspaces. Plus you can always email George for any questions. george.p.pruitt@gmail.com.

This book is for those that have read the Foundation Edition or have some experience working with EasyLanguage and the various functions that help make a trading decision. This book’s audience will be those programmers that want to take an idea, that requires an observation of intraday market movements to make a trading decision, and program it accurately. If you have programmed daily bar systems, and you want to drill down and add some components that require additional market information (like what came first – the high or the low), then you have come to the right place. If you want to buy and sell short in the same day and use trade management principles such as profit targets and stop losses then The Hi-Res Edition is the book you need. There are two paradigms that EasyLanguage covers: daily and intraday bar programming. It’s the same language, but the move from daily to intraday programming can be quite difficult. Learn all the essentials and shortcuts with this edition. 5 HOURS OF VIDEO INSTRUCTION in this Hi-Res edition – one for each tutorial. All source code is available too, and if you have TradeStation, so are the workspaces. Plus you can always email George for any questions. george.p.pruitt@gmail.com.

Advanced Topics (AT) could cover a vast amount of ideas and concepts and be the length of “War and Peace” on steroids. Since this book is part of the series, I wanted to cover a handful of concepts that included the follow programming constructs. Arrays and their manipulation. Buffers (fixed length arrays) and the tools to maintain buffer elements with formulas for extraction and sorting. Finite State Machines using the switch-case construct and range based case values. Using original text graphic objects and retrieving and analyzing their properties to determine X and Y coordinate values of text location. Seasonality: The Ruggiero/Barna Universal Seasonal and the Sheldon Knight Seasonal methods. In AT, you will also find an introduction to EasyLanguage’s Project Concept and the steps to create one by adding/deleting component files. Learn also how to import and export projects. TradeStation now provides access to fundamental data such as Commitment of Traders – learn how to convert the Net Change indicator into a strategy utilizing the FundValue functionality. If you wanted to find out how to merge multiple time frames into a single indicator, you are in luck! Create a MTF indicator for yourself.

Pick up your copies today – e-Book or paperback format – at Amazon.com