Wirtschaftskalender-Blackout + Konsolidierungs-Box beschriftet
core/econ_calendar.py (NEU): High-Impact-News-Fenster als Handels-Sperre. Quelle Forex Factory JSON (kostenlos, kein Key, mit impact-Feld), beide Wochen (thisweek endet Sa, Markt oeffnet So 23:00 -> sonst blinder Fleck), 1x/h im Daemon-Thread, dedupliziert. Filter: High+USD ODER Oil/OPEC-Titel. WICHTIG - nicht gemessen: Events sind zu selten fuer eine 2-Stichproben- Aussage. Das ist die Handbuch-Regel, kein belegter Edge. Deshalb bewusst asymmetrisch: sperrt NUR die autonomen Entries (Auto-Squeeze + Auto-Signal, ohne Dedup-Marker -> feuert nach dem Fenster nach), in der Entry-Checkliste nur "warn" statt "fail". Manuelle Orders bleiben frei. Der gemessene EIA-Blackout (Mi 15:30-16:30) bleibt unabhaengig aktiv und greift auch ohne Netz. Verifiziert: Live-Fetch (EIA korrekt Mi 16:30 Berlin, FOMC/GDP/PCE erkannt) + 4 synthetische Szenarien (5 min vor/nach Event, 50 min davor, disabled). MQL5 v1.30: graue Konsolidierungs-Box beschriftet (User-Frage "was bedeutet die graue Box") - sichtbares Label an der Oberkante, rechtsbuendig wie die uebrigen: "Konsolidierung (10-Bar-Spanne)" bzw. bei Kompression "Konsolidierung - komprimiert, Ausbruch steht bevor". 0 Fehler kompiliert. Frontend v=122: #cal-note in der Meldungen-Karte (laufendes Fenster bzw. Vorwarnung <=60 min). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7e0d2a4563
commit
a6f370ac02
@@ -173,6 +173,15 @@ class TradingEngine:
|
||||
url=_t2.get("hl_walls_url", "http://localhost:8001/api/walls"),
|
||||
enabled=_t2.get("export_hl_walls", "true").lower() == "true")
|
||||
self.candles = CandleLogger(HISTORY_DB_FILE) # M1-Candle-Log (Daten-Sammlung)
|
||||
# Wirtschaftskalender (Handbuch-Regel „keine Trades vor High-Impact-News").
|
||||
# ⚠ NICHT gemessen (Events zu selten für 2-Stichproben) → sperrt nur die
|
||||
# AUTONOMEN Entries + warnt in der Checkliste, blockt keine manuelle Order.
|
||||
# Der gemessene EIA-Blackout (Mi 15:30–16:30) bleibt unabhängig davon aktiv.
|
||||
from core.econ_calendar import EconCalendar
|
||||
self.calendar = EconCalendar(
|
||||
before_min=int(_t2.get("news_blackout_before_min", "15")),
|
||||
after_min =int(_t2.get("news_blackout_after_min", "15")),
|
||||
enabled =_t2.get("news_blackout", "true").lower() == "true")
|
||||
# News: eigener Fetch-Loop (versorgt UI /api/news UND Agent-Kontext)
|
||||
tcfg = self.cfg["translation"]
|
||||
translator = NewsTranslator(
|
||||
@@ -265,6 +274,7 @@ class TradingEngine:
|
||||
self._sr_close_dir = None # → Cooldown gegen Gleichrichtungs-Roundtrip
|
||||
self._open_pos_dir = None # Richtung der aktuell offenen Position (LONG/SHORT)
|
||||
self._pos_close_ts = 0.0 # Zeit + Richtung des letzten Positions-Close (JEDE
|
||||
self._news_block_logged = None # Dedup für die News-Blackout-Logzeile
|
||||
self._pos_close_dir = None # Art) → Squeeze-Wiedereinstiegs-Sperre (gegen die
|
||||
# Open→Instant-Close-Kaskade bei zu engem Notfall-Stop)
|
||||
self._sr_close_min_gain_armed_ticket = None # Ticket, für das die %-Schwelle schon
|
||||
@@ -533,6 +543,10 @@ class TradingEngine:
|
||||
except Exception as ce: log.warning(f"candles.log: {ce}")
|
||||
try: self._evaluate_pbreak_predictions() # ~alle 5 min, self-throttled
|
||||
except Exception as pe: log.warning(f"pbreak_predictions: {pe}")
|
||||
# Wirtschaftskalender (self-throttled ~1 h, außerhalb des mt5_lock
|
||||
# unkritisch — reiner HTTP-Fetch mit Timeout)
|
||||
try: self.calendar.maybe_refresh()
|
||||
except Exception as ke: log.warning(f"calendar.refresh: {ke}")
|
||||
self._run_analysis()
|
||||
except Exception as e:
|
||||
log.error(f"_trend_loop: {e}", exc_info=True)
|
||||
@@ -944,6 +958,18 @@ class TradingEngine:
|
||||
# aktiv. `auto_squeeze_skip_night=false` schaltet aus.
|
||||
if self._squeeze_skip_night and datetime.now(_BERLIN).hour in _SQUEEZE_NIGHT:
|
||||
return
|
||||
# ── News-Blackout (Handbuch-Regel, NICHT gemessen): 15 min um ein
|
||||
# High-Impact-Event (FOMC/NFP/CPI/EIA/OPEC) kein autonomer Entry. Setzt
|
||||
# KEINEN Dedup-Marker → nach dem Fenster feuert der Ausbruch, falls noch
|
||||
# aktiv. Gilt nur für den Bot; manuelle Orders bleiben frei.
|
||||
_bl = self.calendar.blackout()
|
||||
if _bl:
|
||||
if self._news_block_logged != _bl["title"]:
|
||||
self._news_block_logged = _bl["title"]
|
||||
log.info(f"📅 News-Blackout — kein Auto-Entry: {_bl['title']} "
|
||||
f"({_bl['impact']}, in {_bl['minutes']:.0f} min)")
|
||||
return
|
||||
self._news_block_logged = None
|
||||
|
||||
ps = self.trader.snapshot()
|
||||
ticket = ps.get("ticket")
|
||||
@@ -1061,6 +1087,9 @@ class TradingEngine:
|
||||
# damit das Signal nach 08:00 noch feuern kann, falls es dann noch steht.
|
||||
if self._auto_signal_skip_night and datetime.now(_BERLIN).hour in _SQUEEZE_NIGHT:
|
||||
return
|
||||
# News-Blackout (s. `_check_auto_squeeze`) — kein Dedup-Marker.
|
||||
if self.calendar.blackout():
|
||||
return
|
||||
ps = self.trader.snapshot()
|
||||
if ps.get("ticket"):
|
||||
# Position offen: läuft sie in Signalrichtung, gilt die Episode als
|
||||
@@ -2391,6 +2420,25 @@ class TradingEngine:
|
||||
"gemessen: 0–7 Uhr = Kostenfalle (Spread ÷ dünner ATR)")
|
||||
except Exception:
|
||||
pass
|
||||
# ── 8. Wirtschaftskalender (Handbuch: „keine Trades vor High-Impact-News")
|
||||
# ⚠ NICHT gemessen (zu wenige Events für eine 2-Stichproben-Aussage) —
|
||||
# deshalb hier nur „warn", nicht „fail": es soll den Verdict nicht allein
|
||||
# auf `stop` kippen. Der Bot selbst pausiert im Fenster (autonome Entries).
|
||||
try:
|
||||
bl = self.calendar.blackout()
|
||||
if bl:
|
||||
m = bl["minutes"]
|
||||
when = (f"in {m:.0f} min" if m > 0 else f"vor {abs(m):.0f} min")
|
||||
add("event", "warn", f"News-Fenster: {bl['title']} ({when})",
|
||||
"Handbuch-Regel: 15 min um High-Impact-Events nicht handeln "
|
||||
"(im Projekt nicht gegengemessen)")
|
||||
else:
|
||||
nx = self.calendar.next_event()
|
||||
if nx and nx["minutes"] <= 90:
|
||||
add("event", "ok",
|
||||
f"Nächstes Event: {nx['title']} in {nx['minutes']} min", "")
|
||||
except Exception:
|
||||
pass
|
||||
fails = sum(1 for x in items if x["state"] == "fail")
|
||||
warns = sum(1 for x in items if x["state"] == "warn")
|
||||
return {"side": want, "items": items, "fails": fails, "warns": warns,
|
||||
@@ -2689,6 +2737,9 @@ class TradingEngine:
|
||||
"adverse15_atr": self._adverse15_atr,
|
||||
"adverse15_count": self._adverse15_count,
|
||||
"last_trades": last_trades,
|
||||
# Wirtschaftskalender: {blackout, next, enabled, n} — Anzeige + Grund,
|
||||
# warum der Bot gerade keine autonome Order eröffnet.
|
||||
"calendar": self.calendar.snapshot(),
|
||||
}
|
||||
# ── Entry-Checkliste je Richtung (User-Vorgabe 2026-07-30: „blende einen
|
||||
# Hinweis ein, wenn die Checkliste gegen meine Eröffnung ist"). Wird im
|
||||
|
||||
Reference in New Issue
Block a user