Damit ist der geplante Funktionsumfang beisammen. Phase 4 abgeschlossen - Konfigurierbare Cockpit-Kacheln; abgewählte werden nicht berechnet - Einnahmen/Ausgaben je Monat als Balkenpaare mit Saldolinie, aufgesetzt auf den Summenbericht statt auf eine eigene Abfrage - Meine Ausgaben als Ring auf der obersten Kategorieebene - Offene Vorgänge aus allen Modulen Phase 5 – Online-Banking - nemiah/php-fints als Fork auf Codeberg, psr/log direkt eingebaut: die Anwendung hängt an keinem fremden Update-Zyklus - PIN AES-256-GCM-verschlüsselt; Schlüssel aus APP_KEY oder aus data/schluessel/app_key, den der Entrypoint einmalig erzeugt - TAN-Dialog über mehrere Requests (FinTs::persist plus serialisierte Aktion in der Sitzung) – ein wartender Prozess wäre mit php-fpm der falsche Weg - Abgleich statt Direktbuchung: verbinden, übernehmen oder verwerfen - Regeln mit vier Vergleichsarten, Betragsgrenzen und Split-Schema Phase 6 – Wertpapiere - Depotbuchung und Geldbuchung als zwei Seiten eines Geschäfts; betrag ist der Bruttowert, Nebenkosten stehen daneben - Einstand nach gleitendem Durchschnitt, realisierte Gewinne, Erträge - Kursabruf über Leeway oder FMP, täglicher Lauf per Cron - QIF-Import für !Type:Invst Dabei behoben: Der Import buchte den Geldfluss jedes Wertpapiergeschäfts als Abgang – auch bei Verkäufen und Dividenden; und er erkannte das Verrechnungskonto nicht, wenn dem Konto im L-Feld eine Kategorie vorangeht (bei Verkäufen also immer). Phase 7 – Nebenmodule und Zahlungsverkehr - Buchungsvorlagen inklusive QIF-Import, Splits werden mitskaliert - Regelmäßige Buchungen mit Fälligkeitslauf, Finanzkalender, Erinnerungen - Adressen mit IBAN-Prüfung, Verträge mit Kündigungsfrist, Sparziele - Kredite mit gerechnetem Tilgungsplan - Überweisungen über FinTS; die Buchung entsteht erst nach Annahme durch die Bank. Die pain.001-Nachricht erzeugt SepaXml selbst Nebenbei: .dockerignore ergänzt (ohne sie brach der Build an data/mariadb ab), PlatzhalterController entfernt, zwei Inline-Handler beseitigt, die die Content-Security-Policy ohnehin blockierte. 888 PHP-Prüfungen und 45 E2E-Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
5.8 KiB
Markdown
129 lines
5.8 KiB
Markdown
# Wertpapiere und Depots
|
||
|
||
Ein Depot ist ein Konto der Kontoart **Depot**. Was darin liegt, steht nicht in
|
||
`buchungen`, sondern in `depot_buchungen` – dort gehören Stückzahl und Kurs dazu, und der
|
||
Saldo ist keine Summe von Beträgen, sondern der bewertete Bestand.
|
||
|
||
## Zwei Seiten je Geschäft
|
||
|
||
Jedes Wertpapiergeschäft erzeugt bis zu zwei Datensätze:
|
||
|
||
* die **Depotbuchung** mit Stück, Kurs, Gebühren und Steuern,
|
||
* die **Geldbuchung** auf dem Verrechnungskonto – eine gewöhnliche Buchung, die im
|
||
Kontoblatt steht wie jede andere.
|
||
|
||
Verbunden sind sie über `buchung_id`. So bleibt das Girokonto stimmig, ohne dass
|
||
Kontoblätter Stückzahlen führen müssten. Wird die Depotbuchung gelöscht, geht die
|
||
Geldbuchung mit; sie hat ohne das Geschäft keinen Sinn.
|
||
|
||
Ein- und Auslieferungen rühren kein Konto an und haben deshalb keine Geldbuchung.
|
||
|
||
## Was in welchem Feld steht
|
||
|
||
`betrag` ist der **Bruttowert**: Stück × Kurs, bei Dividenden der Bruttoertrag. Gebühren
|
||
und Steuern stehen *daneben*, nicht darin. Daraus folgt der Geldfluss:
|
||
|
||
| Vorgang | Verrechnungskonto |
|
||
|---|---|
|
||
| Kauf | −(Betrag + Gebühren + Steuern) |
|
||
| Verkauf | +(Betrag − Gebühren − Steuern) |
|
||
| Dividende | +(Betrag − Steuern) |
|
||
|
||
Am echten Export nachgerechnet: Ein Verkauf über 4 × 797,00 € mit 15,37 € Gebühren und
|
||
69,46 € Steuern ergibt 3.103,17 € Gutschrift – genau der Wert, den das QIF im Feld `$`
|
||
nennt. Diese Trennung ist nötig, damit der Einstand die Nebenkosten **genau einmal**
|
||
enthält.
|
||
|
||
`stueck` trägt die Richtung: Zugänge positiv, Abgänge negativ, Dividenden null.
|
||
|
||
## Einstand nach gleitendem Durchschnitt
|
||
|
||
Bei jedem Kauf steigt der Einstand um Kaufpreis samt Gebühren, bei jedem Verkauf sinkt er
|
||
anteilig um den bis dahin durchschnittlichen Stückpreis. So rechnet auch der
|
||
FinanzManager, und die Frage, *welches* Stück verkauft wurde, stellt sich gar nicht.
|
||
|
||
Der **realisierte Gewinn** eines Verkaufs ist Erlös minus Kosten minus anteiligem
|
||
Einstand.
|
||
|
||
> Für die Steuererklärung gilt in Deutschland FIFO. Maßgeblich ist dort die Abrechnung der
|
||
> Bank – diese Übersicht ersetzt sie nicht.
|
||
|
||
Vollständig verkaufte Positionen verschwinden aus der Bestandsliste; ihr realisierter
|
||
Gewinn zählt weiter mit.
|
||
|
||
## Kurse
|
||
|
||
Ein Kurs je Wertpapier und Tag. Mehr Auflösung braucht eine Vermögensübersicht nicht, und
|
||
die Freikontingente der Kursdienste geben nicht mehr her.
|
||
|
||
**Ohne Kurs wird nicht bewertet**: Die Position zeigt „kein Kurs“ und fällt aus der
|
||
Wertsumme; die Übersicht sagt ausdrücklich, dass sie unvollständig ist. Der Einstand als
|
||
Ersatzwert wäre bequem, verstellte aber den Blick.
|
||
|
||
### Kursabruf
|
||
|
||
`Services\Kursabruf` kennt zwei Anbieter (siehe `Aktienkurse.md`):
|
||
|
||
* **Leeway.tech** – 50 Abrufe/Tag, kennt ISINs direkt
|
||
* **Financial Modeling Prep** – 250 Abrufe/Tag
|
||
|
||
Beide arbeiten in zwei Schritten: ISIN → Kürzel, Kürzel → Kurs. Das gefundene Kürzel wird
|
||
am Wertpapier gespeichert, damit der erste Schritt künftig entfällt – das halbiert den
|
||
Verbrauch. Abgerufen wird nur für Papiere **mit Bestand**, und nur, wenn für heute noch
|
||
kein Kurs vorliegt.
|
||
|
||
Anbieter und Schlüssel stehen im Einstellungsdialog unter *Wertpapiere*. Der tägliche Lauf
|
||
gehört in einen Cron-Eintrag:
|
||
|
||
```bash
|
||
docker compose exec -u www-data php php scripts/kursabruf.php
|
||
```
|
||
|
||
Das Skript bricht ab, solange der automatische Abruf in den Einstellungen nicht
|
||
eingeschaltet ist – ein vergessener Cron-Eintrag kostet so kein Kontingent. Der Knopf auf
|
||
der Wertpapierseite arbeitet unabhängig davon.
|
||
|
||
Die Feldnamen der Antworten werden nachsichtig gelesen (`close`, `price`, `last` …): Die
|
||
Dienste benennen sie unterschiedlich und gelegentlich um. Der API-Schlüssel steht in der
|
||
Adresse und wird vor jedem Logeintrag maskiert.
|
||
|
||
## QIF-Import (`!Type:Invst`)
|
||
|
||
Der Import legt Wertpapier, Depotbuchung und Geldbuchung in einem Zug an. Zwei
|
||
Eigenheiten des Formats:
|
||
|
||
* **Das `L`-Feld trägt bei Wertpapiergeschäften mehr als sonst.** Dem Verrechnungskonto
|
||
kann eine Kategorie vorangehen: `LKursgewinne:Realisierte Gewinne|[Verrechnungskonto]`.
|
||
Gelesen wird deshalb nur der Teil hinter dem letzten senkrechten Strich.
|
||
* **Das Feld `O` führt zwölf Beträge.** Sicher zugeordnet sind Position 4 (Provision) und
|
||
10 (fremde Spesen) – beides Gebühren. Alles Übrige sind Abzüge des Fiskus und werden zu
|
||
„Steuern“ zusammengefasst. Damit fällt keine Position heraus, ohne dass jede einzeln
|
||
gedeutet werden müsste.
|
||
|
||
Fehlt der Kurs – bei Dividenden immer –, wird der Bruttobetrag aus der Gutschrift plus den
|
||
Abzügen zurückgerechnet.
|
||
|
||
`DivIn` und `DivAus` landen beide auf „dividende“; die ursprüngliche Bezeichnung bleibt in
|
||
`vorgang_qif` erhalten, weil die Zuordnung nicht in jedem Fall umkehrbar ist.
|
||
|
||
**Dabei behoben:** Bis Phase 5 buchte der Import den Geldfluss *jedes*
|
||
Wertpapiergeschäfts als Abgang – auch bei Verkäufen und Dividenden. Das QIF nennt im Feld
|
||
`$` immer einen positiven Betrag; die Richtung steckt allein im Vorgang. Verrechnungskonten
|
||
von Depots wiesen dadurch zu niedrige Salden aus.
|
||
|
||
## Kontenübersicht
|
||
|
||
Depots erscheinen dort mit ihrem **bewerteten Bestand**, nicht mit einem Buchungssaldo. Der
|
||
Anfangssaldo bleibt dabei: Führt ein Depotkonto eine Barreserve, steht sie dort.
|
||
|
||
Damit ist die Voraussetzung erfüllt, um die Summe der Kontostände mit dem FinanzManager zu
|
||
vergleichen (`PLAN.md`, Kapitel 7) – sobald Kurse für alle Positionen vorliegen.
|
||
|
||
## Tests
|
||
|
||
`tests/17_WertpapiereTest.php` – Wiederfinden über ISIN, WKN und Name samt Nachtragen
|
||
fehlender Felder; Bestand und Einstand über mehrere Käufe; Geldseite mit richtigem
|
||
Vorzeichen; Verkauf mit anteiligem Einstand und realisiertem Gewinn; Dividende ohne
|
||
Bestandsänderung; fehlende Kurse; Veräußerungsrechnung über die Historie hinweg; der
|
||
QIF-Import eines echten Ausschnitts (Kauf, Dividende, Verkauf) inklusive Idempotenz;
|
||
Löschen samt Geldbuchung.
|