Artifact taxonomy (living document)#
The catalogue of mechanisms in this specific dataset that manufacture
fake anomalies — a primary deliverable (Q4). Every entry: mechanism,
detection, measured magnitude on this data, neutralization. Magnitudes from
results/expA_data_reality/20260812T054515Z/ and
results/expB_artifact_baselines/20260812T055602Z/ (Q1 2024, RTH 1min,
pre-specified universe). Detectors validated on synthetic ground truth first
(benchmarks/synthetic/test_synthetic_gate.py, 11 tests).
T1 — Bid-ask bounce (Roll)#
- Mechanism. Trades alternate bid/ask; observed returns gain a negative lag-1 autocovariance (−s²/4) with no economics. Masquerades as mean-reversion (Q1).
- Detection. Roll implied relative spread
2·√(−autocov1); bounce-implied AC1;excess_reversion()(validation/artifacts.py). - Measured. Median AC1 by staleness tercile: −0.009 (fresh) / −0.051 (mid) / −0.232 (stale); Roll spread 1.4 / 3.8 / 17.0 bp. Mega-caps: no measurable bounce (AC1 CI covers 0; Roll undefined ~half the time — positive autocov).
- Neutralize. Report reversion net of the liquidity-bucket bounce null; never average AC1 across liquidity buckets; treat Roll-undefined as "no bounce measurable", not zero spread.
- expC amendment (measured). A daily effective spread (EDGE) combined with pure Roll alternation predicts impossible intraday AC1 (−3 to −27): consecutive intraday closes do not flip sides each bar. Spread-based bounce nulls MUST be variance-consistent with the target series; for intraday scans use the MA(1)-consistent VR null (VR_null(q) = 1 + 2·AC1·(1−1/q)) — conservative, absorbs all lag-1 effects.
T2 — Stale prices / missing minutes#
- Mechanism. Bars exist only where trades occurred (expA: zero zero-volume bars; an illiquid name printed 38 bars/day). LOCF joins add a large mass of zero returns and depress variance-ratio statistics.
- Detection.
staleness_ratioon the 390-min RTH grid. - Measured. Staleness up to 0.90 (RITM.B); VR(30) = 0.55 median for the stale tercile — 0.35 extreme — with zero planted economics. 11/42 pre-specified tickers had too little data to analyze at all.
- Neutralize. Explicit grids with observed-masks (never silent LOCF); liquidity filters pre-specified; VR/AC1 claims benchmarked against the staleness-matched null, not against 1.0.
T3 — Non-synchronous lead-lag (LOCF cross-correlation)#
- Mechanism. A fresh series LOCF-joined to a stale one appears to LEAD it: the stale print reflects old common information (classic non-synchronous trading bias).
- Detection. Lagged cross-correlation vs SPY on the LOCF grid; synthetic ground truth: rho=0.7 pair with 30 % observation → spurious +1 lag corr.
- Measured. SPY "leads" mid-staleness tickers by +0.047 at +1 min (Spearman vs staleness +0.43). SPX-vs-SPY: +0.065 with SPY leading 1 min at 0.965 contemporaneous corr — index prints lag the ETF.
- Neutralize. Any Q2 lead-lag claim must exceed the staleness-predicted cross-correlation; test on synchronized (both-fresh) subsamples; index series are stale by construction.
- expD amendments (measured). (i) The artifact is NON-MONOTONE in staleness: LOCF joins inflate leads for mid-stale names (+0.047, expB) but DILUTE them for ultra-stale names (HTD raw +0.048 vs both-fresh +0.073) — naive joins can hide lead-lag as well as manufacture it. (ii) For computed indices, synchronizing on PRINT times removes nothing: SPY leads SPX +0.132 both-fresh (fresh_frac 0.996) — content staleness must be modeled, not print gaps.
T4 — Auction close vs last bar#
- Mechanism. Daily bars carry the official closing-auction print; 1min bars do not (expA: AAPL 312.41 daily close vs 312.49 last RTH 1min close). Mixing conventions manufactures phantom overnight/close-to-close returns.
- Measured. 8 bp discrepancy on a calm day for the most liquid stock.
- Neutralize. Pick ONE close convention per experiment and state it; never compute overnight returns across mixed conventions.
T5 — Rolling adjustment anchor#
- Mechanism.
adj_splitdivre-bases the whole history to the dataset build date (expA: AAPL 2020-08-31 close = 125.17 adjusted vs 129.04 traded). Adjusted series are not point-in-time stable → silent look-ahead and irreproducibility if the cache is refreshed mid-study. - Neutralize. Frozen local cache (hf_client never silently refetches); data-manifest hash in every provenance; intraday work uses within-day returns (adjustment-invariant) or UNADJUSTED plus explicit factors.
T6 — Daily vs intraday volume conventions#
- Mechanism. Daily volume includes auction/consolidated prints absent from 1min bars (expA: 46.1 M daily vs 34.7 M extended-1min sum vs 25.7 M RTH-1min sum for AAPL on one day — a 1.8× spread across conventions).
- Neutralize. Volume-based signals pick one convention; never mix daily and intraday volume in one feature.
T7 — Vendor session / timezone semantics#
- Mechanism. All timestamps are US/Eastern wall-clock without a marker; sessions differ per class (equities 04:00–19:59, SPX prints to 16:20, futures ≈24 h, fx ET-week, crypto 24/7). Cross-asset joins on naive timestamps silently compare different market states.
- Neutralize. One canonical calendar module (
data/calendars.py), explicit session filters per asset class, DST-aware conversions.
T1/T2 intraday profile (measured, expE/H20 — liquid 12, 1min, 2014-2015). Volatility is U-shaped: median |1min return| 6.6 bp at 09:30 → 2.4 bp midday → 2.9 bp at 15:30. The EDGE spread declines monotonically 2.8 → 1.2 bp (an L, not a U). Consequence: intraday return claims concentrated at the open face 2–3× the midday artifact level and double the closing spread — expE/expG must bucket-match their nulls and costs by time of day.
Open items: continuous-futures splice choice (3 variants exposed by the API) as a testable artifact for futures-based hypotheses — expD found the ES→SPY cross-serial effect splice-INVARIANT, so the residual exposure is futures-only level/trend work.