Verdict from the 3-week signal log (backtest/research/news_sentiment_value_2026-09-04.md): news_sentiment is a trailing-24h echo of price (r +0.67 with the past day, +0.03 with the next; most bullish 2 h after the 08-25 top, bearish 44 h later; most bearish at the 09-02 low, bullish 16 h later). Per-headline LLM labels are correct; the 12 h pool and the averaging are what destroy the information. Both gating flags stay False. - news_engine: _LLM_SUMMARY_SYSTEM rewritten for someone who has never traded (no acronyms, two worked examples); summary payload newest-first with the platform's own gold direction; reply cap 300 -> 400 chars. - config: headline_max_age_seconds 43200 -> 14400; RSS items without a parsable pubDate are dropped (they used to be stamped "now" and never aged out). - news_engine.get_fundamental_context drops headlines whose every relevant score is inside +-0.1 (_is_neutral_headline) before the per-currency mean; news_sentiment from this date is a mean over directional <=4 h headlines (log comparisons split here). - static: dashboard copy for the 4 h pool; news_summary.php prompt gets the same style rule. - analyze_signal_log: --news (under --stored-candles) prints sentiment lead/lag with split halves, sentiment at the window high/low with flip lags, and outcome buckets by direction x news x macro stance; LogRow gains news_sentiment/event_risk; _row_index shared. - tests: 406 -> 414 (age window, undated drop, neutral filter, summary prompt/payload/cap, --news helpers, load_rows round-trip); RSS fixtures now carry a pubDate. - docs: user/beginner guides, CLAUDE.md, deploy README. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0169BgFtWjp1SEAa5392Q9jH
8.2 KiB
News sentiment vs. price — does the news score carry information? — 2026-09-04
Source evidence: logs/signals.db 2026-08-13 → 2026-09-03 (14,683 rows, 13,947 with
a price; candle snapshots from 08-18, full-shape headline blobs from 08-19), read with
mode=ro. A continuous H1/M15/M5 candle stream was rebuilt from the stored 14-bar
candles_json snapshots (H1: 293 bars, 08-18 05:00 → 09-03 22:00). Headlines were
resolved through news_snapshots (4,054 distinct headlines, 2,024 gold-tagged).
Outcomes are the repo's own advisory model (advisory_r_multiple on the 144 RESOLVED
rows, R at $0.65). One-year context from the Oanda H1 parquet
(backtest/cache/oanda_XAU_USD_1h_20250814_20260814.parquet). No backtest: the
backtester runs with news off, and no headline history exists beyond these 3 weeks, so a
replay cannot test news. The same check reruns with
python analyze_signal_log.py --stored-candles --news.
Question asked by the owner: the log reads bullish most of the time — what did the indicators and the news say when gold fell, and is news scoring a waste?
Findings
The numeric news score is a rear-view mirror. news_sentiment (= mean gold-headline
score − mean dollar-headline score over the last 12 h) tracks what gold did over the
previous day and says nothing stable about the next one.
| hourly mean sentiment vs. gold return (n≈293 h) | Pearson |
|---|---|
| past 24 h | +0.67 |
| past 8 h / 4 h / 1 h | +0.32 / +0.20 / +0.07 |
| next 1 h / 4 h / 8 h / 24 h | +0.05 / +0.10 / +0.21 / +0.14 |
| next 8 h, first half (08-18→25) / second half (08-26→09-03) | −0.37 / +0.12 |
| next 24 h, first half / second half | −0.12 / −0.09 |
| daily mean vs same-day / previous-day / next-day move | +0.39 / +0.37 / +0.03 |
| change in sentiment vs next 4 h / 8 h / 24 h | −0.09 / −0.03 / −0.01 |
Partial correlation with the next 8 h after removing the past-24 h return is +0.26 pooled but flips sign between halves. Alternative aggregates (gold-specific score only, dropping price-report headlines, only headlines < 2 h old) show the same shape: past-24 h correlation 0.59–0.82, no stable forward correlation. A naive "hold in the direction of sentiment" rule made +$272 over 279 hours vs +$120 buy-and-hold, but trading the change in sentiment lost −$200: the gain is trend persistence (sentiment ≈ trailing return), not news.
Why the log reads bullish. 62% of rows have sentiment ≥ +0.1, 21% ≤ −0.1. Component means: gold headlines +0.158, dollar headlines +0.048. The window was a 16% August rally, so the average headline was bullish because price had been rising. Of the gold-tagged headlines, 42% merely restate the chart (price-up reports 489, mean +0.37; price-down 366, mean −0.18) and 18% are long-horizon forecast pieces ("$5,400 target", "$50,000/oz"; 60% positive, 21% of all positive score mass). The per-headline LLM labels are correct — every regex-flagged "mis-score" was a correctly scored "yields fall → gold up" headline — which matches the 78/78 golden set of 2026-08-06. The averaging, the 12 h window and the headline universe are what destroy the information.
What news and indicators said on the way down (top $4,697 on 08-25 00:00 UTC, low $4,283 on 09-02 03:00; an 8.8% drop in a week):
| moment | price | news sentiment | engine (M15) |
|---|---|---|---|
| top, 08-25 00:00 | 4,697 | +0.30, rising to +0.44 two hours after the top (window high) | ranging, composite +5.9; 9 BUY setups that day: −7.4R, 11% win |
| 08-26/27 | −$60 | still +0.15…+0.37; first bearish hour 08-26 20:00, 44 h after the top, price already −$102 | trending_down 134/138 rows, composite −4.6/−7.8; SELL setups 0/3 on 08-27 |
| 08-28 Warsh speech 13:00–16:00 | −$122 in 3 h, −$140 day | +0.24…+0.30 overnight, 0.00 at 13:00, −0.17 only at 16:00; summary all morning: "a hawkish tone could pressure gold, dovish hints may lift it" | composite −6.2; SELL setups 4/4, +3.3R |
| 09-01 | −$128 day | −0.04…−0.20, "gold under pressure" all day (correct, describing the move) | trending_down 275 rows, composite −12.2, 51 SELL rows; SELL setups 7/7, +3.5R |
| low, 09-02 03:00 | 4,283 | −0.16 at the low, −0.19 six hours later; first bullish hour 09-02 19:00, 16 h after the low, price already +$108 | SELL setups at the bottom 0/2, −2.05R |
Sentiment peaked at the top and troughed at the bottom. The engine also lagged (it is trend-following) but flipped regime and composite within hours; its SELL setups won on the trend days and lost at the turns, the same as its BUYs.
Outcomes (144 RESOLVED, +37.8R): BUY 121 / +43.2R / 70% win; SELL 23 / −5.4R / 48%.
| direction × sentiment bucket (≥+0.15 bull, ≤−0.05 bear) | n | R | mean | win |
|---|---|---|---|---|
| BUY, bullish news | 81 | +32.0 | +0.40 | 73% |
| BUY, neutral | 34 | +10.6 | +0.31 | 68% |
| BUY, bearish news | 6 | +0.65 | +0.11 | 50% |
| SELL, bearish news | 14 | +0.13 | +0.01 | 71% |
| SELL, neutral | 5 | −1.46 | −0.29 | 20% |
| SELL, bullish news | 4 | −4.08 | −1.02 | 0% |
The splits are the trend confound again ("BUY with bullish news" = "BUY in an uptrend"). Cleanest counter-example: 08-25, the most bullish news day of the drop period (+0.25), 9 BUYs at the top lost −7.4R. corr(sentiment, R): BUY +0.10, SELL −0.45 (n=23).
Macro persona (reads the same headlines plus high-impact events): macro=support 51 BUYs → +32.6R, 90% win; macro=neutral 16 → −3.2R, 38%. Within bullish-sentiment BUYs: support 42 → +25.2R (88%) vs neutral 7 → −2.9R (29%). On 08-25 all 7 panel rows were macro=neutral ("awaiting PCE, mixed") while the numeric aggregate said +0.25 bullish. Confounded with regime (64 of 84 supports were trending_up) and n≈16 on the informative side. Not enough to promote or demote; the standing rule (re-score at ~300 outcomes) applies.
Event risk cannot be evaluated here: only 4 event windows in the sample (08-19 17:00, 08-26 12:00, 08-28 13:00, 09-01 13:00), none produced a setup. The standing evidence remains the 2026-07-11 audit (event gates would cost +4.6…+7.2R/yr).
One-year context: daily moves ≤ −$100 happened 17 times in the year to 2026-08-14; 15 of 47 rolling 3-week windows contained a ≥ 8% drawdown. This sample is one ordinary rally-then-drop regime.
Simulated 4 h window + neutral drop (the change shipped with this note, on 11,136 full-shape scans): never leaves a scan empty (median 19 directional headlines, 8 gold-specific; worst hour 02:00 UTC has < 3 gold headlines 28% of the time), the aggregate correlates 0.78 with the old one and becomes bolder (share |score| ≥ 0.3 rises 17% → 41%), with the same lack of forward information (next-24 h corr +0.20; next-8 h halves −0.22 / −0.10).
Method
- Hourly join convention: a scan in hour H (boundary + 10 s) sees the bar closed at H, i.e. bar H's open; forward returns run from bar H's open, past returns end there.
- Correlations are Pearson on hourly means (no scipy); halves split at 2026-08-26 00:00.
- Headline classes by regex on the title (price-up / price-down / forecast-style); the
gold-specific score is
score_by_currency.XAUwhere present, elsescore. - Outcome buckets use the representative row's
news_sentiment; macro stance frompanel_takes_json. - Everything above reproduces from the DB alone; nothing depends on live feeds.
Decisions
sentiment_affects_signalsandevent_risk_caps_signalsstayFalse. No gate is built in either direction (follow or fade): 3 weeks, one regime.- The headline labelling is not the problem; the pool and the averaging are. Shipped the
same day:
headline_max_age_seconds12 h → 4 h with undated items dropped, andget_fundamental_contextdrops headlines whose every relevant score is inside ±0.1 (_is_neutral_headline). The market-summary prompt now writes for someone who has never traded, sends headlines newest-first with the platform's own gold direction, and caps at 400 chars.news_sentimentrows from the deploy date onward are a mean over directional ≤ 4 h headlines — log comparisons must split at that date. - Keep the macro persona running and logging; re-check with
--newsand the persona scorecard at ~300 outcomes. - Refuted hypothesis, do not re-propose without new data: "news sentiment leads price".