Category Archives: EasyLanguage Tutorial

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

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

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


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

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


If barNumber > 1 then
Begin

adxData1 = adx(adxLen) of data1;
adxData2 = adx(adxLen) of data2;
adxData3 = adx(adxLen) of data3;
adxData4 = adx(adxLen) of data4;
adxData5 = adx(adxLen) of data5;

Condition10 = adxData1 > adxTrendVall;
Condition11 = adxData2 > adxTrendVall;
Condition12 = adxData3 > adxTrendVall;
Condition13 = adxData4 > adxTrendVall;
Condition14 = adxData5 > adxTrendVall;

If condition10 then setPlotColor(1,Green) else SetPlotColor(1,Red);
If condition11 then setPlotColor(2,Green) else SetPlotColor(2,Red);
If condition12 then setPlotColor(3,Green) else SetPlotColor(3,Red);
If condition13 then setPlotColor(4,Green) else SetPlotColor(4,Red);
If condition14 then setPlotColor(5,Green) else SetPlotColor(5,Red);

condition6 = condition10 and condition11 and condition12 and condition13 and condition14;
Condition7 = not(condition10) and not(condition11) and not(condition12) and not(condition13) and not(condition14);

If condition6 then setPlotColor(7,Green);
If condition7 then setPlotColor(7,Red);

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

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

end;
MTF with 5 data streams and ADX

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

adxData1 = adx(14) of data1;

adxData2 = adx(14) of data2;

This assumption worked out.

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

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

 

An ES Day Trading Model Explained – Part 2

This is a continuation post or Part 2 of the development of the ES day trading system with EasyLanguage.

If you can understand this model you can basically program any of your day trading ideas.

Inputs Again:

First I want to revisit our list of inputs and make a couple of changes before proceeding.

inputs: volCalcLen(10),orboBuyPer(.2),orboSellPer(.2); 
inputs: volStopPer(.7),Stop$(500);
inputs: volThreshPer(.3),ProfThresh$(250);
inputs: volTrailPer(.2),Trail$(200);
inputs: endTradingTime(1500);
Modification to our inputs

If we want to optimize these values then we can’t use the keyword bigPointValue in the input variable default value.  So I removed them – also I added an input endTradingTime(1500).  I wanted to cut off our trading at a given time – no use entering a trade five minutes prior to the closing.

Disengage the Vol or $Dollar Trade Management:

I may have muddied the waters a little with having volatility and $ values simultaneously.  You can use either for the initial protective stop, profit threshold, and trailing stop amount.  You can disengage them by using large values.  If you want to ignore all the $ inputs just add a couple of 00 to each of the values:

Stop$(50000), ProfThres$(25000),Trail$(50000)

If you want to ignore the volatility trade management stops just put a large number in front of the decimal.

volStopPer(9.7), volThreshPer(9.3), volTrailPer(9.2)

If you make either set large then the algorithm will use the values closest to the current market price.

Computations:

Let’s now take a look at the computations that are done on the first bar of the day:

If d <> d[1] then
Begin
rangeSum = 0.0; // range calculation for entry
For iCnt = 1 to volCalcLen
Begin
rangeSum = rangeSum + (highD(iCnt) - lowD(iCnt));
end;
vol = rangeSum/volCalcLen;
buyPoint = openD(0) + vol*orboBuyPer;
sellPoint = openD(0) - vol*orboSellPer;

longStopAmt = vol * volStopPer;
longStopAmt = minList(longStopAmt,Stop$/bigPointValue);

shortStopAmt = vol *volStopPer;
shortStopAmt = minList(shortStopAmt,Stop$/bigPointValue);

longThreshAmt = vol * volThreshPer;
longThreshAmt = minList(longThreshAmt,ProfThresh$/bigPointValue);

shortThreshAmt = vol * volThreshPer;
shortThreshAmt = minList(shortThreshAmt,ProfThresh$/bigPointValue);

longTrailAmt = vol * volTrailPer;
longTrailAmt = minList(longTrailAmt,Trail$/bigPointValue);

shortTrailAmt = vol * volTrailPer;
shortTrailAmt = minList(shortTrailAmt,Trail$/bigPointValue);

longTrailLevel = 0;
shortTrailLevel = 999999;
buysToday = 0;
shortsToday = 0;
end;
Once a day computations

I determine it is the first bar of the day by comparing the current 5-minute bar’s date stamp to the prior 5-minute bar’s date stamp.  If they are not the same then you have the first bar of the day.  The first thing I do is calculate the volatility of the current market by using a for-loop to accumulate the day ranges for the past volCalcLen days.  I start the iterative process using the iCnt index and going from 1 back in time to volCalcLen.  I use iCnt to index into the function calls HighD and LowD.  Indexing is not really the right word here – that is more appropriate when working with arrays.  HighD and LowD are functions and we are passing the values 1 to volCalcLen into the functions and summing their output.  When you do this you will get a warning “A series function should not be called more than once with a given set of parameters.”  Sounds scary but it seems to work just fine.  If you don’t do this then you have to include a daily bar on the chart.  I like to keep things as simple as possible.   Once I sum up the daily ranges I then divide by volCalcLen to get the average range over the few days.  All of the vol based variables will use this value.

Entries:

Entry is based off a move away from the opening in terms of volatility.  If we use 0.2 (twenty percent) as orboBuyPer then the algorithm will buy on a stop 20% of the average range above the open tick.  Sell short is just the opposite.   We further calculate the longStopAmt as a function of vol and a pure $ amount.  I am using the minList function to determine the smaller of the two values  .This is how I disengage either the vol value or the $ value.  The other variables are also calculated just once a day: shortStopAmt, longThreshAmt, shortThreshAmt, longTrailAmt, shortTrailAmt. You could calculate every bar but that would be inefficient. I am also resetting four values at the beginning of the day:  longTrailLevel, shortTrailLevel, buysToday and shortsToday.

 

The Mighty MP:

mp = marketPosition;

If mp = 1 and mp[1] <> 1 then buysToday = buysToday + 1;
If mp = -1 and mp[1] <> -1 then shortsToday = shortsToday + 1;
MarketPosition monitoring and determining Buys/Shorts Today

I like using a variable to store each bar’s marketPosition.  In this case I am using MP.  By aliasing the marketPosition function call to a variable allows us to do this:

if mp = 1 and mp[1] <> 1 then buysToday = buysToday + 1;

This little line does a bunch of stuff.  If the current bar’s position is 1 and the prior bars position is not one then we know we have just entered a long position.  So every time this happens throughout the day the buysToday is incremented.  ShortsToday works just the same.  Pitfall warning:  If you strategy enters and exits on the same bar then this functionality will not work!  Neither will the call to the marketPosition function.  It will look like nothing happened.  If you need to keep track of the number of trades make sure you can only enter or exit on different bars.  If you stuff is so tight then drop down to a 1 minute or tick bar.

Controlling the Nmber of Buys/Shorts for the Day:

if time < endTradingTime and buysToday < 1 then Buy("ORBo-B") next bar at buyPoint stop;
if time < endTradingTime and shortsToday < 1 then Sellshort("ORBo-S") next bar at sellPoint stop;


If marketposition = 1 then
Begin
longExitPoint = entryPrice - longStopAmt;
sell("L-Exit") next bar at longExitPoint stop;
end;

If marketposition = -1 then
Begin
shortExitPoint = entryPrice + shortStopAmt;
buyToCover("S-Exit") next bar at shortExitPoint stop;
end;

If marketPosition = 1 and maxContractProfit/bigPointValue >= longThreshAmt then
Begin
longTrailLevel = maxList(highest(h,barsSinceEntry) - longTrailAmt,longTrailLevel);
sell("TrailSell") next bar at longTrailLevel stop;
end;

If marketPosition = -1 and maxContractProfit/bigPointValue >= shortThreshAmt then
Begin
shortTrailLevel = minList(lowest(l,barsSinceEntry) + shortTrailAmt,ShortTraillevel);
buyToCover("TrailCover") next bar at shortTrailLevel stop;
end;


SetExitOnClose;
Controlled trade directives

Notice how I am controlling the trade directives using the if statements.  I only want to enter a long position when the time is right and I haven’t already entered a long position for the day.  If you don’t control the trade directives, then these orders are placed on every bar, in our case every 5-minutes.  If you have pyramiding turned off then once you are long the buy directive is ignored.  This is an important concept – let’s say you just want to buy and short only one time per day trade session.  If you don’t control this directive, then it will fire off an order every five minutes.    You don’t want this -at least I hope you don’t.

So controlling the time and number of entries is paramount.  If you don’t control the time of entry then the day can arrive at the last bar of the day and fire off an order for the opening of the next day.  A big no, no !

Put To Work:

Here are the inputs I used to generate the trades in the graphic that follows.

Not Doing Exactly What You Want:

Here is what most day traders are looking for.   I made a comment on the chart – make sure you read it – it is another pitfall.

The trailing stop had to wait for the bar to complete to determine if the profit reached the threshold.  A little slippage here.  You can overcome this if you use the BuiltIn Percent Trailing Strategy or by using the SetPercentTrailing function call.

However, you lose the ability to really customize your algorithms by using the builtin functionalility.  You could drop down to a one minute bar and probably get out nearer your trailing stop amount.

Download the ELD:

Here you go!

GEODAYTRADERV1.01

An ES Day Trading Model Explained – Part 1

Open Range BreakOut with Trade Management

How difficult is it to program a day trading system using an open range break out, protective stop and a trailing stop?  Let’s see.  I started working on this and it got a little more detailed than I wanted so I have now split it up into two posts.  Part 2 will follow very soon.

What inputs do we need?  How about the number of days used in the volatility calculation?  What percentage of the volatility from the open do you want to buy or sell?  Should we have a different value for buys and sells?  Do we want to use a volatility based protective stop or a fixed dollar?  How about a trailing stop?  Should we wait to get to a certain profit level before engaging the trailing stop?  Should it also be based on volatility or fixed dollar amt?  How much should we trail – again an amount of volatility or fixed dollar?

Proposed inputs:

inputs: volCalcLen(10), orboBuyPer(.2), orboSellPer(.2), volStopPer(.7), $Stop(500), volProfThreshPer(.5), $ProfThresh(250),volTrailPer(.2),$Trail(200);

That should do it for the inputs – we can change later if necessary.

Possible pitfalls:

This is where I will save you some time.  If we use an open range break out entry we must limit the number of entries or TradeStation will continue to execute as long as the price is above our buy level.  You might ask, “That’s what we want -right?”  What if we use a tight stop and we get stopped out of our first position.  Do you want to buy again later in the day?  What if we use a trailing stop and we get out of the market above the buy level.  What will TradeStation do?  It will follow your exact instructions and buy again if you don’t control the number of allowed entries.  Do you want to carry the buy and sell stops overnight for execution on the open of the next day – probably not!  So we not only need to control the number of entries buy we also need to control the time period we can enter a trade.

Calculations:

We need to determine the volatility and a good way do this is calculating the average range over the past N days.   There are two ways to do this: 1) incorporate a daily chart as data2 and use a built-in function for the calculation or 2) use a for-loop and use the built-in functions HighD and LowD and just use one data feed.   Both have their drawbacks.  The first is you need to have a multi-data chart and the second you get a warning that you shouldn’t put a series function call inside the body of a for-loop.   I have done it both ways and I prefer to deal with the warning – so far it has worked out nicely – so let’s go with a single data chart.

Building the code:

Inputs:

Since we are combining a volatility and fixed $ amount in our trade management, you will need to set either the vol or dollar amounts to a high value to disable them.  You can use both but I am taking the smaller of the respective values.

inputs: volCalcLen(10),orboBuyPer(.2),orboSellPer(.2); 
inputs: volStopPer(.7),$Stop(500/bigPointValue);
inputs: volThreshPer(.5),$ProfThresh(250/bigPointValue);
inputs: volTrailPer(.2),$Trail(200/bigPointValue);
Inputs We Will Need - Can Changer Later

Variables:

vars:vol(0),buyPoint(0),sellPoint(0),
longStopAmt(0),shortStopAmt(0),longExitPoint(0),shortExitPoint(0),
longThreshAmt(0),shortThreshAmt(0),
longTrailAmt(0),shortTrailAmt(0),
longTrailLevel(0),shortTrailLevel(0),
hiSinceLong(0),loSinceShort(0),mp(0),
rangeSum(0),iCnt(0),
buysToday(0),shortsToday(0)
Variables That We Might Need

Once A Day Calculations:

Since we will be working with five-minute bars we don’t want to do daily calculations on each bar.  If we do it will slow down the process.  So let’s do these calculation on the first bar of the day only.

If d <> d[1] then
Begin
rangeSum = 0.0; // range calculation for entry
For iCnt = 1 to volCalcLen
Begin
rangeSum = rangeSum + (highD(iCnt) - lowD(iCnt));
end;
vol = rangeSum/volCalcLen;
buyPoint = openD(0) + vol*orboBuyPer;
sellPoint = openD(0) - vol*orboSellPer;

longStopAmt = vol * volStopPer;
longStopAmt = minList(longStopAmt,Stop$);

shortStopAmt = vol *volStopPer;
shortStopAmt = minList(shortStopAmt,Stop$);

longThreshAmt = vol * volThreshPer;
longThreshAmt = minList(longThreshAmt,ProfThresh$);

shortThreshAmt = vol * volThreshPer;
shortThreshAmt = minList(shortThreshAmt,ProfThresh$);

longTrailAmt = vol * volTrailPer;
longTrailAmt = minList(longTrailAmt,Trail$);

shortTrailAmt = vol * volTrailPer;
shortTrailAmt = minList(shortTrailAmt,Trail$);

longTrailLevel = 0;
shortTrailLevel = 999999;
buysToday = 0;
shortsToday = 0;
end;
Do These Just Once A Day

 

For All of You Who Don’t Want To Wait – Beta Version Is Available Below:

In my next post, I will dissect the following code for a better understanding.  Sorry I just ran out of time.

inputs: volCalcLen(10),orboBuyPer(.2),orboSellPer(.2); 
inputs: volStopPer(.7),Stop$(500/bigPointValue);
inputs: volThreshPer(.3),ProfThresh$(250/bigPointValue);
inputs: volTrailPer(.2),Trail$(200/bigPointValue);


vars:vol(0),buyPoint(0),sellPoint(0),
longStopAmt(0),shortStopAmt(0),longExitPoint(0),shortExitPoint(0),
longThreshAmt(0),shortThreshAmt(0),
longTrailAmt(0),shortTrailAmt(0),
longTrailLevel(0),shortTrailLevel(0),
hiSinceLong(0),loSinceShort(0),mp(0),
rangeSum(0),iCnt(0),
buysToday(0),shortsToday(0);

If d <> d[1] then
Begin
rangeSum = 0.0; // range calculation for entry
For iCnt = 1 to volCalcLen
Begin
rangeSum = rangeSum + (highD(iCnt) - lowD(iCnt));
end;
vol = rangeSum/volCalcLen;
buyPoint = openD(0) + vol*orboBuyPer;
sellPoint = openD(0) - vol*orboSellPer;

longStopAmt = vol * volStopPer;
longStopAmt = minList(longStopAmt,Stop$);

shortStopAmt = vol *volStopPer;
shortStopAmt = minList(shortStopAmt,Stop$);

longThreshAmt = vol * volThreshPer;
longThreshAmt = minList(longThreshAmt,ProfThresh$);

shortThreshAmt = vol * volThreshPer;
shortThreshAmt = minList(shortThreshAmt,ProfThresh$);

longTrailAmt = vol * volTrailPer;
longTrailAmt = minList(longTrailAmt,Trail$);

shortTrailAmt = vol * volTrailPer;
shortTrailAmt = minList(shortTrailAmt,Trail$);

longTrailLevel = 0;
shortTrailLevel = 999999;
buysToday = 0;
shortsToday = 0;
end;

mp = marketPosition;

If mp = 1 and mp[1] <> 1 then buysToday = buysToday + 1;
If mp = -1 and mp[1] <> -1 then shortsToday = shortsToday + 1;

if time < sessionendTime(0,1) and buysToday < 1 then Buy("ORBo-B") next bar at buyPoint stop;
if time < sessionendTime(0,1) and shortsToday < 1 then Sellshort("ORBo-S") next bar at sellPoint stop;


If marketposition = 1 then
Begin
longExitPoint = entryPrice - longStopAmt;
sell("L-Exit") next bar at longExitPoint stop;
end;

If marketposition = -1 then
Begin
shortExitPoint = entryPrice + shortStopAmt;
buyToCover("S-Exit") next bar at shortExitPoint stop;
end;

If marketPosition = 1 and maxContractProfit/bigPointValue >= longThreshAmt then
Begin
longTrailLevel = maxList(highest(h,barsSinceEntry) - longTrailAmt,longTrailLevel);
sell("TrailSell") next bar at longTrailLevel stop;
end;

If marketPosition = -1 and maxContractProfit/bigPointValue >= shortThreshAmt then
Begin
shortTrailLevel = minList(lowest(l,barsSinceEntry) + shortTrailAmt,ShortTraillevel);
buyToCover("TrailCover") next bar at shortTrailLevel stop;
end;


SetExitOnClose;
Beta Version - I will clean up later and post it