1f44d6bad6
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
6.3 KiB
6.3 KiB
AI signal enrichment — analysis + build record (2026-08-15)
Owner questions:
- If the AI panel also received indicator values and (limited) candle data, would the system be more accurate?
- If AI reviewed every candle alongside the local score system, would prediction accuracy justify the token burn — or is there a middle ground?
What the panel already received (pre-change baseline)
The four personas (agent_panel.py) already got a distilled picture:
per-indicator signal+confidence, MTF signals/scores, key levels,
exhaustion count, full trade-setup math, warnings, headlines/events/
sentiment. Missing: raw indicator values and any candle data.
Cost guardrails already in place: 5 requests/panel, max_panels_per_day
20 (≤100 requests/day), 3600 s reuse-TTL, per-bar exact cache, shared
500 req/day DeepSeek cap, static prompts on the ~50× prefix cache.
Evidence used
- LLM is strong at text, weak at raw numerics (measured in-repo,
llm_news_eval_2026-08-06.md): headline sentiment 100% (78/78) vs keyword scorer 31%; but ~11% of event-direction calls misread close numeric comparisons until code pre-computed the comparison (vs_forecast) and the model only mapped it to doctrine. - The book any filter would act on (M5 canonical,
oanda_m5_12mo_065_nopb_20260814.csv): 577 trades, +106.82R, PF 1.51, win rate 52.9%; A-grade 59.5%, B-grade 51.4% (+67.8R of the total), C-grade 43.8% (−0.23R, 16 trades). Average edge ≈ 0.185R/trade.
Answer 1 — enrich the existing panel? YES (cheap, safe)
- A compact candle is ~15–25 tokens; 20 primary-TF + 10 confirm-TF bars ≈ 500–1,500 tokens per persona call → cents/day at the 20-panel cap. Tokens are not the constraint; the request cap is.
- Measured after the build (dry-test wire log): enriched technical payload with 20 candles ≈ ~380 tokens; replies ≈ 25–35 tokens under the new 15-word note cap.
- Caveats: (a) the panel is advisory-only — richer payloads improve the human's context, not the book's P&L; (b) raw prices are the model's weakest input, so candles are sent ATR-normalized (code does the arithmetic, LLM reads the shape) — the same pattern that fixed the event-direction misreads.
Answer 2 — AI on every candle? NO
- Feasibility: M5+M15+H1+H4 ≈ 414 bars/day → one call/bar nearly exhausts the shared 500/day cap (news needs it too); a panel per bar ≈ 2,070/day = 4× over. Most of that spend reviews HOLD bars.
- Parity (rule 4): an AI gate cannot be replayed by the backtester (news off; no recorded verdicts) — every pinned book becomes non-comparable, and model-version drift silently mutates the strategy.
- Edge: the engine already compresses 380 bars into 9 tuned indicators + regime + MTF validated over 5 years; an LLM's generic prior on raw candles won't beat fitted thresholds, and the margin it would filter is ~0.185R/trade. B-grade holds +67.8R at 51.4% win — a blunt veto is net-negative.
The middle ground (adopted)
- Enrich payloads (built today, see below) — near-zero cost, no rule-1 exposure.
- Log panel verdicts to signals.db (built today) — creates the "does OPPOSE predict losers?" dataset with zero behavior change.
- After 4–8 weeks: join logged verdicts to outcomes; simulate an OPPOSE-veto against the pinned book offline. Only then consider any gating, as a full rule-1 revalidation.
Build record (2026-08-15, owner-approved)
agent_panel.py- Technical persona payload now carries indicator values (units
legend added to the prompt) and ATR-normalized candles per TF:
{"ref_close", "atr", "bars": [[o,h,l,c]…]}oldest-first, each price as(price − ref_close) / atr, 1 decimal. Window: 20 bars primary / 10 per confirmation TF. - New config keys (validated in
app_config.py, inconfig.json.example):candles_enabled(default true),candle_bars_primary(20),candle_bars_confirm(10). - Panel-private provider singleton (
_dp, patchable in tests); TTL cache keeps fetches near-free; any failure → candles silently omitted, panel unaffected. - Prompt hardening: training data declared stale and OFF-LIMITS; "if a fact is not in the JSON, treat it as unknown"; thin evidence → neutral < 30 (unchanged); note cap 25→15 words, rationale 40→30 words (output-token savings).
attach_panelnow callssignal_log.log_panel_update(report)after every successful attach (fresh, exact-cache, and reuse-TTL paths).
- Technical persona payload now carries indicator values (units
legend added to the prompt) and ATR-normalized candles per TF:
signal_log.pylog()returns the rowid;log_signalstashes it on the report as_signal_log_rowid; newlog_panel_update()fills the 4 panel columns on that row post-hoc. No rowid (backtest replay, logging disabled, other process) → silent no-op. Fail-safe contract kept.
- Tests: 381 → 388 passed (7 new: candle normalization math, payload
values/candles, context windows + ATR gating + disable flag, attach
injection (technical persona only), prompt-contract assertions,
panel row update, rowid hand-off + no-op paths). Existing attach tests
stub
ap._dpto stay off the network. - Live dry test (
panel_payload_drytest_2026-08-15.py— script removed 2026-09-06, raw replies kept in the.jsonl— fabricated numbers only, 13 real DeepSeek requests total): 7/7 probes pass — coherent-bullish → support@70; bearish-vs-BUY → oppose@85; genuinely mixed → neutral@30; empty payload → neutral@20 (contract); no invented numbers in any note; all notes ≤ 15 words; synthesizer AGREE with a 21-word rationale. Raw wire log:backtest/research/panel_payload_drytest_2026-08-15.jsonl.- Notable: an early P3 variant with H1 SELL against a BUY brief got oppose@60 — correct per the prompt (higher-TF disagreement is a listed conflict), so the probe was tightened to a genuinely mixed scenario. Documented so nobody "fixes" the prompt for it later.
What this deliberately did NOT change
- No gate reads panel fields; the panel remains advisory-only.
- No backtest-path change (
analyze_from_dataframesuntouched; parity test still locks panel fields empty in replay). - No verdict-based filtering — that decision waits for the signals.db dataset (4–8 weeks) and an offline veto replay against the pinned book.