September 9

0 comments

Prune Your Trend Following Algorithm

By George Pruitt

September 9, 2024

Algorithmic Trading, logic

Multiple trading decisions based on “logic” may not add to the bottom line

In this post, I will present a trend following system that uses four exit techniques. These techniques are based on experience and also logic. The problem with using multiple exit techniques is that it is difficult to see the synergy that is generated from all the moving parts. Pruning your algorithm may help cut down on invisible redundancy and opportunities to over curve fit. The trading strategy I will be presenting will use a very popular entry technique overlaid with trade risk compression.

Entry logic

Long:

Criteria #1: Penetration of the closing price above an 85 day (closing prices) and 1.5X standard deviation-based Bollinger Band.

Criteria #2: The mid-band or moving average must be increasing for the past three consecutive days.

Criteria #3: The trade risk (1.5X standard deviation) must be less than 3 X average true range for the past twenty days and also must be less than $4,500.

Risk is initially defined by the standard deviation of the market but is then compared to $4,500. If the trade risk is less than $4,500, then a trade is entered. I am allowing the market movement to define risk, but I am putting a ceiling on it if necessary.

Short:

Criteria #1: Penetration of the closing price below an 85 day (closing prices) and 1.5X standard deviation-based Bollinger Band.

Criteria #2: The mid-band or moving average must be decreasing for the past three consecutive days.

Criteria #3: Same as criteria #3 on the long side

Exit Logic

Exit #1: Like any Bollinger Band strategy, the mid band or moving average is the initial exit point. This exit must be included in this particular strategy, because it allows exits at profitable levels and works synergistically with the entry technique.

Exit #2: Fixed $ stop loss ($3,000)

Exit #3: The mid-band must be decreasing for three consecutive days and today’s close must be below the entry price.

Exit #4: Todays true range must be greater than 3X average true range for the past twenty days, and today’s close is below yesterday’s, and yesterday’s close must be below the prior days.

Here is the logic of exits #2 through exit #4. With longer term trend following system, risk can increase quickly during a trade and capping the maximum loss to $3,000 can help in extreme situations. If the mid-band starts to move down for three consecutive days and the trade is underwater, then the trade probably should be aborted. If you have a very wide bar and the market has closed twice against the trade, there is a good chance the trade should be aborted.

Short exits use the same logic but in reverse. The close must close below the midband, or a $3,000 maximum loss, or three bars where each moving average is greater than the one before, or a wide bar and two consecutive up closes.

Here is the logic in PowerLanguage/EasyLanguage that includes the which exit seletor.

[LegacyColorValue = true];
Inputs:maxEntryRisk$(4500),maxNATRLossMult(3),maxTradeLoss$(3000),
indicLen(85),numStdDevs(1.5),highVolMult(3),whichExit(7);

Vars: upperBand(0), lowerBand(0),slopeUp(False),slopeDn(False),
largeAtr(0),sma(0),
initialRisk(0),tradeRisk(0),
longLoss(0),shortLoss(0),permString("");

upperBand = bollingerBand(close,indicLen,numStdDevs);
lowerBand = bollingerBand(close,indicLen,-numStdDevs);
largeATR = highVolMult*(AvgTrueRange(20));

sma = average(close,indicLen);

slopeUp = sma>sma[1] and sma[1]>sma[2] and sma[2]>sma[3];
slopeDn = sma<sma[1] and sma[1]<sma[2] and sma[2]<sma[3];

initialRisk = AvgTrueRange(20);
largeATR = highVolMult * initialRisk;
tradeRisk = (upperBand - sma);
// 3 objects in our permutations
// exit 1, exit 2, exit 3
// perm # exit #
// 1 1
// 2 1,2
// 3 1,3
// 4 2
// 5 2,3
// 6 3
// 7 1,2,3

if whichExit = 1 then permString = "1";
if whichExit = 2 then permString = "1,2";
if whichExit = 3 then permString = "1,3";
if whichExit = 4 then permString = "2";
if whichExit = 5 then permString = "2,3";
if whichExit = 6 then permString = "3";
if whichExit = 7 then permString = "1,2,3";


{Long Entry:}
If (MarketPosition = 0) and
Close crosses above upperBand and slopeUp and
(tradeRisk < initialRisk*maxNATRLossMult and tradeRisk<maxEntryRisk$/bigPointValue) then
begin
Buy ("LE") Next Bar at Market;
End;

{Short Entry:}
If (MarketPosition = 0) and slopeDn and
Close crosses below lowerBand and
(tradeRisk < initialRisk*maxNATRLossMult and tradeRisk<maxEntryRisk$/bigPointValue) then
begin
Sell Short ("SE") Next Bar at Market;
End;
{Long Exits:}

if marketPosition = 1 Then
Begin
longLoss = initialRisk * maxNATRLossMult;
longLoss = minList(longLoss,maxTradeLoss$/bigPointValue);

If Close < sma then
Sell ("LX Stop") Next Bar at Market;;

if inStr(permString,"1") > 0 then
sell("LX MaxL") next bar at entryPrice - longLoss stop;

if inStr(permString,"2") > 0 then
If sma < sma[1] and sma[1] < sma[2] and sma[2] < sma[3] and close < entryPrice then
Sell ("LX MA") Next Bar at Market;
if inStr(permString,"3") > 0 then
If TrueRange > largeATR and close < close[1] and close[1] < close[2] then
Sell ("LX ATR") Next Bar at Market;
end;

{Short Exit:}

If (MarketPosition = -1) Then
Begin

shortLoss = initialRisk * maxNATRLossMult;
shortLoss = minList(shortLoss,maxTradeLoss$/bigPointValue);
if Close > sma then
Buy to Cover ("SX Stop") Next Bar at Market;

if inStr(permString,"1") > 0 then
buyToCover("SX MaxL") next bar at entryPrice + shortLoss stop;

if inStr(permString,"2") > 0 then
If sma > sma[1] and sma[1] > sma[2] and sma[2] > sma[3] and close > entryPrice then
Buy to Cover ("SX MA") Next Bar at Market;
if inStr(permString,"3") > 0 then
If TrueRange > largeAtr and close > close[1] and close[1] > close[2] then
Buy to Cover ("SX ATR") Next Bar at Market;
end;

Trend following with exit selector

Please note that I modified the code from my original by forcing the close to cross above or below the Bollinger Bands. There is a slight chance that one of the exits could get you out of a trade outside of the bands, and this could potentially cause and automatic re-entry in the same direction at the same price. Forcing a crossing, makes sure the market is currently within the bands’ boundaries.

This code has an input that will allow the user to select which combination of exits to use.

Since we have three exits, and we want to evaluate all the combinations of each exit separately, taken two of the exits and finally all the exits, we will need to rely on a combinatorial table. In long form, here are the combinations:

3 objects in our combinations of exit 1, exit 2, exit 3

* one – 1
* two – 1,2
* three – 1,3
* four – 2
* five – 2,3
* six – 3
* seven – 1,2,3

There are a total of seven different combinations. Given the small set, we can effectively hard-code this using string manipulation to create a combinatorial table. For larger sets, you may find my post on the Pattern Smasher beneficial. A robust programming language like Easy/PowerLanguage offers extensive libraries for string manipulation. The inStr string function, for instance, identifies the starting position of a substring within a larger string. When keyed to the whichExit input, I can dynamically recreate the various combinations using string values.

1. if whichExit = 1 then permString = “1”
2. if whichExit = 2 then permString= “1,2”
3. if whichExit = 3 then permString = “1,2,3”
4. etc…
As I optimize from one to seven, permString will dynamically change its value, representing different rows in the table. For my exit logic, I simply check if the enumerated string value corresponding to each exit is present within the string.

if inStr(permString,"1") > 0 then
sell("LX MaxL") next bar at entryPrice - longLoss stop;
if inStr(permString,"2") > 0 then
If sma < sma[1] and sma[1] < sma[2] and sma[2] < sma[3] and close < entryPrice then
Sell ("LX MA") Next Bar at Market;
if inStr(permString,"3") > 0 then
If TrueRange > largeATR and close < close[1] and close[1] < close[2] then
Sell ("LX ATR") Next Bar at Market;

Using inStr to see if the current whichExit input applies

When permString = “1,2,3” then all exits are used. If permString = “1,2”, then only the first two exits are utilized. Now all we need to do is optimize whichExit from 1 to 7. Let’s see what happens:

Combination of all three exits

The best combination of exits was “3”. Remember 3 is the permString that = “1,3” – this combination includes the money management loss exit, and the wide bar against position exit. It only slightly improved overall profitability instead of using all the exits – combo #7. In reality, just using the max loss stop wouldn’t be a bad way to go either. Occam uses his razor to shave away unnecessary complexities again!

If you like this code, you should check out the Summer Special at my digital store. I showcase over ten more trend-following algorithms with different entry and exit logic constructs. These other algorithms are derived from the best Trend Following “Masters” of the twentieth century. IMHO!

Here is a video you can watch that goes over the core of this trading strategy.

>> By George Pruitt from blog Georgepruitt.com

George Pruitt

About the author

I have a degree in computer science from UNC-Asheville. I was the Director of Research for Futures Truth (magazine and CTA) for 31 years and have authored many articles in magazines and trade journals. I have written and co-written eight books: The Easing into EasyLanguage Series (four books): Foundation, Hi-Res, Advanced Topics and DayTrade edition. Older books published by John Wiley: The Ultimate Trading Guide, Building Winning Trading Systems with TradeStation (two editions), The Ultimate Algorithmic Trading System Toolbox, Trend Following Systems-A DIY Project: Batteries Included! My research is now focused on day-trading and longer-term trend following systems.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Learn To Code & Build Strategies
Using EasyLanguage. 

>