Replicating Daily Bar Daytrade with Intraday Data – EasyLanguage

Daily Bar Daytrade to 5 Minute Bars — Not So Easy!

Like in the previous post, I designed a simple optimization framework trying to determine the best day of the week to apply a long only volatility based open range break out algorithm on natural gas.  You might first ask why natural gas?  Its a good market that has seasonal tendencies that can actually be day-traded – there is enough volatility and volume.  Anyway, by simply slapping setExitOnClose at the end of your code turns your algorithm into one that exits every day at the settlement (fictional value that is derived via formula) price.  You can always use LIB (look inside bar) to help determine the magnitude of intraday swings and there respective chronological order.  But you are still getting out at the settlement price – which isn’t accurate.  However, if you are trying to get a set of parameters that gets you into the ballpark, then this is a very acceptable approach.  But you will always want to turn you daily bar strategy with LIB intro an intraday version for automation and accuracy purposes.

Day of Week Analysis On Intraday Data

When you optimize the day of week on daily bars, the first bar of the week is usually Monday.  When testing with intraday bars on futures, the trading actually starts on Sunday evening.  If your daily bar analysis reveals the best day of week is Wedneday, then you must inform TradeStation to take the trade from the opening on Tuesday night through the close on Wenesday.  If you are executing a market order, then you just need to tell TradeStation to execute at Tuesday night’s open (1800 eastern standard time).  The TRICK to allow trading throughout the 24 hour session (1800 to 1700) on just a particular day of the week is to capture the day of the week on the first bar of the trading session.  Here is some bookkeeping you can take care of on the first bar of the trading session.

if t = sessionstartTime(0,1) + barInterval Then
Begin

todaysOpen = o;
openDOW = dayOfWeek(d);
canBuy = False;
if openDOW = daysOfWeekToTrade then canBuy = True;
if canBuy and d[1] <> date of data2 then
begin
canBuy = False;
print(d," turning off canBuy");
end;

if mp = 1 then curTradeDays = curTradeDays + 1;
if mp = 0 then curTradeDays = 0;
barsToday = 1;
end;
Bookkeeping on the first bar of trading session

Here, I check to see if the first bar’s time stamp is equal to the sessionStarttime + barInterval.  Remember TradeStation shows the close time of each bar as the time stamp.  If adding a barInterval to the opening time results in a non-time, then use the calcTime function ( 1800 + 60 = 1860 – a non-time.)   I first store the open of the session in my variable todaysOpen.  Why not just use openD(0).  Well openD(0) is great for sessions that don’t span midnight.  If they span midnight then the openD returns the open of the12:00 am bar.  Here is the output from August 9th with the open on August 8th at 1800.  The two other values are midnight on August 8th and August 7th.  So the openD, highD, lowD, and closeD functions involve the 24 hour clock and not the session.

print(todaysOpen:5:5," ",openD(0):5:5," ",openD(1):5:5);

        18:00  midnite[0]   midnite[1]
--------------------------------------
1230808 2.797        2.712      2.588  

On the first bar of the trading session I also capture the day of the week.  The days of the week, using intraday data on a 24 hour session, range from Sunday to Thursday, instead of Monday thru Friday.  CanBuy is turned on if the day of week equals the input provided by the user.  When using daily bars and you optimize the day of the week, remember if you chose 2 or Tuesday you really mean Wednesday.  If today is Tuesday, then buy next bar at…  This will generate trades only on Wednesdays.  When testing on intraday data you do not need to make a modification for the day of week.  If the first bar of the trading session is Tuesday, then you will actually trade what is considered the Wednesday session that starts on Tuesday evening at 18:00.

What About Holidays?

Here is a quick trick to not trade on Holidays.  The daily bar does not include preempted sessions in its database, whereas intraday data does.  So, if at 18:00 the date of the prior bar does not match the date of the daily bar located in the Data2 slot, then you know that the prior day was preempted and you should not trade today.  In other words, Data2 is missing.  Remember, you only want to trade on days that are included in the daily bar database in your attempt to replicate the daily bar strategy.

date             close   moving average
1230703.00 data2         2.57382 2.70300
1230704.00 missing data2 2.57382 2.70300 <-- no trade
1230705.00 data2         2.57485 2.65100 <-- matches

How to count the daily bars since entry.

Here again you need to do some bookkeeping.  If you are long on the first bar of the day, then you have been in the trade for at least a day.  If you are long on two first bars of the trading session, then you have been long for two days.  I use the variable, curTradeDays, to count the number of days we have been in the trade.  If on the first bar of the trading session we are flat, then I reset curTradeDays to zero.  Otherwise, I increment the variable and this only occurs once a day.  You can optimize the daysInTrade input, but for this post we are just interested in getting out at the close on the day of entry.

Controlling one entry per day.

We only want one long entry per day in an attempt to replicate the daily bar system.  There are several ways to do this, but comparing the current bar’s marketPosition to the prior bar’s value will inform you if a position has been transitioned from flat to long.  If the prior bar’s position if flat and the current bar is now long, then we can turn our canBuy off or to false.  If we get stopped out later and the market rallies again to our break out level, we will not execute at that level, because the trade directive will not be issued.  Why not use the entriesToday function?  Again midnight cause this function to reset.  Say you go long at 22:00 and you do not want to enter another position until the tomorrow at 18:00.  EntriesToday will forget you entered the long position at 22:00.

1220726.00 2350.00 entries today 1.00
1220726.00 2355.00 entries today 1.00
1220727.00 0.00 entries today 0.00 < not true!

Cannot execute on the first bar of the day – does it matter?

Since this is an open range break out, we need to know what the open is prior to our break out calculation.  Could the break out level be penetrated on the first 5 minute bar?  Sure, but I bet it is rare.  I tested this and it only occurred 4 or 5 times.  In a back-test, if this occurs and the subsequent bar’s open is still above the break out level, TradeStation will convert the stop order to a market order and you will buy the open of the 2nd five minute bar.  In real time trading, you must tell TradeStation to wait until the end of the first 5 minute bar before issuing the trade directive to replicate your back testing.  If this is an issue, you can test with 5 minute bars, but execute on 1 minute bars.  In other words, have your testing chart and an additional execution chart.  Skipping the first 5 minute bar may be advantageous.  Many times you will get a surge at the open and then the market falls back.  By the time the first 5 minute bar concludes the market may be trading back below your break out level.  You might bypass the bulge. But your still in the game.  Here is how you can test the significance of this event.

	if canBuy and h > todaysOpen + volAmt *volMult then 
Begin
print(d," first bar penetration ",value67," ",value68," ",(c - (todaysOpen + volAmt *volMult))*bigPointValue );
value67 = value67 + 1;
value68 = value68 + (c - (todaysOpen + volAmt *volMult))*bigPointValue;
end;
Testing for penetration on first 5 minute bar of the day

By the end of the first bar of the day we know the open, high, low and close of the day thus far.  We can test to see if the high would have penetrated the break out level and also calculate the profit or loss of the trade.  In a back-test you will not miss this trade if the next bar’s open is still above the break out level.  If the next bar’s open is below the break out level, then you may have missed a fake out break out.   Again this is a rare event.

The rest of the code in its entirety

inputs: daysOfWeekToTrade(1),mavLen(20),volLen(10),volMult(0.25),volComp(.5),stopLoss(500),profitTarget(1000),daysInTrade(0),llLookBack(2);

vars: todaysOpen(0),curTradeDays(0),mp(0),barsToday(0),
openDOW(0),canBuy(False),volAmt(0),movAvg(0);


mp = marketPosition;

if t = sessionstartTime(0,1) + barInterval Then
Begin
todaysOpen = o;
openDOW = dayOfWeek(d);
canBuy = False;
if openDOW = daysOfWeekToTrade then canBuy = True;
if canBuy and d[1] <> date of data2 then
begin
canBuy = False;
end;

if mp = 1 then curTradeDays =curTradeDays + 1;
if mp = 0 then curTradeDays = 0;
barsToday = 1;

volAmt = average(range of data2,volLen);
movAvg = average(c,mavLen) of data2;
if canBuy and h > todaysOpen + volAmt *volMult then
Begin
print(d," first bar penetration ",value67," ",value68," ",(c - (todaysOpen + volAmt *volMult))*bigPointValue );
value67 = value67 + 1;
value68 = value68 + (c - (todaysOpen + volAmt *volMult))*bigPointValue;
end;
end;

if mp[1] = 0 and mp = 1 and canBuy then canBuy = False;

if canBuy and t <> sessionEndTime(0,1) Then
Begin
if barsToday > 1 and
close of data2 > movAvg and
range of data2 > volAmt * volComp then
buy("DMIntraBuy") next bar at todaysOpen + volAmt *volMult stop;
end;
barsToday = barsToday + 1;

if daysInTrade = 0 then
setExitOnClose;

sell("LLxit") next bar at lowest(l of data2,llLookBack) stop;
if mp = 1 and daysInTrade > 0 then
begin
if curTradeDays = daysInTrade then sell("DaysXit") next bar at open;
end;

setStopLoss(stopLoss);
setProfitTarget(profitTarget);
Intrday system that replicates a daily bar day trader

We are using the setStopLoss and setProfitTarget functions in this code.  But remember, if your continuous contract goes negative, then these functions will not work properly.

This type of programming is tricky, because you must use tricks to get EasyLanguage to do what you want it to do.  You must experiment, debug, and program ideas to test the limitations of EasyLanguage to hone your craft.

On the Subject of Data Mining with EasyLanguage

When Mining for Data, Make Sure You Have Accurate Data

Take a look at these results with no execution costs.

Results of Data Mining on Natural Gas

These results were derived by applying a simple algorithm to the natural gas futures for the past 15 or so years.  I wanted to see if there was a day of the week that produced the best results with the following entry and exit techniques:

  • Long entries only
  • Open range break out using a fraction of the N-day average range.
  • Buy in the direction of the moving average.
    • Yesterdays close must be above the X-day moving average of closing prices.
  •  Yesterday must have a wider range than a fractional multiple of the average range.
  • A fixed $stop and $profit used to exit trades.
  • Other exits
    • Exit at low of prior day.
    • Exit at the close of today – so a day trade.

Here is a list of the parameters that can be optimized:

  • daysOfWeekToTrade(1) : 1 – Monday, 2 – Tuesday…
  • mavLen(20): moving average calculation length.
  • volLen(10):  moving average calculation length for range.
  • volMult(0.25): average range multiplier to determine break out/
  • volComp(.5):  yesterday’s range must be greater than this percentage.
  • stopLoss(500): stop loss in $
  • profitTarget(1000): profit objective in $
  • daysInTrade(0): 0 get out at end of day.
  • llLookBack(2): pattern derived stop value – use the lowest low of N-days or the stop loss in $, whichever is closer.

Here is the code:

inputs: daysOfWeekToTrade(1),
mavLen(20),volLen(10),volMult(0.25),volComp(.5),
stopLoss(500),profitTarget(1000),
daysInTrade(0),llLookBack(2);


if dayOfWeek(d) = daysOfWeekToTrade Then
Begin
if close > average(c,mavLen) and range > average(range,volLen)*volComp then
buy("DMDailyBuy") next bar at open of tomorrow + average(range,volLen)*volMult stop;
end;

if daysInTrade = 0 then setExitOnClose;
sell("LLxit") next bar at lowest(l,llLookBack) stop;
if marketPosition = 1 then
begin
if barsSinceEntry = daysInTrade then sell("DaysXit") next bar at open;
end;

setStopLoss(stopLoss);
setProfitTarget(profitTarget);
Mining with bad data!

Why Is This Algorithm Wrong?  It’s Not It’s the Data!

The algorithm, speaking in terms of logic, is accurate. The data is wrong.  You cannot test the exit technology of this algorithm with just four data points per day – open, high, low, and close.  If this simple algorithm cannot be tested, then what can you test on a daily bar basis accurately?

  • Long or short entry, but not both.
    • If the algorithm can enter both long and short, you need to know what occurred first.  Doesn’t matter if you enter via stop or limit orders.  Using a stop order for both, then you need to know if the high occurred first and got you long, and then later the low and got you short.  Or just the opposite.  You can test, with the benefit of hindsight, if only one of the orders would have been elected.  If only one is elected, then you can proceed.  If both then no-go.  You must be able to use future leak to determine if only one of the orders were fillable.  TS-18 allows future leak BTW.  I say that like it’s a good thing!
  • L or S position from either a market or stop, and a profit objective.
    • If a long position is entered above the open, via a stop, and then the market moves even higher, you can get out on a sell limit for a profit.
    • Same goes for the short side, but you need to know the magnitude of the low price in relation to the open.
  • L or S position from either a market or limit and a protective stop.
    • If short position is entered above the open, via a limit order, and the market moves even higher, you can exit the short position at a loss via a buy stop order.
    • Same goes for the long side, but you need to know the magnitude of the high in relation to the open.
  • Long pyramid on multiple higher stop or lower limit orders, but not both.
    • The market opens and then sells off. You can go long on a limit order below the open, then you go long another unit below the original limit price and so on…
    • The market opens and rallies.  You can go long on a stop order above the open, then you can go long another unit above the original stop price and so on…
  • Short pyramid on multiple lower stop or higher limit orders.
    • Same as long entries but in opposite direction.

Can’t you look at the relationships of the open to low and open to high and close to high and close to low and determine which occurred first, the high or the low of the day.  You can, but there is no guarantee.  And you can’t determine the magnitude of intraday day swings.  Assume the market opens and immediately moves down and gets you short via a stop order.  And from looking at a daily chart, it looks like the market never turned around, so you would assume you had a profit going into the close.  But in fact, after the market opened and moved down, it rallied back to the open enough to trigger a protective stop you had working.

The entry technique of this strategy is perfectly fine.  It is only buying in the direction of a breakout.  The problems arise when you apply a profit objective and a stop loss and a pattern-based stop and a market on close order.    After buying did the market pull back to get you stopped out before moving up to get you out at profit objective?  Did the market move up and then down to the prior lowest low of N days and then back up to get you long?  Or just the opposite? In futures, the settlement price is calculated with a formula.  You are always exiting at the settlement price with an MOC or setExitOnClose directive (daily bar basis.)

No Biggie – I Will Just Test with LIB (Look Inside Bar.  That should fix it, right?

It definitely will increase accuracy, because you can see the intraday movements that make up the daily bar.  So, your entries and exits should be executed more accurately.  But you are still getting out at the settlement price which does not exist.  Here are the results from using LIB with 5-minute bar resolution.

You saw the beauty now you see the beast.

We Know the Weakness, But What Can We Do?

Test on a 5-minute bar as Data1 and daily as Data2.  This concept is what my Hi-Res Edition of Easing Into EasyLanguage is all about.  Here the results of using a higher resolution data and exiting on the last tick of the trading day – a known quantity.

As accurate as you can get – well with 5 minute bars that is.

These results validate the LIB results, right?  Not 100%, but very close.  Perhaps this run makes more money because the settlement price on the particular days that the system entered a trade was perhaps lower than the last tick.  In other words, when exiting at the end of the day, the last tick was more often higher than the settlement price.

In my next post, I will go over the details of developing an intraday system to replicate (as close as possible) a simple daily bar-based day trading system.  Like the one we have here.