The short version. A Turtle backtest is harder to get right than it looks, because the rules live at three levels at once: daily channels for signals, intraday prices for fills and stops, and one shared account for unit limits across every market. I built the simulator in Python on Exness tick data from 2020 to 2026, checked fills minute by minute, and then tested the code three ways before trusting any result: 8 tests rebuilt from the worked examples in the original PDF, a look-ahead check at three cut dates, and an independent daily-bar version that matched trade for trade on three markets.

This is part 3 of 5. Part 2 went through the rules. Part 4 has the results.

Why not just use a backtesting library?

Most backtesting tools run one symbol at a time. The Turtle system doesn't work that way. Its unit limits (6 units across closely related markets, 10 across loosely related ones, 12 per direction) and its drawdown rule all depend on what every other market is doing at that minute. A 26-market portfolio has to be simulated as one account, with events from all markets interleaved in time order.

So I wrote a small dedicated simulator. It still writes its trades in the same format as my single-symbol tools, so the same reports and comparisons work on it.

The simulator: Exness ticks, M1 and D1 bars, levels from yesterday's daily bar, fills minute by minute, portfolio limits, trade list

What data does it run on?

  • Prices: the Exness Raw Spread tick archive, January 2020 to September 2026, built into M1 and D1 bid bars with the spread kept per bar.
  • Daily bars: Exness server time is UTC, so forex, gold and indices get a short Sunday bar of a few hours. Counting that as a full day would shorten every 20-day channel by about 4 real days a month, so I merge Sunday into Monday (crypto keeps its 7-day calendar).
  • Currency: profits in JPY, CAD, CHF, EUR and other currencies are converted to USD with the real daily close of the matching pair, not a fixed rate.
  • Gaps in the archive: a few markets have missing months (UKOIL, XPTUSD) or start late (XNGUSD, JP225, XCUUSD). Positions are closed on the last bar before a gap, and the market only trades again after 56 fresh daily bars.

Why check fills minute by minute?

Because a daily bar can't tell you the order things happened in. Here's a real example from the backtest: crude oil on 3 March 2026.

Crude oil on 3 March 2026: the daily bar on the left, the minute path on the right with breakout, add and stop

The daily bar runs from 70.63 to 77.21. From that alone you can't tell whether the low or the high came first. The minute data says:

Time (UTC) Event Price
07:27 System 1 breakout, unit 1 73.105
10:52 add, next unit 76.094
19:41 the added unit's stop is hit 72.109

Unit 1 survived, because its own stop was lower, and it was finally closed on 14 April at 85.986 for +$1,288. The added unit lost $399. A daily-bar test has to guess this order, and over 1,232 positions those guesses add up. That's why my independent daily-bar version (below) disagreed on crude oil and only on crude oil.

Signal levels, on the other hand, only use completed daily bars: the 20-day and 55-day highs are calculated up to yesterday's close, and N is the value from the last Monday. Nothing inside the day can change today's levels.

What does the code look like?

The pyramiding rule is a good example of how small the core is. This is the function that adds a unit to a position:

def add(self, fill, lots, ms, comm_in):
    d, half = self.dir, self.add_n * self.n
    for u in self.units:
        u.stop += d * half
    stop = fill - d * self.stop_n * self.n
    self.units.append(Unit(fill, lots, stop, ms, comm_in, sl0=stop))
    self.next_add = fill + d * half

Three lines carry the whole rule: raise the old stops by half N, put the new unit's stop 2N below its real fill, and set the next add level half N further on. Because each unit keeps its own stop, the gap case from part 2 (a unit filling at 30.80 instead of 30.10) falls out naturally.

The add function with the new-unit stop highlighted

The harder code is everything around it: the order of events within one minute (imaginary filter trades first, then stops, then channel exits, then new entries, then adds), the strongest-market rule when several markets trigger in the same minute, and the bookkeeping for swap (charged triple on Wednesdays, every night for bitcoin).

How do I know the code is right?

I don't trust a backtest number until the code has been checked in ways that don't depend on the result. Three checks:

1. Tests from the original PDF. Eight tests rebuild the worked examples in the rules document: the heating oil unit size, the N table, the crude oil stops including the gap case, the gold add levels, and the drawdown rule's 10%/20% steps. If the code disagrees with the PDF's own numbers, a test fails.

2. A look-ahead check. The simulator is run twice: once on the full data, once with the data cut off at a date (17 March 2021, 2 August 2021, 12 January 2022), so nothing after that date is even loaded. Every trade that opened or closed before the cut has to be identical in both runs: same time, direction, price and size. They are. If any future price leaked into a decision, this would break.

3. An independent second version. I wrote a separate, much simpler implementation that only uses daily bars, on one market at a time, and compared trades:

Market Result
XAUUSD same trades, same direction, same entries
USDCAD same trades
DE30 same trades
USOIL 35 vs 32 trades, from the order of prices inside the day

Long entries differ by exactly the spread, because the daily version ignores it. The only real disagreement is crude oil, which is the intraday-order problem above.

The three checks: tests, the cut-date look-ahead check, and the daily-bar cross-check

All 11 tests (the 8 rule tests plus 3 cut dates) pass in about 14 seconds.

What does a real Turtle trade look like?

Textbook examples are tidy. Here's a real System 2 long on gold from the 26-market run:

Unit Entry Exit Result
1 3 Feb 2025 at 2,817.34 14 May at 3,201.92 (20-day low) +$1,326.99
2 4 Feb at 2,832.65 14 May at 3,201.92 +$950.92
3 30 Mar at 3,092.86 stopped 4 Apr at 3,031.63 −$193.38
4 10 Apr at 3,169.39 14 May at 3,201.92 +$31.93

Total: +$2,116.46, after swap and commission. Two things are not in any textbook. Unit 3 came in eight weeks after unit 2, far beyond half N, because adds were blocked by the portfolio's unit limits while other markets held risk, and each blocked add moved the next level half N further out. And unit 3 was stopped on its own in early April while units 1 and 2 kept running, because its stop was 2N below its own, much higher fill.

A real System 2 gold trade from February to May 2025 with four units and one stopped on its own

What I can't verify

  • Real execution. This is a simulation on archive data. Real fills at breakouts would see slippage the spread doesn't capture.
  • Swap and some commissions. Swap is measured on 3 of 26 markets; commission on the commodity and index CFDs isn't confirmed. Both are set to zero where unknown.
  • Contract sizes for some commodities and indices. With risk sized by N this mostly affects lot rounding, not the result in dollars per unit.
  • The archive itself. I've compared Exness archive ticks against a live MT5 feed before: about 23% of gold ticks are shifted by up to roughly 30 cents, in both directions. That doesn't matter at 2N stops measured in tens of dollars, but it's worth knowing.

Next, part 4: the results.

Research, not financial advice. Past results do not predict future results.