Initial commit: Oil Trading Bot (MT5, WTI)
Headless FastAPI-Backend (server.py + core/engine.py) mit Mobile-PWA (web/), Strategie-/Backtest-Suite und Doku. Secrets, DB, Logs und Laufzeit-State sind via .gitignore ausgeschlossen; Config-Vorlage: oil_widget_config.ini.example. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
core/market_hours.py — Börsen-Session-Status (Frankfurt / US)
|
||||
==============================================================
|
||||
Liefert den aktuellen Session-Status für die Empfehlungs-Logik, das UI und
|
||||
den KI-Agenten. Zeiten in **Berlin-Lokalzeit** (DST-sicher via zoneinfo):
|
||||
|
||||
DE (Frankfurt): 09:00 – 17:30
|
||||
US (Wall St.): 15:00 – 22:00 (Overlap 15:00–17:30 = höchste Liquidität)
|
||||
|
||||
Nutzen:
|
||||
• "just_opened": die ersten _CAUTION_MIN nach einem Open (Whipsaw-Vorsicht)
|
||||
• "active": welche Sessions gerade offen sind (Liquiditäts-Bonus)
|
||||
• "next_open": nächster Open + Minuten bis dahin (UI-Countdown)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
from datetime import datetime, time, timezone
|
||||
|
||||
try:
|
||||
from zoneinfo import ZoneInfo
|
||||
_BERLIN = ZoneInfo("Europe/Berlin")
|
||||
except Exception:
|
||||
_BERLIN = None
|
||||
|
||||
# Sessions in Berlin-Lokalzeit (Start, Ende)
|
||||
_SESSIONS = {
|
||||
"DE": (time(9, 0), time(17, 30)),
|
||||
"US": (time(15, 0), time(22, 0)),
|
||||
}
|
||||
_CAUTION_MIN = 20 # erste N Min nach einem Open = volatil → Vorsicht
|
||||
|
||||
|
||||
def _mins(t: time) -> int:
|
||||
return t.hour * 60 + t.minute
|
||||
|
||||
|
||||
def session_state(now: datetime | None = None) -> dict:
|
||||
now = now or datetime.now(timezone.utc)
|
||||
loc = now.astimezone(_BERLIN) if _BERLIN else now
|
||||
is_weekend = loc.weekday() >= 5 # 5=Sa, 6=So
|
||||
nowm = loc.hour * 60 + loc.minute
|
||||
|
||||
active, since, just_opened, next_open = [], {}, None, None
|
||||
for name, (o, c) in _SESSIONS.items():
|
||||
om, cm = _mins(o), _mins(c)
|
||||
open_now = (not is_weekend) and (om <= nowm <= cm)
|
||||
if open_now:
|
||||
active.append(name)
|
||||
if (not is_weekend) and nowm >= om:
|
||||
since[name] = nowm - om
|
||||
if open_now and (nowm - om) < _CAUTION_MIN:
|
||||
just_opened = name
|
||||
else:
|
||||
since[name] = None
|
||||
if (not is_weekend) and nowm < om:
|
||||
d = om - nowm
|
||||
if next_open is None or d < next_open["in_min"]:
|
||||
next_open = {"name": name, "in_min": d}
|
||||
|
||||
if is_weekend:
|
||||
phase = "Wochenende"
|
||||
elif just_opened:
|
||||
phase = f"{just_opened}-Open frisch (volatil)"
|
||||
elif active:
|
||||
phase = " + ".join(active) + "-Session offen"
|
||||
elif next_open:
|
||||
phase = f"vor {next_open['name']}-Open"
|
||||
else:
|
||||
phase = "außerhalb DE/US"
|
||||
|
||||
return {
|
||||
"active": active,
|
||||
"since_open_min": since,
|
||||
"just_opened": just_opened,
|
||||
"next_open": next_open,
|
||||
"phase": phase,
|
||||
"weekend": is_weekend,
|
||||
}
|
||||
Reference in New Issue
Block a user