News-Sentiment war gesaettigt — Beweismengen-Gewichtung eingebaut

Anlass: Frage nach X-Anbindung fuer den Copiloten. Vorher geprueft, ob der
vorhandene Textstrom ueberhaupt traegt. Er trug nichts, weil der Score kaputt war.

calc_news_sentiment bildete ein REINES VERHAELTNIS (bull-bear)/total, ohne Bezug
zur Beweismenge. Eine einzige bullische Schlagzeile bei null baerischen ergab
+1,00. Diagnose:
- 173 von 173 Log-Eintraegen auf +/-1,00 gesaettigt (100 %)
- in 100 % der Faelle eine Seite exakt 0
- Median-Beweismenge nur 2,8 gewichtete Headlines
- ueber die DB (54.548 Zeilen): 63,9 % auf exakt +1,00
Real am 03.08.: +1,00 waehrend WTI -5,9 % stand. Der Wert trug ein Bit und
loeste im Entry-Check dauerhaft falsche "News-Konflikt"-Warnungen aus.
Die Keyword-Listen sind NICHT die Ursache (56/48, keine Ueberschneidung).

Fix: Shrinkage score = raw * total/(total+4,0). Die 4,0 ist begruendet, nicht
optimiert: bei der realen Median-Beweismenge 2,8 bleibt eine einseitige
3-Headline-Lage unter der 0,5-Warnschwelle, ~10 Headlines ergeben 0,71.
Zusaetzlich raw_score und evidence im Rueckgabewert, damit unterscheidbar wird
ob "ausgewogene Lage" oder "kaum Daten". Leer-Fall liefert dieselben Felder.
6 Szenarien getestet, live verifiziert: bei identischer Beweislage
(bull=1.8, bear=0.0) +1,00 -> +0,31.

EHRLICH: das repariert die Kalibrierung, nicht die Aussagekraft. Parallel
gemessen (Forward-Return 120 min, 38.213 Paare, 84 Tage, 2 Haelften, gegen die
Regime-Drift gelesen): der stark-bullische Bucket liegt in BEIDEN Haelften bei
null bis negativ, der baerische kippt das Vorzeichen. News-Sentiment ist nicht
robust praediktiv. Bleibt Kontext-Chip ohne Verdict-Stimme.

Methodik-Falle dokumentiert: der erste Messlauf nutzte candles_m1 (nur ~14 Tage)
-> bisect lieferte fuer aeltere Zeitstempel denselben Randwert, alle
Forward-Returns exakt 0,0000. Erst der Lauf mit MT5-M5-Bars plus
Deckungspruefung war gueltig.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-03 19:25:47 +02:00
co-authored by Claude Opus 5
parent fe7a1acb97
commit 94fbde1dba
2 changed files with 92 additions and 2 deletions
+39 -2
View File
@@ -39,6 +39,13 @@ NEWS_BEARISH_KW = {
}
# Wie viele gewichtete Headlines es braucht, damit der Score seinen Rohwert
# annähernd erreicht (Shrinkage-Konstante, s. Begründung in `calc_news_sentiment`).
# 4,0 gewählt, weil die reale Median-Beweismenge bei 2,8 liegt: damit bleibt eine
# typische, einseitige 3-Headline-Lage UNTER der 0,5-Warnschwelle des Entry-Checks.
_EVIDENCE_K = 4.0
def calc_news_sentiment(headlines: list, half_life_hours: float = 8.0) -> dict:
"""
Bewertet Headlines per Keyword-Matching mit altersgewichtetem Decay.
@@ -46,7 +53,10 @@ def calc_news_sentiment(headlines: list, half_life_hours: float = 8.0) -> dict:
"""
import time as _time
if not headlines:
return {"score": 0.0, "n_bull": 0.0, "n_bear": 0.0, "samples": []}
# Felder identisch zum Normalfall halten — sonst wirft ein Konsument, der
# `raw_score`/`evidence` liest, ausgerechnet im „keine News"-Fall.
return {"score": 0.0, "n_bull": 0.0, "n_bear": 0.0,
"raw_score": 0.0, "evidence": 0.0, "samples": []}
now = _time.time()
bull_w = bear_w = 0.0
@@ -67,10 +77,37 @@ def calc_news_sentiment(headlines: list, half_life_hours: float = 8.0) -> dict:
samples.append(("bear", h.get("title", "")[:70]))
total = bull_w + bear_w
score = 0.0 if total < 0.1 else (bull_w - bear_w) / total
raw = 0.0 if total < 0.1 else (bull_w - bear_w) / total
# ── BEWEISMENGEN-GEWICHTUNG (Fix 2026-08-03) ─────────────────────────────
# ⚠ Vorher war der Score ein REINES VERHÄLTNIS: `(bull-bear)/total`. Eine
# einzige bullische Schlagzeile bei null bearischen ergab damit +1,00 —
# maximale Überzeugung aus EINEM Treffer. Gemessen an 173 Log-Einträgen:
# **100 % standen auf ±1,00**, und in **100 %** der Fälle war eine Seite
# exakt 0; die Median-Beweismenge betrug **2,8** gewichtete Headlines.
# Über die gesamte DB (54.548 Zeilen) klebten **63,9 % auf exakt +1,00**.
# Real am 03.08.: Sentiment +1,00 („bull=1.8, bear=0.0"), während WTI
# 5,9 % stand. Der Wert trug damit faktisch EIN Bit (das Vorzeichen) und
# löste im Entry-Check dauerhaft falsche „News-Konflikt"-Warnungen aus.
# Korrektur: zur Null schrumpfen, solange die Beweislage dünn ist
# (klassische Shrinkage). Bei der Median-Beweismenge 2,8 wird aus 1,00
# noch 0,41 — unter der 0,5-Warnschwelle des Entry-Checks; erst ~10
# gewichtete Headlines ergeben 0,71.
# ⚠ EHRLICH: Das repariert die KALIBRIERUNG, nicht die Aussagekraft. Der
# Score bleibt das Vorzeichen weniger Keyword-Treffer, und dessen
# Prädiktivität ist gemessen NICHT robust (Forward-Return über 38k Paare,
# 2 Hälften: der bearische Bucket kippt das Vorzeichen, der stark-bullische
# liegt in BEIDEN Hälften bei null bis negativ). `k` ist eine begründete
# Setzung, kein optimierter Wert — es gibt hier nichts zu optimieren,
# solange das Signal selbst nichts vorhersagt.
conf = total / (total + _EVIDENCE_K) if total > 0 else 0.0
score = raw * conf
return {
"score": max(-1.0, min(1.0, score)),
"n_bull": round(bull_w, 1),
"n_bear": round(bear_w, 1),
# Rohwert + Beweismenge mitliefern: so ist im UI/Copilot erkennbar, ob
# ein schwacher Score „ausgewogene Nachrichtenlage" oder „kaum Daten" heißt.
"raw_score": round(max(-1.0, min(1.0, raw)), 3),
"evidence": round(total, 2),
"samples": samples[:5],
}