Automatischer Push: 14 Datei(en) geändert

- .vault-tools/plan-kopie/LIESMICH.md
- .vault-tools/plan-kopie/rot_nachweis.py
- .vault-tools/trading-plattform-pruefung/LIESMICH.md
- .vault-tools/trading-plattform-pruefung/dienste_stand.sh
- .vault-tools/trading-plattform-pruefung/ha_einrichten.py
- .vault-tools/trading-plattform-pruefung/hole_kerzen.py
- .vault-tools/trading-plattform-pruefung/neustart_echtlauf.py
- .vault-tools/trading-plattform-pruefung/ntfy_lesen.py
- .vault-tools/trading-plattform-pruefung/pause_echtlauf.py
- .vault-tools/trading-plattform-pruefung/wiederanlauf_probe.ps1
- AI/Regelwerk/codeworkflow.md
- AI/Regelwerk/projekte.md
- … und 2 weitere

Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5 committed 2026-09-21 04:00:01 +02:00
1 parent 395642c480
commit 246af19683
14 files changed
+964 -7

No files matched your search

@@ -0,0 +1,197 @@
"""Eigener Lauf mit echten Daten: Der Tagesverlust zaehlt nach einem Neustart weiter ab dem Tagesstart.
Koordinator-Pruefcode, nicht Teil des Repos. Echte IEX-Kerzen eines Tages (JSON aus dem lesenden
Abruf der Abschlusspruefung). Aus den Rohkerzen wird vorgerechnet, bei welcher Kerze die Grenze von
0,2 Prozent gegen den Tagesstart reisst und bei welcher gegen den Neustartkurs; dann laufen zwei
echte PaperSitzungen nacheinander (vor und nach dem Neustart) mit gemeinsamer Tagesstart-Datei,
dazu die Gegenprobe ohne Datei. Kein Netz: Meldungen gehen an einen mitschreibenden Transport.
Aufruf (WSL, aus dem Projektordner): python neustart_echtlauf.py <kerzen.json>
"""
import json
import sys
import tempfile
from datetime import datetime
from datetime import timedelta
from pathlib import Path
sys.path.insert(0, str(Path.cwd()))
from trading.killswitch.killswitch import lade_killswitch_config
from trading.paper.config import enge_limits
from trading.paper.config import lade_paper_config
from trading.paper.kerzen import FesteKerzenquelle
from trading.paper.kerzen import Kerze
from trading.paper.melder import Melder
from trading.paper.melder import MelderConfig
from trading.paper.probe import ProbeBroker
from trading.paper.sitzung import PaperSitzung
from trading.paper.status import lies_tagesstart
from trading.paper.uhr import ProbeUhr
from trading.registry.registry import Registry
from trading.risk.limits import lade_limits
STUECK = 20
ZIEL_KAPITAL = 12_676.20 # so klein, dass 20 Stueck die echte Grenze von 0,2 Prozent erreichen
class Protokoll:
def __init__(self):
self.zeilen = []
def schreibe(self, eintrag):
self.zeilen.append(dict(eintrag))
class Antwort:
status_code = 200
class Transport:
def __init__(self):
self.aufrufe = []
def __call__(self, url, *, data=None, headers=None, timeout=None):
self.aufrufe.append(headers["Title"])
return Antwort()
class Flach:
def predict(self, beobachtung, deterministic=False):
return 2, None
def lade(pfad):
roh = json.loads(Path(pfad).read_text(encoding="utf-8"))
return [
Kerze(zeit=datetime.fromisoformat(e["zeit"]), open=e["open"], high=e["high"],
low=e["low"], close=e["close"], volume=e["volume"])
for e in roh
]
def lauf(ordner, kerzen, *, beginn, position, cash, tagesstart, config, limits, ks_config):
ordner.mkdir(parents=True, exist_ok=True)
quelle = FesteKerzenquelle(kerzen)
uhr = ProbeUhr(beginn)
broker = ProbeBroker(kerzenquelle=quelle, uhr=uhr, instrument_id=config.instrument_id,
startkapital=cash, position=position)
registry = Registry(db_pfad=ordner / "registry.db", artefakt_wurzel=ordner / "artifacts")
registry.setze_killswitch_zustand("angehalten", ausloeser="neustart")
protokoll = Protokoll()
transport = Transport()
sitzung = PaperSitzung(
broker=broker, kerzenquelle=quelle, agent=Flach(), agent_beschreibung={"quelle": "echtlauf"},
registry=registry, limits=limits, killswitch_config=ks_config, config=config, uhr=uhr,
protokoll=protokoll, protokoll_pfad="echtlauf.jsonl", status_pfad=ordner / "status.json",
modus="probe", soll_abbrechen=lambda: False,
melder=Melder(config=MelderConfig(aktiv=True, ntfy_url="https://ntfy.sh",
thema="echtlauf-geht-nie-ins-netz", zeitlimit_s=3.0,
stille_nach_s=180.0), token="", transport=transport),
tagesstart_pfad=tagesstart,
)
code = sitzung.laufe()
return code, protokoll.zeilen, transport.aufrufe, broker, registry
def kerze_beim_grenzereignis(zeilen, index_nach_zeit):
"""Index der juengsten Kerze in dem Takt, in dem die Zeile grenzwert steht, oder None."""
for stelle, zeile in enumerate(zeilen):
if zeile.get("was") == "grenzwert":
takt = next(z for z in zeilen[stelle:] if z.get("art") == "takt")
return index_nach_zeit[takt["letzte_kerze"]], takt["schritt"]
return None, None
def erster_riss(schluesse, *, ab, cash, position, bezug, anteil):
for c in range(ab, len(schluesse)):
if (cash + position * schluesse[c] - bezug) / bezug < -anteil:
return c
return None
def main():
kerzen = lade(sys.argv[1])
schluesse = [k.close for k in kerzen]
index_nach_zeit = {k.zeit.isoformat(): i for i, k in enumerate(kerzen)}
config = lade_paper_config("config/paper.toml")
limits = enge_limits(lade_limits("config/risk.toml"), config)
ks_config = lade_killswitch_config("config/killswitch.toml")
anteil = limits.max_tagesverlust_anteil
tag = kerzen[0].zeit.date().isoformat()
print(f"{tag}: {len(kerzen)} Kerzen, Schluss Kerze 0 {schluesse[0]:.2f}, "
f"Tief {min(schluesse):.2f}, Hoch {max(schluesse):.2f}, Grenze {anteil}")
befunde = 0
for seite, position in (("long", STUECK), ("short", -STUECK)):
cash = ZIEL_KAPITAL - position * schluesse[0]
tagesstart_wert = cash + position * schluesse[0]
riss_tag = erster_riss(schluesse, ab=0, cash=cash, position=position,
bezug=tagesstart_wert, anteil=anteil)
if riss_tag is None or riss_tag < 3:
print(f"[{seite}] der Tag reisst die Grenze gegen den Tagesstart nicht (oder sofort): uebersprungen")
continue
# Neustart so, dass die Sitzung davor die Kerze des Risses nicht mehr sieht: Sie laeuft
# ueber kerzen[:r] und sieht die Kerzen 0 bis r-2. Die Sitzung danach beginnt in der
# Minute der Kerze r und sieht zuerst die Kerze r-1.
r = riss_tag
bezug_neu = cash + position * schluesse[r - 1]
riss_neu = erster_riss(schluesse, ab=r - 1, cash=cash, position=position,
bezug=bezug_neu, anteil=anteil)
print(f"[{seite}] Tagesstart {tagesstart_wert:.2f}; vorgerechnet: Riss gegen den Tagesstart "
f"bei Kerze {riss_tag}, gegen den Neustartkurs ({bezug_neu:.2f}) bei Kerze {riss_neu}")
with tempfile.TemporaryDirectory(prefix="neustart-echtlauf-") as wurzel:
wurzel = Path(wurzel)
datei = wurzel / "paper" / "tagesstart.json"
gemeinsam = dict(position=position, cash=cash, config=config, limits=limits,
ks_config=ks_config)
code1, zeilen1, _, _, reg1 = lauf(
wurzel / "vorher", kerzen[:r], beginn=kerzen[0].zeit - timedelta(minutes=1),
tagesstart=datei, **gemeinsam)
riss1, _ = kerze_beim_grenzereignis(zeilen1, index_nach_zeit)
gemerkt = lies_tagesstart(datei, handelstag=tag)
print(f"[{seite}] vor dem Neustart: Exit {code1}, Killswitch "
f"{reg1.killswitch_zustand().zustand}, Riss {riss1}, gemerkt {gemerkt}")
code2, zeilen2, titel2, broker2, reg2 = lauf(
wurzel / "nachher", kerzen, beginn=kerzen[r].zeit - timedelta(minutes=1),
tagesstart=datei, **gemeinsam)
riss2, schritt2 = kerze_beim_grenzereignis(zeilen2, index_nach_zeit)
takte2 = [z for z in zeilen2 if z.get("art") == "takt"]
# Vor dem Ende des with-Blocks lesen: Danach ist das Wegwerf-Register weg.
zustand2 = reg2.killswitch_zustand().zustand
position2 = sum(p.menge for p in broker2.positionen())
print(f"[{seite}] nach dem Neustart mit Datei: Startkapital {takte2[0]['startkapital']}, "
f"Riss bei Kerze {riss2} (Schritt {schritt2}), Exit {code2}, Killswitch "
f"{zustand2}, Position {position2}, Meldungen {titel2}")
code3, zeilen3, _, broker3, reg3 = lauf(
wurzel / "ohne", kerzen, beginn=kerzen[r].zeit - timedelta(minutes=1),
tagesstart=None, **gemeinsam)
riss3, _ = kerze_beim_grenzereignis(zeilen3, index_nach_zeit)
takte3 = [z for z in zeilen3 if z.get("art") == "takt"]
print(f"[{seite}] nach dem Neustart ohne Datei: Startkapital {takte3[0]['startkapital']}, "
f"Riss bei Kerze {riss3}, Exit {code3}, Killswitch {reg3.killswitch_zustand().zustand}")
ok = (
riss1 is None and code1 == 4
and gemerkt is not None and abs(gemerkt - tagesstart_wert) < 1e-9
and takte2[0]["startkapital"] == gemerkt
and riss2 == riss_tag
and zustand2 == "gesperrt"
and position2 == 0
and titel2 == ["Paper-Runner wartet", "Grenze verletzt", "Paper-Runner beendet"]
and abs(takte3[0]["startkapital"] - bezug_neu) < 1e-9
and riss3 == riss_neu
)
print(f"[{seite}] {'OK' if ok else 'BEFUND'}")
befunde += 0 if ok else 1
print(f"{befunde} Befund(e).")
return 2 if befunde else 0
if __name__ == "__main__":
sys.exit(main())