# Validation Log — Gold Allocation Backtest

Per Daniel's rule (2026-09-02): a failed check stops downstream computation. Diagnosis and
correction may proceed without waiting, but every failure, cause and correction is documented,
the full suite is rerun, and no portfolio result is accepted until validation passes.

---

## V1 — Bond total return vs Damodaran — **FAILED, then PASSED after correction**

**Reference:** Aswath Damodaran, `histretSP.xls`, sheet `Returns by year`, column E
"US T. Bond (10-year)". Downloaded 2026-09-02, file last saved 2026-08-24.
SHA-256 `28b8110916a15a4dcc11c87c6422510704608ddedcdfa67a1298abdc22e49c69`.
Common period 1954-2025, 72 complete calendar years.

### First run — FAILED

| Metric | Result |
|---|---|
| Correlation | 0.9441 |
| Mean absolute difference | **6.349 pp** |
| Mean difference (bias) | **+6.339 pp** |
| Annualised ours vs Damodaran | 11.318% vs 5.122% (**+6.196 pp**) |
| Within 1pp | 5.6% of years |

**Failed.** High correlation with a large one-directional bias indicates a systematic
construction error, not sampling noise. Every one of the ten largest discrepancies had our
series higher.

### Cause — coupon double-counted

Total return was computed as `(dirty_price - 100 + accrued_coupon) / 100`.

The dirty price already contains the coupon the holder is entitled to, discounted in its cash
flows. Adding a separate accrued-interest term counted the coupon twice, inflating each month by
roughly `y/12` and therefore each year by roughly the yield itself. That matches the observed
+6.3pp bias across a period whose average 10-year yield was near 6%.

**Detected by a first-principles invariant:** if the yield is unchanged, a par bond's monthly
total return must equal `y/12`. At y=5% the implementation returned **0.829%** against an
expected **0.417%** — very nearly double.

### Correction

`total_return_month = dirty_price(y1) / 100 - 1`, with no separate accrued term.

The invariant is now **asserted in code** (`_self_test()` in `bond_returns.py`), checked across
yields 1%-15%, tolerance 5bp, and it runs on every execution. Max deviation 3.74e-04.

### Second run — PASSED

| Metric | Result |
|---|---|
| Correlation | **0.9910** |
| Mean absolute difference | **0.811 pp** |
| Median absolute difference | 0.519 pp |
| Mean difference (bias) | +0.207 pp |
| Annualised ours vs Damodaran | 5.349% vs 5.122% (**+0.227 pp** over 72 years) |
| Within 0.5pp / 1pp / 2pp | 48.6% / 70.8% / 88.9% |

### Residual differences — explained, not forced to match

Daniel's instruction: do not force an exact match where construction conventions differ; a
material *unexplained* discrepancy is a hard stop.

The residual is small, roughly symmetric (bias +0.21pp against a mean absolute difference of
0.81pp, so it is dispersion rather than drift), and attributable to known convention differences:

1. **Constant-maturity roll.** We hold a par 10-year bond and roll monthly. Damodaran's series is
   built on an annual convention. In years with large intra-year rate moves the monthly roll
   compounds differently, which is why the largest gaps cluster in volatile years (2009, 2022,
   2023, 1982).
2. **Yield observation basis.** We use FRED GS10 monthly averages of daily constant-maturity
   yields; an annual construction using year-end or a single annual yield will differ in years
   with a sharp move.
3. **Coupon reinvestment.** We do not reinvest the coupon within the month.

No single year is an outlier consistent with a coding fault: the largest gap is 4.45pp in 2009,
a year of exceptional rate volatility, and the discrepancies change sign across years.

**Verdict: PASSED.** Differences are explained by documented convention, not error.

**Disclosure requirement:** the page must state that the Treasury sleeve is an independently
constructed constant-maturity total-return series validated against Damodaran, quoting the
correlation and annualised difference, and naming the convention differences above.

---

---

## V2 — Gold series integrity — **PASSED**

World Bank Pink Sheet, Monthly Prices, Gold, `($/troy oz)` throughout the common period.

| Check | Result |
|---|---|
| All 675 common-period months present | PASS |
| Missing values | 0 |
| Zero or negative values | 0 |
| Duplicate months | 0 |
| Range | $34.94 (1970-01) to $2,351.13 (2024-05) |

**Extreme monthly moves flagged for inspection, retained unaltered:** 1980-01 +48.39%,
1974-01 +21.06%, 1982-09 +20.06%, 1979-09 +17.99%, 1973-06 +17.81%, 1999-10 +17.37%,
1980-06 +16.92%, 1980-03 −16.79%.

These observations are present in the authoritative source data and were not missing, corrupted,
smoothed or winsorized. **No winsorising, no smoothing, no deletion applied.**

No historical cause is assigned to any individual month here. Causal explanations require
separate research and sourcing, and V2's purpose is source integrity only.

---

## V3 — Equity total return vs Damodaran — **PASSED**

Reference: `histretSP.xls`, "Returns by year", column B "S&P 500 (includes dividends)".
Common period 1969-2023, 55 complete calendar years.

| Metric | Result |
|---|---|
| Correlation | 0.9864 |
| Mean absolute difference | 2.025 pp |
| Median absolute difference | 1.312 pp |
| Bias | **−0.103 pp** |

Largest discrepancies: 1991 −8.17pp, 1992 +8.01pp, 2003 −6.09pp, 1987 −5.92pp, 1986 +5.62pp,
2019 −5.06pp.

**Assessment.** Bias is essentially zero (−0.10pp) against a mean absolute difference of 2.03pp,
so the residual is dispersion with no directional drift, and the largest discrepancies alternate
in sign. This is the expected signature of the known construction difference: **Shiller monthly
dividends are interpolated from four-quarter totals**, so month-by-month compounding cannot match
a daily-reinvestment total-return index. Adjacent years offsetting (1991/1992) is characteristic
of dividend-timing interpolation rather than error.

No persistent directional bias. **PASSED.**

---

## V4 — CPI vs FRED CPIAUCNS — **PASSED (exact)**

**Correction applied before running:** the plan named CPIAUCSL (seasonally adjusted). Daniel
directed the use of **CPIAUCNS** (not seasonally adjusted) unless Shiller's series was shown to
be seasonally adjusted. Comparing unlike seasonal-adjustment conventions would have manufactured
a false discrepancy.

| Comparison | Correlation | MAE |
|---|---|---|
| Shiller vs **CPIAUCNS** (NSA) | **1.000000** | **0.0000 pp** |
| Shiller vs CPIAUCSL (SA) — reference only | 0.999541 | 0.0593 pp |

Level ratio Shiller/CPIAUCNS is **1.000000 at both the minimum and maximum** across all 675
months: the two are the same series on the same base, not merely correlated.

This **establishes** that Shiller uses the NSA CPI-U series. Had CPIAUCSL been used as primary, a
0.06pp seasonal artefact would have been recorded as a data discrepancy. The correction was
material.

---

## V5 — 0% gold 60/40 baseline — **FAILED (test bug), then PASSED**

### First run — FAILED

`portfolio return == weighted constituent returns, max abs error: 9.04e+00`

### Cause — the TEST was wrong, not the backtest

The check compared a weighted constituent return against a period ratio **without normalising by
the portfolio's start-of-month value**. Since portfolio value compounds to ~139x its base over
the period, the un-normalised comparison drifted proportionally with accumulated wealth. The
9.04 magnitude is consistent with that scaling, not with a portfolio-construction fault.

Verified by isolating the identity independently: with `exp = (w·r) / start_total` the maximum
absolute error is **3.31e-16**, i.e. floating-point exact.

**This was a validation-harness defect. No change was made to the portfolio construction, the
weights, the rebalancing or any return series.** Recording it because the rule is that failures
are documented, and because a test that scales with wealth would silently pass on short samples.

### Second run — PASSED

| Check | Result |
|---|---|
| Target weights | 60% equity / 40% bonds / 0% gold |
| Return identity, max abs error | **3.31e-16** PASS |
| Ending wealth reconstructed from monthly returns | 139.0643628153 vs 139.0643628153 PASS |
| Plausibility (CAGR 5-13%, vol 5-15%, MDD −10..−45%) | PASS |

Baseline over 1968-05 to 2024-06 (674 months): CAGR 9.18%, annualised volatility 8.21%, maximum
drawdown **−24.4% measured from monthly observations**, $100,000 → $13,906,436.

Note: start weights read 0.6090/0.3910 because the series records values **after** the first
month's return; annual rebalancing restores the 60/40 target at each calendar year-end.

---

## V6 — Pre-result integrity — **PASSED**

| Check | Result |
|---|---|
| Contiguous monthly index, no gaps | PASS |
| All common months present in all series | PASS |
| No look-ahead (return_t uses only t−1 and t) | PASS, pair-wise by construction |
| No future value in a prior month's return | PASS |
| Rebalancing only on documented dates | PASS, 56 calendar year-ends |
| 60/40 preserved inside the non-gold portion | PASS (10% gold → 54/36/10) |
| Deterministic reproduction from identical inputs | PASS |
| Source checksums recorded | PASS, `data/SHA256SUMS.txt` |
| Code version recorded | PASS, SHA-256 per module |

---

## Status: V1-V6 ALL PASS

Validated components: World Bank gold, Shiller equity price and dividends, Shiller CPI,
independently constructed FRED constant-maturity Treasury total return, and the 60/40 baseline
engine.

**Portfolio allocation results have NOT been computed.** Awaiting Daniel's go-ahead after review
of this validation report.

### Required disclosures on the published page

1. Treasury sleeve independently constructed from FRED GS10; validated against Damodaran
   (r = 0.991, mean absolute annual difference 0.81pp, annualised difference +0.23pp over 72
   years); convention differences named.
2. Gold is a **monthly average** (World Bank); Shiller equity prices are likewise monthly
   averages of daily closes, so the two are methodologically aligned.
3. Maximum drawdown is **measured from monthly observations** and does not capture intramonth
   peaks or troughs.
4. Shiller monthly dividends are interpolated from four-quarter totals.
5. No transaction costs, fees, taxes, storage or custody charges are modelled. Physical gold in
   an IRA carries storage and custody fees not represented here.
