Module 8

Python: independent verification & load testing

Our Java beta engine has unit tests, integration tests, even golden-file tests. So why does the repo also contain a verification/ directory full of Python? Because every test we've written so far shares a dangerous property with the code it tests: the same blind spots. If the engine misreads the window convention, the test author — same person, same mental model — probably encoded the same misreading into the expected values. The test passes. The number is wrong.

The finance-industry answer is independent price verification (IPV): recompute the same numbers in a stack that shares nothing with production — different language, different libraries, different algorithm — and compare the results point by point. Our ipv_check.py rebuilds everything from scratch: its own CSV parse (pandas, not our Java loader), its own calendar alignment, a two-pass mean-centred cov/var where the service uses a one-pass prefix-sum formula, and Python's Decimal half-up rounding where the service uses BigDecimal. Then it calls the running API over HTTP and demands exact agreement at the served 4-decimal precision.

Same numbers, different code. If both stacks agree on thousands of points, a shared bug is vastly less likely. Try it yourself below — then flip the bug injector to see exactly what IPV catches that a unit test cannot.

🎛 IPV comparator

5d

IPV verdict

8 points, 0 mismatches

engines agree at 4dp

Engine A

prefix-sum

(n·Σxy − Σx·Σy) / (n·Σy² − (Σy)²)

Engine B

two-pass cov/var

mean-centred, pandas-style

DateEngine A (service-style)Engine B (independent)Match
2021-03-052.56792.5679
2021-03-082.57792.5779
2021-03-092.78362.7836
2021-03-103.02183.0218
2021-03-113.19943.1994
2021-03-123.09983.0998
2021-03-153.03203.0320
2021-03-163.11743.1174

Two implementations of the same beta, sharing nothing but the input prices: engine A uses the service's prefix-sum formula, engine B recomputes it the textbook way (subtract the means, then cov/var) — exactly how verification/ipv_check.pyshadows the Java service with pandas. Both round to 4 decimals before comparing, so agreement is exact, not “close enough.” Flip the bug injector: an off-by-one window in engine A that every unit test built on engine Awould happily bless lights up instantly here, because engine B doesn't share the bug. Educational tool — not investment advice.

The real thing: ipv_check.py

The independent engine is deliberately nota port of the Java. Where the service maintains prefix sums for O(1) window queries, the Python leans on pandas' textbook rolling covariance and variance — a completely different numerical path to the same definition of beta (from verification/ipv_check.py):

x = np.log(x_close.loc[common] / x_close.loc[common].shift(1))
y = np.log(y_close.loc[common] / y_close.loc[common].shift(1))

n_returns = window - 1  # w price days -> w-1 returns (docs/DESIGN.txt D1)
...
cov = x.rolling(window=n_returns, min_periods=2).cov(y)
var = y.rolling(window=n_returns, min_periods=2).var()
beta = cov / var

Note how even the window convention from Module 6 is re-derived independently: w price days means w−1 returns, and min_periods=2reproduces the “fewer than 2 returns → null” rule. The comparison loop then walks the live API's response against the reference series and refuses to accept anything but exact agreement — including agreement about definedness (a served number where the reference says null is just as much a failure as a wrong digit):

for point, (ref_date, ref_beta) in zip(served, reference.items()):
    expected = round4(ref_beta)
    got = point["beta"]
    if point["date"] != ref_date.strftime("%Y-%m-%d"):
        mismatches.append(f"date {point['date']} != {ref_date.date()}")
    elif (got is None) != (expected is None):
        mismatches.append(f"{point['date']}: {got} vs {expected} (definedness)")
    elif got is not None and abs(got - expected) > 1e-12:
        mismatches.append(f"{point['date']}: served {got} vs independent {expected}")

The check list covers the brief's three sample calls plus window sweeps (w = 20, 63, 252, 1260) and a self-beta control — SPY vs SPY, which must equal exactly 1 at every point. The real run: thousands of points compared, zero mismatches, and the script exits non-zero on any disagreement so CI can gate on it.

Hammering it: load_test.py

Correct numbers under one request prove little about correct numbers under sixteen concurrent ones. The load harness attacks the running service with a thread pool, timing every call from the outside (from verification/load_test.py):

def fetch(url: str):
    start = time.perf_counter()
    with urllib.request.urlopen(url, timeout=120) as response:
        body = response.read()
    return (time.perf_counter() - start) * 1000, body

# ---- phase 1: cold stampede -------------------------------------------
urls = [beta_url(t, b, 252) for t, b in pairs]
with ThreadPoolExecutor(args.workers) as pool:
    cold = list(pool.map(lambda u: fetch(u), urls))

Three phases, each aimed at a specific failure mode. Phase 1 (cold stampede) fires dozens of never-seen ticker pairs at once, exercising the synchronized pair-cache build path — the server-side tookMs field lets the script verify that each pair's cache was built exactly once rather than redundantly per thread. Phase 2 (warm throughput) sends hundreds of requests with mixed window sizes and reports throughput plus wall-clock p50 / p95 / p99 latencies alongside the server's own timing. Phase 3 (consistency) is the sneaky one — 64 threads issue the identical query and every response body must be byte-identical:

url = beta_url(*pairs[0], 252)
with ThreadPoolExecutor(args.workers) as pool:
    bodies = [b for _, b in pool.map(lambda u: fetch(u), [url] * 64)]
# tookMs varies per request; compare everything before it
canon = {b[: b.rfind(b'"tookMs"')] for b in bodies}
consistent = len(canon) == 1

If concurrency ever corrupted shared state — a half-built cache, a race in the prefix sums — two of those 64 bodies would differ, and the set would have more than one element. One line of Python, and an entire class of race condition has nowhere to hide.

Manufacturing the data: known betas and deliberate defects

A load test needs volume: make_load_data.py generates 100 tickers × 40 years of weekday closes(about a million rows). But it doesn't generate random noise — it uses a one-factor model, so every ticker's beta against the MKT baseline is known by construction and the computed betas are meaningful, not just numerically valid (from verification/make_load_data.py):

# market factor returns, then each ticker = beta * market + idiosyncratic
market = [rng.gauss(0.0003, 0.010) for _ in days]
...
true_beta = 1.0 if name == "MKT" else rng.uniform(0.3, 2.2)
sigma = 0.0 if name == "MKT" else rng.uniform(0.005, 0.02)
for i, day in enumerate(days):
    ret = true_beta * market[i] + (rng.gauss(0.0, sigma) if sigma else 0.0)
    price *= math.exp(ret)

Its sibling, make_dirty_sample.py, does the opposite job: it takes real (unadjusted) Yahoo Finance closes — genuine stock splits and all — and injects specific data-quality defectsso the service's quality analyzer has something real to find:

# stale run: freeze 10 consecutive JPM closes starting 2021-06-01
rows = [(d, t, frozen if t == "JPM" and d in stale_dates else c) for d, t, c in rows]

# calendar gap: SPY loses March 2023 entirely
rows = [r for r in rows if not (r[1] == "SPY" and r[0].startswith("2023-03"))]

# duplicates: repeat five MSFT rows verbatim
rows.extend(msft[100:105])

# single-row ticker
rows.append(("2020-01-02", "GHOST", 12.34))

Duplicates, calendar gaps, stale runs, insufficient history — plus three unparseable rows (garbage text, an invalid date, a negative price) spliced in at fixed positions. Each defect exists to prove one specific detector fires. Test data isn't found; it's engineered.

The engineering lesson

None of these four scripts ships to production. No user ever calls them; the service doesn't know they exist. They surroundproduction instead: one recomputes its numbers in an alien stack, one beats on it with concurrent traffic, two manufacture its inputs — with truth known in advance, or with lies planted on purpose. That's the pattern to take away: same numbers, different code, different language, different failure modes. When two implementations that share nothing agree to the fourth decimal on thousands of points, you've earned real confidence — the kind a green unit-test suite alone can never give you.

Things to try

  • • Drag the window slider across 3–8 days with the bug off — every point matches at every width. That agreement across a sweep is exactly what the real IPV's window checks (w = 20, 63, 252, 1260) establish.
  • • Flip the bug injector. Engine A now uses one return too few — and rows light up red. A unit test that computes its expected values with engine A would still pass; only an independent recomputation notices.
  • • With the bug on, set the window to 3. Engine A is left with a single return and yields null — the definedness mismatch, the exact branch ipv_check.py reports as (definedness).
  • • In verification/ipv_check.py, find the self-beta control: SPY vs SPY, whose beta must be exactly 1. Why is a check whose answer is known in advance still worth running?