Quant from Scratch by QuantWright

Backtesting Essentials · Lesson 9

In-sample, out-of-sample and walk-forward

How to split the past so that a tuned rule meets data its tuning never saw.

Know the basics? Read the lesson notes

Cut the past before you tune

A backtest runs a trading rule over past prices to see what it would have made. Here the prices are simulated, made up by a computer: eight years, 2015 to 2022.

Each day, a rule holds the market or stays out. Holding a day puts $100 in at the day's first price and takes it out at its last. A rule's result is its days' gains and losses added up, with no trading costs. Just holding every day is the yardstick: a rule's lead is its result minus just holding's over the same days.

Each evening the market publishes a made-up number, the reading. We built this market, so we know what is real in it: the next day's move tends to go a little the reading's way. The month changes nothing.

The lesson's rule sorts days by two conditions: was yesterday's reading above 0, and which month is it? That makes 2 × 12 = 24 kinds of day, each with a setting: hold, or stay out. Tuning finds the settings with the best backtest: hold a kind of day if its days added up to a gain.

A tuned rule looks good on the years it was tuned on, whether its settings fit something real or luck. So cut the years in two before tuning. The earlier part is in-sample: the years the rule is tuned on. The later part is kept aside.

Eight simulated years, cut in two

Dark: in-sample. Hatched: kept aside.

In-sample the tuned rule made $113 and just holding $53: a lead of 113 − 53 = $60. The lesson keeps this cut from here on.

Four years the tuning never saw

The years kept aside are out-of-sample: they played no part in tuning the rule. Lesson B8 called the two parts tuning data and new data. The rule keeps its 24 settings.

The tuned rule in-sample, then out-of-sample (simulated)

Colour: the tuned rule. Grey: just holding. Both restart from $0 at the cut.

Out-of-sample the rule made $1. Just holding made $0 there, against $53 in-sample, so the raw results cannot be compared. The lead takes the market out: 1 − 0 = $1, against $60 in-sample.

The reading is real, but its effect is small next to four years of luck, and each of the 24 settings was tuned on about 43 days (1,039 ÷ 24), so the tuning mostly fit chance.

On 1,000 more markets built the same way, the lead averaged $53 in-sample and −$1 out-of-sample. It was above zero out-of-sample in 483 of them: close to a coin flip.

For the preciseTo one decimal, in-sample then out-of-sample: the rule $113.5 and $1.4, just holding $53.4 and −$0.3. Whole-dollar figures round halves up, and every lead is taken between the whole-dollar figures shown. The in-sample years hold 1,039 trading days, about 43 for each setting, and the out-of-sample years 1,044; tuning set 13 of the 24 kinds of day to hold. Over the 1,000 markets the averages were $84 and $29 for the rule and $31 and $30 for just holding, so the average leads are 84 − 31 = $53 and 29 − 30 = −$1. The out-of-sample lead was below the in-sample lead in 975 markets. A rule with the reading alone (2 settings) averaged $51 in-sample and $43 out-of-sample, and beat just holding out-of-sample in 608 markets.

Why cut by time?

Why not mix the days instead: day one in-sample, day two out-of-sample, and so on?

Try both splits on a rule that means nothing: the month alone, 12 settings. One change for this screen: a trade lasts five days. $100 goes in at a day's first price and comes out at the last price four trading days later. A new trade starts on every day of a held month; just holding starts one every day. A lead here adds up every trade, with up to five open at once, so the dollars run larger.

Which split will show the bigger out-of-sample lead?

The month rule, split two ways (simulated)

Top: six five-day trades, split every other day. Dark: in-sample. Colour: out-of-sample.

Your guess:

A rule tuned on luck looks good on the trades it was tuned on. Cut by time, the lead fell from $147 in-sample to $24 out-of-sample. Split every other day, it went from $84 to $112: that test says a rule built on nothing held up.

Why: an out-of-sample trade shares four of its five days with the in-sample trade that started the day before, so its result is mostly a copy, luck included. The in-sample trades give part of the out-of-sample answer away: the split leaks. Cut by time it does not: this test starts no trade in the four days before the cut, so no trade crosses it.

A mixed split leaks whenever neighbouring days share something. A cut by time also copies how a rule is used: tuned on the past, traded after it.

For the preciseOn the 1,000 other markets the out-of-sample lead was above zero in 940 for the every-other-day split and in 270 for the cut by time. The 270 is below half because the made-up market rises over time and a rule built on nothing sits out part of it (it held 7.5 of the 12 months on average), so it tends to trail just holding. The 24-setting rule of the screen before sat out days too, but had the reading to make up for it. Results behind the leads, in-sample then out-of-sample. Cut by time: the rule $385 and $56, just holding $238 and $32. Every other day: the rule $247 and $220, just holding $163 and $108. The numbers are larger than elsewhere in the lesson because up to five trades are open at once. No trade starts on the last four days of a stretch, so none runs past its end. Averages over the 1,000 markets: cut by time, a lead of $145 in-sample and −$63 out-of-sample; every other day, $90 and $80. Cut by time, the rule's out-of-sample result averaged $118 against just holding's $181: about the share of the months it held. With one-day trades, where neighbouring trades share no day, the every-other-day split leaked nothing in this simulation: the month rule's lead went from $34 to $5 by time and from $39 to $2 every other day.

Fifteen rules, one pick

Back to one-day holds, cut by time. After a lead of $1, the tempting move is to try other rules. Here are 15: every mix of four conditions except none (2 × 2 × 2 × 2 − 1 = 15). The conditions are the reading, the month, the weekday, and whether yesterday's price rose. Only the reading is real.

Each is tuned on the in-sample years alone, all 15 run on the out-of-sample years, and you keep the best.

15 tuned rules, then the pick (simulated)

Each dot: one rule's four-year result on 2019–2022, in the order run. Dashed: just holding. Colour: the best.

The pick was the rule with the reading alone, the one real condition: $26 on 2019–2022, where just holding made $0. Over four years that lead is 26 ÷ 4 = $6.50 a year.

No rule was tuned on those years, but the pick was made on them: picking one rule from several is a second way a stretch gets used. So $26 is the best of 15 on the years that picked it, and the best of many tries is partly luck, even when the rule is real (lesson B7, The best of many). On the 1,000 other markets the pick was this rule in only 390. For the pick, 2019–2022 are now in-sample.

The simulated market runs on for two more years, 2023 and 2024, that nothing has touched. There the pick made $30 and just holding $41: a lead of 30 − 41 = −$11, or −11 ÷ 2 = −$5.50 a year. The reading's effect is small, and two years are short.

On the 1,000 other markets the pick's lead averaged $5.7 a year on the years that picked it and $1.2 a year on the two after. It was lower after in 668 of them.

For the preciseIt was higher after in 265 markets and the same in 67, where the pick's tuned settings held every day, as just holding does, so its lead was zero both times. For a rule fixed before any out-of-sample run (the lesson's 24-setting rule) the lead was lower after in 497 markets and higher in 503, averaging −$0.4 and −$0.3 a year: with no picking, no fall on average. The pick used the reading, alone or with other conditions, in 811 markets, so picking this way often found the real condition; its lead on the years that picked it was still too high on average. On the lesson's market 6 of the 15 rules beat just holding on 2019–2022; the worst made −$39. The rule with the best in-sample result ($256, with 240 settings) made −$22 there. The lesson's own rule made $5 on 2023–2024. Those two years hold 522 trading days.

Walk-forward: cut, test, move on

One cut gives one out-of-sample stretch, and a few years are one draw of luck.

Walk-forward repeats the cut. Tune the rule on a tuning window, here three years. Run it on the test stretch right after, here one year. Then move both one year on and repeat. Each test stretch is out-of-sample for the settings that trade it.

The rule is the 24-setting one, on a second simulated market built the same way: ten years, 2015 to 2024, that nothing above has touched.

Walk-forward on ten simulated years

Dark: tuning window. Colour: test stretch. Below: the test stretches so far, joined; grey is just holding.

Tuned on2015–2017
Record so far: rule · holding · lead–

Joined end to end, the seven test stretches (2018 to 2024) make one out-of-sample record, the walk-forward record: $51, where just holding made $43 over the same years. On this market, a lead of 51 − 43 = $8.

The record against a plain backtest

A plain backtest tunes once, on all the data. For comparison, do that with the same rule and market, and read the same seven years.

One rule on 2018–2024, three ways (simulated)

Tuned on all ten years, the rule shows $107 on 2018–2024: a lead of 107 − 43 = $64. Those years are in-sample for it.

On the 1,000 other markets the plain backtest's lead on those years averaged $60 (114 − 54) and the record's −$8 (46 − 54). It showed the more in all 1,000.

So on this market the record led just holding by $8, and across the 1,000 it trailed by $8 on average: it led in 383 and trailed in 617. One record has luck in it, the rule's real effect is small, and its 24 settings mostly fit chance.

Walk-forward does not make a rule work. It shows what a tuned rule did on days its tuning could not see.

For the preciseThe record to one decimal is $50.6. The first tuning window holds 778 trading days, about 32 for each setting. The averages behind the leads: the plain backtest $114, the record $46, just holding $54. A rule with the reading alone (2 settings), walked forward the same way, led just holding in 693 markets, with an average record of $72; on the second market its record was $74, a lead of $31.
Theory

In-sample, out-of-sample and the lead a year

In-sample data is the stretch used to tune a rule or to pick it from several. Out-of-sample data is a later stretch that played no part in either. An out-of-sample test runs the rule there, settings unchanged.

Taking off just holding removes what the market did. Dividing by the years lets stretches of different length be compared. This course calls the result the lead a year.

lead a year = (rule − hold) ÷ years
rule
a rule's result on a stretch of days
hold
just holding's result on the same days
years
the length of the stretch, in years
lead a year
the rule's lead over just holding for each year of the stretch (in the lesson: dollars on $100 held a day, before costs); below zero means behind
Worked example: your pick, on one simulated market

On the four years that picked it: (26 − 0) ÷ 4 = $6.50 a year. On the two years after: (30 − 41) ÷ 2 = −$5.50 a year.

Holds when
  • the out-of-sample stretch comes after the in-sample one and played no part in tuning the rule or in picking it.
Breaks when

The out-of-sample result has been used to re-tune or to pick between rules. That stretch is then in-sample, and a last check needs data nothing has touched (lesson B10, The held-out test).

Over five out-of-sample years a rule made $70 and just holding made $40. What is the rule's lead a year?

(70 − 40) ÷ 5 = 30 ÷ 5 = $6 a year.
Take off just holding first: 70 − 40 = 30. Then divide by the years: 30 ÷ 5 = $6 a year. (70 ÷ 5 = 14 leaves just holding in.)
Theory

Counting a walk-forward

Walk-forward analysis: tune a rule on a window of past data, test it on the stretch right after, move both forward by the length of the test stretch, and repeat to the end of the data.

test stretches = (N − T) ÷ S, rounded down
N
the length of all the data
T
the length of the tuning window, which stays the same as the window moves
S
the length of one test stretch, which is also how far the window moves at each step. N, T and S are in the same unit, such as years
rounded down
a last stretch shorter than S is not used, so the record is test stretches × S long
Worked example: your walk-forward

Ten years of data, a three-year tuning window, one-year test stretches: (10 − 3) ÷ 1 = 7 test stretches, and a record of 7 × 1 = 7 years.

Holds when
  • the window moves by exactly S, so test stretches neither overlap nor leave gaps.
Breaks when

The record stops being out-of-sample when a trade starts in the tuning window and ends in the test stretch (the leak of the five-day test), or when the record is used to pick a rule or a window length.

You have 12 years of data, a tuning window of 4 years and test stretches of 2 years. How many test stretches?

6 is 12 ÷ 2: that tests the first four years too, and nothing lies before them to tune on. Take the first window off: (12 − 4) ÷ 2 = 4.
(12 − 4) ÷ 2 = 4 test stretches, and a record of 4 × 2 = 8 years.
3 is 12 ÷ 4, the number of tuning windows that fit side by side. The window moves by the test stretch, 2 years: (12 − 4) ÷ 2 = 4.

What this means for your backtest

  1. Cut by time, before you tune.

    Tune and pick on the earlier years only. When trades or inputs span several days, never mix the days: neighbours share what they cover.

Practice 1 of 3

"Tested out-of-sample"

The situationAn imagined sales page for a trading system says: "Tested out-of-sample. We tuned 40 rules on 2012–2020, ran each on 2021–2024, and show you the one that did best there."

What is the 2021–2024 result of the rule shown?

True, none was tuned there. But the pick among the 40 was made there, and picking uses a stretch up just as tuning does. The result shown is the best of 40 on the years that picked it.
The order is right, and it is not enough. The stretch must also play no part in picking, and here it picked 1 rule out of 40. The result shown is the best of 40 on the years that picked it.
The years 2021–2024 picked the winner, so its result there is the best of 40, and the best of many is partly luck.
Practice 2 of 3

Plan your own walk-forward

The situationYou have 14 years of data. You plan a walk-forward with a tuning window of 5 years and test stretches of 2 years.

How many test stretches will the record have?

Hint: test stretches = (N − T) ÷ S, rounded down.

(14 − 5) ÷ 2 = 4.5, rounded down: 4 test stretches, and a record of 4 × 2 = 8 years. The last year is left over.
Take the tuning window off, divide, and round down: (14 − 5) ÷ 2 = 4.5, so 4 test stretches and a record of 4 × 2 = 8 years. The last year is left over.
Practice 3 of 3

A split at random

The situationA friend's rule can start a trade every day, and each trade lasts ten days. She split her trades at random: 80 in every 100 in-sample, the rest out-of-sample. "The out-of-sample trades did almost as well, so the rule holds up."

What is wrong with the test?

A ten-day trade shares nine days with the trade that starts a day later. Most out-of-sample trades sit next to in-sample ones, so their results are mostly copies, luck included. The cure is a cut by time.
A bigger out-of-sample share, mixed in the same way, leaks the same way. A ten-day trade shares nine days with the trade that starts a day later, so out-of-sample trades copy the in-sample trades next to them. The cure is a cut by time.
None was tuned on, but a ten-day trade shares nine days with the trade that starts a day later. Out-of-sample trades copy the results of the in-sample trades next to them, luck included. The cure is a cut by time.
✓

Lesson done

Cut the past by time before tuning: tune a rule, or pick one of several, on the earlier, in-sample part, then run it unchanged on the later, out-of-sample part. A stretch stays out-of-sample only until its result is used to tune or to pick. Walk-forward repeats the cut and joins the test stretches into one out-of-sample record: an honest record of a tuned rule, not a way to make one work. In the lesson's simulation it led just holding on the market shown and trailed it on average across 1,000.

Key ideas

In-sample
the stretch of past data used to tune a rule or to pick it from several (lesson B8's tuning data)
Out-of-sample
a later stretch of data that played no part in tuning a rule or in picking it (lesson B8's new data). Once its result is used to tune or to pick, it is in-sample
Lead
a rule's result minus just holding's result over the same days
Walk-forward
tuning a rule on a window of past data (the tuning window), testing it on the stretch right after (the test stretch), then moving both forward and repeating
Walk-forward record
a walk-forward's test stretches joined in order: out-of-sample as long as nothing was picked by looking at it

Formulas

lead a year = (rule − hold) ÷ years

test stretches = (N − T) ÷ S, rounded down

Lead a year: both results over the same days. Test stretches: N, T and S in one unit, the window moving by S.

Lesson notes

Cut the past by time before tuning: tune a rule, or pick one of several, on the earlier, in-sample part, then run it unchanged on the later, out-of-sample part and compare its lead over just holding on each. A stretch stays out-of-sample only until its result is used to tune or to pick. Walk-forward repeats the cut and joins the test stretches into one out-of-sample record. All numbers are from the lesson's simulated markets.

The cut and the two parts

A backtest runs a trading rule over past prices, here simulated ones. Each day a rule holds the market ($100 in at the day's first price, out at its last, no trading costs) or stays out. Its lead is its result minus the result of just holding every day over the same days. One thing in this market is real, because it was built in: each evening the market publishes a made-up number, the reading, and the next day's move tends to go a little the reading's way.

The lesson's rule has one setting, hold or stay out, for each of 2 × 12 = 24 kinds of day, sorted by two conditions: whether yesterday's reading was above 0, and the month. Tuning sets a kind to hold if its in-sample days added up to a gain. The eight years 2015 to 2022 are cut before tuning. The first four are in-sample, the years the rule is tuned on (lesson B8: tuning data). The last four are out-of-sample: they play no part in tuning the rule or in picking it (lesson B8: new data).

In-sample the tuned rule made $113 and just holding $53: a lead of 113 − 53 = $60. Out-of-sample, with the same settings, it made $1 and just holding $0: a lead of 1 − 0 = $1. Raw results from the two stretches cannot be compared, because just holding itself made $53 on one and $0 on the other; the lead takes the market out. The reading is real, but its effect is small next to four years of luck, and each of the 24 settings was tuned on about 43 days (1,039 in-sample days ÷ 24), so the tuning mostly fit chance. On 1,000 more markets built the same way the lead averaged $53 in-sample and −$1 out-of-sample, and was above zero out-of-sample in 483: close to a coin flip.

Why the cut goes by time

A mixed split puts the first day in-sample, the second out-of-sample, and so on. The lesson tries both splits on a rule that means nothing in this market, the month alone (12 settings), with a trade that lasts five days: $100 goes in at a day's first price and comes out at the last price four trading days later. A new trade starts on every day of a held month, and just holding starts one every day. A lead in this test adds up every trade, with up to five open at once, so the dollars run larger.

A rule tuned on luck looks good on the trades it was tuned on. Cut by time, the rule's lead fell from $147 in-sample to $24 out-of-sample. Split every other day it went from $84 to $112: each out-of-sample trade shares four of its five days with the in-sample trade that started the day before, so its result is mostly a copy, luck included. The in-sample trades give part of the out-of-sample answer away: the split leaks. Cut by time it does not, because this test starts no trade in the four days before the cut, so no trade crosses it. A mixed split leaks whenever neighbouring days share something, and a cut by time copies how a rule is used: tuned on the past, traded after it.

The $24 is one market. Across the 1,000 other markets the out-of-sample lead was above zero in 940 for the every-other-day split and in only 270 for the cut by time: the made-up market rises over time and a rule built on nothing sits out part of it, so by time it tends to trail just holding.

How an out-of-sample stretch gets used up

With one-day holds and the cut by time again, 15 rules each sort the days by a different mix of four conditions: every mix except none, 2 × 2 × 2 × 2 − 1 = 15. The conditions are the reading, the month, the weekday and whether yesterday's price rose, and only the reading is real. Each is tuned on the in-sample years alone. All 15 run on the out-of-sample years and the best is kept: the rule with the reading alone, which made $26 against just holding's $0, a lead of 26 ÷ 4 = $6.50 a year.

No rule was tuned on those years, but the pick was made on them: picking one rule from several is a second way a stretch gets used. So $26 is the best of 15 on the years that picked it, and the best of many tries is partly luck, even when the rule is real (lesson B7, The best of many). On the 1,000 other markets the pick was the rule with the reading alone in only 390. For the pick those years are in-sample. Re-tuning a rule by an out-of-sample result, or dropping rules by it, uses a stretch up in the same way.

The simulated market runs on for two more years, 2023 and 2024, that nothing has touched. There the pick made $30 and just holding $41: a lead of 30 − 41 = −$11, or −11 ÷ 2 = −$5.50 a year. The reading is real, but its effect is small, two years are short, and this is one market. On the 1,000 other markets the pick's lead averaged $5.7 a year on the years that picked it and $1.2 a year on the two after. It was lower after in 668 of them.

Walk-forward

One cut gives one out-of-sample stretch, and a few years are one draw of luck. Walk-forward repeats the cut: tune the rule on a tuning window (three years in the lesson), run it on the test stretch right after (one year), move both one year on, and repeat. Each test stretch is out-of-sample for the settings that trade it.

On a second simulated market with ten years, 2015 to 2024, the 24-setting rule gets seven test stretches, the years 2018 to 2024. Joined in order they are the walk-forward record: $51 against just holding's $43, a lead of 51 − 43 = $8 on this market. A plain backtest tunes once, on all the data. Tuned once on all ten years, the same rule shows $107 on those seven years, a lead of 107 − 43 = $64, but they are in-sample for it. On the 1,000 other markets the plain backtest's lead on those years averaged $60 (114 − 54) and the record's −$8 (46 − 54), and the plain backtest showed the more in all 1,000. So the record led just holding by $8 on the one market and trailed it by $8 on average across the 1,000: it led in 383 and trailed in 617. One record has luck in it, the rule's real effect is small, and its 24 settings mostly fit chance. Walk-forward does not make a rule work: it shows what a tuned rule did on days its tuning could not see. And a record is in-sample for any pick made by looking at it.

Formula: the lead a year

In-sample data is the stretch used to tune a rule or to pick it from several. Out-of-sample data is a later stretch that played no part in either. An out-of-sample test runs the rule there, settings unchanged. Taking off just holding removes what the market did. Dividing by the years lets stretches of different length be compared. This course calls the result the lead a year.

lead a year = (rule − hold) ÷ years
rule
a rule's result on a stretch of days
hold
just holding's result on the same days
years
the length of the stretch, in years
lead a year
the rule's lead over just holding for each year of the stretch (in the lesson: dollars on $100 held a day, before costs); below zero means behind
Worked example: the lesson's pick, on one simulated market

On the four years that picked it: (26 − 0) ÷ 4 = $6.50 a year. On the two years after: (30 − 41) ÷ 2 = −$5.50 a year.

Holds whenBreaks when

The out-of-sample result has been used to re-tune or to pick between rules. That stretch is then in-sample, and a last check needs data nothing has touched (lesson B10, The held-out test).

Formula: counting a walk-forward

Walk-forward analysis: tune a rule on a window of past data, test it on the stretch right after, move both forward by the length of the test stretch, and repeat to the end of the data.

test stretches = (N − T) ÷ S, rounded down
N
the length of all the data
T
the length of the tuning window, which stays the same as the window moves
S
the length of one test stretch, which is also how far the window moves at each step. N, T and S are in the same unit, such as years
rounded down
a last stretch shorter than S is not used, so the record is test stretches × S long
Worked example: the lesson's walk-forward

Ten years of data, a three-year tuning window, one-year test stretches: (10 − 3) ÷ 1 = 7 test stretches, and a record of 7 × 1 = 7 years.

Holds whenBreaks when

The record stops being out-of-sample when a trade starts in the tuning window and ends in the test stretch (the leak of the five-day test), or when the record is used to pick a rule or a window length.

One more worked example

Made-up round numbers

A rule is tuned on five years, where it made $120 and just holding $45: (120 − 45) ÷ 5 = $15 a year. On the next three years, which played no part in tuning or picking it, it made $27 with the same settings and just holding $18: (27 − 18) ÷ 3 = $3 a year. Three years are short enough for luck to move that figure either way.

A walk-forward on nine years of data with a three-year tuning window and two-year test stretches: (9 − 3) ÷ 2 = 3 test stretches (years 4 and 5, 6 and 7, 8 and 9), and a record of 3 × 2 = 6 years.

The same on ten years of data: (10 − 3) ÷ 2 = 3.5, rounded down to 3 test stretches. The record is 3 × 2 = 6 years, and the last year is not used.

Common mistakes

Check yourself

  1. Over four out-of-sample years a rule made $64 and just holding made $40. What is the rule's lead a year?
    Answer

    (64 − 40) ÷ 4 = 24 ÷ 4 = $6 a year.

  2. A walk-forward runs on 20 years of data with a tuning window of 8 years and test stretches of 3 years. How many test stretches, and how long is the record?
    Answer

    (20 − 8) ÷ 3 = 4 test stretches, and a record of 4 × 3 = 12 years.

  3. Split every other day, the month rule's lead was $84 in-sample and $112 out-of-sample. Why is that not evidence that the rule holds up?
    Answer

    Each trade lasted five days, so an out-of-sample trade shared four of its five days with the in-sample trade that started the day before. Its result was mostly a copy, luck included. Cut by time, where no trade crosses the cut, the lead fell from $147 to $24. Across the 1,000 other markets the out-of-sample lead was above zero in 940 for the every-other-day split and in only 270 for the cut by time.

  4. A researcher tunes 15 rules on the in-sample years, runs all of them on the out-of-sample years and keeps the best. Is the best rule's result there out-of-sample?
    Answer

    No. Those years were used to pick it, so for the pick they are in-sample: its result there is the best of 15 on the years that picked it. Judging the pick needs data that no tuning or picking has touched.

Try it yourself

Run the script. Every number in this lesson comes from one Python script (below). It builds the simulated markets, cuts them, tunes the rules and walks them forward. Change the cut, the length of the tuning window or the test stretch, or the number of rules to choose from, and see what moves.

By hand, in a spreadsheet. Put the date in column A, a rule's result for each day in column B and just holding's in column C. Pick a cut date. =SUMIF(A:A, "<"&cut, B:B) gives the rule's in-sample result, and the same with ">=" the out-of-sample one; do both for column C, subtract to get the two leads, and divide each by its years. For a walk-forward, write the plan as a table before running anything: one row for each step, with the tuning window's first and last year and the test stretch's. The number of rows should equal (N − T) ÷ S, rounded down.

The script needs simmarket.py, the course's simulated market, in the same folder.

The code behind every number (Python, on the course's simulated-market generator; market seeds 2026113001 to 2026114002)
Loading…

Going further: one-day trades, the place of the cut, fewer settings and a growing window

When the mixed split did no harm. The every-other-day split leaked because five-day trades share days. With one-day trades, where neighbouring trades share no day, it leaked nothing in this simulation: the month rule's lead went from $34 in-sample to $5 out-of-sample by time, and from $39 to $2 every other day. Over the 1,000 other markets its average result went from $61 to $18 by time and from $62 to $18 every other day. That is a fact about this simulation, where each day's luck is drawn separately. It is not a reason to mix days: a rule's trades, or the numbers it reads, often span several days, and then neighbouring days share something. What to do with trades that span a cut or the edge of a window is the subject of lesson C13, Purged cross-validation.

Where to put the cut. The cut trades one thing for another. With the cut after 2018 the in-sample years hold 1,039 trading days, about 43 for each of the 24 settings, and four years are left to check with. A cut after 2016 would leave about 22 days for each setting and six years to check with; a cut after 2021, about 76 days and one year. More in-sample days give the tuning less luck to fit; more out-of-sample years give the check less luck of its own. Decide the cut before any result is seen: moving it afterwards is picking by the out-of-sample result.

A rule with less to fit. Walked forward on the second market, the rule with the reading alone (2 settings) had a record of $74 against just holding's $43, a lead of $31, and its settings changed 1 time in the six moves of the window. The 24-setting rule's settings changed 26 times in all: between 2 and 8 of the 24 at each move. Over the 1,000 other markets the reading-alone record averaged $72, the 24-setting record $46 and just holding $54. In this simulation the twelve month settings gave the tuning more luck to fit and nothing real to find (lesson B8, Overfitting).

A growing window. The lesson's tuning window keeps its length and slides: a rolling window. A variant keeps the window's first day fixed, so the window grows by one test stretch at each step (an expanding, or anchored, window). The count of test stretches is the same, with T the length of the first window. The lesson's script does not run this variant.

Honest notes

Where this comes from