Tag Archives: E-Mini

Super Combo Day Tradng System A 2020 Redo!

If you have some time on your hands and you want to build your own Frankenstein monster from a parts bin, here is your chance.  The Super Combo Day Trading System was originally published in my “Building Winning Trading Systems” book back in 2001.  I designed it to be more of a tutorial than a pure trading system.    You should be able to get the spare parts you need to create your own day trading system.  Back in 2001, I wanted to show how to control and monitor different entry and exit techniques in one complete algorithm.  The system was designed to day-trade the big SP and the results at the time looked promising.  Since the transition to the ES and the higher levels of volatility that we have seen over the years and the adoption of overnight trading,  the system hasn’t fared that well, but the concepts are still viable as an instructional tool today as they were 20 years ago.  EasyLanguage has been improved over this time period so the coding for the Super Combo can definitely take advantage of the new enhancements.

Here are the main premises of the logic:

  • take advantage of a buyEasier and shortEasier pattern setup
  • incorporate daily and 5-minute time frames in one chart
  • include a breakOut, failedBreakOut and reverseOnLiquidation trade entry techniques
  • monitor which signal is currently online and apply the correct exit signal
  • monitor profit and incorporate a break even stop
  • monitor time and incorporate a trailing stop
  • provide an interface into the logic via inputs

Okay here we go – there is quite a bit of code here so let’s divide an conquer by examining just one module at a time.  This first module includes the inputs and variables section plus once per day calculations.

[LegacyColorValue = true]; 

{Super Combo by George Pruitt - redo 2020
 This intra-day trading system will illustrate the multiple data
 handling capabilities of TradeStation.  All pertinent buy and sell
 calculations will be based on daily bars and actual trades will be
 executed on 5-min bars.  I have made most of the parameters input
 variables}

 Inputs:waitPeriodMins(30),initTradesEndTime(1430),liqRevEndTime(1200),
 	thrustPrcnt1(0.30),thrustPrcnt2(0.60),breakOutPrcnt(0.25),
	failedBreakOutPrcnt(0.25),protStopPrcnt1(0.30),protStopPrcnt2(0.20),
	protStopAmt(3.00),breakEvenPrcnt(0.50),avgRngLength(10);
	
Variables:averageRange(0),canTrade(0),buyEasierDay(FALSE),
	sellEasierDay(FALSE),buyBOPoint(0),sellBOPoint(0),longBreakPt(0),
	shortBreakPt(0),longFBOPoint(0),shortFBOPoint(0),barCount(0),buysToday(0),
	sellsToday(0),mp(0),longLiqPoint(0),shortLiqPoint(0),
	longLiqPoint1(0),shortLiqPoint1(0),intraTradeHigh(0),intraTradeLow(999999);


{Just like we did in the psuedo code -- let's start out with the daily 
 bar calculations.  If Date <> Date[1] -- first bar of day}
if(Date <> Date[1]) then {save time by doing these calculations once per day}
begin
 	averageRange = Average(Range,10) of Data2; {Data 2 points to daily bars}
 	
	canTrade = 0;
    if range of data2 < averageRange then canTrade = 1;

	{use close of data2 - seems to be more accurate than CloseD(1)
	buyEasierDay =Close of Data2 >= Close[1] of Data2;
	sellEasierDay = Close of Data2 <  Close[1] of Data2;

	buyBOPoint = Open + thrustPrcnt1*averageRange;
	sellBOPoint= Open - thrustPrcnt2*averageRange;
	
	if(sellEasierDay) then
	begin
		sellBOPoint= Open - thrustPrcnt1*averageRange;
		buyBOPoint = Open + thrustPrcnt2*averageRange; 
	end;

	longBreakPt = HighD(1) + breakOutPrcnt*averageRange;
	shortBreakPt=  LowD(1) - breakOutPrcnt*averageRange;

	shortFBOPoint = HighD(1) - failedBreakOutPrcnt*averageRange;
	longFBOPoint=  LowD(1) + failedBreakOutPrcnt*averageRange;

{Go ahead and initialize any variables that we may need later on in the day}
	
	barCount = 0;
	buysToday = 0;sellsToday = 0;{You can put multiple statements on one line}	
end;
First Modules of SuperCombo 2020

Here I am just setting up the inputs and variables that I will need to execute the algorithm.  If you are using .D data then the code

if date <> date[1] then

is a valid test for the first bar of the day.  A new date will represent the beginning of the next day.  The code controlled by this if-then construct is only executed one time per day.  So if you can put the lion’s share of daily calculations here, then it should speed stuff up.  The first thing I do is calculate the average range of the last 10 daily bars.  I access this date from data2.  Can you build a loop and accumulate the difference between the HighD and LowD function calls?

  1. for i = 1 to 10 begin
  2.      sum = sum + (HighD(i) – LowD(i));
  3. end;

The HighD() and LowD() functions are EasyLanguage enhancements that can help eliminate the need for a multi-data chart.  However, if you do this, you will get an warning message that its not a good idea.  I have done this and it seems to work, but to be safe just use Data2.    Next I determine if there has been a narrow range or range compression by comparing yesterday’s range to the averageRange.  If so, then I allow trading.  This is an old filter that looks for range expansion after compression.  The concept of a buyDay and sellDay was originated in the 1930s by George W. Cole (correct me if I am wrong here).  I use this idea by comparing the prior two bars closing relationships.  If there has been an up close, then I consider the next day to be a buyEasier day.  If the opposite is true, then its a sellEasier day.   This system isn’t unidirectional and does allow buying  and shorting in the same session – hence the word easier.   Continuing I calculate the levels that if the market reaches will hopefully trigger a short term trend in that direction.  This is the once highly respected open range break out or ORBO.  This methodology has lost its luster over the last 10 years or so due to overnight trading and allowing pent up buying and selling to be expressed in the overnight sessions.  Twenty years ago it was still viable.  The next bit of code creates the break out levels based on the buyEasier or sellEasier days.   The thrust is calculated by multiplying the range by thrustPrcnt1 and thrustPrcnt2.

So that is method 1 – break out.  Hope the market breaks out and continues to the close.  I wish it were this easy.  Since its not, the second methodolgy, FailedBreakOut, is calculated.  This is also known as the “ClearOut” trade.   The market is pushed to take out all the buy stops and then pulled back for the professionals to feast on the amateurs.  SuperCombo tries to take advantage of this by calculating the two points to determine a failed break out.  On the long side, it is the two points the market rises up to and then falls back to.  If the market breaches the longBreakPt, then look to sellShort at the shortFBOPoint.    Here is the next module

{Now lets trade and manage on 5-min bars}

barCount = barCount + 1; {count the number of bars of intraday data}
if(barCount >= waitPeriodMins/BarInterval and canTrade = 1) then {have we waited long enough}
begin
	if(MarketPosition = 1) then buysToday = 1;
	if(MarketPosition =-1) then sellsToday= 1;
	
	if(buysToday = 0 and Time < initTradesEndTime) then
		Buy("LBreakOut") next bar at buyBOPoint stop;
		
	if(sellsToday= 0 and Time < initTradesEndTime) then 
		SellShort("SBreakout") next bar at sellBOPoint stop;
				
	if(highD(0) > longBreakPt and sellsToday = 0 and Time < initTradesEndTime) then
		SellShort("SfailedBO") next bar at shortFBOPoint stop;
	if(lowD(0) < shortBreakPt and buysToday = 0 and Time < initTradesEndTime) then
		Buy("BfailedBO") next bar at longFBOPoint stop;
		
Monitor Market Action and Place Trades Accordingly

 

if(barCount>= waitPeriodMins/BarInterval and canTrade = 1) then

Forces the logic to flow only if canTrade is 1 and we have waited for amateur hour to be completed – well 30 minutes to be accurate.  Is the first hour really amateur hour?  I don’t think this applies, but if you think it does this is how you control trading prior to the completion of this period.  By dividing by BarInterval and counting each bar you can generalize this code for any time resolution.   If MarketPosition is 1 then you know you entered a long position and the opposite is true for short positions.  Only place the break out orders if time is less than initTradesEndTime.  If the market penetrates the long and shortBreakPts, then prepare to take advantage of a failed breakout.  Only go short if a short position has not already been entered – same for longs.  So, this logic places the breakOut and failedBreakOut orders.  Now for the last module.

{The next module keeps track of positions and places protective stops}

	mp = marketPosition;
	if(MarketPosition = 1) then
	begin
		longLiqPoint = EntryPrice-protStopPrcnt1*averageRange;
		longLiqPoint = MinList(longLiqPoint,EntryPrice - protStopAmt);
		longLiqPoint1 = EntryPrice - protStopPrcnt2*averageRange;
		longLiqPoint1 = MinList(longLiqPoint1,EntryPrice - protStopAmt);
		if Maxpositionprofit >= breakEvenPrcnt*averageRange*bigPointValue then
		begin
			longLiqPoint = EntryPrice;  {Breakeven trade}
			longLiqPoint1 = EntryPrice;  {Breakeven trade}
		end;
		if(Time >= initTradesEndTime) then
		begin
			longLiqPoint = MaxList(longLiqPoint,Lowest(Low,3)); {Trailing stop}
			longLiqPoint1 = MaxList(longLiqPoint1,Lowest(Low,3)); {Trailing stop}
		end;
		if(Time < liqRevEndTime and sellsToday = 0 and 
		longLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then
			SellShort("LongLiqRev") next bar at longLiqPoint stop;
			 
		Sell("LongLiq-BO") from entry("LBreakOut") next bar at longLiqPoint stop;
		Sell("LongLiq-FBO") from entry("BFailedBO") next bar at longLiqPoint stop;
		Sell("LongLiq-RLoss") from entry("ShortLiqRev") next bar at longLiqPoint1 stop;
	end;
	if(MarketPosition =-1) then
	begin	
		shortLiqPoint = EntryPrice+protStopPrcnt1*averageRange;
		shortLiqPoint = MaxList(shortLiqPoint,EntryPrice + protStopAmt);
		shortLiqPoint1 = EntryPrice + protStopPrcnt2*averageRange;
		shortLiqPoint1 = MaxList(shortLiqPoint1,EntryPrice + protStopAmt);
		if maxPositionProfit >= breakEvenPrcnt*averageRange*bigPointValue then
		begin
			shortLiqPoint = EntryPrice;  {Breakeven trade}
			shortLiqPoint1 = EntryPrice;
		end;
		if(Time >= initTradesEndTime) then
		begin
			shortLiqPoint = MinList(shortLiqPoint,Highest(High,3)); {Trailing stop}
			shortLiqPoint1 = MinList(shortLiqPoint1,Highest(High,3)); {Trailing stop}
	    end;
		if(Time < liqRevEndTime and buysToday = 0 and 
		shortLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then
			Buy("ShortLiqRev") next bar at shortLiqPoint stop;
			
		BuyToCover("ShortLiq-BO") from entry("SBreakOut") next bar at shortLiqPoint stop;
		BuyToCover("ShortLiq-FBO") from entry("SFailedBO") next bar at shortLiqPoint stop;
		BuyToCover("ShortLiq-RLoss") from entry("LongLiqRev") next bar at shortLiqPoint1 stop;			
	end;
end;
SetExitOnClose;
TradeManagement (Enter on Stop Loss or Not?)

This code looks a little hairy, but its not.  Let’s just look at the long side logic to save time here.  First let’s calculate the LongLiqPoints (1 and 2.)  Twenty years ago I thought it would be better to have a smaller stop for entries that occurred on a LiquidationReversal.  Oh yeah that code is in here to.  Back in the day I wanted to make sure the stop was at least 3 handles – ha, ha, ha – no really I am serious.  Really.  Stop laughing!! That code could be eliminated.  After calculating these two points I start to monitor profit and if it reaches a predetermined level I pull the the longLiqPoints toa  BreakEven stop.  If you are fortunate to still be in a trade after initTradesEndTime, then I start trailing the stop by the lowest low of the last 3 five minute bars – I don’t want to turn a small winner into a loser.  Now this is the fun stuff.

  1. if(Time < liqRevEndTime and sellsToday = 0 and
    longLiqPoint <> EntryPrice and BarsSinceEntry >= 4) then
  2.      SellShort(“LongLiqRev”) next bar at longLiqPoint stop;

If time is less than liqRevEndTime and BarsSinceEntry, then reverse and go short at the longLiqPoint stop.  Do this instead of liquidating.  I thought if the market reversed course quickly, then I wanted to take advantage of this counter trend move.  Eliminating this to see if it has any impact would be where I would start to play around with the template.  Okay now the liquidations based on from Entry take place next.  If I am long from a “ShortLiqRev“, then I use longLiqPoint1 instead of longLiqPoint.  Okay that last part was the kitchen sink.  Now you have enough code to make your own day trading system – really too much code, but you should be able to hobble something together from these parts.  Let me know if you can create your own Frankenstein monster.  I will update the parameters to see if there is any hope to the system as a whole.  Keep checking back for updated performance metrics.  Best to all and be safe!

 

 

Please follow and like us:

A Slightly More Eloquent Approach to Programming Our Pyramiding E-Mini DayTrading Algorithm.

Okay let’s see how I was able to add some eloquence to the brute force approach to this pyramiding algorithm.  The original code included multiple entry directives and a ton of hard coded numerical values.   So let me show you how I was able to refine the logic/code and in doing so make it much more flexible.  We might lose a little bit of the readability, but we can compensate by using extra commentary.

First off, let’s add flexibility by employing input variables.  In this case, we need to inform the algorithm the distance from the open to add additional positions and the max number of entries allowed for the day.

inputs : pyramidDistance(5),maxDailyEntries(3);

Now we need to set somethings up for the first bar of the day.  Comparing the date of today with the date of yesterday is a good way to do this.

if d<>d[1] then 
begin
	canSell = true;
	sellMult = 1;
	sellStop = -999999;
	entries = 0;
end;
First bar of the day housekeeping.

Here is a neat way to keep track of the number of entries as they occur throughout the trading day.  Remember the function EntriesToday(date) will not provide the information we need.

mp = marketPosition * currentShares;

if mp[1] <> mp and mp <> 0 then entries = entries + 1;
How to track the number of entries for today.

If the last bar’s mp[1] is not equal to the current bar’s mp then and mp is not equal to zero then we know we have added on another entry.  Okay now let’s think about eliminating the “brute force” approach.

Instead of placing multiple order entry directives I  only want to use one with a variable stop level.  This stop level will be guided by the variable SellMult.  We start the day with a wacky sell stop level and then calculate it based on the SellMult variable and PyramidDistance input.

if low <= sellStop  then
begin
	sellMult = sellMult + 1;
end;

sellStop = openD(0) - sellMult * pyramidDistance;
Calculate and adapt sell stop level as we go along.

So on the first bar of the day the sellStop = openD(0) – sellMult * pyramidDistance or sellStop = openD(0) – 1 * 5.  Or 5 handles below the open.  Note you an change the pyramidDistance input and make it three to match the previous examples.

if entries = maxDailyEntries then canSell = false;
if time < sess1EndTime and canSell then sellShort 1 contract next bar at sellStop stop;
if mp <=-1 {and barsSinceEntry > 0} then buyToCover next bar at sellStop + 2* pyramidDistance stop;

setexitonclose;
That's it! Pretty simple isn't it?

Ok, we need to tell the computer to turn off the ability to place orders if one of two things happens:  1) we have reached the maxDailyEntries or 2) time >= sess1EndTime.    You could make the time to stop entering trades an input as well.  If neither criteria applies then place an order to sellShort at our sellStop level.   If price goes below our sell stop level then we know we have been filled and the new sellStop level needs to be recalculated.  See how we use a calculation to adapt the stop level with a single order placement directive?  This is where the eloquence comes into play.  QED.

Now you code the opposite side and then see if you can make money  (hypothetically speaking of course) with it.  If you think about it, why does this not work.  And the not so obvious reason is that it trades too much.  Other than trading too much it makes perfect sense – buy or sell by taking a nibbles at the market.  If the market takes off then take a big bite.  The execution costs of the nibbles are just way too great.  So we need to think of a filtering process to determine when it is either better to buy or sell or when to trade at all.  Good Luck with this ES [emini S&P ]day trading algorithm!

inputs : pyramidDistance(5),maxDailyEntries(3);
vars: mp(0),icnt(0),sellStop(0),sellMult(0),canSell(true),entries(0);

if d<>d[1] then 
begin
	canSell = true;
	sellMult = 1;
	sellStop = -999999;
	entries = 0;
end;

mp = marketPosition * currentShares;

if mp[1] <> mp and mp <> 0 then entries = entries + 1;
if mp[1] = -1 and mp[0] = 0 then canSell = false;
if time > 1430 then canSell = false;

if low <= sellStop  then
begin
	sellMult = sellMult + 1;
end;

sellStop = openD(0) - sellMult * pyramidDistance;
if entries = maxDailyEntries then canSell = false;
if time < sess1EndTime and canSell then sellShort 1 contract next bar at sellStop stop;
if mp <=-1 {and barsSinceEntry > 0} then buyToCover next bar at sellStop + 2* pyramidDistance stop;

setexitonclose;
Much More Flexible Code
Please follow and like us: