"""Handabruf: Wie alt ist die juengste Minutenkerze, die Alpaca per REST fuer AAPL liefert? Koordinator-Werkzeug zu Teil 10, nicht fuers Repo. Beantwortet vor dem ersten Paper-Lauf die ungemessene Frage, ob der IEX-Feed eine gerade abgeschlossene Minute innerhalb von Sekunden liefert (der Runner wartet `kerze_verzoegerung_s` und versucht es `kerze_versuche` mal) oder ob die Kerzen verzoegert kommen. Sinnvoll, solange gehandelt wird -- Vorboerse ab 10:00 Uhr Berliner Zeit (04:00 ET), regulaer ab 15:30 Uhr. Nachts zeigt es nur die letzte Kerze des Vortags; dann belegt es nur, dass Schluessel und Abruf funktionieren. Liest config/secrets.toml aus dem Projektordner, gibt die Schluessel nie aus. Aufruf aus dem Projektordner (WSL, Venv): ~/venvs/trading/bin/python /mnt/d/Claude/.vault-tools/trading-plattform-pruefung/handabruf_kerzen.py [feed] feed: iex (Standard) oder sip. """ import sys import tomllib from datetime import UTC, datetime, timedelta from pathlib import Path import requests DATEN_URL = "https://data.alpaca.markets" FRISCH_S = 120 # eine abgeschlossene Minute plus die Wartezeit des Runners def main() -> int: feed = sys.argv[1] if len(sys.argv) > 1 else "iex" secrets = Path("config/secrets.toml") if not secrets.exists(): print(f"VORAUSSETZUNG FEHLT: {secrets} (aus dem Projektordner starten)") return 3 werte = tomllib.loads(secrets.read_text(encoding="utf-8"))["alpaca"] kopf = {"APCA-API-KEY-ID": werte["key_id"], "APCA-API-SECRET-KEY": werte["secret_key"]} jetzt = datetime.now(UTC).replace(microsecond=0) parameter = { "symbols": "AAPL", "timeframe": "1Min", "start": (jetzt - timedelta(hours=36)).strftime("%Y-%m-%dT%H:%M:%SZ"), "end": jetzt.strftime("%Y-%m-%dT%H:%M:%SZ"), "feed": feed, "adjustment": "raw", "limit": 10_000, "sort": "desc", } gesendet = datetime.now(UTC) antwort = requests.get(f"{DATEN_URL}/v2/stocks/bars", headers=kopf, params=parameter, timeout=30) dauer_s = (datetime.now(UTC) - gesendet).total_seconds() print(f"Feed {feed}, Anfrage bis {jetzt.isoformat()} (jetzt), Antwort {antwort.status_code} in {dauer_s:.2f} s") if antwort.status_code >= 400: print("Antworttext:", antwort.text[:300]) return 2 kerzen = (antwort.json().get("bars") or {}).get("AAPL") or [] if not kerzen: print("keine Kerze in den letzten 36 Stunden -- das ist bei einem laufenden Handelstag ein Befund") return 2 juengste = kerzen[0] zeit = datetime.fromisoformat(juengste["t"].replace("Z", "+00:00")) alter_s = (jetzt - zeit).total_seconds() berlin = zeit.astimezone().strftime("%H:%M:%S %Z") print(f"{len(kerzen)} Kerzen; juengste Kerze beginnt {zeit.isoformat()} ({berlin}), " f"Alter {alter_s:.0f} s, Schluss {juengste['c']}, Volumen {juengste['v']}") if alter_s <= FRISCH_S: print(f"FRISCH: innerhalb von {FRISCH_S} s -- der Runner bekommt seine Kerze rechtzeitig") else: print(f"ALT: {alter_s / 60:.1f} min -- entweder wird gerade nicht gehandelt, oder der Feed kommt verzoegert; " "waehrend der Handelszeit ist das ein Befund") return 0 if __name__ == "__main__": sys.exit(main())