chapter zerothe confession

Anatomy of a Broken VWAP

We shipped this bug. For weeks, the default workspace on /pro drew a line labeled VWAP that was not VWAP. Nobody noticed — least of all us — because it looked plausible. This page is the full autopsy: the exact code, the lie it drew, how you catch it, and the test that now makes it impossible.

01 · what VWAP claims to be

Volume-Weighted Average Price: the average price actually paid today, weighted by how much volume traded at each price — Σ(typical price × volume) / Σ(volume), with the sums anchored at the session open and reset only when a new session starts. Institutions benchmark fills against it. Its whole value is the anchor: it remembers the entire day. Break the anchor and you break the meaning.

02 · the same tape, both lines — toggle it

exhibit a · two sessions · 5-min bars · same tape
100.2101.4102.5DAY 2 OPENS — the anchor must reset hereMON · SESSION 1TUE · SESSION 2
BROKEN — the line hugs every candle, because it IS every candle: with the anchor resetting each bar, Σ(tp·v)/Σv collapses to (H+L+C)/3 of that bar alone. It looks plausible. It teaches nothing. By the day-1 close it sits $0.90 from the true VWAP.

03 · the autopsy — the code we actually shipped

export function vwap(bars: Bar[]): Series {
  const out: Series = new Array(bars.length).fill(null);
  let cumPv = 0;
  let cumV = 0;
  let lastDay = -1;                                          // ① born a number
  for (let i = 0; i < bars.length; i++) {
    const day = new Date(bars[i].t).toDateString().length;   // ② a string LENGTH
    const dayKey = new Date(bars[i].t).toDateString();
    // reset on session change (UTC day) — close-enough for demo
    if (dayKey !== `${lastDay}`) {                           // ③ "Mon Jul 20 2026" !== "15"
      cumPv = 0;                                             //    …true on EVERY bar
      cumV = 0;
      lastDay = day;
    }
    const tp = (bars[i].h + bars[i].l + bars[i].c) / 3;
    cumPv += tp * bars[i].v;
    cumV += bars[i].v;
    out[i] = cumV ? cumPv / cumV : null;
  }
  return out;
}

Three small sins, stacked: the anchor variable starts life as a number. it gets assigned toDateString().length — the number 15, the length of "Mon Jul 20 2026", not the date. the guard compares the date string against `${lastDay}` — the string "15". A calendar date never equals its own character count, so the reset fires on every single bar.

And Σ(tp·v)/Σv computed over one bar is just… tp. The "VWAP" the default workspace drew was typical price in a trench coat — a line that hugs every candle and carries zero information. Note the cruelest part: the comment says "close-enough for demo." TypeScript never complained, because every comparison was technically legal. The types were fine. The meaning was gone.

04 · how you catch it — assert the MATH, not the render

Two hourly bars, same session. Bar one trades 100 shares at a typical price of 100; bar two trades 300 at 200. A real VWAP must answer 175— the volume-weighted blend. The broken one answers 200, the second bar's own typical price. One assertion separates them. This is the actual test in our suite, verbatim — and it imports vwap from the module that renders on /pro, not a copy:

describe("vwap — the one we shipped broken", () => {
  it("REGRESSION: intraday sums are CUMULATIVE within a session, not reset per bar", () => {
    const t0 = Date.UTC(2026, 6, 20, 14); // intraday spacing (1h)
    const bars = [bar(t0, 110, 90, 100, 100), bar(t0 + HOUR, 210, 190, 200, 300)];
    const out = vwap(bars);
    const tp1 = (110 + 90 + 100) / 3;             // 100
    const tp2 = (210 + 190 + 200) / 3;            // 200
    const expected = (tp1 * 100 + tp2 * 300) / 400; // 175
    expect(out[1]).toBeCloseTo(expected, 10);
    expect(out[1]).not.toBeCloseTo(tp2, 5); // the broken behavior
  });

The general lesson: an indicator can be plausibly wrong forever, because charts don't have error states — a broken line still draws. The only defense is a test that knows the arithmetic answerin advance and asserts against the code that's actually on screen.

postscript · the test ambushed us a second time

Building this page's exhibit caught another bug — in the fix. The repaired VWAP keyed sessions by toDateString(): the viewer's local calendar day. A US session runs 13:30–20:00 UTC, which crosses midnight in Mumbai — so for a viewer in India, the anchor quietly reset mid-session, at their local midnight. Correct in New York, broken in half the world. Sessions now key by UTC day, there are no strings left at the crime scene, and that regression is pinned too. Tests don't just pin the past — they ambush the future.

the receipt

Every indicator on the live chart now carries a in its legend — hover it and it states the exact formula it was verified against, backed by that suite. Go hover the VWAP. It earned the checkmark the hard way.