KI-Copilot: zwei Prompt-Fehler behoben + kalibrierte Groessen ergaenzt
Diagnose zur Frage "Bias fast immer neutral": es ist NICHT der Prompt, es ist
der Provider. Bruch exakt am 20.07. (DeepSeek-Wechsel) - bis 19.07. war der
Copilot zu 42-90 % gerichtet, ab 21.07. nur noch zu 1,5-9,3 %. Der Prompt war
unveraendert. Und er hedged aus KORREKTER Schlussfolgerung: ~90 % WARTEN-Signal,
negativer Track-Record, plus die Prompt-Anweisung, genau dann NEUTRAL zu sagen.
Gemessen, ob mehr Meinung ueberhaupt Wert haette: KI-LONG n=286 +0,29 $ / 76 %
Treffer, KI-SHORT n=93 -0,02 $ / 55 %. ABER die 286 sind 20 Episoden an 5 Tagen
(Minutentakt-Logger), die 93 sind 16 Episoden -> effektiv n~20 gegen n~16, ein
Regime. Kein Beleg. Deshalb BEWUSST nicht am Prompt gedreht, um Meinung zu
erzwingen; Verdict-Gewicht unveraendert.
Zwei echte Prompt-Fehler behoben (Korrektheit, keine Meinungsmache):
- _SYS beschrieb das Wellen-Signal als "ATR-ZigZag" - es ist EMA12 vs EMA50
- _SYS nannte "TradersUnion-Tachos" als Input - TU ist seit 19.07. raus
Kalibrierte Groessen ergaenzt: _tool_market gab dem Copiloten NUR Wellen-Signal
und Session. Neu: p_break_target / p_break_stop (AUC 0,65 bzw. 0,68-0,72) und
cone (80%-Baender mit der REAL gemessenen Abdeckung 77 %), plus Lese-Anleitung
im Prompt (45-55 % explizit als Muenzwurf markiert).
Zwei Fallen an Live-Daten korrigiert: p_break steht bereits in PROZENT (die
zuerst gebaute x100-Umrechnung haette 300 erzeugt), und die Engine-Felder plus
die engine-Referenz im Agent existierten gar nicht - der Fail-safe haette still
nichts geliefert. Snapshot weist die Werte jetzt nur zu, rechnet sie nicht neu.
DeepSeek max_tokens 4000 -> 8000: das JSON brach regelmaessig mitten im Text ab
("Unterminated string"), real 2x in ~14 Zyklen = ~14 % stille Ausfaelle.
Kerzen/Muster bewusst NICHT eingespeist - die Lehrbuch-Lesart ist hier gemessen
invertiert (langer Koerper im Trend -0,132/-0,042; 2x Volumen -0,117/-0,204;
Muster-Kursziel trifft nur 13-38 %). Roh eingespeist wuerden sie schaden.
Verifiziert: Stub-Test von _tool_market (Werte unveraendert durchgereicht, nur
80%-Baender, ohne Engine kein Absturz); Prozess juenger als beide Dateien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
20cd04caa7
commit
00c78201fa
@@ -2539,6 +2539,63 @@ konstant; (d) es gab **keine Telemetrie über die Entscheidung selbst**.
|
||||
in ~20 min gefunden; die Trefferquote spricht für weitere. `analyze_divergence.py`
|
||||
regelmäßig laufen lassen (Kandidat für den Wochenreport).
|
||||
|
||||
## KI-Copilot: warum er neutral ist, und was 2026-08-02 repariert wurde
|
||||
|
||||
**User-Frage:** „Der Bias ist fast immer neutral — können wir konkretere Empfehlungen
|
||||
bekommen? Weitere Quellen (Kerzen, Muster)?"
|
||||
|
||||
**Diagnose — es ist NICHT der Prompt, es ist der Provider.** Der Bruch ist exakt
|
||||
datierbar (`recommendations.ai_sentiment` je Tag): bis **19.07.** war der Copilot zu
|
||||
**42–90 %** gerichtet, ab **21.07.** nur noch zu **1,5–9,3 %**. Dazwischen liegt der
|
||||
Wechsel auf **DeepSeek (2026-07-20)**. Der Prompt war die ganze Zeit unverändert.
|
||||
⚠ Und er hedged **aus korrekter Schlussfolgerung**, nicht aus Schwäche: die
|
||||
Begründungen sind konkret und zutreffend, und er bekommt zu ~90 % ein WARTEN-Signal,
|
||||
einen negativen Track-Record UND die ausdrückliche Prompt-Anweisung, genau dann
|
||||
NEUTRAL zu empfehlen. Er tut, was dort steht.
|
||||
|
||||
**Sind seine gerichteten Rufe etwas wert? Unentschieden — die Stichprobe trägt nicht.**
|
||||
Forward-Return über `verdict_votes` × `candles_m1`: KI-LONG n=286 → **+0,29 $** in
|
||||
Richtung / 76 % Treffer (60 min), KI-SHORT n=93 → −0,02 $ / 55 %. ⚠ **Das ist KEIN
|
||||
Beleg:** die 286 Stimmen sind **20 Episoden an 5 Tagen** (Minutentakt-Logger), die 93
|
||||
sind 16 Episoden — effektiv n≈20 gegen n≈16, alles in einem Regime. Dasselbe
|
||||
Cluster-Muster, an dem ORB gescheitert ist. **Deshalb bewusst NICHT am Prompt gedreht,
|
||||
um mehr Meinung zu erzwingen** — das würde eine korrekte Schlussfolgerung überschreiben
|
||||
und einer unbelegten Stimme mehr Gewicht geben. Verdict-Gewicht bleibt 1,0 bei
|
||||
gerichteter Aussage, 0 bei NEUTRAL.
|
||||
|
||||
**Zwei echte Prompt-FEHLER behoben (Korrektheit, keine Meinungsmache):**
|
||||
1. `_SYS` beschrieb das Wellen-Signal als **„ATR-ZigZag"** — es ist seit Langem
|
||||
**EMA12 vs. EMA50**; ZigZag wurde verworfen (~0 Edge). Das Modell missverstand also
|
||||
die Natur des Hauptsignals.
|
||||
2. `_SYS` nannte **„TradersUnion-Tachos"** als Input — TU ist seit 2026-07-19 gar nicht
|
||||
mehr im Copilot-Kontext (`_tool_market`). Das Modell wurde auf Daten hingewiesen,
|
||||
die es nie sieht.
|
||||
|
||||
**Die zwei kalibrierten Größen ergänzt.** Befund beim Nachsehen: `_tool_market` gab dem
|
||||
Copiloten **nur Wellen-Signal + Session** — er war das schlechtest informierte Modul im
|
||||
System und bekam ausgerechnet die einzigen beiden Komponenten nicht, deren Kalibrierung
|
||||
out-of-sample nachgewiesen ist. Neu im Kontext: **`p_break_target` / `p_break_stop`**
|
||||
(AUC 0,65 bzw. 0,68–0,72) und **`cone`** (80 %-Bänder, mit der REAL gemessenen Abdeckung
|
||||
77 %, nicht dem Nennwert). Dazu eine Lese-Anleitung im Prompt — ohne sie liest ein LLM
|
||||
„P(break) 30 %" als schwaches Signal statt als klare Aussage „Level hält mit 70 %";
|
||||
45–55 % ist ausdrücklich als Münzwurf markiert.
|
||||
⚠ **Zwei Fallen beim Bau, beide an Live-Daten korrigiert:** (a) `p_break` steht im
|
||||
Snapshot **bereits in Prozent** (3 = 3 %, deckungsgleich mit der Chart-Zeile „97 %
|
||||
Abprall") — die zuerst gebaute ×100-Umrechnung hätte 300 erzeugt; (b) die Engine-Felder
|
||||
`_last_sr_close_hint`/`_last_stop_approach`/`_last_cone` und die `engine`-Referenz im
|
||||
Agent **existierten gar nicht** — der Fail-safe hätte still nichts geliefert. Beides
|
||||
verdrahtet, der Snapshot rechnet die Werte ohnehin und weist sie jetzt nur zu (kein
|
||||
zweites Rechnen, anders als beim Konsens-Pfeil). Mit Stub-Test verifiziert (Werte
|
||||
unverändert durchgereicht, nur 80 %-Bänder, ohne Engine kein Absturz).
|
||||
|
||||
⚠ **Kerzen/Muster bewusst NICHT eingespeist.** Ein LLM wendet darauf die Lehrbuch-Lesart
|
||||
an — und die ist hier gemessen **invertiert**: „langer Körper im Trend = Fortsetzung"
|
||||
→ −0,132/−0,042 (Klimax, läuft dagegen); „2× Volumen = echt" → −0,117/−0,204 (monoton
|
||||
falsch herum); Muster-Kursziel trifft nur zu 13–38 %. Roh eingespeist würden sie den
|
||||
Copiloten aktiv verschlechtern. Offener Kandidat: sie MIT der gemessenen Lesart
|
||||
mitgeben („langer Docht oben; gemessen läuft der Kurs danach eher GEGEN die Kerze") —
|
||||
noch nicht gebaut.
|
||||
|
||||
## Gesamtprüfung 2026-08-02 (nach `docs/review-prompt.md`)
|
||||
|
||||
Erster vollständiger Durchlauf des Review-Prompts. **Keine Strategie-Änderung** — alle
|
||||
|
||||
+83
-7
@@ -50,8 +50,16 @@ _OPENAI_URL = "https://api.openai.com/v1/chat/completions"
|
||||
_SYS = (
|
||||
"Du bist ein nüchterner Trading-Copilot für WTI-Rohöl (Intraday-Scalping). "
|
||||
"Du bekommst den aktuellen Systemzustand eines automatischen Handels-Bots "
|
||||
"(Wellen-Signal als ATR-ZigZag, TradersUnion-Tachos, offene Position, "
|
||||
"Konto, Track-Record, News). Beurteile die Lage knapp und ehrlich — keine "
|
||||
# ⚠ KORRIGIERT 2026-08-02: hier stand „Wellen-Signal als ATR-ZigZag,
|
||||
# TradersUnion-Tachos". BEIDES war falsch und hat das Modell in die Irre
|
||||
# geführt: (a) die Richtung kommt seit Langem aus EMA12 vs. EMA50 — ZigZag
|
||||
# wurde verworfen, weil es ~0 Edge hatte; (b) TradersUnion ist seit
|
||||
# 2026-07-19 gar nicht mehr im Copilot-Kontext. Das Modell wurde also auf
|
||||
# Daten hingewiesen, die es nie sieht, und hat die Natur des Hauptsignals
|
||||
# missverstanden.
|
||||
"(Wellen-Signal aus EMA12 gegen EMA50 mit Totband, Durchbruch-"
|
||||
"Wahrscheinlichkeit am nächsten S/R-Level, erwartete Kursspanne, offene "
|
||||
"Position, Konto, Track-Record, News). Beurteile die Lage knapp und ehrlich — keine "
|
||||
"Garantien, keine Hype-Sprache. Wenn die Signale widersprüchlich oder dünn "
|
||||
"sind, sag das klar und empfiehl NEUTRAL/abwarten.\n\n"
|
||||
"WICHTIG — nutze den Track-Record zur Kalibrierung: 'current_setup_history' "
|
||||
@@ -62,6 +70,28 @@ _SYS = (
|
||||
"Live-Signal stark wirkt. Hat es sich bewährt, darf das deine Konfidenz "
|
||||
"stützen. Folge dem Signal nicht blind gegen eine klar negative Historie; "
|
||||
"nenne den Bezug in 'reasoning' kurz (z.B. Setup-Trefferquote).\n\n"
|
||||
# ── Die zwei EINZIGEN kalibriert geprüften Größen des Projekts ────────────
|
||||
# ⚠ Ergänzt 2026-08-02. Der Copilot sah bis dahin NUR das Wellen-Signal und
|
||||
# die Session — er war damit das schlechtest informierte Modul im System und
|
||||
# bekam ausgerechnet die beiden Komponenten nicht, deren Treffsicherheit
|
||||
# out-of-sample nachgewiesen ist. Die Lese-Anleitung ist Pflicht: ohne sie
|
||||
# deutet ein LLM „P(break) 30 %" als schwaches Signal statt als klare
|
||||
# Aussage „das Level hält mit 70 %".
|
||||
"ZWEI KALIBRIERTE GRÖSSEN — nutze sie bevorzugt, sie sind die einzigen "
|
||||
"Zahlen hier mit nachgewiesener Treffsicherheit:\n"
|
||||
"1) 'p_break_target' / 'p_break_stop' = Wahrscheinlichkeit in PROZENT, dass "
|
||||
"der Kurs das jeweilige S/R-Level DURCHBRICHT (Ziel-Level in Trade-Richtung, "
|
||||
"Stop-Level dagegen). Out-of-sample geprüft (AUC 0,65 bzw. 0,68–0,72). "
|
||||
"Lies sie richtig: 30 % Durchbruch heißt 70 % ABPRALL — das ist eine starke "
|
||||
"Aussage, kein schwaches Signal. Ein Abprall am Ziel-Level deckelt den "
|
||||
"Ertrag (spricht gegen einen Einstieg dorthin); ein Durchbruch am "
|
||||
"Stop-Level ist eine Warnung für eine offene Position. Werte zwischen 45 "
|
||||
"und 55 % sind ein Münzwurf — dann NICHT darauf argumentieren.\n"
|
||||
"2) 'cone' = erwartete Kursspanne in 30/60/120 Minuten (80 %-Band). "
|
||||
"Gemessene Abdeckung real 77 %, nicht 80 % — das Band ist eher etwas ZU "
|
||||
"ENG. Es sagt NICHTS über die Richtung, nur über die plausible Weite. "
|
||||
"Nutze es, um Ziele/Stops auf Realismus zu prüfen: liegt ein Kursziel "
|
||||
"außerhalb des 120-min-Bands, ist es für einen Intraday-Trade unrealistisch.\n\n"
|
||||
"Wähle außerdem den Analyse-Timeframe 'timeframe' für die Wellen-Erkennung "
|
||||
"(M1|M5|M15|M30|H1): M1/M5 bei enger Range und ruhigem Markt (Scalping, "
|
||||
"kleine Wellen); höhere TF (M15/M30/H1) bei klarem Trend, hoher Volatilität "
|
||||
@@ -136,7 +166,11 @@ _SCHEMA = {
|
||||
|
||||
class TradingAgent:
|
||||
def __init__(self, cfg, *, data, trader, trail, tu, wave, history,
|
||||
news, elliott=None):
|
||||
news, elliott=None, engine=None):
|
||||
# `engine` (optional, 2026-08-02): Zugriff auf die kalibrierten Größen
|
||||
# P(break) und Kegel, die die Engine ohnehin je Snapshot berechnet.
|
||||
# Optional gehalten, damit Tests/Standalone-Nutzung ohne Engine laufen.
|
||||
self.engine = engine
|
||||
self.cfg = cfg
|
||||
self.data = data
|
||||
self.trader = trader
|
||||
@@ -221,7 +255,7 @@ class TradingAgent:
|
||||
# TU entfernt (2026-07-08): war nach der TU-Entfernung aus Empfehlung/Verdict
|
||||
# die letzte Hintertür — der Copilot ist eine Verdict-Stimme, TU floss so
|
||||
# indirekt wieder ein (lagging, nicht backtestbar).
|
||||
return {
|
||||
out = {
|
||||
"wave_signal": wsig.get("signal"),
|
||||
"wave_confidence": wsig.get("conf_pct"),
|
||||
"wave_setup": wsig.get("setup"),
|
||||
@@ -231,6 +265,42 @@ class TradingAgent:
|
||||
"wave_reasons": wsig.get("reasons", [])[:4],
|
||||
"session": _agent_session(),
|
||||
}
|
||||
# ── Die kalibrierten Größen dazugeben (2026-08-02) ────────────────────
|
||||
# ⚠ Bis hierher sah der Copilot NUR Wellen-Signal + Session. Er war damit
|
||||
# das schlechtest informierte Modul im System — und ihm fehlten
|
||||
# ausgerechnet die beiden Komponenten, deren Kalibrierung out-of-sample
|
||||
# nachgewiesen ist (P(break) AUC 0,65 / Kegel 77 % reale Abdeckung).
|
||||
# Fail-safe: fehlt die Engine oder wirft ein Teil, bleibt das Feld weg —
|
||||
# ein fehlender Kontext darf die Analyse nie verhindern.
|
||||
eng = getattr(self, "engine", None)
|
||||
if eng is not None:
|
||||
try:
|
||||
# ⚠ `p_break` steht im Snapshot BEREITS in Prozent (3 = 3 %,
|
||||
# deckungsgleich mit der Chart-Zeile „97 % Abprall") — NICHT
|
||||
# nochmal ×100 rechnen. Beim Bau zuerst falsch angenommen und
|
||||
# an den Live-Daten korrigiert.
|
||||
hint = getattr(eng, "_last_sr_close_hint", None) or {}
|
||||
if hint.get("p_break") is not None:
|
||||
out["p_break_target"] = round(float(hint["p_break"]))
|
||||
out["p_break_target_level"] = hint.get("level")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
stop = getattr(eng, "_last_stop_approach", None) or {}
|
||||
if stop.get("p_break") is not None:
|
||||
out["p_break_stop"] = round(float(stop["p_break"]))
|
||||
out["p_break_stop_level"] = stop.get("level")
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
cone = getattr(eng, "_last_cone", None) or {}
|
||||
lv = [l for l in (cone.get("levels") or []) if l.get("band") == 80]
|
||||
if lv:
|
||||
out["cone"] = [{"minutes": l["minutes"], "lo": l["lo"], "hi": l["hi"],
|
||||
"abdeckung_real_pct": l.get("coverage")} for l in lv]
|
||||
except Exception:
|
||||
pass
|
||||
return out
|
||||
|
||||
def _tool_position(self) -> dict:
|
||||
ps = self.trader.snapshot()
|
||||
@@ -461,8 +531,14 @@ class TradingAgent:
|
||||
|
||||
def _call_deepseek(self, prompt: str) -> str:
|
||||
# DeepSeek (OpenAI-kompatibel, api.deepseek.com). deepseek-v4-flash ist ein
|
||||
# Reasoning-Modell (content nach reasoning_content) → max_tokens großzügig
|
||||
# (4000), sonst content leer. temperature 0.3 ok. CJK-Drift-Schutz im Aufrufer.
|
||||
# Reasoning-Modell (content nach reasoning_content) → max_tokens großzügig,
|
||||
# sonst content leer. temperature 0.3 ok. CJK-Drift-Schutz im Aufrufer.
|
||||
# ⚠ 4000 → 8000 (2026-08-02): bei 4000 brach das JSON regelmäßig MITTEN im
|
||||
# Text ab („Unterminated string …"), real 2× in ~14 Zyklen ≈ 14 % stille
|
||||
# Ausfälle — das Reasoning frisst das Budget, bevor der content fertig ist.
|
||||
# Ein Fehlschlag verwirft die Analyse und lässt die ALTE Empfehlung stehen,
|
||||
# ist also von außen kaum sichtbar. Mit dem ergänzten P(break)/Kegel-Kontext
|
||||
# wird die Antwort eher länger, nicht kürzer.
|
||||
import requests
|
||||
dc = self.cfg["deepseek"] if self.cfg.has_section("deepseek") else {}
|
||||
base = (dc.get("base_url") or "https://api.deepseek.com").rstrip("/")
|
||||
@@ -474,7 +550,7 @@ class TradingAgent:
|
||||
json={"model": model,
|
||||
"messages": [{"role": "system", "content": _SYS},
|
||||
{"role": "user", "content": prompt}],
|
||||
"temperature": 0.3, "max_tokens": 4000},
|
||||
"temperature": 0.3, "max_tokens": 8000},
|
||||
timeout=120)
|
||||
resp.raise_for_status()
|
||||
return (resp.json()["choices"][0]["message"].get("content") or "").strip()
|
||||
|
||||
+18
-4
@@ -237,7 +237,14 @@ class TradingEngine:
|
||||
self.agent = TradingAgent(
|
||||
self.cfg, data=self.data, trader=self.trader, trail=self.trail,
|
||||
tu=self.tu, wave=self.wave, history=self.history,
|
||||
news=self.news, elliott=self.elliott)
|
||||
news=self.news, elliott=self.elliott, engine=self)
|
||||
# Letzte kalibrierte Größen für den Copiloten (2026-08-02). Der Snapshot
|
||||
# rechnet sie ohnehin je Zyklus — hier nur festhalten, damit `agent.
|
||||
# _tool_market` sie mitgeben kann, OHNE sie ein zweites Mal zu rechnen
|
||||
# (derselbe Fehler wie beim Konsens-Pfeil, der ein zweites `_verdict()` zog).
|
||||
self._last_sr_close_hint: dict | None = None
|
||||
self._last_stop_approach: dict | None = None
|
||||
self._last_cone: dict | None = None
|
||||
# Tägliche WTI-Intraday-Level per Web-Recherche (z.ai/GLM + web_search).
|
||||
# Nur Kontext → S/R + Anzeige, NICHT Handelsrichtung.
|
||||
zc = self.cfg["zai"] if self.cfg.has_section("zai") else {}
|
||||
@@ -3049,6 +3056,15 @@ class TradingEngine:
|
||||
}
|
||||
sr_close_hint = self._sr_close_hint(market, position, wave_snap)
|
||||
stop_approach = self._stop_approach_hint(market, position, wave_snap)
|
||||
_cone = _cone_build(
|
||||
(market or {}).get("bid"),
|
||||
float(((wave_snap or {}).get("pb_feats") or {}).get("atr") or 0.0))
|
||||
# Für den KI-Copiloten festhalten (2026-08-02) — er bekam bis dahin nur
|
||||
# das Wellen-Signal und sah ausgerechnet die beiden kalibrierten Größen
|
||||
# des Projekts nicht. Hier nur ZUWEISEN, nicht neu rechnen.
|
||||
self._last_sr_close_hint = sr_close_hint
|
||||
self._last_stop_approach = stop_approach
|
||||
self._last_cone = _cone
|
||||
# einmal holen — Verdict-Stimme UND Snapshot-Feld nutzen dasselbe Objekt
|
||||
_pat_snap = self.patterns.snapshot()
|
||||
# ── Marktstatus + Hyperliquid-Kurs (User 2026-08-01) ─────────────────
|
||||
@@ -3184,9 +3200,7 @@ class TradingEngine:
|
||||
"candle_anat": self.candle_anat.snapshot(),
|
||||
# Wahrscheinlichkeits-Kegel (Streuung, NICHT Richtung) — out-of-sample
|
||||
# kalibriert (`analyze_cone.py`). Reine Anzeige.
|
||||
"cone": _cone_build(
|
||||
(market or {}).get("bid"),
|
||||
float(((wave_snap or {}).get("pb_feats") or {}).get("atr") or 0.0)),
|
||||
"cone": _cone,
|
||||
"stats_compact": {"week": self._compact_stats("week"),
|
||||
"all": self._compact_stats("all")},
|
||||
"close_alert_count": self._close_alert_count,
|
||||
|
||||
Reference in New Issue
Block a user