Zum Hauptinhalt springen

Wie Python-Skripte Beancount und Fava automatisieren

Beancount und Fava bleiben scriptbar: Nutzen Sie Python, um Berichte, Salden und benutzerdefinierte Arbeitsabläufe gegen Ihr Hauptbuch zu automatisieren.

Beancount (ein einfaches, textbasiertes Buchhaltungstool mit doppelter Buchführung) und Fava (sein Web-Interface) sind hochgradig erweiterbar und scriptbar. Ihr Design ermöglicht es, finanzielle Aufgaben zu automatisieren, benutzerdefinierte Berichte zu erstellen und Benachrichtigungen durch Schreiben von Python-Skripten einzurichten. In den Worten eines Nutzers: „Ich mag es wirklich, meine Daten in einem so praktischen Format zu haben, und ich mag es, dass ich alles nach Herzenslust automatisieren kann. Es gibt keine API wie eine Datei auf deiner Festplatte; die Integration ist einfach.“ Dieser Leitfaden führt durch die Erstellung scriptbarer Workflows – von anfängerfreundlicher Automatisierung bis hin zu fortgeschrittenen Fava-Plugins.

Erkunde ein Live-Belegblatt-Beispiel:

Example Ledger in neuem Tab öffnen

Starte mit der bea Kommandozeile

Bevor Du Python schreibst, prüfe, ob bea die Aufgabe bereits erledigt. Es validiert das Belegblatt, führt BQL-Abfragen aus, erzeugt die vier Finanzberichte und importiert Bankexporte, und global verwandelt --json jedes davon in einen parsebaren Umschlag, den Deine Shell in jq pipen kann. Seine Exit-Codes sind der Vertrag, auf den sich ein geplannter Job verzweigt; cron oder CI benötigen kein Lädt-Skript. Siehe automatisiere die Buchführung mit bea für Zielauflösung, Umschläge und Exit-Code-Zweige und komm zurück, wenn Du eine benutzerdefinierte Berechnung brauchst, die die CLI nicht bietet.

Einstieg: Beancount als Python-Skript ausführen

Für die unten gezeigten benutzerdefinierten Python-Skripte installiere die Skript-Bibliotheken (pip install beancount beanquery beangulp). Die bea Command-Workflows verwenden stattdessen die verwaltete Engine; folge der CLI Schnellstart-Anleitung zur Installation. Da Beancount in Python geschrieben ist, kannst Du es als Bibliothek in Deinen eigenen Skripten verwenden. Die Skripte unten wurden mit Beancount 3.2.3, beanquery 0.2.0 und beangulp 0.2.0 ausgeführt. Die allgemeine Vorgehensweise ist:

  • Lade Dein Beancount-Belegblatt: Verwende den Beancount-Loader, um die .beancount-Datei in Python-Objekte zu parsen. Zum Beispiel:

    from beancount import loader
    entries, errors, options = loader.load_file("myledger.beancount")
    if errors:
        for error in errors:
            print(error)
        raise SystemExit(1)

    Der Loader liefert Einträge und Fehler zusammen zurück. Eine unausgeglichene oder ungültige Datei liefert trotzdem Einträge, also prüfe errors und halte an, bevor Du den Daten vertraust. Alle Deine Konten, Transaktionen und Salden sind nun im Code verfügbar.

  • Nutzen Sie die Beancount Query Language (BQL): Anstatt manuell zu iterieren, können Sie SQL-ähnliche Abfragen auf die Daten ausführen. Abfragen befinden sich im separaten beanquery-Paket. Es gibt kein beancount.query-Modul in Beancount 3.2.3. Zum Beispiel, um die Gesamtausgaben pro Monat zu erhalten, verbinden Sie die geladenen Einträge und führen die Abfrage direkt aus:

    import beanquery
     
    conn = beanquery.connect("beancount:", entries=entries, errors=errors, options=options)
    cur = conn.execute(
        "SELECT year, month, sum(position) WHERE account ~ 'Expenses' GROUP BY year, month"
    )
    for row in cur.fetchall():
        print(row)

    Dies verwendet beanquery zur Aggregation der Daten. Es ist dieselbe Engine hinter bea query, aber hier rufen Sie sie in einem Skript auf. So vermeiden Sie es, in einer Schleife einen externen Befehl auszuführen.

  • Richten Sie eine Projektstruktur ein: Organisieren Sie Ihre Skripte neben Ihrem Hauptbuch. Eine übliche Struktur besteht darin, Verzeichnisse für Importer (zum Abrufen/Parsen externer Daten), Berichte oder Abfragen (für Analyseskripte) und Dokumente (zum Speichern heruntergeladener Kontoauszüge) zu haben. Zum Beispiel behält ein Benutzer:

    • importers/ – benutzerdefinierte Python-Importskripte (mit Tests),
    • queries/ – Skripte zur Berichtserstellung (ausführbar über python3 queries/...),
    • documents/ – heruntergeladene Bank-CSV-/PDF-Dateien, nach Konto organisiert.

Mit dieser Einrichtung können Sie Skripte manuell ausführen (z. B. python3 queries/cash_flow.py) oder sie zeitgesteuert (über cron oder einen Task Runner) laufen lassen, um Ihren Arbeitsablauf zu automatisieren.

Automatisierung von Abstimmungsaufgaben

Abstimmung bedeutet sicherzustellen, dass Ihr Hauptbuch mit externen Aufzeichnungen (Kontoauszügen, Kreditkartenabrechnungen usw.) übereinstimmt. Das Plain-Text-Hauptbuch und die Python-API von Beancount ermöglichen es, einen Großteil dieses Prozesses zu automatisieren.

Importieren und Abgleichen von Transaktionen (Einsteiger)

Für Einsteiger wird empfohlen, Importer aus dem separaten beangulp-Paket zu verwenden. Beancount 3 entfernte das ingest-Modul v2 und dessen extract-Befehl. Sie schreiben eine kleine Python-Klasse, die beangulp.Importer erweitert, um ein gegebenes Format (CSV, OFX, PDF usw.) zu parsen und Transaktionen zu erzeugen. Registrieren Sie diese in einem kurzen Ingest-Skript und führen Sie es dann über bea ingest in der verwalteten Engine aus:

  • Schreiben Sie einen Importeur (eine Python-Klasse mit den Methoden identify(), account() und extract()) für das CSV-Format Ihrer Bank.
  • Fügen Sie ein Ingest-Skript hinzu, das Ihre Importeure registriert. bea ingest führt die Befehle identify, extract und archive des Skripts aus. Beispielsweise führt ein Workflow extract für alle Dateien in ~/Downloads aus und schreibt Transaktionen in eine temporäre Datei.
  • Überprüfen Sie manuell und kopieren Sie Transaktionen aus der temporären Datei in Ihr Hauptbuch, dann führen Sie bea check aus, um sicherzustellen, dass die Salden übereinstimmen.

Ein minimales Beispiel: eine statement.csv mit date,description,amount Spalten, geparst von diesem Importeur (checking_importer.py):

import csv
import datetime
from beancount.core import data
from beancount.core.amount import Amount
from beancount.core.number import D
import beangulp
 
 
class CheckingImporter(beangulp.Importer):
    def identify(self, filepath: str) -> bool:
        return filepath.endswith("statement.csv")
 
    def account(self, filepath: str) -> str:
        return "Assets:Bank:Checking"
 
    def extract(self, filepath: str, existing):
        entries = []
        with open(filepath, newline="") as f:
            for row in csv.DictReader(f):
                date = datetime.date.fromisoformat(row["date"])
                amount = Amount(D(row["amount"]), "USD")
                meta = data.new_metadata(filepath, 0)
                entries.append(
                    data.Transaction(
                        meta, date, "*", None, row["description"],
                        data.EMPTY_SET, data.EMPTY_SET, [
                            data.Posting("Expenses:Food:Groceries", amount,
                                         None, None, None, None),
                            data.Posting("Assets:Bank:Checking",
                                         Amount(-amount.number, "USD"),
                                         None, None, None, None),
                        ]))
        return entries

Das Ingest-Skript (ingest.py) verbindet es:

from checking_importer import CheckingImporter
from beangulp import Ingest
 
ingest = Ingest([CheckingImporter()])
 
if __name__ == "__main__":
    ingest()

Führen Sie es gegen eine heruntergeladene Datei aus. Für eine lokale CSV sind keine Anmeldeinformationen erforderlich. Installieren Sie zuerst die System-libmagic-Bibliothek. Der einmalige Aktivierungsbefehl lädt Beangulp in die verwaltete Engine:

bea engine enable beangulp
bea ingest identify --config ingest.py statement.csv
bea ingest extract --config ingest.py statement.csv -o new.beancount

identify meldet checking_importer.CheckingImporter für die Datei. extract schreibt die Transaktionen im Beancount-Format:

2024-01-08 * "Grocery Store"
  Expenses:Food:Groceries   120.00 USD
  Assets:Bank:Checking     -120.00 USD

Überprüfen Sie new.beancount, kopieren Sie die Einträge in Ihr Hauptbuch und führen Sie bea check aus.

Überspringen Sie den Importeur bei einer Einmalladung

Sie müssen keinen Importeur schreiben, um einen einzelnen Kontoauszug zu konvertieren. Fügen Sie die Datei in den CSV-zu-Beancount-Konverter ein oder verwenden Sie OFX & QIF zu Beancount für .ofx-, .qfx- und .qif-Downloads. Beide laufen vollständig in Ihrem Browser, sodass der Kontoauszug niemals Ihren Rechner verlässt.

Obwohl dieser Prozess immer noch einen Prüfschritt beinhaltet, wird der Großteil der Arbeit des Parsens und Formatierens von Einträgen automatisiert. Importeurskripte können auch Kategorien automatisch zuweisen und sogar Saldo-Behauptungen (Aussagen zu erwarteten Salden) setzen, um Abweichungen zu erkennen. Zum Beispiel könnten Sie nach dem Import eine Zeile wie 2025-04-30 balance Assets:Bank:Checking 1234.56 USD haben, die den Schlusssaldo behauptet. Wenn Sie bea check ausführen, wird Beancount überprüfen, dass alle diese Saldo-Behauptungen korrekt sind und Fehler markieren, wenn Transaktionen fehlen oder doppelt vorhanden sind. Dies ist eine Best Practice: Generieren Sie automatisch Saldo-Behauptungen für jede Auszugsperiode, damit der Computer nicht abgestimmte Differenzen für Sie erkennt.

Benutzerdefinierte Abstimmungsskripte (Fortgeschritten)

Für mehr Kontrolle können Sie ein benutzerdefiniertes Python-Skript schreiben, um die Transaktionsliste einer Bank (CSV oder via API) mit Ihren Buchungseinträgen zu vergleichen:

  1. Externe Daten lesen: Analysieren Sie die CSV-Datei der Bank mit dem Python-Modul csv (oder Pandas). Normalisieren Sie die Daten in eine Liste von Transaktionen, z. B. jeweils mit Datum, Betrag und Beschreibung.
  2. Buchhaltungsbuchungen laden: Verwenden Sie loader.load_file wie zuvor gezeigt, um alle Buchungseinträge zu erhalten. Filtern Sie diese Liste nach dem interessierenden Konto (z. B. Ihrem Girokonto) und gegebenenfalls nach dem Zeitraum des Kontoauszugs.
  3. Vergleichen und Diskrepanzen finden:
  • Für jede externe Transaktion prüfen Sie, ob ein identischer Eintrag im Hauptbuch vorhanden ist (Übereinstimmung nach Datum und Betrag, eventuell Beschreibung). Wenn nicht gefunden, markieren Sie ihn als „neu“ und geben ihn eventuell als Beancount-formatierte Transaktion zur Überprüfung aus.
  • Umgekehrt identifizieren Sie alle Buchungseinträge in diesem Konto, die in der externen Quelle nicht erscheinen – diese könnten Eingabefehler oder Transaktionen sein, die von der Bank noch nicht gebucht wurden.
  1. Ergebnisse ausgeben: Drucken Sie einen Bericht oder erstellen Sie einen neuen .beancount-Ausschnitt mit den fehlenden Transaktionen.

Als Beispiel tut ein Community-Skript namens reconcile.py genau dies: Angenommen eine Beancount-Datei und eine Eingabe-CSV, druckt es eine Liste neuer zu importierender Transaktionen sowie bestehender Hauptbucheinträge, die nicht in der Eingabe enthalten sind (möglicherweise ein Zeichen für Fehleinordnungen). Mit einem solchen Skript kann die monatliche Abstimmung so einfach sein, es auszuführen und dann die vorgeschlagenen Transaktionen an Ihr Hauptbuch anzuhängen. Ein Beancount-Nutzer berichtet, dass er „jeden Monat einen Abstimmungsprozess für alle Konten durchführt“ und eine wachsende Sammlung von Python-Code nutzt, um viel manuelle Arbeit beim Importieren und Abstimmen von Daten zu eliminieren.

Tipp: Nutzen Sie während der Abstimmung Beancounts Werkzeuge für Genauigkeit:

  • Verwenden Sie Saldoüberprüfungen wie erwähnt, um automatisierte Kontostandsprüfungen durchzuführen.
  • Nutzen Sie die pad-Direktive, falls gewünscht, die automatisch Ausgleichsbuchungen für kleine Rundungsdifferenzen einfügen kann (mit Vorsicht anwenden).
  • Schreiben Sie Unit-Tests für Ihren Importer oder Ihre Abstimmungslogik (Beancount bietet Test-Helfer). Beispielsweise wurde in einem Workflow eine Beispiel-CSV genommen, fehlgeschlagene Tests mit erwarteten Transaktionen geschrieben und dann der Importer implementiert, bis alle Tests erfolgreich liefen. Dies stellt sicher, dass Ihr Importskript für verschiedene Fälle korrekt funktioniert.

Eigene Berichte und Zusammenfassungen erstellen

Während Fava viele Standardberichte bietet (Gewinn- und Verlustrechnung, Bilanz etc.), können Sie eigene Berichte mit Skripten erzeugen. Diese reichen von einfachen Konsolenausgaben bis zu reich formatierten Dateien oder Diagrammen.

Abfrage von Daten für Berichte (Anfänger)

Auf einer grundlegenden Ebene können Sie die Beancount Query Language (BQL) verwenden, um zusammenfassende Daten zu erhalten und diese auszudrucken oder zu speichern. Zum Beispiel:

  • Zusammenfassung des Cashflows: Verwenden Sie eine Abfrage, um den Netto-Cashflow zu berechnen. „Cashflow“ könnte definiert werden als die Veränderung des Saldos bestimmter Konten über einen Zeitraum. Mit BQL könnten Sie folgendes tun:

    SELECT year, month, sum(position)
    WHERE account ~ 'Income' OR account ~ 'Expenses'
    GROUP BY year, month

    Dies verrechnet alle Einnahmen- und Ausgaben-Buchungen nach Monat. Filtern Sie mit ~ und einem regulären Ausdruck: LIKE ist ein Syntaxfehler in beanquery 0.2.0. Buchungen tragen position, nicht amount. Jede Zeile enthält ein Inventory, sodass jede Währung separat aufgelistet wird anstatt konvertiert zu werden. Einnahmen erscheinen negativ und Ausgaben positiv. Sie könnten dies über bea query oder über die zuvor gezeigte beanquery Python-API laufen lassen und dann das Ergebnis formatieren.

  • Kategorie-Ausgabenbericht: Abfragen der Gesamtausgaben pro Kategorie:

    SELECT account, sum(position)
    WHERE account ~ 'Expenses'
    GROUP BY account
    ORDER BY sum(position) ASC

    Dies ergibt eine Tabelle der Ausgaben nach Kategorie. Jede Summe ist ein Inventory in der Originalwährung. Umhüllen Sie das Aggregat nicht mit round(): Es gibt keine round(inventory, int)-Funktion, daher schlägt round(sum(position), 2) fehl. Sie können mehrere Abfragen in einem Skript ausführen und die Ergebnisse als Text, CSV oder sogar JSON zur weiteren Verarbeitung ausgeben.

Ein Benutzer fand es „trivial“, finanzielle Daten mit Fava oder Skripten zu analysieren, und gab an, dass er ein Python-Skript verwendet, um Daten mittels der Query Language aus Beancount zu ziehen und dann in ein Pandas DataFrame zu überführen, um einen benutzerdefinierten Bericht zu erstellen. Beispielsweise könnten Sie monatliche Summen mit einer Abfrage abfragen und dann Pandas/Matplotlib verwenden, um eine Cashflow-Grafik über die Zeit zu erstellen. Die Kombination von BQL und Data-Science-Bibliotheken ermöglicht es, Berichte zu erstellen, die über die Standardfunktionen von Fava hinausgehen.

Erweiterte Berichterstellung (Diagramme, Performance usw.)

Für anspruchsvollere Anforderungen können Ihre Skripte Kennzahlen wie die Investment-Performance berechnen oder visuelle Ausgaben erzeugen:

  • Investment-Performance (IRR/XIRR): Da Ihr Hauptbuch alle Cashflows (Käufe, Verkäufe, Dividenden) enthält, können Sie die Renditen Ihres Portfolios berechnen. Beispielsweise könnten Sie ein Skript schreiben, das Transaktionen Ihrer Investmentkonten filtert und dann die Interne Rendite (Internal Rate of Return) berechnet. Es gibt Bibliotheken (oder Formeln), um IRR basierend auf Cashflow-Daten zu berechnen. Einige community-entwickelte Fava-Erweiterungen (wie PortfolioSummary oder fava_investor) erledigen genau dies und berechnen IRR sowie weitere Kennzahlen für Investmentportfolios. Als Skript könnten Sie eine IRR-Funktion (aus NumPy oder Ihre eigene) auf die Serie von Einzahlungen/Auszah-lungen plus Endwert anwenden.

  • Mehrperiodige oder benutzerdefinierte Kennzahlen: Möchten Sie einen Bericht über Ihre Sparrate (Verhältnis von Ersparnissen zu Einkommen) jeden Monat? Ein Python-Skript kann das Hauptbuch laden, alle Einkommenskonten und alle Ausgabenkonten aufsummieren, dann die Ersparnis = Einkommen - Ausgaben und den Prozentsatz berechnen. Dies könnte eine schöne Tabelle ausgeben oder sogar einen HTML/Markdown-Bericht für Ihre Unterlagen erzeugen.

  • Visualisierung: Sie können Diagramme außerhalb von Fava erstellen. Verwenden Sie beispielsweise matplotlib oder altair in einem Skript, um ein Vermögensentwicklung über die Zeit-Diagramm mit den Hauptbuchdaten zu erstellen. Da das Hauptbuch alle historischen Salden enthält (oder Sie diese durch Iteration der Buchungen aufsummieren können), können Zeitreihendiagramme erzeugt werden. Speichern Sie diese Diagramme als Bilder oder interaktive HTML-Dateien. (Wenn Sie lieber Inline-Visualisierungen in der App möchten, siehe den Abschnitt zu Fava-Erweiterungen unten, um Diagramme innerhalb von Fava hinzuzufügen.)

Ausgabeoptionen: Entscheiden Sie, wie der Bericht geliefert werden soll:

  • Für einmalige Analysen kann das Ausgeben auf dem Bildschirm oder das Speichern in einer CSV-/Excel-Datei ausreichen.
  • Für Dashboards sollten Sie erwägen, eine HTML-Datei mit den Daten zu generieren (möglicherweise unter Verwendung einer Template-Bibliothek wie Jinja2 oder indem Sie einfach Markdown schreiben), die Sie im Browser öffnen können.
  • Sie können auch Jupyter Notebooks für eine interaktive Berichts-Umgebung integrieren, obwohl dies eher für Exploration als für Automatisierung gedacht ist.

Auslösen von Warnungen aus Ihrem Hauptbuch

Eine weitere leistungsstarke Anwendung skriptgesteuerter Workflows ist das Einrichten von Warnungen basierend auf Bedingungen in Ihren Finanzdaten. Da Ihr Hauptbuch regelmäßig aktualisiert wird (und zukünftige Buchungen wie anstehende Rechnungen oder Budgets enthalten kann), können Sie es mit einem Skript durchsehen und über wichtige Ereignisse benachrichtigt werden.

Warnungen bei niedrigem Kontostand

Um Überziehungen zu vermeiden oder einen Mindestsaldo zu behalten, möchten Sie vielleicht eine Warnung erhalten, wenn ein Konto (z. B. Giro- oder Sparkonto) unter einen Schwellenwert fällt. So können Sie das umsetzen:

  1. Aktuelle Salden ermitteln: Nachdem Sie entries über den Loader geladen haben, berechnen Sie den aktuellen Saldo der interessierenden Konten. Dies können Sie durch Aggregation von Buchungen oder durch eine Abfrage tun. Zum Beispiel verwenden Sie eine BQL-Abfrage für den Saldo eines bestimmten Kontos:

    SELECT sum(position) WHERE account = 'Assets:Bank:Checking'

    Dies gibt den aktuellen Saldo dieses Kontos zurück (Summe aller Buchungen). Alternativ verwenden Sie Beancounts interne Funktionen, um eine Bilanz zu erstellen. Zum Beispiel:

    from beancount.core import realization
    tree = realization.realize(entries)
    acct = realization.get_or_create(tree, "Assets:Bank:Checking")
    balance = acct.balance  # an Inventory of commodities

    Übergib nur die Buchungen: der zweite Parameter ist min_accounts, nicht die Optionskarte. Dann wird der numerische Wert extrahiert (z. B. gibt balance.get_currency_units('USD') den Dezimalbetrag in USD zurück). Wie bei einer Abfrage-Aggregation wird der Saldo für jede Währung separat geführt. Die Verwendung der Abfrage ist jedoch in den meisten Fällen einfacher.

  2. Schwellenwert prüfen: Vergleichen Sie den Saldo mit Ihrem vordefinierten Limit. Wenn darunter, lösen Sie eine Warnung aus.

  3. Benachrichtigung auslösen: Dies kann so einfach sein wie das Ausgeben einer Warnung in der Konsole, aber für echte Alarmmeldungen könnten Sie eine E-Mail oder Push-Benachrichtigung senden. Sie können eine Integration mit E-Mail (über smtplib) oder einem Dienst wie IFTTT oder Slacks Webhook-API vornehmen, um den Alarm zu senden. Zum Beispiel:

    if balance < 1000:
        send_email("Low balance alert", f"Account XYZ balance is {balance}")

    (Implementieren Sie send_email mit Ihren E-Mail-Serverdetails.)

Wenn Sie dieses Skript täglich ausführen (über einen Cron-Job oder Windows Aufgabenplanung), erhalten Sie proaktive Warnungen. Da es das Ledger nutzt, können alle Transaktionen berücksichtigt werden, auch solche, die Sie gerade hinzugefügt haben.

Anstehende Zahlungstermine

Wenn Sie Beancount zur Verfolgung von Rechnungen oder Fristen verwenden, können Sie zukünftige Zahlungen markieren und Skripte erinnern lassen. Zwei Möglichkeiten, bevorstehende Verpflichtungen in Beancount darzustellen:

  • Events: Beancount unterstützt eine event-Direktive für beliebige datierte Notizen. Zum Beispiel:

    2025-05-10 event "BillDue" "Mortgage payment due"

    Dies beeinflusst die Salden nicht, speichert jedoch ein Datum mit einem Label. Ein Skript kann entries nach Event-Einträgen durchsuchen, bei denen Event.type == "BillDue" (oder ein von Ihnen gewählter benutzerdefinierter Typ) vorliegt, und prüfen, ob das Datum beispielsweise innerhalb der nächsten 7 Tage ab heute liegt. Wenn ja, wird eine Warnung ausgelöst (E-Mail, Benachrichtigung oder sogar ein Popup).

  • Zukünftige Transaktionen: Manche erfassen zukunftsdatierte Transaktionen (post-datiert) für geplante Zahlungen. Diese werden erst nach Erreichen des Datums in den Salden angezeigt (es sei denn, Sie führen Berichte mit dem Stichtag in der Zukunft aus). Ein Skript kann nach Transaktionen suchen, die auf ein nahes zukünftiges Datum datiert sind, und diese auflisten.

Mit diesen könnten Sie ein „Tickler“-Skript erstellen, das beim Ausführen eine Liste von bald fälligen Aufgaben oder Rechnungen ausgibt. Eine Integration mit einer API wie Google Kalender oder einem Aufgabenmanager ist möglich, wenn Sie dort automatisch Erinnerungen erstellen wollen.

Anomalieerkennung

Über bekannte Schwellenwerte oder Daten hinaus können Sie benutzerdefinierte Warnungen für ungewöhnliche Muster skripten. Zum Beispiel, wenn eine normalerweise monatliche Ausgabe nicht stattgefunden hat (vielleicht haben Sie vergessen, eine Rechnung zu bezahlen), oder wenn die Ausgaben einer Kategorie in diesem Monat ungewöhnlich hoch sind, könnte Ihr Skript dies melden. Dies beinhaltet typischerweise das Abfragen von aktuellen Daten und den Vergleich mit der Historie (was ein fortgeschrittenes Thema sein könnte – möglicherweise unter Einsatz von Statistik oder ML).

In der Praxis verlassen sich viele Nutzer auf die Abstimmung, um Abweichungen (unerwartete Transaktionen) zu erkennen. Wenn Sie Bankbenachrichtigungen erhalten (z. B. E-Mails für jede Transaktion), könnten Sie diese mit einem Skript auslesen und automatisch in Beancount einfügen oder zumindest verifizieren, dass sie erfasst sind. Ein Enthusiast hat sogar seine Bank so konfiguriert, dass sie Transaktionswarn-E-Mails sendet, mit dem Plan, diese automatisch zu parsen und an das Hauptbuch anzuhängen. Diese Art von ereignisgesteuerten Warnungen kann sicherstellen, dass keine Transaktion unaufgezeichnet bleibt.

Erweiterung von Fava mit benutzerdefinierten Plugins und Ansichten

Fava ist bereits durch sein Erweiterungssystem skriptfähig. Wenn Sie Ihre Automatisierung oder Berichte direkt in die Weboberfläche integrieren möchten, können Sie eine Fava-Erweiterung (auch Plugin genannt) in Python schreiben.

Funktionsweise von Fava-Erweiterungen: Eine Erweiterung ist ein Python-Modul, das eine Klasse definiert, die von fava.ext.FavaExtensionBase erbt. Sie registrieren sie in Ihrer Beancount-Datei über eine benutzerdefinierte Option. Zum Beispiel, wenn Sie eine Datei myextension.py mit einer Klasse MyAlerts(FavaExtensionBase) haben, können Sie sie aktivieren, indem Sie Folgendes zu Ihrem Hauptbuch hinzufügen:

1970-01-01 custom "fava-extension" "myextension"

Beim Laden von Fava wird dieses Modul importiert und Ihre MyAlerts-Klasse initialisiert.

Erweiterungen können mehrere Dinge tun:

  • Hooks: Sie können sich in Ereignisse im Lebenszyklus von Fava einklinken. Zum Beispiel wird after_load_file() aufgerufen, nachdem das Hauptbuch geladen wurde. Sie könnten dies nutzen, um Prüfungen durchzuführen oder Daten vorab zu berechnen. Wenn Sie die Niedrigsaldo-Prüfung innerhalb von Fava implementieren wollten, könnte after_load_file Kontostände durchlaufen und vielleicht Warnungen speichern (obwohl deren Darstellung in der Benutzeroberfläche etwas mehr Aufwand erfordern könnte, wie das Auslösen eines FavaAPIError oder die Verwendung von Javascript zur Anzeige einer Benachrichtigung).
  • Benutzerdefinierte Berichte/Seiten: Wenn Ihre Erweiterungsklasse ein report_title-Attribut setzt, fügt Fava dafür eine neue Seite in der Seitenleiste hinzu. Sie stellen dann eine Vorlage (HTML/Jinja2) für den Inhalt dieser Seite bereit. So erstellen Sie völlig neue Ansichten, wie etwa ein Dashboard oder eine Zusammenfassung, die Fava standardmäßig nicht hat. Die Erweiterung kann beliebige Daten sammeln (Sie können self.ledger zugreifen, das alle Buchungen, Salden usw. enthält) und rendert anschließend die Vorlage.

Zum Beispiel fügt die eingebaute portfolio_list-Erweiterung in Fava eine Seite hinzu, die Ihre Portfolio-Positionen auflistet. Community-Erweiterungen gehen darüber hinaus:

  • Dashboards: Das fava-dashboards Plugin ermöglicht das Definieren benutzerdefinierter Diagramme und Panels (unter Verwendung von Bibliotheken wie Apache ECharts). Es liest eine YAML-Konfiguration mit auszuführenden Abfragen, führt sie über Beancount aus und erstellt eine dynamische Dashboard-Seite in Fava. Im Wesentlichen verbindet es Beancount-Daten mit einer JavaScript-Diagrammbibliothek, um interaktive Visualisierungen zu erzeugen.
  • Portfolioanalyse: Die Erweiterung PortfolioSummary (von Nutzern beigesteuert) berechnet Investment-Zusammenfassungen (Kontogruppierungen, IRR-Berechnung usw.) und zeigt diese in der Fava-Benutzeroberfläche an.
  • Transaktionsüberprüfung: Eine weitere Erweiterung, fava-review, hilft dabei, Transaktionen über die Zeit zu überprüfen (z. B. um sicherzustellen, dass Sie keine Belege übersehen haben).

Um selbst eine einfache Erweiterung zu erstellen, beginnen Sie mit der Unterklasse von FavaExtensionBase. Zum Beispiel könnte eine minimale Erweiterung, die eine Seite hinzufügt, folgendermaßen aussehen:

from fava.ext import FavaExtensionBase
 
class HelloReport(FavaExtensionBase):
    report_title = "Hello World"
 
    def __init__(self, ledger, config):
        super().__init__(ledger, config)
        # any initialization, perhaps parse config if provided
 
    def after_load_file(self):
        # (optional) run after ledger is loaded
        print("Ledger loaded with", len(self.ledger.entries), "entries")

Wenn Sie dies in hello.py platziert und custom "fava-extension" "hello" zu Ihrem Hauptbuch hinzugefügt hätten, würde Fava eine neue „Hello World“-Seite anzeigen (Sie bräuchten außerdem eine Vorlagendatei HelloReport.html in einem Unterordner templates, um den Seiteninhalt zu definieren, es sei denn, die Erweiterung verwendet nur Hooks). Die Vorlage kann Daten verwenden, die Sie der Erweiterungsklasse anhängen. Fava verwendet Jinja2-Vorlagen, sodass Sie Ihre Daten in dieser Vorlage etwa als HTML-Tabelle oder Diagramm rendern könnten.

Hinweis: Favas Erweiterungssystem ist mächtig, wird aber als „instabil“ betrachtet (änderbar). Es erfordert einige Kenntnisse in der Webentwicklung (HTML/JS), wenn Sie benutzerdefinierte Seiten erstellen. Wenn Ihr Ziel einfach nur das Ausführen von Skripten oder Analysen ist, ist es möglicherweise einfacher, diese als externe Skripte zu belassen. Verwenden Sie Fava-Erweiterungen, wenn Sie eine maßgeschneiderte In-App-Erfahrung für Ihren Arbeitsablauf wünschen.

Integration von Drittanbieter-APIs und Daten

Einer der Vorteile skriptbarer Workflows ist die Möglichkeit, externe Daten einzubinden. Hier sind gängige Integrationen:

  • Wechselkurse & Rohstoffe: Beancount holt Preise nicht automatisch (um die Berichte deterministisch zu halten), bietet aber eine Price-Direktive, damit Sie Kurse angeben können. Sie können das Abrufen dieser Preise automatisieren. Zum Beispiel kann ein Skript eine API (Yahoo Finance, Alpha Vantage etc.) für den aktuellen Wechselkurs oder Aktienkurs abfragen und eine Preiseintragung in Ihr Journal anhängen:

    2025-04-30 price BTC 30000 USD
    2025-04-30 price EUR 1.10 USD

Es gibt Werkzeuge wie bea price, unterstützt von Beanprice im Managed Engine, die tägliche Kurse abrufen und im Beancount-Format ausgeben. Sie könnten es einmal mit bea engine enable beanprice aktivieren und dann bea price main.beancount so planen, dass es jede Nacht ausgeführt wird, um eine prices.beancount Include-Datei zu aktualisieren. Oder verwenden Sie Python, z. B. mit der requests-Bibliothek, um eine API anzusprechen. Die Beancount-Dokumentation empfiehlt, dass Sie bei öffentlich gehandelten Assets „Code ausführen, der Preise herunterlädt und die Direktiven für Sie schreibt.“ Mit anderen Worten: Lassen Sie ein Skript die Abfrage durchführen und die price-Zeilen einfügen, statt es manuell zu tun.

  • Aktienportfolio-Daten: Ähnlich wie bei Wechselkursen können Sie APIs integrieren, um detaillierte Aktieninformationen oder Dividenden abzurufen. Beispielsweise kann die Yahoo Finance API (oder Community-Bibliotheken wie yfinance) historische Daten für ein Ticker-Symbol liefern. Ein Skript könnte Ihr Journal mit monatlichen Kursverläufen jeder Aktie aktualisieren, die Sie besitzen, was genaue historische Berichte zum Marktwert ermöglicht. Manche benutzerdefinierte Erweiterungen (wie fava_investor) holen Kursdaten sogar live zur Anzeige, aber am einfachsten ist es, Preise regelmäßig ins Journal zu importieren.

  • Banking APIs (Open Banking/Plaid): Anstatt CSV-Dateien herunterzuladen, können Sie APIs verwenden, um Transaktionen automatisch abzurufen. Dienste wie Plaid aggregieren Bankkonten und ermöglichen den programmatischen Zugriff auf Transaktionen. In einem fortgeschrittenen Setup könnten Sie ein Python-Skript haben, das täglich neue Transaktionen über die Plaid-API abruft und in einer Datei speichert (oder direkt ins Ledger importiert). Ein Power-User hat ein System entwickelt, bei dem Plaid in seine Import-Pipeline einspeist, was seine Bücher nahezu automatisch macht. Er merkt an, dass „nichts Sie davon abhält, sich bei der Plaid-API anzumelden und dasselbe lokal zu tun“ – d.h. Sie können ein lokales Skript schreiben, um Bankdaten zu erhalten, und dann Ihre Beancount-Importer-Logik verwenden, um diese in Ledger-Einträge zu parsen. Manche Regionen bieten Open Banking APIs von Banken an; diese könnten ähnlich genutzt werden.

  • Andere APIs: Sie könnten Budgetierungstools integrieren (durch Export geplanter Budgets zum Vergleich mit den Ist-Daten in Beancount) oder eine OCR-API verwenden, um Belege zu lesen und automatisch Transaktionen zuzuordnen. Da Ihre Skripte vollen Zugriff auf das Python-Ökosystem haben, können Sie alles integrieren – von E-Mail-Diensten (zum Versenden von Benachrichtigungen) über Google Sheets (z.B. monatliche Finanzkennzahlen aktualisieren) bis hin zu Messaging-Apps (senden Sie sich einen zusammenfassenden Bericht per Telegram-Bot).

Beim Einsatz von Drittanbieter-APIs denken Sie daran, Ihre Zugangsdaten zu sichern (verwenden Sie Umgebungsvariablen oder Konfigurationsdateien für API-Schlüssel) und Fehler (Netzwerkprobleme, API-Ausfälle) in Ihren Skripten elegant zu behandeln. Es ist oft sinnvoll, Daten zu cachen (zum Beispiel abgerufene Wechselkurse zu speichern, damit Sie nicht dieselben historischen Kurse mehrfach abfragen).

Best Practices für modulare, wartbare Skripte

Wenn Sie skriptgesteuerte Workflows aufbauen, halten Sie Ihren Code organisiert und robust:

  • Modularität: Teilen Sie verschiedene Anliegen in unterschiedliche Skripte oder Module auf. Zum Beispiel separate Skripte für „Datenimport/-abgleich“ vs. „Berichtserstellung“ vs. „Benachrichtigungen“. Sie können sogar ein kleines Python-Paket für Ihr Ledger mit Modulen wie ledger_import.py, ledger_reports.py usw. erstellen. Das macht jeden Teil leichter verständlich und testbar.

  • Konfiguration: Vermeiden Sie fest codierte Werte. Verwenden Sie eine Konfigurationsdatei oder Variablen am Skriptanfang für Dinge wie Kontonamen, Schwellenwerte, API-Schlüssel, Zeiträume usw. So lassen sich Anpassungen leicht vornehmen, ohne tief in den Code eingreifen zu müssen. Definieren Sie zum Beispiel LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000} oben, und Ihr Benachrichtigungsskript kann über dieses Dictionary iterieren.

  • Tests: Behandle deine Finanzautomatisierung wie missionskritischen Code – denn genau das ist sie! Schreibe Tests für komplexe Logik. Beancount stellt einige Test-Hilfsmittel bereit (intern für Importer-Tests verwendet), die du nutzen kannst, um Ledger-Eingaben zu simulieren. Selbst ohne ausgefeilte Frameworks kannst du eine Dummy-CSV und erwartete Ausgangsbuchungen haben und prüfen, ob dein Importskript die korrekten Einträge erzeugt. Wenn du pytest verwendest, kannst du diese Tests leicht integrieren (wie Alex Watt es über einen just test-Befehl gemacht hat, der pytest umschließt).

  • Versionskontrolle: Halte deinen Ledger und deine Skripte unter Versionskontrolle (git). Das gibt dir nicht nur Backups und Historie, sondern fördert auch kontrollierte Änderungen. Du kannst Releases deiner „Finanzskripte“ taggen oder Unterschiede vor dem Debuggen eines Problems überprüfen. Manche Nutzer verfolgen sogar ihre Finanzdaten in Git, um Veränderungen im Zeitverlauf zu sehen. Achte nur darauf, sensible Daten (wie Roh-Kontoauszüge oder API-Schlüssel) im Repo zu ignorieren.

  • Dokumentation: Dokumentiere deine individuellen Workflows für dein zukünftiges Ich. Eine README in deinem Repository, die erklärt, wie man die Umgebung einrichtet, wie jedes Skript ausgeführt wird und was es macht, ist nach Monaten Gold wert. Kommentiere auch deinen Code, insbesondere nicht offensichtliche Buchhaltungslogik oder API-Interaktionen.

  • Pflege von Fava-Plugins: Wenn du eine Fava-Erweiterung schreibst, halte sie einfach. Fava kann sich ändern, daher sind kleinere Erweiterungen mit gezielter Funktionalität leichter zu aktualisieren. Vermeide zu viel Logik-Duplikation – nutze nach Möglichkeit Beancounts Abfrage-Engine oder bestehende Hilfsfunktionen, anstatt Berechnungen hart zu kodieren, die empfindlich auf Ledger-Änderungen reagieren könnten.

  • Sicherheit: Da deine Skripte sensible Daten verarbeiten und externe Dienste anbinden können, behandle sie sorgfältig. Gib keine API-Schlüssel preis und führe deine Automatisierung vorzugsweise auf einem sicheren Rechner aus. Wenn du eine gehostete Lösung oder Cloud nutzt (wie geplante GitHub Actions oder einen Server für Fava), stelle sicher, dass deine Ledger-Daten verschlüsselt gespeichert sind und du mit den Datenschutzimplikationen einverstanden bist.

Indem du diese Praktiken befolgst, stellst du sicher, dass dein Workflow zuverlässig bleibt, auch wenn sich deine Finanzen (und die Werkzeuge selbst) weiterentwickeln. Du möchtest Skripte, die du Jahr für Jahr mit minimalen Anpassungen wiederverwenden kannst.

Fazit

Beancount und Fava bieten eine leistungsstarke, flexible Plattform für technikaffine Nutzer, um ihre persönliche Finanzverfolgung komplett anzupassen. Durch das Schreiben von Python-Skripten können Sie lästige Aufgaben wie die Abstimmung von Kontoauszügen automatisieren, umfangreiche Berichte erstellen, die auf Ihre Bedürfnisse zugeschnitten sind, und mit zeitgerechten Benachrichtigungen den Überblick über Ihre Finanzen behalten. Wir haben eine Reihe von Beispielen von grundlegend bis fortgeschritten behandelt – angefangen bei einfachen Abfragen und CSV-Importen bis hin zu vollwertigen Fava-Plugins und externen API-Integrationen. Während Sie diese umsetzen, beginnen Sie einfach und bauen Sie allmählich auf. Schon einige kleine Automatisierungsskripte können stundenlange Arbeit sparen und die Genauigkeit erheblich verbessern. Und denken Sie daran, da alles reiner Text und Python ist, haben Sie die volle Kontrolle – Ihr Finanzsystem wächst mit Ihnen und passt sich Ihren spezifischen Bedürfnissen an. Viel Spaß beim Skripten!

Quellen: Die oben genannten Techniken stammen aus der Beancount-Dokumentation und Erfahrungen der Community. Für weiterführende Lektüre siehe die offiziellen Beancount-Dokumente, Community-Guides und Blogs sowie das Awesome Beancount-Repository für Links zu nützlichen Plugins und Tools.

Quelle: https://beancount.io/de/docs/Solutions/scriptable-workflows