Files
ForexBot2/backtest/research/news_sentiment_value_2026-09-04.md
T
Harwinder Sekhon a3d7594f0c news: beginner-language summary, 4 h headline window, drop neutral headlines; --news log report
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.

Claude-Session: https://claude.ai/code/session_0169BgFtWjp1SEAa5392Q9jH
2026-09-04 15:14:58 -04:00

8.2 KiB
Raw Blame History

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".