TF-Churn-Fix: Breakout-Bestaetigung ueberlebt TF-Wechsel + Verweildauer
Auslöser: User-Frage warum der Bot die Rally am 31.07. (82,5 -> 84,4 in 2 h) nicht gehandelt hat. Befund: in dem Fenster 99x WARTEN und 1x SHORT - und dieser SHORT wurde am Tief autonom eroeffnet (AUTOSIG, -26,31 EUR, per adverse15 geschlossen). Den LONG machte der User um 11:56 von Hand. Der WARTEN-Anteil ist strukturell zu hoch: Backtest-Erwartung (backtest_dist.py) ~43 % Live letzte 24 h 93,2 % Live letzte 7 Tage 88,0 % Live gesamt (93.599 Zeilen) 78,6 % MECHANISMUS (im Log belegt): _choose_tf setzt den Score einer TF hart auf 0,0, sobald sie ueberdehnt ist - genau das passiert M5 IM Trend. Real am 31.07.: M5 sprang zwischen 0,00 und 1,93, die TF wechselte 33x am Tag (Median-Abstand 5 min). Die 1,2x-Hysterese ist dagegen wirkungslos. Und set_timeframe() verwarf bei JEDEM Wechsel die laufende Breakout- Bestaetigung und verankerte sie beim aktuellen Kurs neu. Bei k=0,3 und ATR_M30~0,53 braucht sie ~0,16 $, der Kurs lief ~0,08 $ je 5 min -> die Bestaetigung konnte rechnerisch nie fertig werden. Gleicher Fehlertyp wie beim P(break)-Modell am selben Tag: backtest_breakout.py hat k=0,3 auf einer FESTEN Zeitebene validiert; live wandert sie - die Bedingung, unter der die Messung gilt, existiert im Betrieb nicht. FIX 1 (wave_rec.set_timeframe): _pend wird nicht mehr zurueckgesetzt. Der Anker gehoert zum Signal, nicht zur Zeitebene; bei Richtungswechsel verankert _confirm_breakout ohnehin neu. FIX 2 (engine._tf_loop): Mindest-Verweildauer [trading] tf_min_dwell_s=900. Verifiziert mit synthetischen Szenarien: Fix 1 - Bestaetigung ueberlebt M5->M30 und feuert LONG, Richtungswechsel verankert korrekt neu; Fix 2 - an der ECHTEN Score-Folge vom 31.07. nachgespielt: 4 Wechsel -> 2. NICHT backtestbar (Backtests laufen auf fester TF, das Churning existiert dort nicht). Begruendung ist "stellt die Bedingung her, unter der die Messung gilt", nicht "gemessen besser". Erfolgskontrolle = WARTEN-Anteil muss sich Richtung ~43 % bewegen. Bewusst nicht behoben: der 0,0-Einbruch des Scores bei Ueberdehnung selbst - das waere eine Aenderung der TF-Bewertung und damit messpflichtig. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0944b1c13a
commit
0208101707
+23
-1
@@ -258,6 +258,14 @@ class TradingEngine:
|
||||
if self._tf_max not in _ATR_TF_MAP:
|
||||
self._tf_max = "H1"
|
||||
self._wave_tf_label = "M5"
|
||||
# Mindest-Verweildauer der TF-Heuristik (Fix 2026-07-31, s. `_tf_loop`):
|
||||
# verhindert das Hin-und-Her, wenn der Score einer TF im Trend auf 0,0 fällt
|
||||
# (Überdehnung) und nach dem Rücksetzer zurückspringt. 0 = aus.
|
||||
try:
|
||||
self._tf_min_dwell_s = float(self.cfg["trading"].get("tf_min_dwell_s", "900"))
|
||||
except (TypeError, ValueError):
|
||||
self._tf_min_dwell_s = 900.0
|
||||
self._tf_last_switch = 0.0
|
||||
self._last_rec: dict = {}
|
||||
self._agent_fail_cnt = 0
|
||||
self._close_alert_ts = 0.0 # letzter CLOSE-Telegram-Push (5-min-Cooldown)
|
||||
@@ -1523,9 +1531,23 @@ class TradingEngine:
|
||||
if self.data.symbol:
|
||||
best, scores = self._choose_tf(self.data.symbol)
|
||||
cur = self._wave_tf_label
|
||||
# ── Mindest-Verweildauer (Fix 2026-07-31) ────────────────────
|
||||
# Die 1,2×-Hysterese unten ist WIRKUNGSLOS, sobald der Score der
|
||||
# aktuellen TF auf exakt 0,0 fällt — und genau das tut er, sobald
|
||||
# sie überdehnt ist (`_choose_tf`). Im Trend kippt M5 dadurch
|
||||
# regelmäßig raus (Score 0,00) und nach dem nächsten Rücksetzer
|
||||
# wieder rein (1,93): real 33 Wechsel/Tag, Median-Abstand 5 min.
|
||||
# Zusammen mit dem `_pend`-Reset (dort gefixt) hielt das die
|
||||
# Breakout-Bestätigung dauerhaft offen → 93 % WARTEN live gegen
|
||||
# ~43 % Backtest-Erwartung. Die Verweildauer gibt einer TF Zeit,
|
||||
# ihr Signal überhaupt fertig zu bestätigen. 0 = aus.
|
||||
now = time.time()
|
||||
dwell_ok = (self._tf_min_dwell_s <= 0 or
|
||||
(now - self._tf_last_switch) >= self._tf_min_dwell_s)
|
||||
# Hysterese: nur wechseln, wenn klar besser (kein Flattern)
|
||||
if best and best != cur and \
|
||||
if dwell_ok and best and best != cur and \
|
||||
scores.get(best, 0) > scores.get(cur, 0) * 1.2:
|
||||
self._tf_last_switch = now
|
||||
self._apply_wave_tf(best, source=f"Heuristik {scores}")
|
||||
except Exception as e:
|
||||
log.warning(f"_tf_loop: {e}")
|
||||
|
||||
Reference in New Issue
Block a user