# 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.XAU` where present, else `score`. - Outcome buckets use the representative row's `news_sentiment`; macro stance from `panel_takes_json`. - Everything above reproduces from the DB alone; nothing depends on live feeds. ## Decisions - `sentiment_affects_signals` and `event_risk_caps_signals` stay `False`. 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_seconds` 12 h → 4 h with undated items dropped, and `get_fundamental_context` drops 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_sentiment` rows 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 `--news` and the persona scorecard at ~300 outcomes. - Refuted hypothesis, do not re-propose without new data: "news sentiment leads price".