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:
1 parent
395642c480
commit
246af19683
14 files changed
+964
-7
No files matched your search
@@ -0,0 +1,184 @@
|
||||
"""Eigener Lauf mit echten Daten: Tagesverlust-Schutz in der Pause auf den IEX-Kerzen eines echten Tages.
|
||||
|
||||
Koordinator-Pruefcode, nicht Teil des Repos. Er rechnet aus den Rohkerzen vor, in welchem Takt die
|
||||
Grenze reissen muss, und haelt das gegen das Protokoll der echten PaperSitzung. Der einzige
|
||||
Netzzugriff holt lesend die Kerzen; Meldungen gehen an einen mitschreibenden Transport, nie ins Netz.
|
||||
Aufruf (aus dem Projektordner, in WSL): python pause_echtlauf.py JJJJ-MM-TT
|
||||
"""
|
||||
|
||||
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 HANDELSZONE
|
||||
from trading.paper.kerzen import AlpacaKerzenquelle
|
||||
from trading.paper.kerzen import FesteKerzenquelle
|
||||
from trading.paper.kerzen import handelsende
|
||||
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.uhr import ProbeUhr
|
||||
from trading.registry.registry import Registry
|
||||
from trading.risk.limits import lade_limits
|
||||
from dataclasses import replace
|
||||
|
||||
|
||||
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"], headers["Priority"], data.decode("utf-8")))
|
||||
return Antwort()
|
||||
|
||||
|
||||
class Flach:
|
||||
def predict(self, beobachtung, deterministic=False):
|
||||
return 2, None
|
||||
|
||||
|
||||
def lauf(kerzen, *, config, limits, killswitch_config, zustand, position):
|
||||
ordner = Path(tempfile.mkdtemp(prefix="pause-echtlauf-"))
|
||||
quelle = FesteKerzenquelle(kerzen)
|
||||
uhr = ProbeUhr(kerzen[0].zeit - timedelta(minutes=1))
|
||||
broker = ProbeBroker(
|
||||
kerzenquelle=quelle, uhr=uhr, instrument_id=config.instrument_id, position=position
|
||||
)
|
||||
registry = Registry(db_pfad=ordner / "registry.db", artefakt_wurzel=ordner / "artifacts")
|
||||
registry.setze_killswitch_zustand(zustand, ausloeser="echtlauf")
|
||||
protokoll = Protokoll()
|
||||
transport = Transport()
|
||||
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,
|
||||
)
|
||||
sitzung = PaperSitzung(
|
||||
broker=broker,
|
||||
kerzenquelle=quelle,
|
||||
agent=Flach(),
|
||||
agent_beschreibung={"quelle": "echtlauf"},
|
||||
registry=registry,
|
||||
limits=limits,
|
||||
killswitch_config=killswitch_config,
|
||||
config=config,
|
||||
uhr=uhr,
|
||||
protokoll=protokoll,
|
||||
protokoll_pfad="echtlauf.jsonl",
|
||||
status_pfad=ordner / "status.json",
|
||||
modus="probe",
|
||||
soll_abbrechen=lambda: False,
|
||||
melder=melder,
|
||||
)
|
||||
code = sitzung.laufe()
|
||||
return code, protokoll.zeilen, transport.aufrufe, broker, registry
|
||||
|
||||
|
||||
def main():
|
||||
datum = sys.argv[1]
|
||||
mitternacht = datetime.strptime(datum, "%Y-%m-%d").replace(tzinfo=HANDELSZONE)
|
||||
config = lade_paper_config("config/paper.toml")
|
||||
killswitch_config = lade_killswitch_config("config/killswitch.toml")
|
||||
quelle = AlpacaKerzenquelle.from_secrets("config/secrets.toml", feed=config.feed, timeout=10.0)
|
||||
kerzen = list(quelle.kerzen_des_tages(config.instrument_id, handelsende(mitternacht)))
|
||||
print(f"{datum}: {len(kerzen)} IEX-Kerzen in {config.instrument_id}")
|
||||
schluesse = [k.close for k in kerzen]
|
||||
print(f"Schluss erste Kerze {schluesse[0]:.2f}, Tief {min(schluesse):.2f}, Hoch {max(schluesse):.2f}")
|
||||
|
||||
stueck = 20
|
||||
cash = 100_000.0
|
||||
befunde = 0
|
||||
for seite, position in (("long", stueck), ("short", -stueck)):
|
||||
# Eigene Rechnung aus den Rohkerzen: Takt k (schritt k) sieht die Kerze k-1 als juengste
|
||||
# abgeschlossene. Startkapital ist der Kontowert im ersten Takt.
|
||||
start = cash + position * schluesse[0]
|
||||
schlechtester = min(cash + position * s for s in schluesse) - start
|
||||
if schlechtester >= 0:
|
||||
print(f"[{seite}] der Tag bringt dieser Seite keinen Verlust gegen den ersten Takt; uebersprungen")
|
||||
continue
|
||||
# Die Grenze liegt bei der Haelfte des groessten Verlusts dieses Tages: Der Tag reisst sie
|
||||
# wirklich, und nicht im ersten Takt.
|
||||
anteil = abs(schlechtester) / start / 2.0
|
||||
erwartet = None
|
||||
for k, s in enumerate(schluesse, start=1):
|
||||
if (cash + position * s - start) / start < -anteil:
|
||||
erwartet = k
|
||||
break
|
||||
limits = replace(enge_limits(lade_limits("config/risk.toml"), config), max_tagesverlust_anteil=anteil)
|
||||
code, zeilen, meldungen, broker, registry = lauf(
|
||||
kerzen, config=config, limits=limits, killswitch_config=killswitch_config,
|
||||
zustand="angehalten", position=position,
|
||||
)
|
||||
takte = [z for z in zeilen if z.get("art") == "takt"]
|
||||
grenz = [z for z in zeilen if z.get("was") == "grenzwert"]
|
||||
# Der Takt, in dem die Zeile grenzwert steht: die Taktzeile danach traegt seinen Schritt.
|
||||
stelle = zeilen.index(grenz[0]) if grenz else None
|
||||
schritt = next((z["schritt"] for z in zeilen[stelle:] if z.get("art") == "takt"), None) if grenz else None
|
||||
titel = [m[0] for m in meldungen]
|
||||
print(f"[{seite}] Startkapital {start:.2f}, Grenze {anteil:.6f} (= {anteil * start:.2f} USD), "
|
||||
f"erwarteter Takt {erwartet}, gemessen {schritt}")
|
||||
print(f"[{seite}] Zustand {registry.killswitch_zustand().zustand}, Position danach "
|
||||
f"{sum(p.menge for p in broker.positionen())}, Exit {code}, Meldungen {titel}")
|
||||
print(f"[{seite}] schliesse_alle: {broker.aufrufe.count('schliesse_alle')}, Takte {len(takte)}, "
|
||||
f"grenzwert-Zeilen {len(grenz)}")
|
||||
ok = (
|
||||
schritt == erwartet
|
||||
and registry.killswitch_zustand().zustand == "gesperrt"
|
||||
and broker.aufrufe.count("schliesse_alle") == 1
|
||||
and len(grenz) == 1
|
||||
and titel == ["Paper-Runner wartet", "Grenze verletzt", "Paper-Runner beendet"]
|
||||
and code == 0
|
||||
)
|
||||
print(f"[{seite}] {'OK' if ok else 'BEFUND'}")
|
||||
befunde += 0 if ok else 1
|
||||
|
||||
# Gegenprobe: dieselben Kerzen, angehalten, ohne Position -- genau eine Kontoabfrage, nichts geschlossen.
|
||||
limits = enge_limits(lade_limits("config/risk.toml"), config)
|
||||
code, zeilen, meldungen, broker, registry = lauf(
|
||||
kerzen, config=config, limits=limits, killswitch_config=killswitch_config,
|
||||
zustand="angehalten", position=0,
|
||||
)
|
||||
titel = [m[0] for m in meldungen]
|
||||
ok = (
|
||||
broker.aufrufe.count("konto") == 1
|
||||
and "schliesse_alle" not in broker.aufrufe
|
||||
and titel == ["Paper-Runner wartet", "Paper-Runner beendet"]
|
||||
and code == 0
|
||||
)
|
||||
print(f"[ohne Position] konto {broker.aufrufe.count('konto')}, Meldungen {titel}, Exit {code}: "
|
||||
f"{'OK' if ok else 'BEFUND'}")
|
||||
befunde += 0 if ok else 1
|
||||
|
||||
# Gegenprobe 2: aktiv mit dem echten Haltemuster aendert sich nicht -- das prueft die Probe des Skripts.
|
||||
print(f"{befunde} Befund(e).")
|
||||
return 2 if befunde else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user