20 Point-in-Time Filing-Date Integrity Audit of Vendor Fundamentals — Findings
Question. Does the platform ever use a fundamental before it was public? A point-in-time backtest is only trustworthy if a historical date sees accounting figures that had already been filed with the SEC on that date. We test the falsifiable hypothesis that our stored fundamentals carry a knowledge date on or after their SEC filing date; any row whose knowledge date precedes its filing is a look-ahead leak that inflates every result built on it.
Verdict — look-ahead detected. Auditing the platform’s stored fundamentals against the SEC’s own EDGAR filing dates, we find a systematic leak: 184 of 184 matched fundamentals carry a knowledge date earlier than their EDGAR 10-K/10-Q filing date. Of 1,181 panel rows evaluated, 184 matched a filing and all 184 violated; 997 could not be matched. The median violation gap is roughly −49 days (range about −45 to −53 days), and the leak appears in both the annual and quarterly cadence legs. The result is empirically real for what could be verified; two caveats below bound how economically alarming it is.
Provenance. The verdict here is a corrected re-run against a live EDGAR fetch (sample AAPL / MSFT / JPM / XOM / JNJ), recorded 30 July 2026. An earlier run (27 July 2026) reported a spurious “185/185 at roughly −210 days” that turned out to be an instrument bug, not a platform leak: the loader reconstructed quarterly period-ends from a calendar-quarter assumption, while the quarterly fundamentals table stores fiscal year and period, so non-December fiscal-year-end names (Microsoft, a June year-end filer) mis-paired to the wrong EDGAR filing. We fixed it by deriving each row’s period-end from its own fiscal-period key; the numbers below are that corrected re-run. The harness is read-only and re-runnable — re-run it after each data refresh (§5).
20.1 1. The result, in detail
The leak is systematic and splits cleanly along the two cadence legs, exactly as our a-priori prediction (below) anticipated:
- Annual leg (
earnings_yield/pe_ratio/roic, sourced from the annual valuation and moat tables): stamped at the platform’s cadence-aware first-of-January knowledge snap (e.g.20190102), while the 10-K was filed in late February (e.g. 2019-02-20) — a gap of about −49 days. Prediction confirmed. - Quarterly leg (
earnings_growth_qoq, from the quarterly fundamentals table): stamped at the fiscal period-end (e.g. 2020-12-31) with no dissemination lag, while the 10-Q was filed around 2021-02-22 — a gap of about −53 days. This refutes the a-priori guess that the quarterly leg was safer. As a method note, the quarterly table stamps knowledge at the fiscal period-end (aCOALESCE(period_date_sk, …)expression), and the vendor’s configured minimum-age dissemination lag does not propagate into that knowledge date.
Two caveats bound how alarming this is (read before acting).
- The anchor is the 10-K/10-Q filing date, but preliminary figures usually reach the market two to three weeks earlier via an 8-K earnings release. So −49 / −53 days is an upper bound on the economically-relevant look-ahead. Even measured against the earlier 8-K, a period-end stamp (December 31) or a first-of-January snap still precedes the announcement, so a non-trivial leak remains — it is just smaller than the headline. A later extension of the audit wired this into code: it now fetches 8-K Item 2.02 earnings releases, takes the earlier of the 8-K release and the definitive filing as the true first-availability date, and reports both the conservative gap (versus the definitive filing) and a tighter gap versus the earnings release. The tighter empirical numbers land on the next live run — an operator step: the code and offline fixtures are delivered and unit-tested, but the live 8-K fetch has not yet been run.
- Small, partial coverage. 184 matched of 1,181 (997 unmatched) rests on only five tickers, only 10-K/10-Q forms, and a 20-day pairing tolerance. The verdict is “100% of what could be verified,” not “100% of the universe” — a scope limit, not an empirical ceiling on the leak rate. The as-reported value-agreement leg returned 0 comparable of 0, because the four audited concepts (
earnings_yield/pe_ratio/roic/earnings_growth_qoq) are derived ratios with no single us-gaap XBRL tag; that is an absence of a comparable twin, not a disagreement. The audit extension addressed both halves: the sample was widened to fifteen tickers mixing fiscal-year-ends (adding Microsoft-June, Nike-May, Walmart-January, and others alongside the December-year-end names) so the non-December pairing path is exercised, and a vendor-to-us-gaap concept map was wired so two raw as-reported sources with real twins (annual revenue againstRevenues, net income againstNetIncomeLoss) can produce non-zero comparable evidence; the four derived ratios are now reported explicitly as “no us-gaap twin” rather than silently as zero-of-zero. Non-zero comparable counts land on the next live run.
Reasoned a-priori prediction (retained for the record; confirmed for the annual leg, refuted for the quarterly leg). The platform’s cadence-aware knowledge-date snap stamps annual (10-K-sourced) fundamentals at the first US market day on or after January 1 of the following calendar year. A 10-K is actually filed roughly 60–90 days after fiscal year-end — late February to March for the December-year-end population — so we predicted the annual leg would leak by about 40–90 days (confirmed at roughly 49). We predicted the quarterly leg would be safer, on the assumption that the configured dissemination lag pushed the knowledge date past the period-end; the live run shows that lag never reaches the quarterly knowledge date, so the quarterly leg leaks too.
20.2 2. Method
Read-only and standard-library-only (urllib / json / dataclasses / datetime); no ingestion, no Gold write, no new runtime dependency.
- CIK resolution — EDGAR
company_tickers.jsonparsed into a{TICKER: CIK}map, then zero-padded to the 10-digit path token. - Filing dates — the submissions API (
data.sec.gov/submissions/CIK…json),filings.recentparallel arrays zipped into filing records carryingform/filed/acceptanceDateTime/accessionNumber/reportDate. - As-reported values — the XBRL companyfacts API (
data.sec.gov/api/xbrl/companyfacts/CIK…json),facts → us-gaap → <Concept> → units → USD. - Pairing — a panel row pairs to a filing when
abs(period_of_report − period_end) ≤ 20 days; the earliest-filed match wins (first public disclosure, the conservative choice). Period fields are used only to pair — never for the leak test itself. - The leak test — the stored knowledge date (decoded from its key) versus the paired filing’s SEC filing date.
gap_days = knowledge_date − filing_date; a negative gap is a look-ahead violation. Following the lesson learned earlier in the programme, the leak is always anchored on the filing date, never period-end. - As-reported agreement — the platform’s stored value versus EDGAR’s as-reported value, treated as material when the relative difference exceeds 1%.
Sample. The 30 July 2026 verdict used a five-name cross-section (AAPL, MSFT, JPM, XOM, JNJ); the audit extension widened the sample to fifteen mixed-year-end names (adding Nike, Walmart, Home Depot, Costco, Target, Procter & Gamble, Coca-Cola, Cisco, Oracle, Pepsi) now that the standing rule — widen only if leakage is found — is satisfied. Filings audited: recent 10-K and 10-Q for the leak test, plus 8-K Item 2.02 earnings releases for the first-availability anchor; us-gaap concepts Revenues, NetIncomeLoss, Assets, StockholdersEquity.
20.3 3. Worst-offender table
Corrected live run, 30 July 2026 (top 20 of 184 violations, most-negative gap_days first). Every worst offender is JNJ — a December-year-end filer that files relatively late (around February 20), giving the widest gap; the other sampled names also violate, with smaller gaps. The knowledge date at the fiscal period-end (quarterly) or the early-January snap (annual) sits about seven weeks before the filing date.
Note. This table pre-dates the 8-K anchor. The report now emits two additional columns — the earlier of the 8-K Item 2.02 earnings release and the definitive filing, and the tighter (less-negative) gap against it — so a JNJ row filing its 8-K about two weeks before its 10-K would show a release-anchored gap roughly 14 days less negative than the
gap_daysshown here. The widened columns and tighter numbers repopulate on the next live run. The rows below are the versus-definitive upper bound and remain valid as such.
| symbol | concept | source_table | period_end | knowledge_date_sk | edgar_filed | gap_days |
|---|---|---|---|---|---|---|
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2020-12-31 | 20201231 | 2021-02-22 | -53 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2018-12-31 | 20181231 | 2019-02-20 | -51 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2019-12-31 | 20191231 | 2020-02-18 | -49 |
| JNJ | earnings_yield | fact_valuation_annual | 2018-12-31 | 20190102 | 2019-02-20 | -49 |
| JNJ | earnings_yield | fact_valuation_annual | 2020-12-31 | 20210104 | 2021-02-22 | -49 |
| JNJ | pe_ratio | fact_valuation_annual | 2018-12-31 | 20190102 | 2019-02-20 | -49 |
| JNJ | pe_ratio | fact_valuation_annual | 2020-12-31 | 20210104 | 2021-02-22 | -49 |
| JNJ | roic | fact_moat_annual | 2020-12-31 | 20210104 | 2021-02-22 | -49 |
| JNJ | roic | fact_moat_annual | 2018-12-31 | 20190102 | 2019-02-20 | -49 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2021-12-31 | 20211231 | 2022-02-17 | -48 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2022-12-31 | 20221231 | 2023-02-16 | -47 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2023-12-31 | 20231231 | 2024-02-16 | -47 |
| JNJ | earnings_yield | fact_valuation_annual | 2019-12-31 | 20200102 | 2020-02-18 | -47 |
| JNJ | pe_ratio | fact_valuation_annual | 2019-12-31 | 20200102 | 2020-02-18 | -47 |
| JNJ | roic | fact_moat_annual | 2019-12-31 | 20200102 | 2020-02-18 | -47 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2024-12-29 | 20241229 | 2025-02-13 | -46 |
| JNJ | earnings_growth_qoq | fact_fundamental_quarter | 2025-12-28 | 20251228 | 2026-02-11 | -45 |
| JNJ | earnings_yield | fact_valuation_annual | 2021-12-31 | 20220103 | 2022-02-17 | -45 |
| JNJ | earnings_yield | fact_valuation_annual | 2023-12-31 | 20240102 | 2024-02-16 | -45 |
| JNJ | pe_ratio | fact_valuation_annual | 2021-12-31 | 20220103 | 2022-02-17 | -45 |
20.4 4. Coordination boundary
This audit owns only the point-in-time filing-date and as-reported-value integrity leg — is a fundamental usable before it was filed, and does its stored value match EDGAR’s? The EDGAR delisting-confirmation leg — positive confirmation of a performance versus benign delisting via EDGAR — is a separate concern owned by the survivorship-bias measurement work. That work was unbuilt when this audit was written, so this harness carries its own standard-library EDGAR fetcher and CIK-resolution surface; any shared EDGAR fetch or CIK-resolution surface must be reused, not forked. When the survivorship delisting audit lands, the two fetchers should be consolidated into one shared EDGAR access module rather than maintained in parallel.
20.5 5. How to run
Read-only, re-runnable, no gate:
# Live fetch (operator step; caches snapshots for reproducibility):
.venv\Scripts\python.exe scripts\research\edgar_pit_audit.py --live --out <report.md>
# Default (re-runs against the already-cached local snapshot):
.venv\Scripts\python.exe scripts\research\edgar_pit_audit.py
--sample AAPL,MSFT,... overrides the audited tickers; --snapshot-dir overrides the local cache (default data/reference/edgar_snapshots); --out writes the report markdown (default stdout). SEC requires a declared User-Agent on every request — already set. The DuckDB connection is opened read-only; if the database or the live fetch is unavailable the script prints a clear INDETERMINATE report and exits cleanly (it is a diagnostic, not a gate). Transcribe the verdict and worst-offender rows from the live run into §1 / §3 above.
20.6 6. Follow-ups (raised by the 30 July 2026 result)
The corrected run turns the leak from a hypothesis into a measured fact, but two things must happen before it can be acted on with confidence:
Widen the anchor to 8-K earnings-release dates — delivered in code. The audit now fetches 8-K Item 2.02 earnings releases, takes the earlier of the 8-K release and the definitive filing as the true economic first-availability date, and reports both the conservative gap (versus the definitive filing) and the tighter gap versus the release. The sample was widened to fifteen mixed-year-end tickers and the vendor-to-us-gaap concept map wired (two raw as-reported sources added) so the value-agreement leg produces evidence. Remaining: the live re-run (an operator step) to populate §1 / §3 with the tighter empirical numbers and non-zero comparable counts. The corrected fiscal period-end derivation is preserved.
Remediate the knowledge-date dissemination lag. The honest fix is to push the knowledge date to on-or-after the actual filing or announcement date — a dissemination lag on the quarterly period-end stamp and a move of the annual first-of-January snap onto the real filing date, mirroring the approach already used for short-interest data. This is not a quick patch: changing the knowledge date rewrites every downstream point-in-time join and forks factor history, so it needs its own design brief, a report-then-enforce switch, and operator bring-up rather than an inline change. The lag size should be chosen from the 8-K-anchored numbers once the live re-run lands.
This remediation is now delivered as a default-off, report-then-enforce dissemination-lag switch (
SB_FUNDAMENTALS_DISSEMINATION_LAG_ENABLED): a fixed proxy lag of period-end plus N calendar days snapped forward to the first US market day — quarterly 60 days, annual 90 days — applied to the quarterly and annual fundamentals, valuation, and moat tables (superseding the December-31/January-1 annual snap when on), with the signal panel and coverage rerouted through the switched expression (flipping it on forks the model identifier). The re-lift is an operator step viapython -m sbfactors.cli relift-fundamentals-factors [--all]. Off is byte-identical to the prior behaviour. The design is captured in a dedicated brief for the fundamentals dissemination-lag remediation.