Zum Inhalt springen
KI angewandt

Das Trade-Republic-Depot in Home Assistant

· ca. 7 Minuten

Illustration auf dunklem Grund: Links ein Telefon mit einem Kerzenchart, rechts eine hell leuchtende Dashboard-Fläche mit Diagrammen. Dazwischen führt eine leuchtende Leitung über einen runden Knopf, auf den ein Finger drückt, und weiter durch ein kleines Haussymbol.

Das Ziel war bescheiden: Das Depot bei Trade Republic soll in Home Assistant stehen, auf derselben Übersichtsseite wie Fixkosten und Budgets. Kein Handel, keine Orders — nur der Blick, den die App der Bank auch gibt.

Der unangenehme Teil steht gleich am Anfang: Es gibt dafür keine Schnittstelle. Nicht „eine schlecht dokumentierte", sondern keine.

Drei Wege, und nur einer führt zum Bestand

Die europäische Zahlungsschnittstelle regelt, was ihr Name sagt: Zahlungskonten. Ein Depot fällt nicht darunter. Der CSV-Export in der App ist ordentlich, aber Handarbeit — und er enthält Transaktionen, keinen Bestand: Stückzahlen ergeben sich erst durch Aufsummieren, aktuelle Kurse stehen gar nicht darin.

Geblieben ist pytr, ein quelloffener, inoffizieller Client, der die private Schnittstelle der App spricht. Er liefert Positionen mit Stückzahl und Einstand, den letzten Kurs, das Barguthaben und die ganze Zeitleiste. Eine Order-Funktion hat er nicht: Das Werkzeug kann lesen, nicht handeln.

Drei Dinge, die man dazu ungeschönt wissen sollte:

Kein Zeitplan, sondern ein Knopf

Hier kommt die Entscheidung, um die es eigentlich geht, und sie läuft der Gewohnheit zuwider: Der Abruf wird nicht automatisiert. Kein Cron, keine nächtliche Aufgabe, kein Intervall. Es gibt einen Knopf im Dashboard, und nur der löst ihn aus.

Der Grund ist nicht Bequemlichkeit. Die Anmeldung erzeugt eine Freigabe-Meldung in der App der Bank, und diese Meldung ist die einzige Stelle, an der ein fremder Zugriff auf das Depot sichtbar würde. Läuft jeden Morgen um halb sieben eine Aufgabe, die dieselbe Meldung erzeugt, dann bedeutet sie „wahrscheinlich die Aufgabe" und wird weggewischt. Nur wenn ausschließlich der eigene Klick sie auslösen kann, heißt eine unerwartete Meldung: jemand anders ist dran.

Das ist dasselbe Muster wie bei jeder Prüfung, die von selbst anspringt: Sie prüft nichts mehr.

Was der Knopf nicht einspart: Der Client sendet Telefonnummer und PIN, bevor die Freigabe kommt — die Zugangsdatei liegt also genauso auf der Platte wie bei einem Zeitplan. Der Knopfdruck bringt kein geringeres Geheimnis, sondern dreierlei: keinen unbeaufsichtigten Zugriff, einen scharfen Stolperdraht, und keinen stillen Dauerbetrieb, der monatelang unbemerkt scheitert.

Gebaut ist es so, dass keine Seite neue Rechte bekommt — möglich war das nur, weil schon etwas dazwischenlag. Seit dem Fixkosten-Dashboard vermittelt ein MQTT-Broker zwischen NAS und Home Assistant: Einer hinterlegt dort Werte unter einem Namen, ein anderer holt sie ab, und keiner muss den anderen kennen. Der Knopf geht denselben Weg rückwärts — er steht in Home Assistant und veröffentlicht nur eine Nachricht; auf der NAS lauscht ein Container darauf und startet den Abruf. Kein offener Port, kein Schlüssel, keine Administratorzugangsdaten — beide Seiten benutzen, was sie schon benutzen. Die naheliegenderen Wege schieden aus: SSH ist aus Home Assistants Appliance-Betriebssystem heraus nicht vorgesehen, und die Aufgabenschnittstelle der NAS hätte deren Administratorzugangsdaten verlangt.

Zwei Kleinigkeiten, die den Unterschied machen: Der Knopf muss sperren, solange ein Lauf läuft — die Freigabe hat zwei Minuten Countdown, und ein zweiter Klick erzeugt sonst eine zweite Meldung, womit der Stolperdraht schon wieder stumpf wäre. Und der Zeitstempel des letzten geglückten Abrufs gehört sichtbar auf die Karte: Ein Depotwert ohne Datum daneben lügt, sobald er einen Tag alt ist.

Eine Ehrlichkeit noch: Wer im Heimnetz die Zugangsdaten des Brokers hat, kann dasselbe Thema ebenfalls beschicken. Das schwächt die Zusage „nur mein Klick", bricht sie aber nicht — ein fremder Anstoß käme als Freigabe-Meldung an, die niemand ausgelöst hat. Genau das ist der Alarm.

So sieht das Ergebnis aus. Oben die vier Summen, darunter ein Balken je Position, ganz unten der Zeitstempel und der Knopf, um den es hier ging.

Dunkle Dashboard-Karte: vier Kacheln mit Depotwert, Gewinn gesamt, nicht realisiert und realisiert; darunter ein waagerechter Balken je Position mit Name, Wert, Stückzahl und Gewinn; darunter eine Kennzeile und ein Knopf „Depot abrufen“.
Ein Balken je Position, Länge nach Anteil am Depot, Farbe nach Gewinn. Beispieldaten — Wertpapiere, Stückzahlen und Beträge sind erfunden.

Was ein Bestand nicht weiß

Die erste Fassung zeigte ein paar Prozent Gewinn, und das fühlte sich falsch an. Es war richtig und trotzdem zu klein: Eine Bestandsübersicht kennt nur den aktuellen Kurs gegen den Einstand der Stücke, die heute noch im Depot liegen. Alles Verkaufte kommt darin grundsätzlich nicht vor.

Zwei Begriffe verrutschen dabei gern. Realisiert heißt „verkauft", nicht „ausgezahlt" — wäre das Geld als Guthaben im Depot geblieben, wäre der Gewinn genauso realisiert. Und ein Teilverkauf senkt den Prozentsatz nicht: Bei unverändertem Durchschnittseinstand schrumpfen Wert und Einstand gleichmäßig.

Die Quelle muss also das Transaktionsjournal sein: je Wertpapier chronologisch durchgehen, einen laufenden Einstandswert führen, bei jedem Verkauf Erlös minus anteiligen Einstand.

Darin steckt eine Falle, die man nicht raten darf: Der gemeldete Erlös ist netto. Belegt ist das aus den eigenen Daten statt aus einer Annahme — ein Verkauf über einen runden Betrag erschien mit krummem Erlös, und Erlös plus einbehaltene Steuer ergaben genau den runden Betrag der Order. Der gemeldete Wert ist also der gutgeschriebene nach Steuerabzug, und die Rechnung lautet: Gewinn = Erlös + Steuer − Einstand. Angezeigt wird beides getrennt statt einer Zahl, die alles vermischt.

Die Gegenprobe: zwei Quellen, die sich nicht kennen

Die App der Bank liegt auf dem Telefon und ist von hier aus nicht lesbar. Die Probe, die ohne sie auskommt, ist die Kapitalflussrechnung:

Was heute im Depot liegt, minus dem, was netto eingezahlt wurde, muss der Gesamtgewinn sein.

Ein- und Auszahlungen gehen nie durch die Gewinnrechnung — genau deshalb taugt der Vergleich als Beweis. Er ging auf wenige Zehntelprozent auf.

Die Fallen stecken im Status: Eine Einzahlung stand auf „abgelehnt", zwei Auszahlungen auf „fehlgeschlagen" — und alle drei stehen trotzdem in der Liste. Wer sie mitzählt, landet bei einem negativen Barguthaben. Mit dieser Bereinigung ging das Verrechnungskonto auf wenige Cent genau auf den gemeldeten Stand auf; damit war belegt, dass kein Ereignis fehlt.

Zwei echte Fehler wurden erst dadurch sichtbar. Steuerkorrekturen fehlten völlig: Einen Tag nach einem Verkauf kam ein Teil der einbehaltenen Steuer zurück, die Karte behauptete aber weiterhin eine Summe „einbehalten", die um ein Vielfaches zu hoch war. Und Zinsen auf das Verrechnungskonto fehlten in der Gesamtsumme.

Vier Fehlgriffe

Eine einzelne Datei lässt sich nicht in einen read-only eingehängten Ordner hängen. Der Container starb, bevor er lief; gemeldet wurde nur failed to create shim task, die eigentliche Ursache stand weit rechts in derselben Zeile, hinter dem Waagerecht-Balken: Die Laufzeitumgebung muss den Einhängepunkt im Ziel erst anlegen und darf das dort nicht.

Eine Paketversion, die es nicht gibt, ergibt mit „immer neu starten" eine stille Schleife. Die Versionsnummer stand so im Quellbaum beim Code-Hoster, veröffentlicht war die Nummer davor. Die Installation scheitert, das Startskript bricht ab, die Neustartregel startet neu — und weil sich die Paketzeile nicht geändert hat, wird die halbfertige Umgebung jedes Mal weggeworfen und neu gebaut. Von außen sieht das aus wie „baut noch". Die Lehre ist der Satz, der nirgends steht: Die Version im Quellbaum ist nicht die veröffentlichte.

Ein Ereignistyp auf dem falschen Namen — der Zweig lief lautlos ins Leere. Die Bibliothek bildet Steuerkorrekturen nicht auf „Steuern" ab, sondern auf „Steuererstattung". Die erste Fassung prüfte nur auf „Steuern": kein Treffer, kein Fehler, kein Eintrag in der Liste der unverrechneten Ereignisse — die Zahl blieb einfach stehen. Gesehen nur, weil hinterher jemand nachgeschaut hat. Zwei Lehren: Namen von Ereignistypen schlägt man in der installierten Fassung nach, statt sie zu raten. Und was eine Rechnung nicht zuordnen kann, muss in einer sichtbaren Liste landen — sonst ist eine Summe, die sich nicht ändert, von einer richtigen nicht zu unterscheiden.

Ein geschenkter Anteil wird als Kauf gezählt. Eine Werbeaktion legte einen Bruchteil einer Aktie kostenlos ins Depot; in der Zeitleiste erscheint er als Kauf mit Betrag, obwohl kein Cent geflossen ist. Der Einstand ist dadurch um genau diesen Betrag zu hoch, der realisierte Gewinn um denselben zu niedrig. Gefunden nur, weil die Abweichung der Gegenprobe wuchs. Noch offen — wie auch der Konstruktionsfehler daneben: „Nicht realisiert" rechnet mit dem Einstand der Bank, „Realisiert" mit dem eigenen. Zwei Maßstäbe in einer Summe, deshalb wandert die Abweichung mit jedem Handel, statt fest zu stehen.

Technischer Anhang

← Alle Beiträge