Day Trading the 30-Minute Breakout, Again

The 30-Minute Breakout: A Classic Strategy Built on Simplicity

It seems like it is a good time, based on performance, to take another look at this simple model.  Just like bell bottom jeans, this simple approach flows in and out of fashion.  I revisited this strategy in my 2023 Easing Into EasyLanguage – Day Trading Edition book.

Most day traders spend hours glued to their monitors, constantly scanning charts and second-guessing every tick. But some of the most effective strategies take the opposite approach: one trade per day, simple execution, and fixed risk.

Back in 1998, I was sitting in a crowded ballroom at a trading convention in Orlando. The keynote speaker stepped up to the stage, pulled up a PowerPoint presentation, and unveiled a strategy that was already considered an “oldie” back then.  We had actually been trading derivatives of this approach for more than 15 years at this point.

“Oldie” is probably an appropriate word to use—the core concepts of the Opening Range Breakout (ORB) and the Opening Range Breakout with Pattern (ORBP) had already been floating around the industry for over two decades.

In fact, one of the most exhaustive works on the subject was published back in 1990 by Toby Crabel and released by my friend Ed Dobson at Traders Press: Day Trading with Short Term Price Patterns and Opening Range Breakout. Following the multi-billion-dollar success of Crabel’s firm, Crabel Capital Management, that single out-of-print hardcover became a true holy grail item—regularly fetching anywhere from $500 to over $1,000 on the secondary market.

You can even watch the team at Crabel Capital discuss how those core quantitative principles scaled up in this J.P. Morgan featured interview.

Getting back to 1998 – Looking around the room, people were transfixed. Slide after slide showed successful breakout after successful breakout.

Even today, breakout trading remains one of the most widely used entry techniques across the market. Why? Because this specific approach checks almost every box a trader could want:

  • One trade per day — Essential for preventing revenge trading, though modern market microstructure often causes early breakouts to fail. Hard-coding a rule to fade that initial failed move can actually turn a false breakout into a high-alpha opportunity.

  • No screen-staring — You don’t need to sit at your desk all session.

  • Defined risk — Built-in protection and zero overnight risk.

  • Easy automation — Follows the Keep It Simple, Stupid (KISS) principle to a T.

Here is how the classic setup works:

  1. Wait for the Setup: At 9:55 AM Eastern, look at your 5-minute chart. Wait for that 9:50–9:55 AM bar to close (completing the first 30 minutes of the trading day).

  2. Set Your Orders: Place a buy stop order one or two ticks above the highest high of those first six 5-minute bars, and a sell short order one or two ticks below the lowest low.

  3. Execute: Wait for one side to trigger. As soon as you get filled on one side, cancel the opposing entry order and convert it into your liquidation/stop-loss order.

  4. Manage Risk: Early market volatility can sometimes create a range that is too wide for your risk parameters. If the dollar risk between the channel high and low is greater than your personal risk tolerance, use a pre-set disaster stop or simply skip the trade.

Trading doesn’t have to be complex to be effective. Sometimes, stepping back and letting a classic rule-based strategy do the heavy lifting is the smartest move you can make.

Step 1: Fire Up the Code Editor

When a strategy with this kind of mileage crosses your desk and catches your eye, your first job isn’t to start placing trades—it’s to translate it into code.

Now, since the rules fit on the back of a napkin, you’d naturally assume writing the script would be a piece of cake. You can even prompt your favorite AI to whip up the code, and to its credit, it’ll get you about 80% of the way there. But as any veteran EasyLanguage programmer knows, the devil is always in the details. There’s almost always a sneaky little hiccup lurking in the logic—whether it’s an off-by-one error in your bar counts, a weird multi-data alignment issue, or an order that fills when it shouldn’t.

More importantly, build parameterization into the code from the beginning. The goal is not to curve-fit the past into a fragile illusion of perfection, but to find parameter ranges that place the strategy in the right ballpark for today’s electronic markets. Risk and reward characteristics evolve as stock indexes reach new highs and volatility changes with them.

Email me at george.p.pruitt@gmail.com to get the EasyLanguage source code.  

Here are the key variables I always like to isolate and test:

  • The Breakout Window: The 30-minute opening range is a classic that’s been around since the floor-trading days. But is 30 minutes still the magic number? Opening up a search space for 15, 30, or 45-minute ranges lets the data tell us how modern volatility behaves.

  • Capital & Trade Risk: On fast-moving contracts like the ES or NQ, a wide 30-minute opening bar can yield a stop-loss distance that’ll blow right past your account’s risk budget. We need a disaster stop override.

  • Profit Targets: Riding a trade to the final bell sounds great in theory, but late-day momentum can turn on a dime. Testing defined profit objectives helps us figure out if taking money off the table beats hoping for a strong closing bell.

  • Break-Even Triggers: Let’s face it—false breakouts happen, and in today’s algorithmic environment, they happen a lot. Introducing a break-even stop once a trade moves a certain number of ticks or dollars into the green can salvage capital when the initial push was purely manifested by stop runners.

  • Adding the Pattern to the ORB:  If you allow this simple strategy free reign it will almost certainly trade every day.  In this simple test we could utilize some of Toby Crabels volatility constraints.
    • NR-7:  Yesterday must be the narrowest range of the past seven days
    • NR-4:  Yesterday must be the narrowest range of the past four days.
    • TR vs. ATR:  Compression or expansion.  Was yesterday’s True Range greater than or less than the 20-Day Average True Range.

Here is the analysis from July 2018 through July 2023.  I picked this period because this is when I published the book plus five years prior.  Tested on @NQ.D without commission and slippage.

  1. Optimize risk versus reward while ignoring break even and range filters:
    1. profit objective from $3,000 to $9,000 by $1000
    2. stop loss: $1500 to $3000 by $500
  2. Optime risk versus reward versus breakeven ignoring range filters
    1. profit objective from $3,000 to $9,000 by $1000
    2. stop loss: $1500 to $3000 by $500
    3. break even stop from $1500 to $2000 by $250
  3. Optimize range filters
    1. NR4
    2. NR7
    3. Range compression and expansion

3-D Visualizations and Discussion

Optimzation 1:

Objective function:  Net Profit

Reward.  Nice results across a nice high and level plateau.  The strategy wants a lower risk per trade, but it wants the profits to run.

Objective Function:  Maximum Draw down

Risk.  Well, we are dealing with AI and Mag7 volatility here.  Very few results with less than a $30K draw down.  We are bumping up against the wall risking $1000 per trade.  We could push through the wall but I personally don’ think it is worth the effort.

Optimzation 2:

Objective function:  Net Profit

Plotting two parameters on a 3D chart is straightforward. Adding a third requires an extra step. We can still plot a result at each X–Y coordinate, but multiple results may now share that same coordinate because of the third parameter. To create one surface, we accumulate those results and plot their average at each unique X–Y location.

Reward.  Nice results across a nice high and level plateau, again.  The introduction of the Break-Even optimization changed the surface.  Remember we are looking at aggregate statistic at each X and Y not just one point.

Objective Function:  Maximum Draw down

Risk.  This was somewhat surprising.  The Break-Even addition returned all values (when averaged) at each X and Y between -$20K and -$30K.

Heat Map of Risk (3 parameters).  A chart like this is hard to see unless you rotate them.  I like to look at a Heat Map as well.  The following map shows a lower draw down values at lower stop loss and profit objectives.  Logical, right?

Heat Map of Risk (2 parameters).  What does the 2 parameter optimization heat map look like?

I think we can conclude the application of a Break-Even trade as a plus for the system.  Very few X and Y show desirable results.

Trade Filtering – Good or Bad:  Neither NR4 or NR7 were productive.  However, comparing yesterday’s True Range with the ATR produced some surprising results.

This demonstrates we need volatility but not too much volatility.  Trade filtering goes hand in hand with the amount you want to risk.  Filtering trades diminishes executions and therefore reduces exposure.

Final System:

Risk: $2000 – Reward: $9000 – BreakEven: $1500 – Volatility TR < 1.5 X ATR

Walk It Forward

Walked forward from August 2023 – this was the endpoint we used in the optimization process. A good fellow with just a hint of a temper.

Too much money too quickly!

Incubation Assessment
Overall Assessment: Degraded
Risk Assessment: High Risk
Incubation Readiness Score: 4 / 8
Return delivery is running ahead of baseline: expected annual return is 318% versus actual annual return of 434%, and expected annual gain of $39,732 compares with actual annual gain of $54,197. However, that stronger return delivery has come with a less stable path and/or materially heavier risk than history would suggest. Risk is materially worse than the historical profile: actual worst drawdown of $35,005 is 1.720 times the historical drawdown of $20,355. Risk conditions are in the High Risk range. The realized monthly path is no longer tightly aligned with the baseline, based on monthly equity correlation of 0.960, projection RMSE of $55,147, normalized RMSE of 1.388, and path wander ratio of 0.476.

Correlation still shows directional similarity, but the path wander ratio indicates noticeable drift away from the projected path over the same window. Monte Carlo context is cautionary: actual forward equity is $162,590, gain percentile is 78% (in the upper quartile), and drawdown percentile is 84% (in the upper quartile for drawdown stress). Taken together, the system shows meaningful deterioration in incubation.

That flat period at the beginning of the test period looks a little suspect, right?

Monte Carlo It

Running 2500 simulations with $50K initial capital and then extracting a typical year out of the results you get this:

This is what you get when you perform a Monte Carlo analysis over a time period that shows exceptional results.  However, the very best and very worst trades were removed first before the shuffling.

Top left corner is most dense quadrant = GOOD!

Walk it Backward!  Bell Bottom Jeans!

The 30-minute rule during this period of time was dismal.  Just like Bell Bottom jeans in the 1980s.

From Generated Strategies to Engineered Systems: Why Good Numbers Are Only the Beginning

The Throwing-Spaghetti-Against-the-Wall Approach

We now have all sorts of platforms that can test seemingly innumerable combinations of patterns, indicators, trade management rules, markets, and time frames. We have seen systems work on 60-minute bars, but fail completely on 120-minute bars. Popular candlestick patterns are aggressively used to generate systems that pass whatever gauntlet has been defined as “robust.”

The no-coding-required algo generators only look at the metrics. If a system looks great on a 445-minute chart using a HAMMER or HANGING MAN candlestick pattern, and it passes the out-of-sample inline stress testing, then it becomes one of the noodles that sticks to the wall.

I am not being critical of this overall approach. Expanding the search universe by using a wide spectrum of multi-minute bars is somewhat newer, and in many ways, somewhat novel. Back in the day, we mostly stuck with daily bars or common intraday intervals such as 15-, 30-, and 60-minute bars. A 445-minute bar was not even considered. But with today’s tools, almost anything is possible.

What I am critical of is the tendency to take those results at face value without drilling down into the underlying logic. A system that passes a statistical test is not automatically a system that makes sense. And more importantly, it is not automatically a system that can actually be traded.

Enter the Engineer

Plotting the algorithm on the chart and examining the actual trades is Step #1.

Why do this, you may ask? The code is the code. The generator produced an impressive-looking algorithm. The metrics passed the test. The equity curve looked promising.

But what if the algorithm just got lucky? What if it is already drifting away from its foundational premise?

For example, suppose I create a strategy using momentum along with the Hammer and Hanging Man candlestick patterns. The results look promising. A hypothetical generator was fed the available data, churned through the combinations, and produced the following candidate system:

Candidate System

Time Frame: 445-minute bars
Market: @ES
Buy: Momentum is positive and Hammer pattern occurs
Sell Short: Momentum is negative and Hanging Man pattern occurs
Risk: $2,800 to make $5,600

From an engineer’s perspective, I kind of like it.

It has several attractive qualities:

  • It is symmetrical.
  • It is applied to a good, liquid market.
  • It uses a different time frame.
  • It involves time-tested candlestick patterns.
  • The risk-to-reward ratio is a clean 1-to-2.

But then the engineer asks the next question:

Can the bar interval itself be trusted?

You cannot slice a pie evenly using 30% slices.

Most trading days consist of 23 hours, or:

23 × 60 = 1,380 minutes

Now divide the trading day by the selected bar interval:

1,380 ÷ 445 = 3.10

Or stated another way:

445 + 445 + 445 = 1,335 minutes

That leaves a remainder of:

1,380 – 1,335 = 45 minutes

So, the chart does not really consist of four equal 445-minute bars. It consists of three large 445-minute bars and one small 45-minute bar.

That matters.

A Hammer or Hanging Man pattern detected on a 445-minute bar is not the same thing as a Hammer or Hanging Man pattern detected on a 45-minute cleanup bar at the end of the trading session. The pattern may have the same name, and the code may treat it the same way, but structurally it is not the same event.

This is where generated code becomes engineering work.

The generator saw a metric.
The engineer sees a structural problem

Small Piece at the EOD

But Can You Argue With the Numbers?

At this point, someone could reasonably say, “Yes, the final bar is shorter, but look at the performance report.”

And that is a fair objection.

The numbers appear impressive. The system produced a solid net profit, a respectable profit factor, and a reasonable percentage of winning trades. On the surface, the strategy looks like something worth pursuing.

But this is exactly where the engineer has to slow down.

Most candlestick patterns are built around the range, body, open, close, high, and low of the bar. In other words, the pattern is not just a label. It is a structural interpretation of price movement during a specific unit of time.

Here is the EasyLanguage definition of the Hammer and Hanging Man pattern:

BodyHi = MaxList( Close, Open ) ;
BodyLo = MinList( Close, Open ) ;
Body = BodyHi - BodyLo ;
PriceAvg = XAverage( Close, Length ) ;
BodyAvg = XAverage( Body, Length ) ;

oHammer = 0 ;
oHangingMan = 0 ;

if Body < BodyAvg
and Body > 0
and BodyLo > MedianPrice
and BodyLo - Low > Factor * Body
and High - BodyHi < Body

then
if Close < PriceAvg then
{ TREND IS down }
oHammer = 1
else if Close > PriceAvg then
{ TREND IS up }
oHangingMan = 1 ;

C_Hammer_HangingMan = 1 ;
Hammer/Hanging Man

The key element here is the current bar’s open-to-close body, along with the relationship of that body to the bar’s total range. That makes perfect sense when each bar represents the same amount of market time.

But do you really want to rely on a bar that always covers only 45 minutes when the other bars cover 445 minutes?

From an engineering standpoint, I do not believe the smaller bar provides enough information to fulfill the spirit of the complete pattern. The code may identify the shape, but the market structure behind that shape is not equivalent.

In this case, it appears that a malformed Hanging Man triggered a short sale. The 45-minute cleanup bar completed the pattern, and the system acted on it.

And yet, the numbers look good.

That is the trap.

A performance report can tell you that something worked historically. It cannot tell you whether the logic that produced those trades was structurally sound. That part still requires inspection, judgment, and engineering.

My goodness!

Do You Throw the Results Out Over a Technicality?

Not necessarily.

This is where engineering judgment comes into play. The fact that one of the trades was triggered by a malformed candlestick pattern does not automatically mean the entire idea is worthless. The performance report may still be pointing toward something useful. The combination of market, time frame, momentum filter, and trade management rules may contain real information.

But it does mean the results should not be accepted at face value.

The first question is not, “Did it make money?”

The first question is, “Did it make money for the reason I think it made money?”

That distinction matters.

If a strategy is supposed to exploit a Hammer or Hanging Man pattern, then the bars used to form those patterns should be structurally comparable. A 45-minute cleanup bar sitting next to three 445-minute bars may satisfy the code definition, but it does not necessarily satisfy the trading premise.

So I would not immediately throw the results away. I would move the candidate system into the engineering phase. That means isolating the questionable condition, removing or correcting the structural flaw, and then testing whether the core idea still holds up.

If the system falls apart after making those changes, then the original result was probably more fragile than it appeared. If the system survives, then you may have something more interesting.

That is the difference between throwing spaghetti against the wall and engineering a tradable system.

The generator found a candidate.

The engineer decides whether that candidate deserves to live.

Next Installment — Working Your Craft: Some Coding Required

Don’t worry — I do the heavy lifting for you.

But there is real value in sitting in front of the editor, debugging the code, flipping system components on and off, and examining the functions that drive the trades. That process is a win-win. Even if you do not grasp every minute detail, that is okay.

You do not have to become a full-time programmer to benefit from this exercise.

Understanding the why behind the code is often enough. Why did the trade trigger? Why did the pattern qualify? Why did the system enter there instead of somewhere else? Why did the performance report look good?

Those questions move you beyond blind acceptance of the results. They turn a generated candidate into something you can inspect, challenge, and eventually trust — or reject.

In the next installment, we will take the same strategy idea and make it more practical for real-time trading. The larger bar can still generate the signal, but the smaller bar can handle the execution. In TradeStation terms, Data2 can define the setup, while Data1 handles the trade.

Watching a system trade on a large bar can hide too much of the story. If a trade enters, gets stopped out, or takes a profit all within the same large bar, the report may tell you what happened, but it does not really show you how the trade unfolded.

That is why I like to watch the trade develop on a smaller execution bar. Seeing the five-minute bar crawl across the screen gives you a much better feel for the trade. You can see the entry, the follow-through, the hesitation, the stop pressure, and the eventual exit in a way that a large bar simply cannot reveal.

Maybe that is just me, but I find it much more satisfying.

And more importantly, it provides a clearer picture of whether the trade was realistically executable.

That is where the engineering really begins.

 

 

 

Is Your System Broken?

The Ultimate Test of Efficacy: Time

At Futures Truth, we focused on one thing: walk-forward results across hundreds of trading systems. Back then, the industry was unrecognizable compared to today—if you can even call what exists now an “industry.”

Just this morning, I was reminiscing about vendors who sold strategies for upwards of $5,000. Those packages usually consisted of a set of rules (disclosed or not) and a rudimentary DOS-based program to generate simple charts and next-day orders. This was well before the heyday of TradeStation; those days are gone forever, for better or worse.

Back then, if a system performed well in walk-forward analysis, it earned a ranking. In those days, a Futures Truth ranking meant something. Sometimes, however, that ranking felt like a curse. Critics even argued you could “fade” a top-ranked system and make more money by taking the opposite trade. The reality is that many of those legendary “Top Ten” systems—built for a different era—simply wouldn’t survive in today’s markets.

Side Bar:  There are still services such as Striker and The Collective that still monitor trading systems.  Striker only shows real execution, which is mostly a good thing – real execution costs are shown.  But so is broker error.  Heck, we are all human and we all make mistakes.  Striker is upfront with this, and they state they will do the best they can – execution can be a beast. 

Speaking of execution – The Sunday Evening Gap

A Traders Nightmare. Most likely Trend Followers were short during the huge gap here!

 Imagine, having a stop order in crude oil on a Sunday evening when the market opens thousands of dollars through your stop.  If a system derived position is short the broker’s only directive is to GET OUT – no matter what, at the first off ramp.  The above chart shows a single contract slip on crude futures at the genesis of the Iran War.

The Million-Dollar Question

The second most common question I was asked at Futures Truth was: “How do I know if the system I’m trading is broken?”

Can you guess the most common one? “If it were your money, which system would you trade?”

We never answered that one directly; there was never a simple black-and-white answer. But the second question—the one about “broken” systems—usually surfaced when a trader was deep in a drawdown.

Seasoned traders know that systems ebb and flow; drawdown is simply the “tax” we pay to play the game. Others, however, get “married” to a system and stick with it until “death do us part.” My standard response back then was always:

“Is your current performance still within the boundaries of the backtest?”

In other words, has the system exceeded its historical maximum drawdown by a meaningful margin? Does the current real-time performance look like other “rough patches” in the historical equity curve? If the answer was yes, the trader would usually give it a little more room.

Into Unexplored Waters

Those were simpler days, but the core problem remains: we cannot see the future. No forward analysis can tell you with certainty what a trading system will do next. What it can do is tell you when the system has moved into “unexplored waters.” Once you know that, the decision to stay, abandon, or pause becomes much easier.

All trading systems oscillate between success and failure. If a system has a genuine technical edge, that edge may eventually reassert itself—provided you have the time and capital to wait. But most traders don’t have unlimited resources.

Recently, the market activity surrounding the Iran conflict has pushed many systems into intense drawdowns. The same old questions have reared their heads again. This time, however, I wanted to provide a more empirically derived analysis. I’ve developed a disciplined approach to measuring risk and reward that moves beyond “gut feel.”

To illustrate, I pulled a system off my shelf that I originally designed for retail consumption back in June 2018. Here is how it’s currently holding up:

Hypothetical Results: Before and after development.

The Profit Mirage

This is a mean-reversion approach. I remember thinking back in 2018: How much longer can this bull market continue? With that in mind, I utilized a simple regime filter. After just a few weeks of trading later that year, I genuinely thought the system might be broken. The market spent a large portion of that fall and winter below its 200-day moving average, and shorting simply wasn’t working.

The system then went dormant for a long stretch, finally “waking up” at the height of the pandemic. Had you stuck with it, you would be up significantly today (though we stopped trading right at the onset of the pandemic).

But here is the catch: Profit, by itself, is not enough to determine if a system is broken. As long as they are making money, most traders never bother to peek below the surface. This system would likely be sitting near the top of the Futures Truth rankings today. But let’s dive in and see how it actually performed on its “test of time.”

Test 1 – Baseline Projection

Baseline projection based on in sample average monthly return.

Is Outperformance Always a Good Thing?

Looking at the chart above, you see a steep deviation below the baseline initially, followed by a rocket-ship move to the upside. By late 2025, the actual equity is sitting way above the red dashed expectation line.

Most traders would see this and think they’ve struck gold. “Isn’t this what we want—a strong positive deviation?” they’d ask.

In a world of simple “bottom-line” thinking, the answer is yes. But in the world of professional algorithmic trading, this chart is screaming a different story. To a seasoned developer, deviation is deviation. Whether it’s to the upside or downside, moving this far away from the “expected” path suggests that the strategy’s original statistical model is no longer in control.

When a system starts generating 190% of its expected return, it’s often because it has inadvertently stepped into a high-volatility regime it wasn’t designed to navigate. If the “upside” is this aggressive, you can bet the “downside” risk has scaled right along with it.

Test 2:  Monte Carlo Analysis on Walk Forward

Return and Drawdown sitting on the tails.

The Statistical Reality Check

To understand why I called this system “Degraded” despite the profits, we have to look at the Monte Carlo Walk Forward distributions. This is where we compare real-time performance against thousands of simulated “alternate realities” based on the system’s history.

1. The Equity Distribution (The Good News… or is it?)

In the top chart, our actual forward equity of $67,575 (the dashed red line) sits at the 97th percentile.

  • Interpretation: Out of 1,000 possible outcomes, the system performed better than 970 of them. While this looks great, being this far out on the “tail” of the distribution suggests we are no longer operating in a normal environment.

2. The Drawdown Distribution (The Warning)

The bottom chart is the real story. The actual worst drawdown reached $18,588, placing it in the 98th–99th percentile of severity.

  • The Comparison: The median expected drawdown was only $8,125.

  • The Verdict: We have blown past the 90th percentile of $13,611 and are deep into the “danger zone.”

The Bottom Line

When a system hits the 97th percentile for gains but simultaneously hits the 99th percentile for drawdown stress, the math is telling you that the character of the strategy has changed. You aren’t just trading a system in a “rough patch”—you are trading a system that has moved into a risk regime it was never built to survive. This is the “evidence-based framework” I mentioned earlier. Without these charts, you’re just guessing. With them, you have the data to justify stepping aside.

The Verdict: Test 3 – Overall Assessment

This is where the “gut feel” ends and empirical analysis begins. The following commentary is generated by my new software; a quasi-expert system designed to pair raw statistical results with descriptive, actionable interpretation.

Status: DEGRADED

Risk Assessment: CRITICAL RISK

Incubation/Trading Readiness Score: 4 / 8

Overall Assessment: Degraded
Risk Assessment: Critical Risk
Incubation/Trading Readiness Score: 4 / 8
Return delivery is running ahead of baseline: expected annual return is 18% versus actual annual return of 34% and expected annual gain of $4,477 compares with actual annual gain of $8,536. However, that stronger return delivery has come with a less stable path and/or materially heavier risk than history would suggest. Risk is materially worse than the historical profile: actual worst drawdown of $18,588 is 2.670 times the historical drawdown of $6,962. Risk conditions are now in the Critical Risk range. The realized monthly path is poorly aligned with the baseline, based on monthly equity correlation of 0.960, projection RMSE of $21,789, normalized RMSE of 4.867, and path wander ratio of 0.621. Correlation still describes directional similarity, but RMSE and path wander ratio show how far the realized path has wandered from projection over the same window. Monte Carlo context is cautionary: actual forward equity is $67,575, gain percentile is 97% (near the top of the simulated distribution), and drawdown percentile is 99% (in the worst decile for drawdown stress). Taken together, the system shows meaningful deterioration in incubation.

A readiness reading of 4 out 8 indicates this system right now has degraded to a point where caution and I mean extreme caution should be used in your decision to trade this strategy.  The major factors influencing rating are:

  • Return Attainment:   1.907 – — This indicates the system has achieved 190.7% of its historically expected return during the incubation/trading period. In practical terms, the strategy is generating returns at nearly twice the pace implied by its historical baseline.
  • Path Wander Ratio: 0.621 — This indicates that the actual equity path is deviating from the projected path by a meaningful amount relative to the total expected move over the incubation/trading period. Put simply, the system is not wildly off course, but it is no longer tracking the historical baseline tightly.
  • Drawdown Stress: 2.67 — This indicates that the actual worst drawdown has expanded to roughly 2.7 times the level suggested by the system’s historical baseline. Put simply, the strategy may still be generating return, but it is doing so while absorbing far more pain than its historical profile would justify.

Hindsight is always 20/20, and it is easy to say now that you should have stuck with the system. But with a large sample of out-of-sample trades showing this degree of deterioration, if I had to choose between staying with it, abandoning it entirely, or temporarily shutting it down, I would probably step aside and wait for conditions to settle down. Remember, not trading is an algorithm too.   Having the right tools to make this kind of decision is paramount, because they give you an evidence-based framework for explaining and defending your reasoning.

The Power of Incubation: Is Your Best System Collecting Dust?

How many systems do you have sitting on your shelf? I personally have at least a hundred, probably more. In this business, a little dust is not always a bad thing.

Figuratively speaking, the more “dust” a system has collected, the more real-time incubation it has endured. And that gives you something far more valuable than any backtest: pure, unadulterated out-of-sample evidence.

Seeds Waiting to Be Planted

Algorithmic trading development rarely produces just one system. It creates a trail of offshoots—versions that may have looked unremarkable or even mediocre during the initial build. Yet many of these forgotten systems are really just seeds waiting for the right environment.

When you revisit them months or even years later, you may find that a strategy which struggled in 2022 has bloomed into a powerhouse in 2026. Without a framework to measure that growth, you would never know.

Why You Need an Incubation Framework

Most traders revisit old systems by simply eyeballing an equity curve. An incubation framework goes much deeper by providing:

Historical Context: Does the dusty system’s recent performance still match its original DNA?

Regime Readiness: Has the market finally moved into the regime this offshoot was designed for?

The Go/No-Go Signal: A quantitative way to decide whether a seed is finally ready to be moved from the shelf to the server.

TS-SystemChecker software

TS-System Check Control Panel

A Tool Built for the Journey

I built TS-SystemChecker because, after decades at Futures Truth and years of developing my own strategies, I needed a better way to cut through the emotional fog that surrounds system evaluation.
I didn’t design TS-SystemChecker to be a black box or some kind of get-rich-quick shortcut. I built it because, after decades at Futures Truth and years of developing my own strategies, I wanted a better way to cut through the emotional fog that surrounds system evaluation.

Whether you are a retail trader focused on refining one core system, or a developer like me with a shelf full of offshoots, this framework was built for that journey.

For the specialist: If you have one system you live and die by, the deep-dive analysis helps define its boundaries of truth. You can begin to see whether a drawdown is simply part of the system’s normal character or evidence of something more structural.

For the portfolio manager: If you are tracking a library of ideas, the Batch Analysis feature helps you monitor many systems at once. Import the trade files, review the evidence, and identify which seeds may finally be ready to move from the shelf to a live account.

Looking Beneath the Surface

At the end of the day, this is why I built TS-SystemChecker. Traders need more than opinions, hope, or fear. They need a framework grounded in evidence.

That is the real purpose of this tool: not to make decisions for you, but to help you make better ones.

TS-SystemChecker Batch Mode

Generated Code Is Not the Same as Engineered Code

AI can write structure, but experienced programmers still supply the craft

The more we rely on generated code, the more disciplined we must become in questioning it.

AI and modern frameworks now provide valuable insights that, just a few years ago, would have required significant time and effort to obtain. However, while they offer tremendous macro-level leverage, they can also introduce subtle assumptions that lead to impossible scenarios and misleading downstream analysis. This is especially true in environments designed for rapid idea testing, where convenience can come at the expense of deeper, microscopic introspection.

For example, in my PatternSmasher framework, I use constructs like BarsSinceEntry to control trade duration and evaluate pattern efficacy. This makes it very easy to test thousands of ideas quickly. But that convenience comes with a responsibility. If you rely on these abstractions without thinking through the details, you can end up with behavior that looks perfectly valid in code but could never occur in the real world.

I have seen this problem in other frameworks and in AI-generated code as well. This is why it is so important to continue to hone your craft and take a deep dive into the results produced by generated code. In the quant world, the first step is to study the trades and isolate problems such as what I call simultaneous same-direction exit and reentry. Once you see it, the job is to fix it without changing the intent of the algorithm.

Let me show you exactly what I mean. The logic behind this example looks perfectly fine on the surface. But when you dig into the trades, you see the problem immediately. In this chart, the system exits a long position and then turns right around and buys again at the same time and price. That is not a reversal. It is a same direction exit and reentry that simply cannot happen in the real world, and it pollutes the back test with trades that should not exist.

Example of simultaneous same-direction exit and reentry at the same time and price—an impossible trade sequence that distorts backtest results – 640 minute bar on Gold – why 640?

We entered a long position, the trade expired, immediately re-entered on the next setup, that trade expired as well, and then entered again—only to get stopped out. That’s three round turns, each incurring commission and slippage.

// Simple code that is of course mean reversion.
// However, since we seem to be in this regime
// let's hone our craft to make this work as intended.

input:movAvgLen(50),consCloses(1),exitAfterNBars(5),stopLoss(3000);

value1 = countIF(c < c[1],consCloses);
value2 = countIF(c > c[1],consCloses);


if value1 = consCloses and close > average(c,movAvgLen) then
buy ("lentry") next bar at open;
if value2 = consCloses and close < average(c,movAvgLen) then
sellShort ("sentry") next bar at open;


if barsSinceEntry > exitAfterNBars then
Begin
sell("lx-exp") next bar at open;
buyToCover("sx-exp") next bar at open;
end;
setStopLoss(stopLoss);
Simple entry with expiration exit

The code that produced this looks pretty clean. You have your entry logic, a BarsSinceEntry exit, and a stop loss. On the surface, everything seems fine.

But you don’t find this kind of problem by staring at the code. You find it by looking at the trades.  This is the one thing AI or a framework doesn’t examine.  At first, the natural reaction is to slap a MarketPosition “gate” on the entry logic. The word “gate” may be a dead giveaway that AI has influenced the discussion. But I like it. It has been around since the early days of electrical circuits, and it is very appropriate here.  I’ve noticed that many of the words AI uses have started to creep into my own vocabulary. Funny how that happens.

Fix #1

input:movAvgLen(50),consCloses(1),exitAfterNBars(5),stopLoss(3000);

value1 = countIF(c < c[1],consCloses);
value2 = countIF(c > c[1],consCloses);


if marketPosition <> 1 and
value1 = consCloses and close > average(c,movAvgLen) then
buy ("lentry") next bar at open;

if marketPosition <> -1 and
value2 = consCloses and close < average(c,movAvgLen) then
sellShort ("sentry") next bar at open;


if barsSinceEntry > exitAfterNBars then
Begin
sell("lx-exp") next bar at open;
buyToCover("sx-exp") next bar at open;
end;
setStopLoss(stopLoss);
Fix #1 - solves the simultaneous exit and re-entry same direction glitch

So what does that MarketPosition “gate” actually do?

It fixes the symptom. The same-bar exit and reentry disappears, and the trades look cleaner.

But it also changes the algorithm in a much deeper way.

In the original design, a new long signal while already long reaffirmed the position and should have kept the trade alive. The gate removes that behavior. Now the strategy must exit first and then wait until the next bar to reenter.

And that delay matters.

By the time the next bar arrives, the setup may be gone. What should have been one continuous trade is now split into pieces—or missed entirely.

You didn’t just clean up the trades. You changed which trades exist.  The new strategy more or may not be more efficient, but just know the algorithm is now different.

Fix #2

We bought, suppressed the expiration exit due to a new buy setup—twice—and were ultimately stopped out at the level where the stop loss from the final trade that didn’t occur—but whose properties we were monitoring—would have been triggered.

Could most quants who aren’t programmers solve this riddle? Probably not. My 40 years of programming experience certainly played a role, and my familiarity with EasyLanguage—especially its limitations—helped guide me down the right path. But more importantly, I was able to recognize the nature of the problem, apply targeted fixes, and then analyze the resulting trades. I repeated this process—wash, rinse, repeat—until the issue was resolved.

Much of the knowledge I relied on has been documented by myself and others over the years. Investing time in books, videos, and webcasts specific to your programming language remains essential—it forms the foundation. But ultimately, refining your own skills and developing your craft is a time-consuming process that pays lasting dividends.

Groundwork for the Fix

Solving what initially appears to be a simple riddle requires recognizing several underlying behaviors. I was able to correct the issue because I could anticipate when a new trade was about to occur. When both the exit gate for an existing position and the entry gate for a new position in the same direction were simultaneously open, I prevented the transition by closing both gates.

However, simply blocking the transition was not enough. I had to simulate the trade that would have occurred. This meant marking the hypothetical entry price, resetting the stop-loss based on that price, and reinitializing my own bars-in-trade counter.

At this point, I could no longer rely on EasyLanguage’s built-in functions such as BarsSinceEntry or SetStopLoss. Those functions assume an actual executed trade and therefore could not reflect the internal state I needed to maintain. To solve the problem correctly, I had to take full control of trade state management and explicitly track these values myself.

input:movAvgLen(50),consCloses(1),exitAfterNBars(5),stopLoss(3000);

vars: mp(0),barsMult(1),barsIntrade(0),lStopLevel(0),sStopLevel(0),closedTrades(0);
vars: canGoLong(False),canGoShort(False);

canGoLong = countIF(c < c[1],consCloses) = consCloses and close > average(c,movAvgLen) ;
canGoShort = countIF(c > c[1],consCloses) = consCloses and close < average(c,movAvgLen);

mp = marketPosition;

//Exit Technology

closedTrades = totalTrades;
//long exit on bar after entry
if mp[1] <> mp and mp = 1 or (closedTrades > closedTrades[1]) Then
begin
barsInTrade = 0;
lStopLevel = open[0] - stopLoss/bigPointValue ;
end;

//short exit on bar after entry
if mp[1] <> mp and mp = -1 or (closedTrades > closedTrades[1]) Then
begin
barsInTrade = 0;
sStopLevel = open[0] + stopLoss/bigPointValue ;
end;

//long reentry stop reset
if mp = 1 and canGoLong and barsInTrade > exitAfterNBars Then
begin
lStopLevel = open of tomorrow - stopLoss/bigPointValue ;
// print(d," ",t," should exit and renter long tomorrow ",barsInTrade," ",barsSinceEntry," ",open of tomorrow);
barsInTrade = -1;
end;

//short reentry stop reset
if mp = -1 and canGoShort and barsInTrade > exitAfterNBars Then
begin
sStopLevel = open of tomorrow + stopLoss/bigPointValue ;
// print(d," ",t," should exit and renter short tomrorrow ",barsInTrade," ",barsSinceEntry," ",open of tomorrow);
barsInTrade = -1;
end;

if mp = 1 then
sell("lx-stopLoss") next bar at lStopLevel stop;

if mp = -1 then
buyToCover("sx-stopLoss") next bar at sStopLevel stop;

//Entry Logic
if canGoLong then
buy ("lentry") next bar at open;
if canGoShort then
sellShort ("sentry") next bar at open;


//Bars in trade expiration exit
if barsInTrade > exitAfterNBars then
Begin
sell("lx-exp") next bar at open;
buyToCover("sx-exp") next bar at open;
end;

//Day of entry protection
setStopLoss(stopLoss);
//Increment barsInTrade - mimic TradeStation here too!
if mp <> 0 then barsInTrade = barsInTrade + 1;
Fix #2 - difficult initially but reusable

This version fixes the problem by taking control of the trade state instead of relying on EasyLanguage’s built-in functions.

First, I define whether I can go long or short, independent of my current position. Then I track my own state variables—market position, bars in trade, stop levels, and trade count—so I know exactly what the system is doing at all times.

The key occurs when a same-direction signal appears after the trade has technically expired. Instead of allowing an exit and immediate reentry, I suppress both actions and simulate the renewed trade. I mark the hypothetical entry price, reset the stop based on that level, and restart my bars-in-trade counter.

Because of this, I can no longer rely on BarsSinceEntry or SetStopLoss—they depend on actual trades. I manage everything explicitly.

The result is a continuous position that preserves the original intent of the algorithm without introducing impossible trades into the backtest.

EasyLanguage also has its share of esoteric nuances. Code order can matter in some places and not in others, particularly with order execution. Even detecting position changes requires a bit of finesse. These details matter, but they are beyond the scope of this discussion.

This is where the difference between generated code and engineered code becomes clear.

A programmer who is not willing to put in the work—and instead relies on AI to solve the problem—will likely stop at the first acceptable fix. The code will run, the trades will look cleaner, and the issue will appear resolved. But the deeper problem remains: the structure has changed, trades may be missing, and the original intent of the algorithm has been compromised.

As we become more dependent on code generation through AI and frameworks, it becomes even more important to validate that the output is reasonable and reflects something that could occur in the real world. That responsibility does not go away—it increases. And it requires us to continue honing our craft.

AI can generate code and even suggest reasonable fixes, but it does not truly understand the nuances of the language, the sequencing of events, or the intent behind the strategy. It cannot look at a trade and say, “that shouldn’t have happened.” It does not debug by questioning reality—it follows patterns.

Arriving at the correct solution required recognizing the problem, iterating through possible fixes, examining the trades, and refining the logic until the behavior matched the intent. That process—wash, rinse, repeat—is the craft.

Generated code can get you started. Engineered code is what gets you to the truth.  Take a look at the two following reports.  Similar results, but look at the number of trades and those statistics tied to this number.

 

Monte Carlo: Garbage or Gold?

A quick history of Monte Carlo (the short version)

 

Monte Carlo Tool Link:  Read blog first:  Monte Carlo Trade Flight Simulator · Streamlit

Youtube Video on Monte Carlo Tool:  Youtube Monte Carlo Tool Video

Monte Carlo methods took off in the 1940s during wartime research at Los Alamos, when scientists needed a practical way to estimate outcomes for complex systems that couldn’t be solved with a single clean equation. Trading has the same problem: there’s no tidy formula that can tell you the order your wins and losses will arrive in—and that order is where luck lives.

So we do the next best thing: we add randomness on purpose. Monte Carlo repeatedly reshuffles the same trade outcomes to create many plausible equity paths, revealing how smooth (or brutal) the ride can be even when the system’s edge stays the same.

“No matter how sophisticated our choices, how good we are at dominating the odds, randomness will have the last word.” — Nassim Nicholas Taleb, Fooled by Randomness

What Monte Carlo does in trading

In trading system analysis, the “Monte Carlo move” is straightforward: you take your historical list of trade results (P/L per trade), then you randomly reshuffle (or resample) that list thousands of times. Each reshuffle produces a new, plausible equity curve built from the same underlying trade outcomes. From there, you measure the things that actually determine whether a system is tradeable—how deep the drawdowns get, how long the slumps can last, and how often the path gets ugly enough to force you to quit or cut size.

This doesn’t predict the future. It answers a different (and more practical) question:

Given the trade outcomes you’ve already seen, how good—or how bad—can the ride get due to sequencing alone?

That’s the value, or is it?

Why traders debate the value of Monte Carlo

Why some traders love it

  • It exposes fragility that a single backtest can hide.
    A backtest is one historical path—one specific order of wins and losses. Monte Carlo reshuffles that order to show other plausible paths. If a strategy only “works” when winners show up early, Monte Carlo will expose that quickly.

  • It turns vague fear into a measurable risk.
    Traders feel risk but struggle to quantify it. Monte Carlo lets you define a failure line (for example, “equity falls below 60% of starting capital”) and estimate how often that happens across thousands of simulated lives. You may still trade it—but now you’re choosing with a probability, not a gut feeling.

  • It helps you size the system rationally.
    Most blow-ups aren’t caused by a bad system—they’re caused by a decent system traded too big. By running the same trades under different starting capital (or leverage), Monte Carlo shows where the strategy becomes survivable. It often reveals a capital/size “threshold” where ruin risk drops and drawdowns become tolerable.

Why some traders hate it

  • It assumes the future behaves like the past.
    Monte Carlo can’t detect regime change. If your edge only works in certain “market moods” (trending vs choppy, low-vol vs high-vol), the simulation may look great right up until the market stops playing that game.

  • It assumes trades can be shuffled like a deck of cards.
    Many Monte Carlo runs treat each trade as an independent draw from the same bag of outcomes. Real systems aren’t that clean—markets come in streaks and clusters (volatility spikes, choppy stretches, correlation breaks), and those dependencies don’t always survive a simple reshuffle. Monte Carlo still helps measure sequence risk, but it isn’t a full market simulator.

  • It can punish good systems—and flatter lucky ones.
    A solid system can look worse if its history includes a few rare “tail” events—Monte Carlo will replay those tails in many sequences. Meanwhile, a strategy that enjoyed an unusually favorable historical run can look sturdier than it deserves, because the simulation is only as honest as the sample you feed it.

So, it’s not garbage… but it’s not gold automatically either.

Monte Carlo is a tool. Like any tool, it can be used well or used blindly.

The setup: how I ran these simulations

Monte Carlo Trade Flight Simulator · Streamlit

For these tests, I used my Streamlit-based Monte Carlo “Trade Flight Simulator.” You paste a column of trade P/L and the simulator generates:

  • Risk of Ruin based on a user-defined ruin line
  • Median drawdown across thousands of randomized equity paths
  • Worst-case outcomes (1st percentile)
  • Distribution visuals (“broom chart” equity fan + destination histogram)
  • Scaling table across start equity levels

Key settings used here

  • Ruin threshold: 60% of starting equity
  • Position size: 1 contract per trade
  • Execution costs: $40 per trade included in the trade list results you pasted
  • Horizon: number of trades pasted (the simulator runs “N trades” each life)
  • Optional CAGR is computed from first/last trade dates when provided

System #1: Mean Reversion on the Mini Nasdaq (MNQ)

~$40 execution costs, ~20 years

Start Equity Risk of Ruin Median DD Annual Return Worst Case (1st %)
$25,000 30% 42.9% 12.9% $85,019
$31,250 14% 36.2% 11.7% $80,747
$37,500 8% 32.2% 10.8% $93,072
$43,750 4% 30.1% 10.0% $78,308
$50,000 1% 26.4% 9.6% $81,352
$56,250 1% 25.2% 8.8% $93,765
$62,500 0% 24.4% 8.4% $77,907
$68,750 0% 23.0% 8.1% $81,076
$75,000 0% 20.6% 7.7% $81,108
$81,250 0% 19.1% 7.4% $89,028
$87,500 0% 18.9% 7.0% $81,982

System #1 — Mean Reversion (MNQ)

  • At $25,000 start equity
    • Risk of Ruin: 30%
    • Median Drawdown: 42.9%
    • Annual Return: 12.9%
    • Worst Case (1%): +$85,019
    • Prob > 0: 99.9%
  • At $50,000 start equity (“still not comfortable”)
    • Risk of Ruin: 1%
    • Median Drawdown: 26.4%
    • Annual Return: 9.6%
  • At $62,500 start equity (“stability zone”)
    • Risk of Ruin: 0%
    • Median Drawdown: 24.4%
    • Annual Return: 8.4%

What Monte Carlo reveals about this system

This is what a tradeable but under-capitalized system looks like.

The edge is real (the probability of finishing positive is essentially ~100%), but the sequence risk at low starting equity is still brutal:

  • A 30% risk of ruin at $25k (with a 60% ruin line) is not a rounding error.

  • Even at $31,250, ruin risk is still 14%.

  • The system doesn’t start to feel “professional” until you get into the $60k+ range, where ruin drops to 0% and median drawdowns settle into the mid-20% area.

Monte Carlo’s message:
If you want this system to behave like something you can actually stick with, you don’t optimize parameters — you capitalize it properly.

System #2: Trend Following on Crude Oil

~$40 execution costs, ~20 years

System #2 — Trend Following (Crude)

Start Equity Risk of Ruin Median DD Annual Return Worst Case (1%)
$25,000 68% 89.7% 8.7% -$99,928
$31,250 58% 80.7% 7.8% -$95,663
$37,500 47% 71.3% 7.1% -$100,991
$43,750 40% 64.1% 6.5% -$111,564
$50,000 31% 58.3% 6.2% -$98,846
$56,250 28% 57.0% 5.5% -$94,594
$62,500 22% 51.1% 5.3% -$100,349
$68,750 19% 49.9% 4.8% -$103,016
$75,000 16% 47.2% 4.5% -$112,968
$81,250 12% 42.3% 4.5% -$98,345
$87,500 11% 42.1% 4.0% -$98,228
  • At $25,000 start equity
    • Risk of Ruin: 68%
    • Median Drawdown: 89.7%
    • Annual Return: 8.7%
    • Worst Case (1%): -$99,928
    • Prob > 0: 87.3%
  • At $50,000 start equity
    • Risk of Ruin: 31%
    • Median Drawdown: 58.3%
    • Annual Return: 6.2%
    • Worst Case (1%): -$98,846
    • Prob > 0: 90.1%
  • At $81,250 start equity
    • Risk of Ruin: 12%
    • Median Drawdown: 42.3%
    • Annual Return: 4.5%
    • Worst Case (1%): -$98,345
    • Prob > 0: 86.4%
  • At $87,500 start equity
    • Risk of Ruin: 11%
    • Median Drawdown: 42.1%
    • Annual Return: 4.0%
    • Worst Case (1%): -$98,228
    • Prob > 0: 87.1%

What Monte Carlo reveals about this system

This is a classic crude trend-following signature: the system can be profitable over time, but the path can be violently unforgiving—especially when under-capitalized.

  • The probability of finishing positive is only in the mid-to-high 80% range, not “near-certain.”
  • At $25,000, the system is living on the edge: 68% risk of ruin with an 89.7% median drawdown.
  • Even after you scale up, the ride is still rough. At $81,250, ruin risk is still 12% with a 42.3% median drawdown.

The most important tell is the left tail: the 1% worst-case outcome is negative at every starting equity you tested (roughly –$94,594 to –$112,968). That means there are plausible sequences where the system not only suffers deep drawdowns, but ends the run down money—even with larger starting capital.

Monte Carlo’s message: This isn’t just a “start with more money” situation. Increasing capital helps, but the strategy’s tail risk remains severe. If you trade this, you need materially more capitalization, smaller sizing, or a risk overlay—because crude can deliver adverse sequences that this system does not comfortably absorb.

Which system is “superior” under your ruin rule?

You asked earlier: with a 60% ruin line and 1 contract per trade, does Monte Carlo reveal superiority?

Yes — because it reframes superiority as:

Which system survives at realistic starting equity levels with tolerable drawdowns?

Under-capitalized start: both are dangerous

At $25k, both systems are dangerous under the 60% ruin definition:

  • MNQ MR: 29% ruin, 41% median DD
  • Crude TF: 48% ruin, 55% median DD

So if someone insists on $25k and 1 contract, System #1 is clearly less fragile than System #2.

Once you move into realistic capital, System #1 stabilizes sooner

MNQ MR drops into “sane” ruin probabilities faster:

  • MNQ MR hits ~0% ruin by $56,250
  • Crude TF doesn’t really calm down until $75k–$81k

That’s not a judgment against trend following — it’s a reminder that instrument volatility matters and crude can be a different animal.

If you define “superior” as best risk-adjusted scaling

Based on your tables:

  • MNQ mean reversion looks easier to scale under your assumptions
  • Crude trend following can still be very viable, but it demands more capitalization to get into the same comfort zone

Monte Carlo didn’t make either system “good” or “bad.”
It made the capital requirements and sequence risk visible.

The visuals I include from the Streamlit app

System #1 Mean Reversion on the NQ

The Journey (“broom chart”)
Shows the median equity path with a confidence band.
Great for communicating “how rough can the ride get?”

Broom Chart

The Destination (ending equity histogram)
Think of each green bar as a bucket of endings. After 1,000 randomized runs, some endings cluster in the middle (the “typical” outcomes), while a smaller number land in the tails (the “lucky” and “unlucky” sequences). The dashed line marks your starting equity ($25,000). If the histogram sits mostly to the right, the system usually finishes positive. If a meaningful chunk sits to the left, that’s your “this can end down” reality—even with the same system and the same trades, just a different order.

Destination Histogram

Efficiency cloud (drawdown vs net profit scatter)

  • Each dot = one simulated run (“one life”).
  • Left to right (x-axis) = max drawdown during that run (more right = more pain).
  • Down to up (y-axis) = net profit at the end (higher = more gain).
  • The dotted lines mark the median drawdown and median profit, splitting the plot into four zones.
  • Best zone: upper-left (good profit with smaller drawdowns).
  • Worst zone: lower-right (big drawdowns with poor outcomes).

If most dots sit upper-left, the system is efficient. If the cloud spreads far right, the system’s edge may be real, but the ride can be brutal unless you reduce size or add capital.

Efficiency Cloud

Pros and cons of Monte Carlo (in plain English)

Pros

  • It highlights sequence risk that backtests hide
  • It gives you a practical scaling map
  • It converts drawdown fear into probability
  • It forces you to confront whether your system is truly robust or just lucky

Cons

  • Garbage in, garbage out (your trade list must be clean)
  • It assumes your future trade distribution resembles the past
  • It doesn’t simulate regime shifts (it’s not a market model)
  • It can create false confidence if you treat it as prophecy

Monte Carlo is not a crystal ball. It’s a stress test.

Conclusion: Garbage or Gold?

Monte Carlo is gold when it’s used as a risk lens.

It’s garbage only when people use it as a substitute for thinking — or when they treat it as a promise about the future.

For me, the biggest takeaway from these two systems is simple:

  • A profitable system can be untradeable if it’s under-capitalized.
  • Monte Carlo makes that obvious — quickly and brutally.
  • And it gives you something most trading metrics do not:
    a realistic map from “this looks good” to “this can survive.”

If you want to know what your system really feels like under stress, run it through my free Monte Carlo Trade Flight Simulator (Streamlit). Paste your trade list, set a starting equity, and it will generate a distribution of possible equity paths—so you can see the range of outcomes, not just the single backtest line. In a minute or two you’ll know whether your strategy is sturdy (most paths survive and grow) or fragile (too many paths crater early), and you’ll get practical numbers like “typical drawdown,” “worst-case runs,” and “probability of finishing above zero.”

Trades vs. Time: Two Monte Carlo Styles

Monte Carlo has to “shuffle” something: you can shuffle trades or you can shuffle time periods (daily/weekly/monthly returns). Trade-shuffling is great for a single system because it keeps each trade intact—entry and exit stay married—so you’re mainly testing how sensitive results are to the order trades arrive.

Devil’s Advocate: shuffling time can feel less “real,” because it breaks those trade narratives. A multi-day trade becomes a series of daily fragments, and once you reshuffle daily P/L you can build equity paths that no single set of trades could have produced exactly.

That’s the tradeoff TS-PortfolioMerge makes on purpose. It builds a daily mark-to-market equity curve (open positions are revalued each day), then resamples those daily equity changes so every system stays aligned to the same calendar. This isn’t about “reinvesting” or scaling up contracts—it’s about equity path risk: the way good and bad stretches of days create drawdowns, recovery difficulty, and survival pressure for a portfolio even when trade size stays constant.

 

Reverse-Engineering a Trading Indicator with AI

From Raw Wavelet Code to a Trading Tool with More Knobs Than Anyone Was Turning

The Indicator I Thought I Understood

A client sent me a trading indicator they had just started using.
It was short. Clean. About a page of code.

I’m not entirely sure where it originated, but it had the unmistakable feel of something machine-generated — technically sound, compact, and largely undocumented.

Their usage was simple:

  • Plot one line
  • Look at its slope compared to one bar ago
  • Go long or short accordingly
{---------------------------------------------------------
Causal True ? trous Wavelet Indicator
---------------------------------------------------------}
Inputs:
UseD1(true),
UseD2(false),
UseD3(false),
UseD4(false),
UseD5(false),
UseD6(false),
ColorBarsByTrend(true),
InvertTrendColorMap(false), // optional flip if colors look reversed
TrendColor(green), // used only if ColorBarsByTrend = false
DenoisedColor(white),
ResidualColor(red);
Vars:
Price(0),
c0(3.0/8.0),
c1(1.0/4.0),
c2(1.0/16.0),

...
...
...
// --- Step 1: Current price ---
Price = Close;
// --- Step 2: A0 is raw price ---
A0 = Price;
// --- Step 3: Causal ? trous B3-spline filter (past bars only) ---
A1 = c0*A0 2*c1*A0[1] 2*c2*A0[2];
A2 = c0*A1 2*c1*A1[2] 2*c2*A1[4];
A3 = c0*A2 2*c1*A2[4] 2*c2*A2[8];
A4 = c0*A3 2*c1*A3[8] 2*c2*A3[16];
A5 = c0*A4 2*c1*A4[16] 2*c2*A4[32];
A6 = c0*A5 2*c1*A5[32] 2*c2*A5[64];
// --- Step 4: Details ---
D1 = A0 - A1;
D2 = A1 - A2;
D3 = A2 - A3;
D4 = A3 - A4;
D5 = A4 - A5;
D6 = A5 - A6;
// --- Step 5: Trend ---
Trend = A6;
...
...
// --- Step 7: Residual ---
Residual = Price - Reconstructed;
// --- Step 8: Plot ---
Plot1(Trend, "Trend");
Plot2(Reconstructed, "Denoised");
Plot3(Residual, "Residual");
Wavelet a trous snippet

They were using a single configuration — effectively listening to just one component of the indicator: Trend. And to be fair, it mostly worked. The trouble only appeared when the Residual (what ever that is) was plotted alongside it. Because it lived on a very different scale, it crushed the display and made the indicator look unusable. See the section at the bottom of this post for how to fix that. Other than that, nothing was actually “broken.”

That behavior was also an early clue that the code itself was likely AI-generated. If you’ve worked with John Ehlers–style indicators, you may recognize the fingerprints of Digital Signal Processing here: fixed coefficients, repeated smoothing, and the output of one calculation feeding directly into the next in a cascading fashion. Those are classic DSP techniques — powerful, but easy to mislabel or oversimplify when dropped directly into a trading context.

In hindsight, the breadcrumbs were right in the header: wavelet and à trous. Even if you’ve never heard those terms, you can paste them into an AI chat and ask, “What does this mean?” That won’t instantly tell you how to trade it — but it will give you the vocabulary and the map so you’re not reverse-engineering in the dark. From there, the real work becomes translating the math into something a trader can actually see and use.

What is a wavelet à trous?

A wavelet à trous (“with holes”) method is a signal-processing technique that breaks a data series into multiple layers, each representing a different time scale. It does this by repeatedly smoothing the data while spacing the filter farther apart at each step, without downsampling the signal.

The result is a set of detail layers (short-term to long-term) plus a final smooth baseline. By recombining selected layers, you can emphasize noise, structure, or long-term movement — depending on what you want to study.

In other words, you define the underlying structure of the market and then decompose that structure into layers of different frequencies. If you want to emphasize noise, you limit the smoothing. If you want to emphasize trend, you add more layers. Many indicators require you to constantly adjust lookback lengths to achieve smoother results, but this approach—much like an audio equalizer—only requires adding or removing layers. That alone is an extremely nice feature.

What caught my attention wasn’t that the indicator failed—it was that the code itself clearly had more depth than how it was being used. There were multiple inputs, multiple layers, and multiple outputs, yet only a single switch was being flipped. That mismatch—between the richness of the code and the simplicity of its use—is what made me start pulling on the thread.

I Knew What the Code Was Doing — But Not What It Was

I understood the mechanics.
Repeated smoothing.
Differences between layers.
A clean reconstruction.

But the script was labeled with terms like wavelet and à trous — language most traders (myself included) don’t use day-to-day. The variable names didn’t help either. Everything technically worked, but nothing explained itself.

This wasn’t an exotic math problem.
It was a communication problem.

So I did what most of us do now when we want clarity: I brought AI into the conversation.

Using AI to Understand — Not to Predict

This is important.

I didn’t ask AI to:

  • optimize anything
  • generate a strategy
  • predict markets

I asked it questions I’d normally ask another developer:

  • What is this code actually doing conceptually?
  • Why does the reconstruction work so cleanly?
  • What is changing when different layers are included or excluded?

The first pass gave me structure.
The second pass gave me language.
The third pass gave me something unexpected: metaphors.

Not all of them worked.

When the Right Metaphor Finally Clicked

AI proposed several ways to think about the indicator — mechanical, mathematical, spatial. Some were accurate, but none quite matched how traders experience charts.

Then we circled around sound.

Filtering.
Layers.
Mixing.

That’s when it clicked.

This indicator wasn’t a “trend line.”
It was an equalizer.

Once I framed it that way, everything snapped into place:

  • The slowest layer wasn’t “trend” — it was the bass line
  • Faster layers weren’t noise — they were texture and rhythm
  • Turning components on and off wasn’t optimization — it was listening choice

The metaphor wasn’t decorative.
It became a tool.

From Cryptic Code to Wavelet Analog

With that framing, I cleaned up the code:

  • Renamed variables so they described what they felt like, not how they were computed
  • Grouped logic around intention, not math
  • Made the behavior readable on a chart

What emerged from this process was Wavelet Analog — an indicator that separates price into layers and lets the trader decide which ones to listen to.

So why describe it as analog?

When I first saw six True/False toggles as inputs, my refactoring instincts immediately kicked in. Why six switches? Why not a single input that lets the user pick a number from one to six and choose a single layer? After all, that’s how we usually simplify interfaces. And that’s exactly how my client was using it — with only UseD1 enabled.

That kind of refactor is clean. It’s digital. It reduces complexity.

But it also misses the big picture.

The original design wasn’t meant to select one layer — it was meant to let the user combine layers. One switch, or several. Fine detail alone, coarse structure alone, or anything in between. Layers could be stacked, blended, and cascaded.

That’s where the analog idea comes in. Instead of choosing a single, precise value—a digital decision—the original script let the trader feather the signal. Think of it like adjusting bands on an audio equalizer: you’re not flipping one switch on and everything else off; you’re shaping the mix.

Once I saw it that way, the six toggles stopped looking awkward and started looking intentional. Intentional—but also redundant. Imagine having to flip six separate switches on or off, in various combinations, all while keeping in mind that you may want to optimize how those layers interact. You could encode the toggles as 0s and 1s—false and true—and that would indeed open the door to optimization. It works, but it’s still clunky. Zeros and ones everywhere.

That naturally raises the question: can this be reduced to a simple binary pattern? If you’re familiar with my Pattern Smasher work, you already know the answer is yes—binary representations are compact, expressive, and highly optimizable. It’s an excellent approach. The downside is that it requires the user (and any downstream logic) to understand base-2 numbering, which isn’t a reasonable expectation for most traders.

So instead, we sidestep the binary scaffolding while keeping its power by leaning on EasyLanguage’s string-handling capabilities. Rather than six individual toggles, we represent them as a single string of six characters, each a 0 or 1. For example:

“110000”

This string simply means UseD1 and UseD2 are active. You don’t need to know—or care—what the decimal value of “110000” is. A 1 turns on the corresponding UseDX; a 0 turns it off. When more than one 1 appears in the string, the layers are cascaded automatically.

Same analog flexibility. Cleaner interface. Far less friction.

Parsing a string with one simple function: MidStr

Having a nice library of string manipulation functions enforces my prior post on why Quant languages should use the EasyLanguage model. I can easily extract the character at each location located in the string. The first location is represented by one and the last by six.

    if MidStr(Switchboard, 1, 1) = "1" then MasterOut = MasterOut   Band1_Hiss;
if MidStr(Switchboard, 2, 1) = "1" then MasterOut = MasterOut Band2_Treble;
if MidStr(Switchboard, 3, 1) = "1" then MasterOut = MasterOut Band3_Presence;
if MidStr(Switchboard, 4, 1) = "1" then MasterOut = MasterOut Band4_Mids;
if MidStr(Switchboard, 5, 1) = "1" then MasterOut = MasterOut Band5_Body;
if MidStr(Switchboard, 6, 1) = "1" then MasterOut = MasterOut Band6_Bass;
Using MidString to parse a String

Here the string is represented by Switchboard and is decomposed by the MidStr function. This function expects two arguments – starting postion and the number of characters to gather. As you can see by the code, we are stepping through each character in the string and extracting that particular character. Based on its value, we integrate that particular layer into the final calculation.

Same math.
Same structure.
Completely different understanding.

One Indicator, Multiple Trading Tempos

Here’s where the iceberg metaphor really matters.

The client had been trading the tip:

  • One layer
  • One tempo
  • One interpretation

But underneath that single line were multiple valid ways to trade:

  • Scalpers listening to fast detail
  • Swing traders listening to rhythm and rotation
  • Trend followers locking onto structure

Nothing was added.
Nothing was optimized.
We just stopped pretending the indicator was simpler than it really was.

The Real Lesson (and Why AI Matters Here)

AI didn’t invent anything in this process.

What it did was help surface alternative ways of thinking — some useful, some not — until the right framing emerged. The insight came from the interaction, not the output.

That’s the part of AI that excites me most for traders.

Not as a signal generator.
Not as a replacement for thinking.

But as a tool for understanding what we already have.

Closing Thought and Nex Steps

Most traders inherit indicators they never fully unpack.
They trade what’s visible and ignore what’s underneath.

Sometimes, the most valuable work isn’t finding something new —
it’s learning how to see what’s already there.

That’s what this exercise reminded me.

In the next installment, i will unpack this intriguing indicator and turn it into a complete trading system.

Final Code and Enhancements

{-------------------------------------------------------------------------------
Indicator Name: Wavelet Analog (Equalizer Naming)

Switchboard: "1 2 3 4 5 6"
1: Fine Grain Detail --- 6: Coarse Structural Detail
The Anchor (SubBass) is the permanent baseline track.
-------------------------------------------------------------------------------}
Inputs:
Switchboard("000000") [DisplayName = "Analog Switches (Bands 1-6)"],
ViewMode(0) [DisplayName = "0:Signal View, 1:Difference"];

Vars:
// "Tone Curve" Weights (fixed EQ kernel)
Tone0(0.375), Tone1(0.25), Tone2(0.0625),

// Tracks: Raw progressively stronger low-pass versions
RawTrack(0), LP1(0), LP2(0), LP3(0), LP4(0), LP5(0), SubBass(0),

// EQ Bands (detail layers)
Band1_Hiss(0), // Ultra-high: micro flicker / "hiss"
Band2_Treble(0),
Band3_Presence(0),
Band4_Mids(0),
Band5_Body(0),
Band6_Bass(0), // Low: macro structure / "bass"

// Outputs
MasterOut(0), Anchor(0), CutSignal(0);

Vars: j(0), ValidSwitches(True);

// --- Step 1: The "Analog Console" Smoothing Ladder ---
RawTrack = Close;
LP1 = Tone0*RawTrack 2*Tone1*RawTrack[1] 2*Tone2*RawTrack[2];
LP2 = Tone0*LP1 2*Tone1*LP1[2] 2*Tone2*LP1[4];
LP3 = Tone0*LP2 2*Tone1*LP2[4] 2*Tone2*LP2[8];
LP4 = Tone0*LP3 2*Tone1*LP3[8] 2*Tone2*LP3[16];
LP5 = Tone0*LP4 2*Tone1*LP4[16] 2*Tone2*LP4[32];
SubBass = Tone0*LP5 2*Tone1*LP5[32] 2*Tone2*LP5[64];

// --- Step 2: Split into EQ Bands (details between tracks) ---
Band1_Hiss = RawTrack - LP1;
Band2_Treble = LP1 - LP2;
Band3_Presence = LP2 - LP3;
Band4_Mids = LP3 - LP4;
Band5_Body = LP4 - LP5;
Band6_Bass = LP5 - SubBass;

// --- Step 3: Master bus anchor switchboard mix ---
Anchor = SubBass;
MasterOut = Anchor;

// --- Validate the switchboard ONCE ---
once
begin
if StrLen(Switchboard) > 6 then
ValidSwitches = false
else
begin
for j = 1 to 6
begin
if MidStr(Switchboard, j, 1) <> "0" and MidStr(Switchboard, j, 1) <> "1" then
begin
ValidSwitches = false;
break;
end;
end;
end;
end;

if ValidSwitches then
begin
if MidStr(Switchboard, 1, 1) = "1" then MasterOut = MasterOut Band1_Hiss;
if MidStr(Switchboard, 2, 1) = "1" then MasterOut = MasterOut Band2_Treble;
if MidStr(Switchboard, 3, 1) = "1" then MasterOut = MasterOut Band3_Presence;
if MidStr(Switchboard, 4, 1) = "1" then MasterOut = MasterOut Band4_Mids;
if MidStr(Switchboard, 5, 1) = "1" then MasterOut = MasterOut Band5_Body;
if MidStr(Switchboard, 6, 1) = "1" then MasterOut = MasterOut Band6_Bass;

// --- Step 4: What you CUT from the mix ---
CutSignal = Close - MasterOut;

// --- Step 5: Plotting ---
if CurrentBar > 130 then
begin
if ViewMode = 0 then
begin
Plot1(MasterOut, "MasterOut", White, default, 1);
Plot2(Anchor, "Anchor", DarkGreen, default, 1);
end
else
begin
Plot3(CutSignal, "CutSignal", Red, default, 1);
Plot4(0, "Zero", LightGray);
end;
end;
end;
Wavelet Analog

Examples

Three charts are shown with three different presets.

Plotting 2 Scales in TradeStation

You can’t plot a single multiple output indicator with different scales in the same chart in TradesStation (well not easily). You have to plot either one or the other and this can be accomplished by using a plot toggle. Here is the toggle in EasyLanguage.

If ViewMode = 0 then
begin
Plot1(MasterOut, "MasterOut", White);
Plot2(Anchor, "Anchor", DarkGreen);
end
else
begin
Plot3(CutSignal, "CutLine");
Plot4(0, "Zero");
Different Plot Scale Toggle

Why EasyLanguage Should Be the Blueprint for Quant Languages

When I first ran into EasyLanguage, I didn’t take it seriously.

I come to this with a bias: I’m a lifelong systems programmer, and I helped build a trading platform the old-fashioned way.

Years ago I co-created Excalibur, a Fortran-based trading and backtesting engine. In that world, everything is explicit. If you want rolling windows, you build them. If you want indicator “memory,” you write the storage. If you want speed, you earn it with careful code and a lot of scaffolding.

So when I first encountered EasyLanguage, I didn’t take it seriously. It looked too simple—almost like “training wheels” for people who didn’t want to program.

Then time did what time always does: it changed my opinion.

After decades of building systems, libraries, and tooling—and watching how often good ideas get buried under boilerplate—I started to see EasyLanguage differently. It’s not “cute.” It’s a purpose-built quant DSL with one superpower that most general-purpose languages don’t give you for free:

Native time-series semantics.

In other words, EasyLanguage starts you in a world where “one bar ago” is normal, rolling windows are natural, and stateful indicators can be expressed as simple algebra. If I were building a quant language today, I’d copy that blueprint: human-readable rules plus time-series semantics baked into the language.

To explain why, I like a metaphor: Flatland versus Spaceland.


Flatland versus Spaceland

Flatland is where most beginners start—especially if they come from C, Python, or Excel. In Flatland, a variable is simply “a value right now.” The world feels perfectly sensible, but it’s missing something. The moment you need yesterday, or the last 30 bars, you’re forced into extra machinery: arrays, indexing, loops, buffers, bookkeeping.

Then comes the EasyLanguage moment—the part that feels like science fiction the first time you truly get it.

In Spaceland, the “missing dimension” exists: time. Variables don’t just have a current value; they have a built-in past. Close naturally includes Close[1]. Your own variables remember prior values. Rolling functions like Average() and RSI() aren’t special libraries—they’re native operations on values that already extend through time.

So the breakthrough isn’t learning a new function. It’s realizing you’ve been thinking on a plane, and EasyLanguage is operating in a world with one more dimension.

(If you’ve never read Edwin Abbott’s novella Flatland, no worries—this post borrows the idea, not the geometry. Abbott’s missing dimension is spatial; mine is time.)


Scalar versus series (without the esoterica)

In most general-purpose languages, a variable is a scalar: one value right now. If you want the last 30 values, you must store them and manage the indexing yourself.

In EasyLanguage, variables behave like series: the current value plus an implicit history. That’s why these feel natural:

If Close > Close[1] then ...
value1 = Average( (High + Low) / 2, 30 )
value2 = Average( RSI(Close, 14), 30 )


The “series prep” tax in Python

EasyLanguage can do this in one line because it can treat the expression (High + Low)/2 as a time series automatically:

MidPointAvg = Average((High + Low)/2, 30)

In Python—even if high and low already exist as lists—you still have to manufacture the series you want to average. Before you can average midpoints, you must first create a new midpoint list for the last lookBack bars:

# Assume:
# - high and low are lists (oldest -> newest)
# - currentBar is the index of the bar we're on "right now"
# - lookBack is how many bars we want to include
lookBack = 30

# Step 1) Build a NEW series (midpoint) for the last lookBack bars
midpointSeries = []

for barsAgo in range(lookBack):
bar = currentBar - barsAgo
if bar < 0:
break # ran out of history

midpoint = (high[bar] + low[bar]) / 2.0
midpointSeries.append(midpoint)

# Step 2) Now we can feed that newly created series to the generic average
mid_avg = average(midpointSeries)

Same goal. Totally different assumptions.

  • Python is scalar-first: you build the series.

  • EasyLanguage is series-first: the platform quietly supplies the time dimension.

Why EasyLanguage is a great engineering-to-trading bridge

If you’re coming from DSP or any engineering intensive discipline, you already know what you want to test: filters with memory, rolling statistics, trigger lines, crossings, parameter tweaks you can validate visually. The last thing you want is to burn weeks building infrastructure—buffers, indexing rules, warm-up handling—before you ever test the idea. EasyLanguage skips that entire tax. It starts you in Spaceland: time-series semantics are native, history is built in, and writing a filter looks like writing the math.

The mind-meld example (Ehlers High Pass)

Here’s a (simplified) EasyLanguage high-pass filter. From a programmer’s perspective, it’s mind-bending because it reads like algebra, but behaves like a stateful filter:


//Ehlers HighPass function - from his website
//https://www.mesasoftware.com/papers/

Inputs: Price(NumericSeries), Period(NumericSimple);


Vars: a1(0),
b1(0),
c1(0),
c2(0),
c3(0);

a1 = ExpValue(-1.414*3.14159 / Period);
b1 = 2*a1*Cosine(1.414*180 / Period);
c2 = b1; c3 = -a1*a1;
c1 = (1 + c2 - c3) / 4;
If CurrentBar >= 4 Then
EhlersHighPass = c1*(Price - 2*Price[1] + Price[2]) +
c2*EhlersHighPass[1] + c3*EhlersHighPass[2];
If CurrentBar < 4 Then
EhlersHighPass = 0;

The “magic” is here:

c2*EhlersHighPass[1] + c3*EhlersHighPass[2]

In computer-science terms, this is not “recursion” (no function calls itself). In signal-processing terms, it’s feedback: today’s output uses prior output. EasyLanguage makes that look effortless because the platform runs once per bar and preserves the prior values automatically.


Brain Meld Squared

If you’re a programmer, you know what kind of scaffolding this should require:

value1 = EhlersHighPass(Close, 14);
value2 = EhlersHighPass(Close, 28);

Those are two independent filters. Each one needs its own private memory—its own prior outputs—yet EasyLanguage gives you two clean calls. No objects. No buffers. No state management. It just works.


Ultra special: chaining filters

And if you can do that, you can do this:

value1 = EhlersHighPass(EhlersHighPass(Close, 14), 20);

That single line implies two live filter instances with separate state, running bar-by-bar, with the outer filter consuming the inner filter’s output as a time series. That’s series semantics and object-like behavior showing up at the same time—without the programmer ever building the scaffolding.


Closing thought

If I were designing a quant language today, I’d copy EasyLanguage’s blueprint: human-readable rules plus native time-series semantics. It lowers the barrier for non-programmers and removes the infrastructure tax for engineers who just want to test ideas quickly—especially the DSP-to-trading crowd.

Mean Reversion in 5 lines of code:

input: mDay(0),nDay(1),stopLossAmt$(1750),profitTargAmt$(5000),tradeLife(5);

if close > average(close,100) and close mDay days ago < close mDay + nDay days ago then
buy next bar at market;
if barsSinceEntry > tradeLife then sell next bar at open;

setStopLoss(stopLossAmt$);
setProfitTarget(profitTargAmt$);
Could be written as 5 lines, right?

Results

Simple EasyLanguage Code

This POINT is AVERAGE of 66 Values

All points that start with the address 2, 4 were all positive.  There were 66 observations.

66 addresses @ MDAY = 2 AND NDAY = 4

Splicing away all but MDAY = 2!  Big BLOBS.  Some were good (green) and some were bad (purple!)

Volumetric SLICED @ MDAY = 2

Magnifying the blobs – they break away into 6 distinct values – 4 dimensions in 3D Space.

Entering the MATRIX 4 Parameters plotted in 3 Dimensional

These graphs demonstrate a certain level of robustness.   As long as we stay in a bull market to a certain degree.

A Turtle Thermometer for Trend-Following: 2025 Results

A Bare-Bones Turtle Algorithm for Gauging Trend-Following Conditions

The core Turtle rules were fully mechanical, but several operational choices, such as position sizing nuances, market selection, roll/contract handling, and execution practices were left to judgment or circumstance. Many would argue the philosophy behind the Turtles mattered as much as the rules themselves, and differing interpretations of that philosophy go a long way toward explaining why their results diverged so widely. The mechanics, however, are straightforward: you can distill them from the published books and courses, strip them down even further, and apply the resulting rule set across a broad portfolio to take the pulse of trend-following today.

I’ve worked with the Turtle framework for many years, coded numerous variants, and even compared notes with a handful of original Turtles. If any method can “take the temperature” of market trendiness, this one can. This system synthesizes a shorter-term trend mechanism (that limits execution based on the prior outcome) with a true longer term trend following entry and exit method (two months of data are used to determine entry).   Short-term trading is difficult and often falls victim to over trading.  The shorter-term entry is used to try and capitalize on the genesis of a big trend.  Preventing another trade after a winner is one method of reducing trading and chop.  If the short-term break out turns into a trend and entry is prevented, then the 55-day break out is there to capture it.  Below are the rules I extracted to build a fully mechanical, bare-bones algorithm for that purpose.

Rules Used in This Analysis

Conventions & Definitions

  • Breakout (stop basis): Enter on a stop when price exceeds the specified lookback extreme by 1 tick (or exactly at the extreme if your platform supports that).
  • N: 20-day weighted Average True Range (WATR) used for both systems.
  • Risk stop (volatility stop): A stop placed 2×N from the entry price.
  • Swing stop: For System #1, use the 10-day highest high/lowest low; for System #2, use the 20-day highest high/lowest low.
  • Closest stop wins: The active protective stop at any time is the tighter of the risk stop and the swing stop.
  • Loser vs. non-loser (for System #1’s gating rule):
    • A trade that is stopped out by 2×N is a loser.
    • A trade that exits via the 10-day swing stop (even if it’s a loss) is not counted as a loser for gating.
    • Profitable exits via the 10-day swing stop are obviously not losers.

System #1 — 20-Day Breakout (Conditional)

Purpose: Only take the next 20-day breakout if the most recent 20-day breakout resulted in a 2×N loss.

  • Entry condition (gated):
    • Compute the 20-day Donchian channel.
    • You may only take a long (new 20-day high) or short (new 20-day low) breakout if the last System #1 trade ended with a 2×N risk stop.
    • If the last System #1 trade exited via the 10-day swing stop, it does not unlock the gate.
  • Initial protective stops (at entry):
    • Risk stop: 2×N from entry.
    • Swing stop: Opposite extreme of the past 10 days (lowest low for longs, highest high for shorts).
    • Use the tighter of the two stops at all times (“closest stop wins”).
  • Exit rules:
    • Exit if price hits the active stop (risk or swing).
  • Bookkeeping for gating:
    • If exit was the 2×N risk stop, mark the trade as a loser (this unlocks the gate for the next entry).
    • If exit was the 10-day swing stop, do not mark as a loser (gate remains locked).
  • Always evaluating: System #1’s breakout logic runs continuously, but entries are allowed only when the gate is unlocked by a prior 2×N loss.


System #2 — 55-Day Breakout (Always On)

Purpose: Classic trend capture that runs regardless of System #1’s state; does not affect System #1’s gating.

  • Entry condition (ungated):
    • Enter long on a 55-day high breakout; enter short on a 55-day low breakout.
  • Initial protective stops (at entry):
    • Risk stop: 2×N from entry.
    • Swing stop: Opposite extreme of the past 20 days (lowest low for longs, highest high for shorts).
    • Use the tighter of the two stops at all times.
  • Exit rules:
    • Exit if price hits the active stop (risk or swing).
  • Isolation from System #1:
    • System #2 trades and outcomes do not influence System #1’s “last-trade-was-a-loser” gate (also known as a filter).

The Portfolio

Currencies (CME FX)

Preferred name Short Futures ticker
Australian Dollar AUD @AD (6A)
British Pound GBP @BP (6B)
Canadian Dollar CAD @CD (6C)
Euro EUR @EC (6E)
Japanese Yen JPY @JY (6J)
Swiss Franc CHF @SF (6S)

Rates (CBOT)

Preferred name Short Futures ticker
30-Year U.S. Treasury Bond 30Y @US (ZB)
10-Year U.S. Treasury Note 10Y @TY (ZN)
5-Year U.S. Treasury Note 5Y @FV (ZF)

Equity/Index

Preferred name Short Futures ticker
E-mini S&P 500 ES @ES (CME)
U.S. Dollar Index DXY @DX (ICE)

Metals (COMEX/NYMEX)

Preferred name Short Futures ticker
Gold XAU @GC
Copper Cu @HG
Silver XAG @SI
Palladium Pd @PA=11INC
Platinum Pt @PL

Energies (NYMEX)

Preferred name Short Futures ticker
RBOB Gasoline RBOB @RB
Heating Oil HO @HO
WTI Crude Oil WTI @CL
Henry Hub Natural Gas NatGas @NG

Grains/Oilseeds (CBOT)

Preferred name Short Futures ticker
Soybeans Beans @ZS
Corn Corn @ZC
Rough Rice Rice @ZR
Wheat (SRW) Wheat @ZW
Soybean Meal Meal @ZM

Livestock (CME)

Preferred name Short Futures ticker
Feeder Cattle Feeders @FC (GF )
Live Cattle LiveCat @LC (LE )
Lean Hogs Hogs @LH (HE)

Softs (ICE)

Preferred name Short Futures ticker
Frozen Concentrated Orange Juice OJ @OJ
Sugar No. 11 Sugar @SB
Cotton No. 2 Cotton @CT
Coffee “C” Coffee @KC
Lumber Lumber @LBR = @LB legacy

Market Normalization (Fixed-Fractional Sizing)

To level the playing field across markets, I used fixed-fractional position sizing keyed to the Turtle “quick” 20-day ATR.

Risk budget per trade

  • Account equity =$250,000
  • Fraction at risk per trade =2%
  • Dollar risk per trade: = × = 0.02 × 250,000 = $5,000

Contracts to trade

  • Let ATR = 20-day (Turtle quick) Average True Range in price units
  • Let BPV = Big Point Value (dollars per 1.0 move)
  • Dollar risk per 1 contract: ATR × BPV
  • Position size (contracts):
  • Contracts =⌊ / ATR × BPV⌋

In words: allocate $5,000 of risk to each trade and size the position by dividing that risk by the market’s expected dollar move (ATR×BPV).   Round down to an integer.

Notes & conventions
  • ATR is the 20-day Turtle quick ATR (same used in the rules).
  • Use the correct BPV for each contract (e.g., ES $50/pt, CL $1,000/pt, SI $5,000/pt).
  • Enforce at least on contract per signal.
  • $50 slippage and $10 commission per round turn.

Results

Large portfolio performance on Bare Bones Turtle

This equity curve is very typical across the spectrum of most trend following systems.  There have been big years to keep the trend following momentum going – recently 2008, 2010, 2014, 2018, 2020.

Big Years – pushes the popularity of Trend Following

Many times, the futures and commodity markets are there to benefit from global events such as the banking collapse (2008) and the pandemic (2020).

Over the years, markets have fallen in and out of favor with Trend Following.  The best market over the past twenty years or so turned out to be sugar.  With its smaller size and associated volatility and trends it was the clear winner.

Many HOT SPOTS on the Correlation Heat Map

Pearson Correlation Matrix

But, what about smaller accounts?

There exist sub portfolios with better profit to draw down ratios.  If you could only choose ten markets and wanted to know the best combination, you can do this with my TS-PortfolioMerge software.  In fact, all the metrics and images I have shown in this post were generated with TS-PortfolioMerge.  If your budget only allows for 10 markets and you want to evaluate every combination you will need to wait a while for TS-PM to run through every combination.

Search space C(37,10) = 348,330,136 (subset) – yes that is 300 million combinations.  Just set up your computer for overnight processing.

But if you want a speedy answer, that will approximate the entire search space you can do that as well.

Sampled (limit 50,000; randomized).

# P/DD Net Profit ($) Max DD ($) Symbols
1 12.071 1,615,758.97 133,857.91 @EC, @HG, @HO, @JY, @LB=11INC, @LH, @RB, @SB, @SM, @TY
2 11.676 1,333,359.10 114,194.85 @FC, @GC, @HO, @KC, @LB=11INC, @LBR=11INC, @LH, @OJ, @SB, @SM
3 11.563 1,473,201.00 127,410.20 @C, @CL, @EC, @ES, @GC, @HG, @HO, @LBR=11INC, @LH, @SB
4 11.545 1,601,806.50 138,745.50 @C, @CL, @CT, @GC, @HO, @LH, @RB, @SB, @SM, @US
5 11.444 1,427,082.85 124,705.40 @CL, @GC, @HO, @KC, @LB=11INC, @LBR=11INC, @LH, @OJ, @S, @SB

Run the speedy version multiple times to see if the same portfolio bubbles to the top.  If you continue getting different portfolios, you can run the exhaustive mode.  Here the best 10 markets were:

@EC, @HG, @HO, @JY, @LB=11INC, @LH, @RB, @SB, @SM, @TY

Here you have two currencies (EC and JY), one metal (HG), two energies (HO and RBOB), one interest rate (TY), sugar, lumber, soybean meal and lean hogs.  But are we guilty of cherry picking?  Maybe the Monte Carlo analysis will provide some insight.

Monte Carlo Analysis on 10 of the best combination from the Speedy output.

Conclusion

Trend Following as of late October 2025 is doing well and doing as expected.  The pandemic pulled the algorithm out of the doldrums and positive years have been banked since.  The current year looks like the exception, but we still have two months left.  With the Gold move you would think 2025 would have been a banner year.  The Trend is STILL OUR FRIEND.

Email me if you would like my code for my bare bones Turtle system, I utilized to create all these results.  The code includes the human curated LAST TRADE WAS A LOSER function.

Concentration, Catalysts, and Crickets: Brewing the Perfect Slippage Storm

In today’s trading environment where a single stock dominates an index, you must be careful with your order placement (if you can) around potentially large news events.

I am late with this post, but my client (I program for him) suffered through a Perfect Slippage Storm.  A short-term system is only as good as its ability to be properly executed.    On August 27th, 2025, NVIDIA announced after the market close.  According to ChatGPT.

Yes—that timing lines up with Nvidia’s earnings release hitting after the bell. On Wed, Aug 27, 2025, outlets were primed for the NVDA press release around 4:20 pm ET; live blogs called out that exact time window, and NVDA headlines/press release started landing shortly after, with shares dipping in early after-hours. That kind of instant move in NVDA typically ripples straight into NQ.

Check out the following graphic.

The Perfect Slippage Storm!

Can this really happen?

Come on – this is a 5-minute bar, right?  A lot of things can happen in five minutes.  I was a futures broker for many years and my rule of thumb during my tenure was you MAY get out at the low of the minute bar if there is a hiccup and your sell stop is blown.  Here is a one-minute chart.

Some orders were filled at the tick up on the 2nd minute bar.

My client wasn’t lucky this day and got filled near the low of the 2nd minute bar.  He was using a % trailing stop and when the high breached his threshold his protective stop was cancelled, and the new stop was implemented.  All this activity takes time.  And this strategy is professionally managed.

Why was this a perfect storm?

The report pop printed a new intraday high, likely tripping resting buy-stops and in this case pulling trailing protective stops tighter. Those sell-stops then fired into a thinning book after 4:00 p.m. ET, where Nasdaq futures liquidity is razor thin. Some orders were rejected or re-priced and ended up converting to market, worsening slippage. In this case, my client would have been better off if the trailing-stop threshold hadn’t been touched.

Electronic Trading and Fast Market Conditions and Not Held Order!

You believed the trade was up $400, but your day-end statement shows a loss of –$3,400. That disconnect usually comes from execution during a fast market. When your order is not held, the broker (or algo) has time/price discretion and no obligation to fill at a specific price. In a sudden air-pocket, quotes vanish and price gaps; the broker can’t predict the next print, so the only thing he can do is hit the first available liquidity. The result is slippage and a gaping difference between what is on the screen on what is in your pocket or the lack thereof.

A Stop Limit order can help during spikes or down drafts

This type of order is not universally available – especially when using algos.

A buy stop-limit order is a two-part order used to enter long above the market (or cover a short) with price control.

  • Stop price (trigger): When the market trades at or above this price, your order activates.

  • Limit price (cap): Once activated, the order becomes a limit buy at your limit price (or better). It will not pay more than the limit.

What should my client do in the future?

He tested his strategy over 15 years of data and felt secure enough to trade the system.  He knows that this market action could have just as easily gone in his favor.  Had the initial reaction carried on, he may have made a nice profit.  Should he augment his strategy to get out at the end of the day and then get back in – step over post-closing reports?  Maybe, but there is always the potential of slippage on this out and back in trade.  Also, you would need to use discretion as to when a handful of stocks controls the entire index.

Percent trailing stops only help if your profit trigger is meaningfully large and you’re willing to give back a realistic slice of that profit.

When a client shows me an equity curve that looks too good to be true, my first question is whether they’re using a percent trailing stop. I hope they say no—but usually it’s yes. Then I ask two things:

  1. What’s the profit threshold that arms the trail?

  2. How much are you willing to give back once it arms?

If the threshold is small and the give-back is ~20% or less, I know we’re in Best-Case Scenario Syndrome: backtests assume friendly fills. Platforms like TradeStation or MultiCharts will print a theoretical fill on every trade, but as we saw earlier, the gap between theory and actual can be huge.

You must accept that slippage is going to occur

If you don’t then you cannot trade.  You might come back at me and say: “Well, I will only use stop limit orders.”  That is great. but what about when exiting a trade.  “I will only develop a strategy that enters on limits – that way I can cut slippage in half.”  That is definitely a possibility.  “I will execute myself and forgo the convenience of algo order placement.”   Well, you better quit your day job and trade during the day.

Backtesting with [Trade Station,Python,AmiBroker, Excel]. Intended for informational and educational purposes only!