# Feed Divergence Study — Oanda vs Tiingo vs Twelve Data (Phase 1) **Date:** 2026-08-14 **Test plan:** `docs/data_source_change_test_plan.md` (removed 2026-09-06, in git history; the pending Phase 4 shadow scan is noted in CLAUDE.md) **Tool:** `scripts/feed_divergence_study.py` (script removed 2026-09-06, in git history) (fetch_range over 365d, parquet-cached) **Window:** 2025-08-14 → 2026-08-14 (12 months); Oanda-vs-TD truncated at TD cache head (2026-07-23) ## Headline Three mid-price feeds for the *same* XAU/USD instrument are **not** interchangeable: | Pair | mean \|ΔClose\| | p95 \|ΔClose\| | direction agree (M5/M15/H1) | bias | extra bars | |---|---|---|---|---|---| | Oanda vs **Tiingo** | ~$0.13 | ~$0.29 | 97.7% / 98.3% / 98.5% | ~0 | Tiingo +3–5% (weekend) | | Oanda vs **Twelve Data** | ~$3.0 | ~$11.9 | 81.0% / 88.7% / 92.7% | −$1.06 (TD high) | — | - **Oanda ↔ Tiingo are near-identical** on shared bars (~1–2% of ATR on close), but Tiingo emits ~22 hours of Sunday bars/week that Oanda omits. - **Oanda ↔ Twelve Data diverge massively** — ~$3 mean close gap (≈30% of M5 ATR, ~30% of M15 ATR), a systematic ~$1 bias (TD quoted ~$1 high), and 19% of M5 bars even disagree on bar *direction*. This is the documented "Oanda/TD divergence" that motivated re-pinning the books on Oanda bars. ## Oanda vs Tiingo — detailed Both feeds: same bar grid, same head/tail timestamps. Tiingo simply has more bars (Sunday bars). ### Coverage (12 months) | TF | Oanda bars | Tiingo bars | shared | Oanda-only | Tiingo-only | |---|---|---|---|---|---| | M5 | 70,901 | 73,241 | 70,807 (96.6%) | 94 (0.13%) | 2,434 (3.32%) | | M15 | 23,640 | 24,922 | 23,615 (94.7%) | 25 (0.11%) | 1,307 (5.24%) | | H1 | 5,912 | 6,246 | 5,909 (94.6%) | 3 (0.05%) | 337 (5.40%) | | H4 | 1,597 | 1,657 | 1,597 (96.4%) | 0 | 60 (3.62%) | - **Tiingo-only bars cluster exclusively on Sundays** (e.g. M15: 88/Sunday; H1: 22/Sunday; H4: 5/Sunday). Tiingo's FX endpoint resamples continuously and does **not** apply the FX weekend calendar — it emits ~22h of flat/stale Sunday bars that Oanda omits. - Oanda-only bars cluster on holidays (2025-11-27 Thanksgiving, 2025-12-10, 2026-06-25) — a handful of bars, negligible. ### Per-bar OHLC diffs (Oanda − Tiingo, $) | TF | Close mean | Close p95 | Close max | \|ΔClose\| / ATR14 (mean / p95) | |---|---|---|---|---| | M5 | $0.123 | $0.285 | $26.10 | 2.1% / 5.0% | | M15 | $0.128 | $0.290 | $26.10 | 1.3% / 2.9% | | H1 | $0.145 | $0.335 | $26.10 | 0.7% / 1.6% | | H4 | $0.168 | $0.340 | $17.53 | 0.4% / 0.8% | - Bias is ~zero on every TF (no systematic one-feed-high). - Tail outliers (p99/max up to $26–88) occur during fast moves — the two feeds' bar extremes disagree on the exact tick, as expected. - **Direction agreement is 97.7–98.4%**, return correlation 0.995–0.9997. - Divergence is concentrated at the Friday-close / Sunday-reopen boundary (UTC hours 20–22; mean |ΔClose| ~2–4× the 08–19h baseline). ### Phase 1 pass/fail vs plan thresholds | Criterion | Plan threshold | Result | Verdict | |---|---|---|---| | timestamp overlap | ≥ 99.5% | 94.6–96.6% | **FAIL** (Tiingo weekend bars) | | Close p95 \|Δ\| | ≤ 0.01% of price | ~0.0067% | PASS | | no systematic bias | mean Δ ≈ 0 | ~0 | PASS | The overlap "failure" is a *coverage* difference, not a data-quality one: the shared bars agree tightly, but Tiingo's extra Sunday bars mean the feeds have a different bar *count*. The signal pipeline's own `_is_forex_weekend` filter (NY-anchored) gates those Sunday bars, so live impact depends on whether any code trusts the feed's raw bar set directly. ## Oanda vs Twelve Data — detailed (M5/M15/H1, cached TD parquets) | TF | common | mean \|ΔClose\| | median \|ΔClose\| | p95 | max | bias (TD−Oanda) | dir agree | |---|---|---|---|---|---|---|---| | M5 | 66,246 | $2.999 | $1.118 | $11.934 | $101.85 | +1.05 | 80.96% | | M15 | 22,090 | $2.951 | $1.051 | $11.848 | $101.85 | +1.08 | 88.71% | | H1 | 5,526 | $2.902 | $0.985 | $11.908 | $70.94 | +1.06 | 92.67% | - The ~$3 mean / ~$1 median / ~$12 p95 gap is **~23× larger** than the Oanda–Tiingo gap, and the ~$1 bias is systematic (Twelve Data quotes gold ~$1 high vs Oanda's true mid). - M5 direction agreement of **81%** means one bar in five disagrees on direction — M5 signals on TD bars are effectively a *different instrument* than on Oanda bars. - This matches the prior Oanda/TD M15 study of 2026-07-24 (`oanda_td_divergence.py`, removed 2026-09-06 as superseded by this report; mean |ΔClose| ~$3.3) and confirms the standing rule: **pinned Twelve-Data books are not comparable to Oanda live signals without a re-pin.** ## Conclusions for the data-source switch 1. **Oanda → Tiingo (or back) is signal-safe in magnitude** (price diffs ~1–2% of ATR, 98% direction agreement), *provided* the weekend-bar coverage difference is handled — Tiingo's Sunday bars must be gated by the aggregator's NY-anchored weekend filter, not by raw bar count. 2. **Oanda → Twelve Data is a recalibration, not a formality.** Phase 2/3 must quantify the actual signal/book delta; the $3/$1 bias alone guarantees a large fraction of marginal bars flip. 3. Re-pinning the books on Oanda bars (Phase 3) remains mandatory before any TD-pinned baseline is treated as a live expectation. ## Next steps - Phase 2: signal-level replay parity (`analyze_from_dataframes` on both feeds) to turn price-diff stats into flip rates. - Phase 3: backtest re-pin on Oanda bars at $0.30 and $0.65.