GrowthLedger ist eine lokale Python-3-Terminalanwendung zur Verwaltung und Analyse eines ETF-Sparplans. Sie importiert Trade-Republic-CSV-Dateien, erfasst manuelle Transaktionen, erstellt Berichte und Prognosen, zeichnet ASCII-Diagramme und exportiert Tabellen. Phase 2 kann auf ausdrücklichen Befehl Marktpreise von Alpha Vantage oder Twelve Data abrufen. Die Anwendung verwendet ausschließlich die Python-Standardbibliothek und Decimal für Finanzwerte.
GrowthLedger ist ein Rechen- und Dokumentationswerkzeug, keine Finanzberatung. Prognosen sind keine Renditegarantie. Märkte schwanken und Verluste sind möglich.
- Python 3.10 oder neuer
- Linux-Terminal; keine externen Pakete
Nach dem Klonen ist keine Installation nötig:
python3 growthledger.py --help
python3 growthledger.py sample-data
python3 growthledger.py list
python3 growthledger.py reportBeim ersten Zugriff entstehen data/transactions.csv und data/config.json automatisch. Mit der globalen Option --data-dir PFAD lässt sich ein anderes Datenverzeichnis verwenden. Die lokale Konfiguration enthält nur Phase-2-Metadaten wie ETF-Name, ISIN, Ticker und Börsenplatz; config.example.json ist die Vorlage.
growthledger.py Modell, Speicherung, Import, Berechnung und CLI
data/transactions.csv private lokale Daten; automatisch erzeugt
data/config.json lokale Konfiguration; automatisch erzeugt
export/ lokale Exporte
examples/trade_republic_sample.csv anonymisierte Importdatei
tests/test_growthledger.py unittest-Suite ohne Netzwerkzugriff
config.example.json Konfigurationsvorlage ohne Geheimnisse
Der Ein-Datei-Aufbau ist logisch in Modell, Speicherung, Import, Berechnung, Prognose, Diagramme, Export, CLI und Kursversorgung getrennt. Er kann später ohne Änderung der Berechnungslogik in Module aufgeteilt werden.
Manuell erfassen; wenn keine Parameter angegeben werden, fragt das Programm interaktiv:
python3 growthledger.py add
python3 growthledger.py add --date 2026-01-01 --amount 25 --buy-price 100 --market-price 102
python3 growthledger.py add --date 2026-01-01 --transaction-type etf_buy \
--amount 25 --buy-price 100 --shares 0.25 --name "Beispiel ETF" \
--identifier IE00B4L5Y983 --currency EUREine angegebene tatsächliche Stückzahl hat Vorrang. Nur wenn sie bei einem Kauf fehlt, berechnet GrowthLedger shares = amount / buy_price; bei einem Verkauf entsprechend mit sell_price. Manuelle Kurse erhalten price_source=manual.
Anzeigen und berichten:
python3 growthledger.py list
python3 growthledger.py reportlist zeigt keine Namen von Gegenparteien oder IBANs. report zeigt Zahlungsströme, Käufe, Verkäufe, Bestand, gewichteten Kaufkurs, Gebühren, Steuern, Dividenden, Marktwert und vereinfachte Rendite. Fehlt für eine aktive Position ein market_price, wird kein Depotwert vorgetäuscht.
Prognosen und Diagramme:
python3 growthledger.py forecast --monthly 25 --years 20 --return 5
python3 growthledger.py forecast --monthly 25 --years 20 --returns 3 5 7
python3 growthledger.py forecast --monthly 25 --years 20 --return 5 --start-value 1000
python3 growthledger.py chart
python3 growthledger.py chart --monthly 50 --years 15 --return 6forecast verwendet standardmäßig den berechenbaren aktuellen Depotwert als Startwert; --start-value überschreibt ihn. chart zeigt kumulierte Käufe, verfügbare historische Depotwerte, Investition gegen Depotwert und ein Prognoseszenario.
Export und Beispieldaten:
python3 growthledger.py export --output export/growthledger.csv
python3 growthledger.py sample-dataBeispieldaten sind klar anonymisiert und werden nicht erneut eingespielt oder über bestehende Daten geschrieben.
python3 growthledger.py import-trade-republic --file traderepublic.csv
python3 growthledger.py import-trade-republic --file traderepublic.csv --preview
python3 growthledger.py import-trade-republic --file traderepublic.csv --dry-run
python3 growthledger.py import-trade-republic --file traderepublic.csv --skip-duplicates --verbose--preview prüft und zeigt erkannte Datensätze, --dry-run prüft den gesamten Import; beide verändern die Daten nicht. Standardmäßig bricht eine bekannte oder innerhalb der Datei doppelte transaction_id den kompletten Import ab. --skip-duplicates überspringt sie. Ungültige Zeilen führen nie zu einem Teilimport; erst nach vollständiger Prüfung wird die Zieldatei atomar ersetzt.
Unterstützt werden UTF-8 und UTF-8 mit BOM, leere Felder sowie zusätzliche Spalten. Pflichtspalten sind date, category, type und transaction_id. Erwartete Felder sind:
datetime,date,account_type,category,type,asset_class,name,symbol,shares,price,
amount,fee,tax,currency,original_amount,original_currency,fx_rate,description,
transaction_id,counterparty_name,counterparty_iban,payment_reference,mcc_code
Gegenpartei, IBAN, Zahlungsreferenz und MCC werden nicht gespeichert oder ausgegeben. Eine vollständige anonymisierte Datei liegt unter examples/.
CASH / CUSTOMER_INPAYMENTist eine Einzahlung auf das Verrechnungskonto (cash_deposit), kein Wertpapierkauf. Sie erhöht nicht den ETF-Kaufbetrag.TRADING / BUY / FUNDmit Kennung,sharesundpriceist ein ETF-Kauf. Broker-Stückzahl und Ausführungskurs werden unverändert übernommen.TRADING / SELList ein Verkauf und reduziert den Bestand.- Eindeutige Dividenden, Gebühren und Steuern werden getrennt erfasst. Unklare Fälle werden als
unknowngespeichert, nicht geraten. - Das Trade-Republic-Feld
symbolkann eine ISIN wieIE00B4L5Y983enthalten; gültige ISINs werden zusätzlich im ISIN-Feld gespeichert. - Ist
amountvorhanden, giltamount_source=csv. Fehlt er bei Kauf oder Verkauf, wird exaktshares * pricemitDecimalberechnet undamount_source=calculatedgesetzt.
buy_price beziehungsweise sell_price ist der tatsächliche Broker-Ausführungskurs. market_price ist ausschließlich ein manueller oder automatisch abgerufener Bewertungs-, Stichtags- oder Monatsendkurs. Ein Marktpreis überschreibt nie den Kaufkurs.
Bestand = Summe gekaufter Anteile - Summe verkaufter Anteile
Gewichteter Kaufkurs = Summe(shares × buy_price) / Summe gekaufter shares
Depotwert je Position = Bestand × letzter vorhandener market_price
Vereinfachter Gewinn/Verlust = Depotwert + Verkaufserlöse + Dividenden
- Kaufbeträge - Gebühren - Steuern
Vereinfachte Rendite = Gewinn/Verlust / (Kaufbeträge + Gebühren + Steuern) × 100
Kontoeinzahlungen werden separat berichtet und nie zusätzlich als investierter ETF-Betrag gezählt. Verkäufe reduzieren Stückzahlen und ihre Erlöse fließen in die vereinfachte Gesamtbetrachtung ein. Gebühren und Steuern mindern, Dividenden erhöhen sie. Ohne steuerliche Loszuordnung kann GrowthLedger realisierte und unrealisierte Gewinne nicht belastbar trennen; die Anzeige vermeidet diese falsche Genauigkeit. Bei mehreren Währungen findet keine Umrechnung statt.
Für Prognosen gilt die effektive Monatsrendite
monthly_rate = (1 + annual_rate) ** (1 / 12) - 1
Der Startwert verzinst sich monatlich; die Sparrate wird jeweils am Monatsende hinzugefügt. Die Spalte „Einzahlungen kum.“ beginnt mit den tatsächlich importierten Kontoeinzahlungen und addiert anschließend die geplanten künftigen Sparraten. Sind keine Kontoeinzahlungen vorhanden, dienen die ETF-Kaufbeträge als Fallback. Der bestehende Depotwert bleibt Startkapital und wird dadurch nicht doppelt addiert.
Transaktionen werden als UTF-8-CSV mit allen Modellfeldern in data/transactions.csv gespeichert. Schreiben erfolgt über eine temporäre Datei und atomaren Austausch. Dezimalwerte werden ohne unnötige Rundung gespeichert.
Der Benutzerexport ist eine UTF-8-CSV mit BOM und Semikolon als Trennzeichen für Excel und ONLYOFFICE. Spalten: Datum, Transaktionstyp, ETF-Name, ISIN oder Symbol, Stückzahl, Kaufkurs, Verkaufskurs, Marktpreis, Betrag, Herkunft des Betrags, Gebühren, Steuern, Währung, Datenquelle, Kursquelle und transaction_id.
Broker-CSV-Dateien und data/ enthalten sensible Finanzdaten. Sie gehören nicht in öffentliche Repositories oder unverschlüsselte Freigaben. .gitignore schließt Daten, Exporte, beliebige CSV/JSON, .env, Bytecode und Caches aus; nur die anonymisierte Beispieldatei und Konfigurationsvorlage sind freigegeben. GrowthLedger speichert weder Gegenpartei noch IBAN und niemals API-Schlüssel oder Passwörter.
Unterstützte Anbieter sind Alpha Vantage und Twelve Data. Lege den jeweiligen Schlüssel ausschließlich als Umgebungsvariable fest:
export ALPHA_VANTAGE_API_KEY="..."
python3 growthledger.py update-prices --provider alpha_vantage --symbol EUNL.DEX --history
export TWELVE_DATA_API_KEY="..."
python3 growthledger.py update-prices --provider twelve_data --symbol EUNL:XETR --history--symbol ist der anbieterspezifische Ticker. Bei genau einer vorhandenen Position wird deren ISIN/Symbol automatisch zugeordnet; sonst ist zusätzlich --asset-id IE00B4L5Y983 erforderlich. --history lädt standardmäßig fünf Jahre Historie und speichert Monatsendkurse; Alpha Vantage nutzt dafür seinen langfristigen Monatsendpunkt. Ohne diese Option wird nur der jüngste Kurs gespeichert. --start, --end, --currency und --refresh steuern Zeitraum, Fallback-Währung und Cache-Aktualisierung. Anbieter und Ticker können auch in data/config.json gesetzt werden.
Antworten werden ohne API-Schlüssel unter data/cache/prices/ zwischengespeichert (aktuelle Kurse 15 Minuten, Historie 24 Stunden). API-, Netzwerk-, Symbol- und Ratenlimitfehler führen zu verständlichen Fehlermeldungen und verändern bestehende Transaktionen nicht. Automatische Datensätze erhalten source=market_api, price_capture=automatic und den Anbieter als price_source. Broker-Kaufkurse bleiben unverändert.
Nach mindestens zwei Marktpreisen über mindestens 30 Tage zeigt chart standardmäßig einen Vergleich aus annualisierter historischer Kurssteigerung (CAGR), 3 %, 5 % und 7 %. forecast verwendet ohne Renditeparameter denselben Szenarienvergleich. --return beziehungsweise --returns überschreibt diese Auswahl. Die Historie ist keine Vorhersage oder Garantie.
- Lokale, deutschsprachige Terminalanwendung ausschließlich mit der Python-Standardbibliothek und
Decimal. - Sicherer Trade-Republic-CSV-Import mit Preview, Dry-run, Duplikaterkennung und atomarer Speicherung.
- Manuelle Erfassung von Einzahlungen, Auszahlungen, Käufen, Verkäufen, Dividenden, Gebühren und Steuern.
- Bestands-, Depotwert- und vereinfachte Renditeberechnung mit strikter Trennung von Kontoeinzahlung und ETF-Kauf.
- Berichte, ASCII-Diagramme, Sparplanprognosen und Excel-/ONLYOFFICE-kompatibler CSV-Export.
- Datenschutz durch Verwerfen sensibler Gegenparteidaten und Ignorieren lokaler Finanzdateien in Git.
- Vorbereitete abstrakte
PriceProvider-Schnittstelle, jedoch noch ohne Netzwerk- oder API-Zugriff.
- Konkrete Kursanbieter für Alpha Vantage und Twelve Data über die gemeinsame
PriceProvider-Schnittstelle. - API-Schlüssel werden ausschließlich aus
ALPHA_VANTAGE_API_KEYbeziehungsweiseTWELVE_DATA_API_KEYgelesen und niemals gespeichert. - Neuer Befehl
update-pricesfür aktuelle Kurse und historische Monatsendkurse. - Standardmäßig fünf Jahre Kurshistorie; Alpha Vantage verwendet dafür den langfristigen Monatsendpunkt.
- Lokaler JSON-Cache ohne Zugangsdaten mit 15 Minuten Gültigkeit für aktuelle und 24 Stunden für historische Abfragen.
- Automatische Kurse werden ausschließlich als
market_pricemit Anbieter, Kursdatum, Abrufzeitpunkt undprice_capture=automaticgespeichert. Broker-Kauf- und Verkaufskurse bleiben unverändert. - Gegenüberstellung von ETF-Kaufbeträgen und Depotwert inklusive Kursgewinn oder -verlust.
- Annualisierte historische Kurssteigerung (CAGR) sowie Prognosevergleich mit historischem Wert, 3 %, 5 % und 7 %.
- Prognosetabelle mit tatsächlichen bisherigen CSV-Einzahlungen plus kumulierten künftigen Sparraten und prognostiziertem Depotwert je Jahr; vorhandenes Depotvermögen bleibt getrenntes Startkapital.
- Sicherer Umgang mit Netzwerk-, API-, Symbol- und Ratenlimitfehlern sowie erweiterte netzwerkfreie Tests für beide Anbieter.
python3 -m unittest discover -v
python3 -m py_compile growthledger.pyBeiträge sollten Standardbibliothek und Decimal beibehalten, anonymisierte und netzwerkfreie Tests ergänzen und python3 growthledger.py --help funktionsfähig halten. Fehlerberichte dürfen keine echten Finanzdaten oder API-Schlüssel enthalten.
MIT License, Copyright © 2026 GrowthLedger Contributors. Siehe LICENSE.
GrowthLedger is a local Python 3 terminal application for managing and analysing an ETF savings plan. It imports Trade Republic CSV files, records manual transactions, creates reports and forecasts, draws ASCII charts, and exports tables. Phase 2 can retrieve market prices from Alpha Vantage or Twelve Data when explicitly requested. It uses only the Python standard library and Decimal for financial values.
GrowthLedger is a calculation and record-keeping tool, not financial advice. Forecasts are not return guarantees. Markets fluctuate and losses are possible.
- Python 3.10 or newer
- Linux terminal; no third-party packages
No installation is required after cloning:
python3 growthledger.py --help
python3 growthledger.py sample-data
python3 growthledger.py list
python3 growthledger.py reportOn first access, data/transactions.csv and data/config.json are created automatically. The global --data-dir PATH option selects another data directory. The local configuration contains only Phase 2 metadata such as ETF name, ISIN, ticker, and exchange; config.example.json is its template.
growthledger.py model, storage, import, calculations, and CLI
data/transactions.csv private local data; created automatically
data/config.json local configuration; created automatically
export/ local exports
examples/trade_republic_sample.csv anonymised import file
tests/test_growthledger.py offline unittest suite
config.example.json secret-free configuration template
The single-file implementation is logically separated into model, storage, import, calculations, forecast, charts, export, CLI, and price supply. It can later be split into modules without changing calculation logic.
Add data manually; when no parameters are supplied, the application prompts interactively:
python3 growthledger.py add
python3 growthledger.py add --date 2026-01-01 --amount 25 --buy-price 100 --market-price 102
python3 growthledger.py add --date 2026-01-01 --transaction-type etf_buy \
--amount 25 --buy-price 100 --shares 0.25 --name "Sample ETF" \
--identifier IE00B4L5Y983 --currency EURAn explicitly supplied actual share count takes priority. Only when it is missing for a purchase does GrowthLedger calculate shares = amount / buy_price; a sale uses sell_price correspondingly. Manual prices receive price_source=manual.
List and report:
python3 growthledger.py list
python3 growthledger.py reportlist never shows counterparty names or IBANs. report shows cash flows, purchases, sales, holdings, weighted purchase price, fees, taxes, dividends, market value, and simplified return. If an active position has no market_price, no portfolio value is fabricated.
Forecasts and charts:
python3 growthledger.py forecast --monthly 25 --years 20 --return 5
python3 growthledger.py forecast --monthly 25 --years 20 --returns 3 5 7
python3 growthledger.py forecast --monthly 25 --years 20 --return 5 --start-value 1000
python3 growthledger.py chart
python3 growthledger.py chart --monthly 50 --years 15 --return 6forecast uses the calculable current portfolio value as its default starting value; --start-value overrides it. chart shows cumulative purchases, available historical portfolio values, investment versus portfolio value, and a forecast scenario.
Export and sample data:
python3 growthledger.py export --output export/growthledger.csv
python3 growthledger.py sample-dataSample records are clearly anonymised and are neither inserted twice nor written over existing data.
python3 growthledger.py import-trade-republic --file traderepublic.csv
python3 growthledger.py import-trade-republic --file traderepublic.csv --preview
python3 growthledger.py import-trade-republic --file traderepublic.csv --dry-run
python3 growthledger.py import-trade-republic --file traderepublic.csv --skip-duplicates --verbose--preview validates and displays recognised records; --dry-run validates the entire import. Neither modifies data. By default, a known or file-internal duplicate transaction_id aborts the entire import. --skip-duplicates skips it. Invalid rows never cause a partial import; only after full validation is the target file replaced atomically.
UTF-8 and UTF-8 with BOM, empty fields, and additional columns are supported. Required columns are date, category, type, and transaction_id. Expected fields are:
datetime,date,account_type,category,type,asset_class,name,symbol,shares,price,
amount,fee,tax,currency,original_amount,original_currency,fx_rate,description,
transaction_id,counterparty_name,counterparty_iban,payment_reference,mcc_code
Counterparty, IBAN, payment reference, and MCC are neither stored nor displayed. A complete anonymised file is provided under examples/.
CASH / CUSTOMER_INPAYMENTis a settlement-account deposit (cash_deposit), not a security purchase. It does not increase the ETF purchase total.TRADING / BUY / FUNDwith an identifier,shares, andpriceis an ETF purchase. Broker share count and execution price are preserved exactly.TRADING / SELLis a sale and reduces holdings.- Unambiguous dividends, fees, and taxes are recorded separately. Ambiguous cases are stored as
unknown, never guessed. - Trade Republic's
symbolfield may contain an ISIN such asIE00B4L5Y983; valid ISINs are also stored in the ISIN field. - If
amountis present,amount_source=csv. If it is absent for a purchase or sale, exactshares * priceis calculated withDecimalandamount_source=calculatedis stored.
buy_price or sell_price is the actual broker execution price. market_price is exclusively a manual or automatically retrieved valuation, cut-off-date, or month-end price. A market price never overwrites a purchase price.
Holdings = sum of purchased shares - sum of sold shares
Weighted purchase price = sum(shares × buy_price) / sum of purchased shares
Portfolio value per position = holdings × latest available market_price
Simplified profit/loss = portfolio value + sale proceeds + dividends
- purchase amounts - fees - taxes
Simplified return = profit/loss / (purchase amounts + fees + taxes) × 100
Cash deposits are reported separately and never counted again as invested ETF capital. Sales reduce shares and their proceeds enter the simplified total view. Fees and taxes reduce it; dividends increase it. Without tax-lot assignment, GrowthLedger cannot reliably separate realised and unrealised gains; the display avoids such false precision. No currency conversion is performed for multiple currencies.
Forecasts use the effective monthly rate:
monthly_rate = (1 + annual_rate) ** (1 / 12) - 1
The starting value compounds monthly; the savings contribution is added at the end of each month. The “cumulative contributions” column starts with actual imported cash deposits and then adds planned future savings contributions. ETF purchase amounts are used as a fallback when no cash deposits exist. The current portfolio value remains starting capital and is therefore not added twice.
Transactions are stored as UTF-8 CSV with all model fields in data/transactions.csv. Writes use a temporary file followed by atomic replacement. Decimal values are stored without premature rounding.
The user export is a UTF-8 CSV with BOM and semicolon delimiter for Excel and ONLYOFFICE. Columns: date, transaction type, ETF name, ISIN or symbol, shares, purchase price, sale price, market price, amount, amount origin, fees, taxes, currency, data source, price source, and transaction_id.
Broker CSV files and data/ contain sensitive financial information. Do not put them in public repositories or unencrypted shares. .gitignore excludes data, exports, arbitrary CSV/JSON, .env, bytecode, and caches; only the anonymised sample and configuration template are allowed. GrowthLedger stores neither counterparties nor IBANs, and never API keys or passwords.
Supported providers are Alpha Vantage and Twelve Data. Set the applicable key exclusively as an environment variable:
export ALPHA_VANTAGE_API_KEY="..."
python3 growthledger.py update-prices --provider alpha_vantage --symbol EUNL.DEX --history
export TWELVE_DATA_API_KEY="..."
python3 growthledger.py update-prices --provider twelve_data --symbol EUNL:XETR --history--symbol is the provider-specific ticker. With exactly one existing position, its ISIN/symbol is assigned automatically; otherwise also pass --asset-id IE00B4L5Y983. --history retrieves five years of history by default and stores month-end prices; Alpha Vantage uses its long-term monthly endpoint. Without it, only the latest price is stored. --start, --end, --currency, and --refresh control the period, fallback currency, and cache refresh. Provider and ticker can also be set in data/config.json.
Responses are cached without API keys under data/cache/prices/ (current prices for 15 minutes, history for 24 hours). API, network, symbol, and rate-limit errors produce understandable messages and do not alter existing transactions. Automatic records receive source=market_api, price_capture=automatic, and the provider as price_source. Broker purchase prices remain unchanged.
After at least two market prices spanning at least 30 days, chart shows a default comparison of annualised historical price growth (CAGR), 3%, 5%, and 7%. Without return parameters, forecast uses the same scenario comparison. --return or --returns overrides this selection. Historical growth is not a prediction or guarantee.
- Local German-language terminal application using only the Python standard library and
Decimal. - Safe Trade Republic CSV import with preview, dry-run, duplicate detection, and atomic storage.
- Manual entry of deposits, withdrawals, purchases, sales, dividends, fees, and taxes.
- Holdings, portfolio value, and simplified return calculations with strict separation of cash deposits and ETF purchases.
- Reports, ASCII charts, savings-plan forecasts, and Excel-/ONLYOFFICE-compatible CSV export.
- Privacy protection by discarding sensitive counterparty data and excluding local financial files from Git.
- Prepared abstract
PriceProviderinterface, but no network or API access yet.
- Concrete Alpha Vantage and Twelve Data providers using the shared
PriceProviderinterface. - API keys are read exclusively from
ALPHA_VANTAGE_API_KEYorTWELVE_DATA_API_KEYand are never stored. - New
update-pricescommand for current prices and historical month-end prices. - Five years of price history by default; Alpha Vantage uses its long-term monthly endpoint.
- Local credential-free JSON cache with a 15-minute lifetime for current and 24-hour lifetime for historical requests.
- Automatic prices are stored only as
market_price, including provider, price date, retrieval timestamp, andprice_capture=automatic. Broker purchase and sale prices remain unchanged. - Comparison of ETF purchase amounts and portfolio value, including price gain or loss.
- Annualised historical price growth (CAGR) plus a forecast comparison using the historical value, 3%, 5%, and 7%.
- Forecast table with actual previous CSV deposits plus cumulative future savings contributions and projected portfolio value for every year; existing portfolio value remains separate starting capital.
- Safe handling of network, API, symbol, and rate-limit errors, plus extended network-free tests for both providers.
python3 -m unittest discover -v
python3 -m py_compile growthledger.pyContributions should retain standard-library-only operation and Decimal, add anonymised network-free tests, and keep python3 growthledger.py --help working. Bug reports must not contain real financial data or API keys.
MIT License, Copyright © 2026 GrowthLedger Contributors. See LICENSE.