Automatischer Push: 46 Datei(en) geändert
- .claude/agents/planer.md - .claude/skills/plan-executer-pruefung/SKILL.md - .claude/workflows/plankritik.js - .claude/workflows/planung.js - .claude/workflows/pruefung.js - .claude/workflows/verbrauch.js - .claude/workflows/welle-mit-pruefung.js - .vault-tools/plan-kopie/LIESMICH.md - .vault-tools/plan-kopie/plan_zeichen.py - .vault-tools/plan-kopie/rot_nachweis.py - .vault-tools/trading-plattform-pruefung/LIESMICH.md - .vault-tools/trading-plattform-pruefung/abendkontrolle.py - … und 34 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
67d674cd0b
commit
ea278dc24f
46 files changed
+8403
-80
No files matched your search
@@ -0,0 +1,157 @@
|
||||
"""Lesende Probe gegen das IBKR-Paper-Konto ueber ein laufendes IB Gateway: verbindet, liest, trennt.
|
||||
|
||||
Koordinator-Werkzeug, nicht fuers Repo. Pascal am 2026-09-24: Zugang zum IBKR-Paper-Konto vorbereiten,
|
||||
Gateway als Container in WSL (ghcr.io/gnzsnz/ib-gateway, wie DockerizedIBGatewayConfig in NautilusTrader
|
||||
1.231.0), erster Test heute nach 22:00. Diese Probe handelt nie: Das Gateway laeuft mit READ_ONLY_API=yes,
|
||||
und sie ruft keine Order-Methode.
|
||||
|
||||
Entscheidungsliste (Frage -> Herkunft der Antwort):
|
||||
1. Erreicht die Probe das Gateway? Quelle: EClient.connect/isConnected (nautilus-ibapi 10.45.1),
|
||||
Port 4004 = Paper aussen (gateway.py:43-45 in Nautilus 1.231.0).
|
||||
2. Welches Konto ist angemeldet? Quelle: managedAccounts-Rueckruf. Ausgegeben: die ersten zwei
|
||||
Zeichen und die Laenge (Paper-Konten beginnen im Doku-Beispiel
|
||||
mit "DU"; das ist ein Beispiel, keine Zusage).
|
||||
3. Welche Kontoart, welche Waehrung? Quelle: reqAccountSummary mit den Tags AccountType, Currency;
|
||||
Betraege (NetLiquidation, TotalCashValue) nur als "vorhanden".
|
||||
4. Wie viele Positionen? Quelle: reqPositions -> positionEnd; nur die Zahl.
|
||||
5. Kommt ein verzoegerter Kurs? Quelle: reqMarketDataType(4, DELAYED_FROZEN; ohne Abo laut
|
||||
Nautilus-Doku) und reqMktData(AAPL, SMART, USD, Snapshot).
|
||||
Ausgegeben: welche Kursart kam (Tick-Typ), nie der Kurs.
|
||||
6. Welche Fehler meldet das Gateway? Quelle: error-Rueckruf; Code und gekuerzter Text (ohne Zahlen
|
||||
ueber vier Stellen).
|
||||
Eine fehlende Antwort nach ZEITLIMIT_S ist ein Befund, kein Schweigen. Exit 0 alles beantwortet,
|
||||
1 mindestens ein Befund, 3 keine Verbindung, 2 abgebrochen.
|
||||
Aufruf (WSL, eigenes Venv): ~/venvs/ibkr-probe/bin/python ibkr_probe.py [--port 4004] [--client-id 991]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
ZEITLIMIT_S = 30
|
||||
NUR_LESEN_TAGS = "AccountType,Currency,NetLiquidation,TotalCashValue"
|
||||
BETRAGS_TAGS = {"NetLiquidation", "TotalCashValue"}
|
||||
|
||||
|
||||
def maskiere(text: str) -> str:
|
||||
return re.sub(r"\d{5,}|\d+\.\d+", "[x]", str(text))
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
zerleger = argparse.ArgumentParser(allow_abbrev=False)
|
||||
zerleger.add_argument("--host", default="127.0.0.1")
|
||||
zerleger.add_argument("--port", type=int, default=4004)
|
||||
zerleger.add_argument("--client-id", type=int, default=991)
|
||||
argumente = zerleger.parse_args(argv)
|
||||
|
||||
from ibapi.client import EClient
|
||||
from ibapi.contract import Contract
|
||||
from ibapi.wrapper import EWrapper
|
||||
|
||||
class Probe(EWrapper, EClient):
|
||||
def __init__(self):
|
||||
EWrapper.__init__(self)
|
||||
EClient.__init__(self, self)
|
||||
self.ereignisse = {name: threading.Event() for name in ("konto", "zusammenfassung", "positionen", "kurs", "zeit")}
|
||||
self.konten = ""
|
||||
self.zusammenfassung = {}
|
||||
self.positionen = 0
|
||||
self.kurs_arten = set()
|
||||
self.fehler = []
|
||||
self.zeit = None
|
||||
|
||||
def managedAccounts(self, accountsList):
|
||||
self.konten = accountsList
|
||||
self.ereignisse["konto"].set()
|
||||
|
||||
def currentTime(self, zeit):
|
||||
self.zeit = zeit
|
||||
self.ereignisse["zeit"].set()
|
||||
|
||||
def accountSummary(self, reqId, account, tag, value, currency):
|
||||
self.zusammenfassung[tag] = "vorhanden" if tag in BETRAGS_TAGS else value
|
||||
|
||||
def accountSummaryEnd(self, reqId):
|
||||
self.ereignisse["zusammenfassung"].set()
|
||||
|
||||
def position(self, account, contract, position, avgCost):
|
||||
self.positionen += 1
|
||||
|
||||
def positionEnd(self):
|
||||
self.ereignisse["positionen"].set()
|
||||
|
||||
def tickPrice(self, reqId, tickType, price, attrib):
|
||||
if price and price > 0:
|
||||
self.kurs_arten.add(tickType)
|
||||
|
||||
def tickSnapshotEnd(self, reqId):
|
||||
self.ereignisse["kurs"].set()
|
||||
|
||||
def error(self, reqId, *rest):
|
||||
# Die Signatur wechselt zwischen ibapi-Versionen: nautilus-ibapi 10.45.1 schickt vor dem
|
||||
# Code einen Zeitstempel in Millisekunden (gemessen am 2026-09-24: 1790261211141 vor 502).
|
||||
# Fehlercodes sind klein; der Zeitstempel ist es nicht.
|
||||
werte = [r for r in rest if isinstance(r, (int, str))]
|
||||
code = next((w for w in werte if isinstance(w, int) and 0 < w < 100000), None)
|
||||
text = next((w for w in werte if isinstance(w, str) and len(w) > 3), "")
|
||||
self.fehler.append((reqId, code, maskiere(text)[:160]))
|
||||
|
||||
probe = Probe()
|
||||
befunde = []
|
||||
try:
|
||||
probe.connect(argumente.host, argumente.port, argumente.client_id)
|
||||
faden = threading.Thread(target=probe.run, daemon=True)
|
||||
faden.start()
|
||||
beginn = time.time()
|
||||
while not probe.isConnected() and time.time() - beginn < 10:
|
||||
time.sleep(0.2)
|
||||
if not probe.isConnected():
|
||||
print(f"BEFUND: keine Verbindung zu {argumente.host}:{argumente.port}")
|
||||
for f in probe.fehler[:5]:
|
||||
print(f" fehler {f}")
|
||||
return 3
|
||||
print(f"verbunden mit {argumente.host}:{argumente.port}, Serverversion {probe.serverVersion()}")
|
||||
|
||||
probe.reqCurrentTime()
|
||||
probe.reqAccountSummary(9001, "All", NUR_LESEN_TAGS)
|
||||
probe.reqPositions()
|
||||
probe.reqMarketDataType(4)
|
||||
aapl = Contract()
|
||||
aapl.symbol, aapl.secType, aapl.exchange, aapl.currency = "AAPL", "STK", "SMART", "USD"
|
||||
probe.reqMktData(9002, aapl, "", True, False, [])
|
||||
|
||||
for name, ereignis in probe.ereignisse.items():
|
||||
if not ereignis.wait(ZEITLIMIT_S):
|
||||
befunde.append(f"keine Antwort auf {name} in {ZEITLIMIT_S} s")
|
||||
|
||||
konten = [k for k in probe.konten.split(",") if k]
|
||||
print(f"konten: {len(konten)}; " + ", ".join(f"{k[:2]}… ({len(k)} Zeichen)" for k in konten))
|
||||
print(f"serverzeit: {'da' if probe.zeit else 'fehlt'}")
|
||||
print(f"kontoart: {probe.zusammenfassung.get('AccountType')}, waehrung: {probe.zusammenfassung.get('Currency')}, "
|
||||
f"nettoliquidation: {probe.zusammenfassung.get('NetLiquidation', 'fehlt')}, bargeld: {probe.zusammenfassung.get('TotalCashValue', 'fehlt')}")
|
||||
print(f"positionen: {probe.positionen}")
|
||||
print(f"kursarten AAPL (Tick-Typen mit Preis): {sorted(probe.kurs_arten) or 'keine'}")
|
||||
for f in probe.fehler:
|
||||
print(f"gateway meldet: reqId {f[0]}, code {f[1]}: {f[2]}")
|
||||
if not konten:
|
||||
befunde.append("kein Konto gemeldet")
|
||||
if not probe.kurs_arten:
|
||||
befunde.append("kein verzoegerter Kurs fuer AAPL")
|
||||
probe.cancelAccountSummary(9001)
|
||||
probe.cancelPositions()
|
||||
except Exception as ausnahme:
|
||||
print(f"BEFUND: Probe abgebrochen: {type(ausnahme).__name__}: {maskiere(ausnahme)[:200]}")
|
||||
return 2
|
||||
finally:
|
||||
if probe.isConnected():
|
||||
probe.disconnect()
|
||||
for befund in befunde:
|
||||
print(f"BEFUND: {befund}")
|
||||
print(f"ERGEBNIS: {len(befunde)} Befund(e)")
|
||||
return 1 if befunde else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user