Tag Archives: Finite State Machine

Unlocking Sequential™ in EasyLanguage via Dueling Finite State Machines

Disclaimer:
“Sequential™” is a registered trademark of Tom Demark. This post presents an independent, educational interpretation of the components of the Sequential™ pattern as described in Tom Demark’s book, The New Science of Technical Analysis. The analysis, opinions, and code examples provided herein are solely those of the author and are intended for informational and educational purposes only. This work is not affiliated with, endorsed by, or officially connected to Tom Demark or any related entities.

Sequential™ Pattern – Setup and Countdown

This pattern is fully described in Tom Demark’s book and consists of two distinct phases.  For brevity’s sake, I will just discuss the buy setup.  This indicator is designed to help determine when a trend is becoming or has become exhausted.  Unlike a trend following indicator that helps you get in at the genesis of the trend, Sequential indicates when to take an opposing position after trend termination.  This post doesn’t concern itself with the efficacy of Sequential, but with the process of programming such a difficult pattern and all the conditions that it involves.  The indicator consists of two parts or phases. The Setup phase is stringent, requiring that the same price pattern occur for at least nine consecutive days (bars). In contrast, the Countdown phase is less strict; it mandates that a different price pattern occurs over a span of 13 days (or bars), although these occurrences do not need to be consecutive.  Setup is complete when an “intersection” occurs, marking the point where prices start to brake or consolidate. Countdown, on the other hand, is finished when the 13th instance of the designated price pattern is observed. Because the pattern in the Countdown phase does not have to appear on consecutive bars, this phase can take many days to complete.  During Countdown, three scenarios can occur that either restarts or recycles the process.

Setup – Sequence

  1. close[0] > close[4]
  2. followed by nine consecutive Close[0] < Close[4]

Intersection- Sequence

  • If nine bars fulfill the Setup then examine the following
    • Bar 8  High[1] > Lowest(Low[4],5)
    • Bar 9  High[0] > Lowest(Low[3],5)

Countdown – Sequence

  • 13 days or bars fulfill Close[0] < Low[2] over any number of days

Sequential™ – Completion, Restart, Recycle

  • Completion – once Countdown reaches 13, the Sequential pattern is completed.
  • Restart – during countdown if a Close > Highest High during setup or a Sell Setup occurs – start either from scratch or start the Sell Countdown
  • Recycle – a new Buy Setup occurs, then start from the Countdown phase again.

Because we have to monitor a Sell Setup during the Countdown phase, we need to run two Finite State Machines concurrently.  These two state machines will duel with each other.  Both searching for their own solutions and knocking each other out during the process.

Finite State Machine Structure

Years ago, I embarked on building a theoretical compiler—a challenging project I nearly finished. The initial step was to create a parser that transforms high-level code into tokens according to the language’s grammar. In doing so, I learned about Finite State Machines (FSMs) as my program processed source code one character at a time and used FSM logic to build a token table.

Sample FSM to Find Pivot Highs and Pivot Lows

From this experience, I quickly discovered that even the most complex patterns can be detected using a Finite State Machine. Below is a graphical representation of a simple FSM that identifies the following pattern that can take up to 90 days (bars) to complete.

  1. A high pivot with strength 2

  2. Followed by a low pivot with strength 2

  3. Followed by another high pivot with strength 2

In this example, a pivot is defined such that the central (or “pivot”) bar must have a higher high (for a high pivot) or a lower low (for a low pivot) than both the two bars preceding it and the two bars following it. Additionally, I also added the high pivot requires that the two prior bars exhibit ascending highs and that the two subsequent bars exhibit descending highs, while a low pivot follows the opposite pattern.

Finite State Machines (FSMs) consist of a limited number of states that describe the various conditions of a system:

  • Start State: The initial point where processing begins.  Looking for the first Pivot High.
  • Intermediate States: The stages the FSM progresses through as it processes input.  Looking for the first Pivot Low.
  • Terminal (or Accepting) States: The final state(s) indicating the system has completed its task.  Locating the final Pivot High.

Transition logic (the “rules” for shifting between states) guides the FSM’s movement, and some FSMs include a timeout function that resets the machine if it stays in one state too long.

Transition from FSM State 0 to FSM State 1

Acting like Pac-Man, the FSM gobbles one bar at a time and looks for this pattern:

  • high[2] >high[1] – right side
  • high[1] >high[0] – right side
  • high[2] > high[3] – left side
  • high[3] > high[4] – left side

Transition from FSM State 1 to FSM State 2

Now we look for the specific low pivot pattern

  • low[2] <low[1] – right side
  • low[1] <low[0] – right side
  • low[2] < low[3] – left side
  • low[3] < low[4] – left side

Transition from FSM State 2 to Completion and then back to FSM State 0

The pattern is completed after the subsequent high pivot pattern is confirmed.  Once completed the machine resets itself to FSM State 0.

  • high[2] >high[1] – right side
  • high[1] >high[0] – right side
  • high[2] > high[3] – left side
  • high[3] > high[4] – left side

FSM Clock Override

If the pattern is not recognized within 90 days or bars from the first pivot high, then the machine resets back to FSM State 0.

Pivot Point FSM Output

Simple FSM Output

Switch Case in EasyLanguage

When I started programming in Python, the Case statement was not included which shocked me.  Since Python 3.10 it has been introduced.  A Switch Case structure lets a program choose among several execution paths based on a variable’s value, using distinct cases instead of long if-else chains. This results in cleaner, more efficient, and more readable code.  Take a look at the syntax of the Switch Case in EasyLanguage – remember much of the following code is dedicated to painting the bars.


Inputs:
PivotStrength(3); // Strength parameter for pivot high detection

Variables:
FSMState(0),barCount(0),j(0); // FSM state: 0 (none), 1 (first), 2 (second), 3 (third)

switch (FSMState)
begin
case 0:
begin
if h[2] > h[3] and h[3] > h[4] and h[2] > h[1] and h[1] > h then
begin
FSMState = 1;
Print(d," First pivot high found: ",h[2]);
barCount = 0;
for j = 0 to 4
begin
plotPB[j](h[j],l[j],"FSM PVT Patt.",yellow);
end;
end;
end;

case 1:
begin
if l[2] < l[3] and l[3] < l[4] and l[2] < l[1] and l[1] < l then
begin
FSMState = 2;
for j = 0 to 4
begin
plotPB[j](h[j],l[j],"FSM PVT Patt.",red);
end;

end;
end;

case 2:
begin
if h[2] > h[3] and h[3] > h[4] and h[2] > h[1] and h[1] > h then
begin
FSMState = 0;
for j = 0 to 4
begin
plotPB[j](h[j],l[j],"FSM PVT Patt.",cyan);
end;
end;
end;

end; // End case-switch

barCount = barCount + 1;
if barCount = 90 then
FSMState = 0;
Simple FSM to locate Pivot Point Pattern

The syntax is straightforward: the keyword switch is used, and the variable FSMState directs the flow through different case blocks. Initially, FSMState is set to 0, and a transition occurs only when the specified criteria are met. Once met, FSMState is updated to 1. EasyLanguage follows a non-fall-through paradigm—once a state transition occurs, no other case statements are evaluated during that iteration; the program simply reaches the end of the block and awaits the next cycle. By contrast, in some languages, when the state changes (for example, from 0 to 1), the corresponding case for state 1 may be evaluated immediately within the same cycle.

Is it as Complicated as it Looks?

Not at all.  Look at the different case blocks and you will see a very similar structure.  As stated earlier the code to paint the bars take up 12 lines of code.  The timer is located at the bottom of the code – once barCount = 90, the FSM resets to 0.

Is Sequential ™ Easy to Program with a Finite State Machine?

I wouldn’t say easy, but I wouldn’t say hard either.    With a little elbow grease and knowledge of how TradeStation works, and some EasyLanguage knowledge it is not difficult.  If it were easy, you would see the code all over the place.  I have previously programmed parts of it in my Easing into EasyLanguage books.

You might think you could have ChatGPT generate the code for you, and for laughs, I tried asking ChatGPT to program Sequential™ using FSM and Switch/Case in EasyLanguage. While the output provided a foundation, most of the syntax was off, and the patterns weren’t defined properly. Ultimately, I discovered that simplifying the design—by using separate FSMs for the buy side and the sell side—made the implementation more manageable. To simplify the process, I focused exclusively on the buy side at first. I figured that once I had programmed the complete pattern for the buy side, adapting it to the sell side would be as simple as reversing the logic—since the overall structure remains identical.

Using “Backward Scanning” for the Setup Phase.

Setup requires analyzing at least 10 consecutive bars. When you hear “consecutive,” think of looping through each bar in sequence. A stringent consecutive pattern is best uncovered by looping back through historical data. For example, you can loop through the bars to identify a sequence of nine consecutive bars where the close of the current bar is less than the low from four bars prior. However, immediately preceding those nine bars, you must verify that the prior bar’s close is greater than the close from four days earlier. This additional condition ensures the pattern begins under the correct circumstances.

Is there a Method to this Madness?

I’ve found that using methods in EasyLanguage is a real asset during development. Methods work like functions but are local to the module in which they’re defined, so all the code is right there for easy reference, debugging, and testing. I typically reserve methods for code segments that I’ll reuse multiple times, which helps keep my project organized and efficient as it grows.  Here is the method that uses back scanning to uncover the Sequential Setup phase.

method bool seqBuySetup(int numConsDays)
var: int result;
begin

result = countif(c[0] < c[LookBack],suTarg);
if result = numConsDays and c[suTarg]>c[suTarg+LookBack] then
return(True)
else
return(False);
end;
Notice the syntax of the method structure

You might not recognize all parts of this code at first glance. First, I’m using a method, and I must specify its return type as bool (Boolean) in the method header. This indicates that the method will return either True or False. I’m also passing an integer variable, numConsDays, into the method.

Inside, I use the countIf function to evaluate the relationship between close[0] and close[4]—with LookBack set to 4. Essentially, countIf counts how many times the condition close[0] < close[4] is met over a span of bars defined by suTarg (or SetUpTarg). If this count equals 9, I then compare close[9] with close[13]. If close[9] is greater, I determine that this portion of the Setup phase has been successfully completed.

Does close[0] mean today’s close or the close of what George has penned as the close of the Focus Bar.

  • close[0] – close of today – not really unless it is the last bar on the chart – we could call this the Focus Bar if not
  • close[1] – yesterday – 1 day back or 1 day prior to the focus bar
  • close[2] – 2 days prior
  • close[3] – 3 days prior
  • close[4] – 4 days prior

When you run a backtest in TradeStation, the engine iterates through every bar in your dataset—even though your chart window only shows a subset of those bars. To make this clear, I use the term Focus Bar for whichever bar is currently being processed as the system moves from left to right. Think of it like a big loop over all bars: when the loop index is 50, bar #50 is the Focus Bar and you reference its values with [0] (e.g., close[0]). When the loop advances to index 51, bar #51 becomes the Focus Bar—still accessed with [0]—and so on.

Here’s the key point: all price series—high, low, open, etc.—are zero‑indexed, so the Focus Bar is always referenced with index 0. That means the Focus Bar’s closing price is close[0], its high is high[0], and its low is low[0]. For instance, if the Focus Bar is dated January 3, 1999, and it completes the Setup phase, you’d paint it using high[0] and low[0]. Remember, close[0] only represents “today’s” close when you’re on the very last bar; otherwise, it simply refers to whatever bar is currently the Focus Bar during a historical back-test.

Take a look at the following method to see this in action.

method void paintBuySetup(int numPaintDays)
var: int j;
begin
PlotPB[suTarg](High[suTarg],Low[suTarg],"DMSeq.",cyan,3);
for j = 0 to numPaintDays-1
begin
PlotPB[j](High[j],Low[j],"DMSeq.",yellow,3);
end;
end;
Looping from bar 0 to bar 8 or nine bars

Notice how I loop from 0 to numPaintBars-1 or 8 to paint the last nine bars in the sequence.  If you want to paint bars in a back-scan make sure you use the same syntax I have used here.  Use an offset for the PlotPB along with the same offset for each bar’s high and low in the loop.

 PlotPB[j](High[j],Low[j],"DMSeq.",yellow,3); // j goes from 0 to 8

If I want to compare the 10th bar in my series, then I refer to it as close[9].  I compare the 10th bar with 13th bar (close[9 + 4]) to see if I have the genesis of the Setup phase.  Bar number 0 is the first bar in the series going back in time (Focus Bar.)  Could I offset everything by one bar to get rid of the 0 offset?   Many people have a problem dealing with 0s so you could but if you want to turn this into a strategy and you want to execute on the next bar’s open, then you will need to stick with the 0.  Let’s break each state down and you will see how I was able to program this monster.

FSM State 0 – Scanning for the first part of the Setup Phase

    Case 0:
// Find a long setup - retroactively
if seqBuySetup(suTarg) then
begin
stateBuy = 1;
SetupCountBuy = suTarg;
SetUpHigh = highest(h,suTarg);
if plotBuySetUp then
paintBuySetUp(suTarg);
end;

If the seqBuySetup method returns True, the FSM transitions to State 1 and SetupCount is set to 9. This triggers a look-back over the past nine bars to determine the highest high, which serves as a reset level in the Countdown phase if a close exceeds that value.

In the PaintBar routine, the user can choose to display the Sequential Buy Setup, the Sell Setup, or both. If the user opts to display the Buy Setup, the paintBuySetUp method is executed.

FSM State 1 – Looking for an Intersection

This state uses a hybrid approach to detect an intersection. Now that we’re in FSM State 1, we’re close to completing the first phase. We start by examining bars 8 and 9, which are the final two bars of the nine-bar setup from the latest data. First, we check if the high of bar 8 is greater than any of the low values in bars 5, 4, 3, 2, or 1—if it is, an intersection is found. If not, we compare the high of bar 9 with the low values in bars 6, 5, 4, 3, or 2. Should bar 9 also fail to meet the criteria, we then switch to forward scanning for any subsequent bar that fits the criteria. Note that the bars identified in the forward scan do not have to meet the same stringent conditions as the consecutive bars in the Setup phase.  Eventually, a bar will meet the criteria, and we then can move to the Countdown phase also known as FSM State 2

    Case 1:
// Setup complete now look for intersection
// may take a couple of days
if SetupCountBuy = suTarg then
begin
//going back to get lowest lows of 5 bars
//prior to bar 8 and bar 9
minLow1 = lowest(low[4],5);
minLow2 = lowest(low[5],5);
if (High[1] > minLow1 or High[2] > minLow2) then
begin
stateBuy = 2;
CountdownCountBuy = 0;
if close <= Low[2] then
begin
CountdownCountBuy = 1;
if PlotBuySetup then
PlotPB(High,Low, "DMSeq.",green);
end;
buyIntersectBarNum = barNumber;
Value1 = MyColors("Orange");
Value2 = iff(High[1] < minLow1,2,1);
if PlotBuySetup then
PlotPB[value2](High[value2],Low[value2], "DMSeq.",value1); // paint intersection
end;
end;
Intersection

The Devil is in the Details

Counting bars is challenging. Because our FSM doesn’t fall through states after a transition, once the consecutive nine-bar sequence is complete, we’re already at the next bar—the tenth. To determine whether the high of the ninth bar exceeds the lows from earlier bars, I compare the high of bar 9 (denoted as high[1]) with the lowest low from three bars earlier (low[4]) across a range covering five bars. If necessary, I repeat a similar comparison, starting with the high of bar 8, using high[2] and the corresponding lows starting from low[5] over a five-bar range.   There is a chance neither bar will fulfill the criteria.  In this case we start forward scanning to see if high[1] fulfills the criteria.  Could we check for high[0] if the bar 8 and bar 9 fail?  You might be able to – it might be worth investigating. However, it will add more code.  Given some time you’ll see that high[1] will form an intersection. For clarity, I introduced the term Focus Bar to refer to the bar at index [0] in the historical data. In this context, if high[1] represents the prior bar relative to the Focus Bar and meets the intersection criteria, we need to immediately assess the Focus Bar to determine if it signals the start of the Countdown phase. Why act now? Because, without fall-through in our FSM, once a state transition occurs, the Focus Bar, if examined in the next state, would be skipped—so it’s essential to evaluate the Focus Bar (today’s bar if last bar on chart) right away while we are in the current state.  If close[0] < low[2], the bar is painted with the Countdown theme color and this phase begins and the state machine transitions

FSM State 2 – Looking for a completion of Countdown

We are almost there.  All we need are 13 bars that fulfill this criteria, close[0] < low[2].  From this point on we will be forward scanning and counting bars that fit the previously mentioned criteria.  Once we reach 13, we are done.  Finally, our journey to program the Sequential has come to an end.  Or has it?  So far, the description of the pattern has been straightforward, and we are in the home stretch?   The completion of 13 bars who’s close[0] < low[2] can take many days to complete and many things can happen during this time.   In his book, Tom Demark mentions three things that can derail the completion of the Countdown phase.  This is where the fun really begins.

	Case 2:
// Sell Countdown Phase
if close <= Low[2] then
begin
CountdownCountBuy = CountdownCountBuy + 1;
if PlotBuySetup then
PlotPB(High,Low, "DMSeq.",green); // Generate a buy signal
end;
if CountdownCountBuy = ctTarg then
begin
if PlotBuySetup then
begin
value99 = Text_new(d,t,low - range*0.2,"B");
PlotPB(High,Low, "DMSeq.",red); // Generate a buy signal
end;
resetBuyFSM();
end;
Portion of FSM State 2 that looks for and paints Countdown bars

This code is very simple – paint the bar if close[0] < low[2] and then count the bar.  Once the number of bars = ctTarg (CountDown Target). then place the letter “B” above the high and paint the bar a different color.  Sequential is now complete and so the Buy Finite State Machine needs to be reset.  Bar are we really done?

Countdown derailment

  • If a close exceeds the highest high during Setup – restart the process from scratch – reset the FSM
  • If a Sell Setup completes while waiting for the 13 bars – cancel the Buy Countdown and start the Sell Countdown
  • If a fresh Buy Setup reveals itself, recycle and restart the Countdown process.
       // Invalidate buy countdown if 
// 1.) a close > high during setup
// 2.) a sell setup occurs
// 3.) recycling occurs - new buy setup
if c > SetupHigh then
resetBuyFSM();
if stateSell = 2 then
resetBuyFSM();
if (seqBuySetup(suTarg) and barNumber - buyIntersectBarNum > suTarg) then
begin
stateBuy = 1;
SetupCountBuy = suTarg;
CountdownCountBuy = 0;
if plotBuySetUp then
paintBuySetup(suTarg);
SetUpHigh = highest(h,suTarg);
end;
2ND half of FSM State 2

The Dueling Nature of this Pattern Recognition Tool often prevents Sequential from reaching Completion – maybe a good thing.

Well, that is part of the reason.  The recycle of the Setup is also a culprit.  Because the pattern for the Buy and Sell requires the consumption of so many bars, the two FSM must run independent of each other and at times contradict each other.  Here is as good example of the FSMs taking action in the recent (April 2025) GOLD market.  Click on images to expand.

Wow! What a great buy!
It started out really great, but like any Trend Following approach…
Months to Complete

Sequential™ Examples

Shampoo, Rinse, Repeat.  Bingo!

We had 4 complete buy Setups with interrupted Countdowns

We had 4 complete buy Setups with interrupted Countdowns before it finally stuck!

Count-Downus Interruptus

Blow off top with Buy Setup completion interrupts the short Countdown.

Sell Countdown interrupted by Buy Setup

Market congestion, just like smoking, stunts growth of patterns.

Congestion Phase validated by incomplete Sequential Setups and Countdowns

Many Complex Patterns can be Programmed by using Multiple FSMs

Almost anything can be programmed with EasyLanguage and the concept of a Finite State Machine and the Switch-Case structure.  If you don’t know where to start, ask ChatGPT with the best and most descriptive prompt you can come up with.  Then start small and build up – always check your progress before moving on to the next phase.  Email me with any questions and if you like this type of content check out my books at Amazon.

George’s Amazon Author Page

Implementing Finite State Machine Functionality with EasyLanguage (Last Trade Was Loser Filter a la Turtle)

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-daysLxp 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

Last Trade Loser In Action

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.