Quant from Scratch by QuantWright

Testing Popular Ideas · Lesson 6

VWAP

An average that counts shares, two opposite readings, 24 versions, and what costs do to a rule that trades all day.

Know the basics? Read the lesson notes

An average that counts shares

A five-minute bar is a candle for a five-minute window: its first price, highest, lowest and last price (the close). Its volume is how many shares traded in it.

VWAP, the volume-weighted average price, averages the prices so far with each bar counted by its shares. Each bar needs one price. Here it is the typical price, (high + low + close) ÷ 3. Multiply it by the bar's volume, add these up from the first bar, and divide by the shares added up. Press Next bar.

Four bars (made up)

Dots: each bar's typical price. Grey bars: its volume. Orange: VWAP so far.

Price × volume, added up–
Shares, added up–
VWAP–

After four bars VWAP is 990 ÷ 10 = 99.

The plain average of the four prices is 99.5; the heaviest bar, at 96, pulls VWAP below that. Sources differ on the price: with the closes instead, VWAP is 98.5. Now add a fifth bar at 104 and drag its volume up.

With 90 thousand shares, nine times the first four together, VWAP is (990 + 104 × 90) ÷ 100 = 103.5. One heavy bar can set the line almost alone.

VWAP through one simulated day

Now a simulated market: prices and volumes made up by a computer, not a real market. It rises slowly on average, its moves come in calm and wild spells, and past prices, the day's earlier bars included, say nothing about which way the next move goes (we built it so: the generator's own setting for five-minute bars leaves a small pull-back within each day, so this lesson uses its corrected setting, which removes it). Its day runs from 09:30 to 16:00: 78 five-minute bars. Its volumes are made up too: more shares near the open and the close, more on wild days, and not tied to which way a bar moved.

Slide through the day. At each bar, VWAP uses only the bars so far.

One day, 5 June 2017 (simulated)

Dark: the close of each bar. Orange: VWAP. Grey bars: volume.

Close–
VWAP so far–

At the 16:00 close of 98.25, VWAP was 97.71. Early on it follows each new price; by the afternoon it has most of the day's shares behind it and barely moves.

Where the sums start

The VWAP above starts its sums at the day's first bar and goes back to zero the next morning: a session VWAP. The moment it starts again is the reset time. Markets that also trade before the open, or around the clock, need a choice of hour, and each choice draws another line.

An anchored VWAP starts at one bar you choose and never resets. One choice is each Monday's first bar. Try three starting points on one simulated week.

One week, 5–9 June 2017 (simulated)

Dark: the closes. Orange: VWAP from the chosen start.

Sums start
VWAP at Friday's 16:00 close–

Friday closed at 99.26. VWAP read 99.96 from that morning, 98.83 from Monday and 99.18 from Wednesday.

Same prices, same volumes: the line depends on where its sums start.

Bands around VWAP

Bands put a distance around VWAP. Their unit is a volume-weighted standard deviation: lesson A8's typical distance from the average, with each bar counted by its shares. For each bar so far, square its price's distance from VWAP and multiply by its volume; add these up, divide by the shares, take the square root.

Your four bars: distances from 99 of −1, +5, +1 and −3; squared and times volume, 1 + 50 + 3 + 36 = 90; 90 ÷ 10 = 9; √9 = 3.

The bands sit k of these above and below VWAP; k is the width. With width 2: 99 ± 2 × 3 = 105 and 93. Try both widths on the simulated day.

VWAP and its bands, 5 June 2017 (simulated)

Dark: the closes. Orange: VWAP and its bands. Dots: closes outside the bands, from 09:45.

Width
Closes outside, of 75?

Of the day's 75 closes from 09:45 on, 37 were outside the width-1 bands and 8 outside the width-2 bands.

One name, opposite rules

"Trade VWAP" is not a rule yet. Two common readings are opposite:

Width 0 means VWAP itself. A rule decides at a bar's close and trades at the next bar's first price. It starts at 09:45, after three bars, and sells whatever it holds at 16:00: nothing is kept overnight. Each buy and each sale counts as one trade. Try both readings at every width.

Two readings, three widths, 5 June 2017 (simulated)

Shaded: bars the rule is in the market. Dots: the closes that made it buy.

Reading
Width
Trades that day–
Bars in the market–

At width 0 trend made 8 trades that day and mean reversion 6; at width 2, 2 each.

At width 0 the two readings split the day: after the first signal, exactly one of them is in the market at every bar (37 bars and 38).

A cost on every trade

Start with one exact rule: trend at width 0, typical price, session VWAP. Call it the baseline version: this lesson's starting point, not a rule from any source.

It runs on seven years of the simulated market, 2015 to 2021, against just holding: buying at the first 09:45 close and never selling, nights included (lesson E1). Each buy and each sale costs 0.1% of the price, the round figure lessons E2 to E5 use (lesson A16, Costs); holding pays it once. Results are in % a year: the steady yearly growth that gives the same total.

Holding made 4.4% a year. The baseline version traded 7.6 times a day on average. After costs, where did it end?

7.6 trades a day at 0.1% each is about 0.76% a day, paid on every trading day.
Before costs it already trailed by about 8 points a year. The costs come on top, every day.
A small cost, paid about 7.6 times a day.
Costs: 7.6 × 0.1% ≈ 0.76% a day. Each trade keeps 99.9% (0.999) of the account, so n trades keep 0.999n (0.999 multiplied by itself n times). This calendar has about 261 trading days a year, so costs alone keep 0.9997.64 × 261 ≈ 0.136 of the account each year. Before costs it made −3.6% a year; after costs 0.964 × 0.136 ≈ 0.131: −86.9% a year.

The baseline version and holding, 2015–2021 (simulated)

Accounts starting at 100, on a scale where each line marks ten times less. Dotted: holding. Grey: the baseline version before costs. Orange: after costs.

One market is one draw

Now the baseline version and its opposite, mean reversion at width 0, on each of 1,000 other simulated markets built the same way, first before costs. A result's lead is its % a year minus holding's, in points (a point is 1% a year).

The baseline version's lead over holding on 1,000 simulated markets, before costs

Bars: markets by size of lead. Orange: ahead of holding.

Ahead of holding?
Average lead?

Before costs the baseline version beat holding in 157 of the 1,000 simulated markets and its opposite in 192: the two readings came out close.

Both trailed holding in most markets: each was in the market about half of each day and never overnight, while holding always was, in markets that rise on average.

For the preciseBefore costs, across the 1,000 simulated markets: average leads −5.60 (the baseline version) and −5.19 (its opposite) points a year; a gain in 747 and 770; both a gain in 575; both ahead of holding in 68. The gap between the two averages, 0.41 points a year, is about two standard errors and small next to either lead. After costs the baseline version averaged −85.9% a year; even in its best market it made −82.8% a year.

These markets were built with nothing to find in which way prices move next, so the counts show what these rules produce when there is nothing to find. They cannot show whether VWAP rules work in any real market.

How many versions?

The tempting next move is to try other settings: either reading; width 0, 1 or 2; the typical price or the close; and sums that reset each day or each Monday.

Each different set of choices is a version: the same idea with different settings (lesson E1, How we test an idea). How many versions is that?

That adds the choices (2 + 3 + 2 + 2). Every choice goes with every other, so they multiply.
That is 2 × 3 × 2, which leaves one choice out. There are four: reading, width, price and reset.
2 readings × 3 widths × 2 prices × 2 resets = 24 versions.

All 24, on one market and on 1,000

Here are all 24 versions on the same simulated market, 2015 to 2021. Call those the choosing years: a version will be chosen on them. Show them, then turn the costs on.

24 versions: each one's lead over holding, 2015–2021 (simulated)

One bar per version, fewest trades a day on the left; ordered by trades, not by lead, so a bar can be taller than the one before it. Above the line: beat holding. Black: the baseline version.

Costs
Beat holding?
The best?

Before costs 2 of the 24 beat holding; after costs, none. The best after costs was mean reversion at width 2, with the close and sums from Monday, 20.0 points a year behind holding. It traded least, 0.83 times a day. A version's place counts from the largest lead; its place was 1st (a blind draw is first 1 time in 24).

After costs, in none of the 1,000 simulated markets did any version beat holding, or even end with a gain.

For the preciseAcross the 1,000 simulated markets the versions traded from 0.81 to 7.69 times a day on average. After costs the four at width 2 with sums from Monday averaged places 1.5 to 3.5; the four at width 0 with daily sums, 21.7 to 23.3. Before costs 3.7 of the 24 beat holding, on average, and at least one did in 394 markets. At a fifth of the cost, 0.02% a trade, no version beat holding in 933 of the 1,000.

Does the best stay best?

2022 to 2024 were set aside as years held out, to be used once for the choice below (lesson The held-out test).

On each of the 1,000 simulated markets we took the best of the 24 on the choosing years after costs, the pick, and ran it once on the held-out years, after costs. A blind draw, one version taken at random, would be best there in about 42 of 1,000 (1,000 ÷ 24). Guess how often the pick stayed best.

The pick's held-out place on 1,000 simulated markets, after costs

One bar per place, 1st on the left. Dashed: a blind draw.

Stayed best?

The pick stayed best in 517 of the 1,000 simulated markets, against a blind draw's 42. It still trailed holding there in 998.

The extra is not skill. The version with the fewest trades on the choosing years, chosen without looking at any result, was best on the held-out years in 587: the low trade count paid off again, not a find.

For the preciseAll after costs, across the 1,000 simulated markets. The pick was the fewest-trades version in 647; it was always at width 2 with the close and sums from Monday (mean reversion in 649, trend in 351). Its held-out place averaged 1.8 of 24, and it beat holding there in 2 markets. Picked and judged before costs, it stayed best in 62. At 0.02% a trade it stayed best in 233. No two versions shared first place on the held-out years in any market, so a blind draw is best in exactly 1,000 ÷ 24 = 41.7.
Theory

VWAP and its bands

VWAP (volume-weighted average price) is the average price since an anchor, the first bar its sums use, with each bar counted by its volume. Its bands sit k volume-weighted standard deviations of the same prices above and below it. All three are worked out again at every bar, that bar included.

VWAPt = (PaVa + … + PtVt) ÷ (Va + … + Vt)
st = √( [Va(Pa − VWAPt)² + … + Vt(Pt − VWAPt)²] ÷ (Va + … + Vt) )
uppert = VWAPt + k × st,   lowert = VWAPt − k × st
Pi
bar i's price: in this lesson the typical price, (high + low + close) ÷ 3; some tools use the close
Vi
bar i's volume: the shares traded in it
a
the anchor: the first bar the sums use (each day's first bar for a session VWAP)
st
the volume-weighted standard deviation: the typical distance of the prices from VWAP, each bar counted by its volume
t
the bar now; each sum runs over every bar from a to t
k
the width, in volume-weighted standard deviations
Worked example: your four bars

Prices 98, 104, 100, 96; volumes 1, 2, 3, 4 thousand. VWAP = (98 + 208 + 300 + 384) ÷ 10 = 990 ÷ 10 = 99. Squared distances times volume: 1 × 1 + 2 × 25 + 3 × 1 + 4 × 9 = 90, so s = √(90 ÷ 10) = 3. At k = 2: 105 and 93.

Holds when
  • each volume is the shares actually traded in that bar, all of them (one trading venue's volume gives another line; lesson B4, Data quality);
  • the price, the anchor and the reset time are stated.
Breaks when

One bar's volume dwarfs the rest: the line then sits near that bar's price for the rest of the day (a fifth bar of 90 thousand shares at 104 moved it from 99 to 103.5).

Three new bars: typical prices 20, 23 and 22 with volumes 5, 2 and 3 thousand shares. What is VWAP after the third bar?

20 × 5 + 23 × 2 + 22 × 3 = 100 + 46 + 66 = 212; shares 5 + 2 + 3 = 10; VWAP = 212 ÷ 10 = 21.2.
Multiply each price by its volume: 100 + 46 + 66 = 212. Divide by all the shares, 10: VWAP = 21.2. The plain average, 21.67, ignores the volumes.

What this means for your test

  1. Write the exact rule before you run it.

    "VWAP" names a line, not a rule. Say the price, the anchor and reset time, the band and its width, the reading, the entry and the sale, the price each trade gets and the costs.

Practice 1 of 2

A friend's spreadsheet

The situationA friend's sheet shows a VWAP of 49.2 after three bars. Bar 1: high 51, low 49, close 50, 2 thousand shares. Bar 2: high 54, low 50, close 52, 1 thousand. Bar 3: high 53, low 47, close 47, 2 thousand. You check by hand with the typical price.

What VWAP do you get?

Hint: each bar's typical price is (high + low + close) ÷ 3. Multiply each by its volume, add up, divide by the shares.

Typical prices: 150 ÷ 3 = 50, 156 ÷ 3 = 52, 147 ÷ 3 = 49. Then 50 × 2 + 52 × 1 + 49 × 2 = 250, and 250 ÷ 5 = 50.
Typical prices: (51 + 49 + 50) ÷ 3 = 50, (54 + 50 + 52) ÷ 3 = 52, (53 + 47 + 47) ÷ 3 = 49. Price × volume: 100 + 52 + 98 = 250. Shares: 5. VWAP = 50.
The sheet is not broken. It used the closes: (100 + 52 + 94) ÷ 5 = 49.2. Two tools can draw different VWAPs from the same bars.
Practice 2 of 2

A backtest "before fees"

The situationAn invented post shows a VWAP rule's backtest "before fees". The rule makes 5 trades a day (buys and sales counted). Your own cost estimate is 0.1% a trade.

About how much, in % of the account, would it pay in costs each day?

Hint: trades a day × cost of one trade.

5 × 0.1% = 0.5% a day.
Each trade costs 0.1%, and there are 5 a day: 5 × 0.1% = 0.5% a day.
Over about 261 trading days, this lesson's calendar year, that keeps 0.9995 × 261 ≈ 0.27 of the account a year from costs alone. Ask for the result after costs, and what just holding made.
✓

Lesson done

VWAP is the running sum of price × volume divided by the running sum of volume, from an anchor: each day's first bar for a session VWAP, any chosen bar for an anchored one. Its bands sit a set number of volume-weighted standard deviations away. Reading, width, price and reset made 24 versions. These markets were built with nothing to find in which way prices move next, so the counts show what these rules produce when there is nothing to find: before costs the baseline version and its opposite each trailed holding on average; at 0.1% a trade, no version beat holding or even ended with a gain in any of 1,000 simulated markets, and the best of 24 stayed best on held-out years in 517 (a blind draw: about 42) because it traded least, not because it found anything. They cannot show whether VWAP rules work in any real market.

Key ideas

VWAP
volume-weighted average price: the prices since the anchor, each bar counted by its volume
Typical price
(high + low + close) ÷ 3 for a bar; some tools use the close instead
Anchor, reset time
the bar where VWAP's sums start; a session VWAP starts again at each day's first bar, an anchored VWAP never resets
Volume-weighted standard deviation
the typical distance of the prices from VWAP, each bar counted by its volume; bands sit k of these above and below VWAP
Trend, mean reversion
opposite readings of VWAP: buy above it, or buy below it. Each is a rule to test, not a finding
Version
the same idea with different settings; count every one

Formulas

VWAPt = (PaVa + … + PtVt) ÷ (Va + … + Vt)  ·  P = (high + low + close) ÷ 3

st = √( [Va(Pa − VWAPt)² + … + Vt(Pt − VWAPt)²] ÷ (Va + … + Vt) )  ·  bands = VWAP ± k × s

Lesson notes

VWAP, the volume-weighted average price, is the running sum of price × volume divided by the running sum of volume, from an anchor bar: each day's first bar for a session VWAP, any chosen bar for an anchored VWAP. Its bands sit a set number of volume-weighted standard deviations away. Reading, width, price and reset made 24 versions. The lesson's simulated markets were built with nothing to find in which way prices move next, so its counts show what these rules produce when there is nothing to find. At 0.1% a trade, no version beat just holding (buying once and never selling), or even ended with a gain, in any of 1,000 simulated markets. The counts cannot show whether VWAP rules work in any real market.

Building VWAP

A five-minute bar records five minutes of trading: its first, highest (high), lowest (low) and last price (close), and its volume, the shares traded in it. Take one price per bar, here the typical price, (high + low + close) ÷ 3; multiply by the volume, add up from the first bar, and divide by the shares added up. Four made-up bars with typical prices 98, 104, 100 and 96 and volumes 1, 2, 3 and 4 thousand: 98 + 208 + 300 + 384 = 990, shares 10, VWAP = 99. The plain average is 99.5; the heaviest bar, at 96, pulls VWAP below that. With the closes (98, 106, 97 and 96) VWAP is 98.5: sources differ on the price. A fifth bar at 104 with 90 thousand shares moves it to (990 + 104 × 90) ÷ 100 = 103.5: one heavy bar can set the line almost alone.

A simulated day, and where the sums start

The simulated market rises slowly on average, its moves come in calm and wild spells, and past prices, the day's earlier bars included, say nothing about which way the next move goes: the generator's own setting for five-minute bars leaves a small pull-back within each day, so the lesson uses its corrected setting, which removes it. Its day runs 09:30 to 16:00 in 78 bars; its made-up volumes are heavier near the open and the close and not tied to which way a bar moved. VWAP uses only the bars so far, so it follows each new price early in the day and barely moves by the afternoon: on the simulated 5 June 2017 it read 97.71 at the 16:00 close of 98.25.

A session VWAP starts again at each day's first bar: its reset time. Markets with longer hours need a choice of hour. An anchored VWAP starts at one chosen bar and never resets. At the simulated Friday close of 99.26 (9 June 2017), VWAP read 99.96 from that morning, 98.83 from Monday and 99.18 from Wednesday: same prices and volumes, three lines.

The bands use a volume-weighted standard deviation: square each price's distance from VWAP, multiply by its volume, add up, divide by the shares, take the square root. For the four bars: 1 × 1 + 2 × 25 + 3 × 1 + 4 × 9 = 90; 90 ÷ 10 = 9; √9 = 3. At width 2 the bands are 99 ± 6: 105 and 93.

Two opposite readings, and costs

Trend buys when a close is above the upper band and sells on a close below VWAP; mean reversion buys when a close is below the lower band and sells on a close above VWAP. Width 0 means VWAP itself. A rule decides at a bar's close, trades at the next bar's first price, starts at 09:45 and sells everything at 16:00; each buy and each sale is one trade. On the simulated 5 June 2017 at width 0, trend made 8 trades and mean reversion 6.

The baseline version (the lesson's starting point, not a rule from any source) is trend at width 0, typical price, session VWAP. At 0.1% a trade it traded 7.6 times a day on the lesson's simulated market, 2015 to 2021: about 0.76% a day. With about 261 trading days a year, and each trade keeping 99.9% (0.999) of the account, costs alone keep 0.9997.64 × 261 (0.999 multiplied by itself 7.64 × 261 times) ≈ 0.136 of the account each year. It made −3.6% a year before costs and −86.9% after; holding made 4.4%. On 1,000 other simulated markets, before costs, it beat holding in 157 and its opposite in 192: the two readings came out close, and both trailed holding in most markets, being in the market about half of each day and never overnight in markets that rise on average. After costs neither ended with a gain in any of the 1,000.

24 versions, and the held-out years

2 readings × 3 widths × 2 prices × 2 resets = 24 versions. After costs, on the choosing years (2015 to 2021, the years a version is chosen on), no version beat holding or ended with a gain in any of the 1,000 simulated markets, and the order followed the trades: the least-trading versions placed highest. The years 2022 to 2024 were held out. The pick, the best of 24 after costs on the choosing years, stayed best on the held-out years in 517 of the 1,000, against about 42 for a version drawn at random (1,000 ÷ 24). The extra is not skill: the fewest-trades version, chosen without looking at any result, was best there in 587. The pick still trailed holding on the held-out years in 998.

Formula: VWAP and its bands

VWAP is the average price since an anchor, the first bar its sums use, with each bar counted by its volume. Its bands sit k volume-weighted standard deviations of the same prices above and below it. All three are worked out again at every bar, that bar included.

VWAPt = (PaVa + … + PtVt) ÷ (Va + … + Vt)
st = √( [Va(Pa − VWAPt)² + … + Vt(Pt − VWAPt)²] ÷ (Va + … + Vt) )
uppert = VWAPt + k × st,   lowert = VWAPt − k × st
Pi
bar i's price: in this lesson the typical price, (high + low + close) ÷ 3; some tools use the close
Vi
bar i's volume: the shares traded in it
a
the anchor: the first bar the sums use (each day's first bar for a session VWAP)
st
the volume-weighted standard deviation: the typical distance of the prices from VWAP, each bar counted by its volume
t
the bar now; each sum runs over every bar from a to t
k
the width, in volume-weighted standard deviations
Worked example: the lesson's four bars

Prices 98, 104, 100, 96; volumes 1, 2, 3, 4 thousand. VWAP = 990 ÷ 10 = 99. Squared distances times volume add up to 90, so s = √(90 ÷ 10) = 3. At k = 2: 105 and 93.

Holds whenBreaks when

One bar's volume dwarfs the rest: the line then sits near that bar's price for the rest of the day (a fifth bar of 90 thousand shares at 104 moved it from 99 to 103.5).

One more worked example

Made-up round numbers

Three bars with typical prices 10, 13 and 12 and volumes 4, 1 and 5 thousand shares. Price × volume: 40 + 13 + 60 = 113; shares 10; VWAP = 11.3. The plain average of the three prices is 35 ÷ 3 ≈ 11.67: the light bar at 13 counts least.

The volume-weighted standard deviation around 11.3: distances −1.3, +1.7, +0.7; squared 1.69, 2.89, 0.49; times volume 6.76 + 2.89 + 2.45 = 12.1; ÷ 10 = 1.21; √1.21 = 1.1. At width 2 the bands are 11.3 ± 2.2: 13.5 and 9.1.

Common mistakes

Check yourself

  1. Two bars: typical prices 50 and 53, volumes 3 and 1 thousand shares. What is VWAP after the second bar, and what is the plain average?
    Answer

    (50 × 3 + 53 × 1) ÷ 4 = 203 ÷ 4 = 50.75. The plain average is 103 ÷ 2 = 51.5: the heavier bar at 50 pulls VWAP towards it.

  2. VWAP is 40 and the volume-weighted standard deviation 0.5. A trend rule at width 2 is out of the market; the bar closes at 41.2. Does it buy? A mean-reversion rule at width 2 holds; the next close is 40.1. Does it sell?
    Answer

    The upper band is 40 + 2 × 0.5 = 41; 41.2 is above it, so the trend rule buys. The mean-reversion rule sells on a close back above VWAP; 40.1 is above 40, so it sells.

  3. A rule makes 3 trades a day at 0.1% a trade. About how much does it pay in costs a day?
    Answer

    3 × 0.1% = 0.3% a day.

  4. In the lesson's simulation, after costs, the best of 24 versions stayed best on held-out years in 517 of 1,000 simulated markets, against about 42 for a version drawn at random. What does that suggest, and what does it not show?
    Answer

    The pick was best much more often than a blind draw. It does not show that picking found anything in the prices: choosing the version with the fewest trades, without looking at any result, was best in 587, because the version that trades least pays the least in costs. The pick also trailed holding on the held-out years in 998. And it is a simulation; a real market needs its own test.

Try it yourself

The rules, written before the code. Five-minute bars, 09:30 to 16:00. Price: the typical price (high + low + close) ÷ 3, or the close. VWAP at each bar = the sum of price × volume over the sum of volume, from the anchor up to and including that bar; the anchor is each day's first bar (session) or each Monday's first bar (weekly). Volume-weighted standard deviation = the square root of the sum of volume × (price − VWAP)² over the sum of volume. Bands = VWAP ± k × that, with k = 0, 1 or 2. Trend buys on a close above the upper band and sells on a close below VWAP; mean reversion buys on a close below the lower band and sells on a close above VWAP. At each close from 09:45 to 15:55: if the condition for selling holds, the rule is out; otherwise, if the entry holds, it is in; otherwise it stays as it was. Fill at the next bar's open; sell everything at the 16:00 close. In the market or in cash, never short. 0.1% of the price per buy and per sale, or no costs. Compared with buying at the first 09:45 close and holding, which pays 0.1% once.

Run the script. Every number in this lesson comes from one Python script (below). It imports simmarket.py, the course's simulated market, which must sit in the same folder; the script builds every market with its setting bridge="normal". Change the cost per trade or add a wider band and watch the counts across markets move.

By hand. In a spreadsheet with highs, lows, closes and volumes in columns B to E from row 2: the typical price in F2 is =(B2+C2+D2)/3, price × volume in G2 is =F2*E2, and VWAP in H2 is =SUM(G$2:G2)/SUM(E$2:E2). Copy each down. Start the sums again at each day's first row.

Written by QuantWright from the published definition of the volume-weighted average price: the formulas only. No code from any indicator script or charting platform.

The code behind every number (Python, SimMarket seed 20261109)
Loading…
For the preciseThe lesson's market: SimMarket Market 1, seed 20261109, effect=False (no planted pattern), bridge="normal", five-minute bars. The 1,000 simulated markets: the same generator with seeds 2026160601 to 2026161600. Decisions at the closes of bars 3 to 77 (09:45 to 15:55); fill at the next bar's open, which in this market is the previous close; every position sold at the 16:00 close. Choosing years: 5 Jan 2015 to 31 Dec 2021 (6.99 calendar years, 1,825 trading days); held-out: 2022 to 2024 (3.00 years). % a year = exp(sum of daily log returns)1 ÷ years − 1. Costs: a factor (1 − 0.001) per buy or sale; holding pays it once, at its buy; on the held-out years holding simply continues. Volume-weighted standard deviation computed as √(sum(V P²) ÷ sum(V) − VWAP²). The lesson's market: holding 4.38% a year before its cost and 4.37% after (choosing), 10.00% (held-out). Baseline version: −3.58% a year before costs, −86.90% after, 7.64 trades a day, in the market at 49.9% of decisions, 23rd of 24; held-out −88.01%. Its opposite: +3.11% before costs (lead −1.28), −85.99% after, 21st. Best after costs (mean reversion, width 2, close, weekly): +4.94% before costs, −15.61% after, 0.83 trades a day, 1st on both periods (a blind draw: 1 in 24 each). Across the 1,000 simulated markets the baseline version made 7.60 trades a day and was in the market at 50.8% of decisions, on average. A rule long at every decision of every day (bought at 09:45, sold at 16:00) trailed holding by 2.20 points a year before costs on average (ahead in 296 markets) and by 45.46 after.

Honest notes

Where this comes from