Files
ForexBot2/backtest/research/feed_divergence_2026-08-14.md
T
Harwinder Sekhon 1f44d6bad6 chore: repo hygiene — stale gitignore entries, finished plans, superseded research, doc drift
Removed (all recoverable from git history):
- llm_provider_switch_plan.txt: 2026-08-08 DeepSeek/Anthropic multi-provider plan that
  was never implemented (llm_client.py is DeepSeek-only, no `anthropic` anywhere)
- docs/superpowers/specs/2026-07-21-centralized-config-design.md: spec of the
  config.json migration that merged the same day; its plan sibling was already
  deleted in b936d44. app_config's docstring pointer goes with it.
- scripts/compare_oanda_tiingo.py: Phase 0 recent-tail smoke test, superseded by the
  Phase 1/2 study scripts (result recorded in the test plan)
- backtest/research/fetch_oanda_m15.py + oanda_td_divergence.py + its by-hour CSV:
  the 2026-07-24 Oanda-vs-Twelve-Data study, superseded by the 2026-08-14 feed
  divergence report (which keeps a one-line citation)
- backtest/research/burst_profile.py and hour_analysis.py: superseded by burst_map.py
  and book_report.py
- backtest/research/panel_payload_drytest_2026-08-15.py: one-off live DeepSeek probe;
  its .jsonl evidence stays

Added to git: the three 2026-08-14 research reports (feed divergence, re-pin
verification, signal parity), their CSV, and docs/data_source_change_test_plan.md -
CLAUDE.md cites them as standing evidence and their driver scripts were already
committed. The test plan no longer refers to auto-execution.

Hygiene:
- .gitignore: drop oanda_state.json (daemon removed 2026-09-03) and the vanished
  backtest/trailgrid dirs; ignore .coverage
- docs/architecture_diagram.py only writes the PNG when run as a script (it used
  to plot on import); test box refreshed to 461 tests / 31 modules; PNG regenerated
  (the committed one still drew the removed daemon)
- backtest/instructions_for_backtest.txt: manual-journal head replaced by the
  actual reporting checklist; the 2026-09-06 addendum is unchanged
- doc drift: README/publisher list all six published files; news_summary.php says
  4 h (not 24 h) and drops the pre-id sha1 fallback whose window closed;
  analyze_signal_log lists six lenses and --scan-outcomes; agent_panel cost model
  says 4 requests by default; tiingo comment cites the NY-anchored weekend;
  Module 21 title no longer says "execution lifecycle"

Suite: 461 passed, 0 failed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0156YhogLytzj9P2iH1skGbL
2026-09-06 17:01:07 -04:00

5.3 KiB
Raw Blame History

Feed Divergence Study — Oanda vs Tiingo vs Twelve Data (Phase 1)

Date: 2026-08-14 Test plan: docs/data_source_change_test_plan.md Tool: scripts/feed_divergence_study.py (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.