Verbraucherkredit & Annuitätendarlehen mit anfänglicher Tilgung.

Python PyQt6 Applikation

Zurück

Moin moin 💸! Willst du schon immer mal wissen, wie viel ein 10.000 € Kredit bei 5 % Zinsen wirklich kostet? (Spoiler: Die Bank verdient gut daran 😉).

Willkommen im echten Leben! Ein Ort, an dem Dinge teuer sind und Banken einem Geld leihen – aber leider nicht aus Nächstenliebe. Wer die Zinsen nicht versteht, zahlt am Ende drauf. Wer sie aber selbst in Python berechnen kann, behält die Kontrolle. In diesem Paket findest du den vollständigen Code für eine echte Finanz-App mit Benutzeroberfläche (PyQt6), Tabellen und Live-Diagrammen (Matplotlib). Wenn du das Feld „Tilgung“ leer lasst, rechnet die App im Hintergrund magisch die perfekte Monatsrate für dich aus. Speichere den Code ab, installiert die zwei Bibliotheken (Pip-Packages) und schau dir an, wie aus einfachen Formeln eine Python - Anwendung wird!


Verbraucherkredit


Das Eingabefeld „Tilgung“ ist leer! Klassischer Verbraucherkredit.
Ein klassischer Verbraucherkredit (auch Ratenkredit genannt) wird als Annuitätendarlehen berechnet. Das bedeutet, dass die monatliche Kreditrate über die gesamte Laufzeit hinweg konstant gleichbleibt. Die Rate setzt sich jeden Monat neu aus einem Zinsanteil und einem Tilgungsanteil zusammen. Da der Zinssatz und die Laufzeit vorgegeben sind und am Ende keine Restschuld verbleiben darf, ergibt sich die anfängliche Tilgung automatisch aus der Laufzeit.


Excel


Verbraucherkredit: CSV-Export. Visualisierung in Excel.

Verb_Berechnen


Wie wird ein Verbraucherkredit berechnet? Das obige Bild zeigt die mathematischen Zusammenhänge.

Python Code
#!/usr/bin/env python3 # ***************************************************************************** # Darlehensrechner (PyQt6). Berechnet die monatliche Rate eines Darlehens. # Ist das Tilgung-Feld leer, wird die Tilgung automatisch so berechnet, dass # das Darlehen am Ende der Laufzeit exakt auf 0 EUR steht. (klassischer # Verbraucherkredit / Annuitätendarlehen). Ist eine Tilgung angegeben, wird # die monatliche Rate aus Zinssatz + Tilgungssatz gebildet (klassisches # Annuitätendarlehen mit Zinsbindung). Reicht die Laufzeit nicht für die # Volltilgung, wird die verbleibende Restschuld ausgewiesen. # Benötigte Pakete: pip install PyQt6 matplotlib ; pip install openpyxl # optional, für Excel-Export. (c) Hans Busche Datum: 04.09.2026 # ***************************************************************************** import sys, os import csv from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QGroupBox, QLineEdit, QLabel, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QSplitter, QFrame, QComboBox, QFileDialog, QMessageBox, ) from PyQt6.QtCore import Qt from PyQt6.QtGui import QDoubleValidator, QIcon import matplotlib matplotlib.use("QtAgg") from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg from matplotlib.figure import Figure try: from openpyxl import Workbook from openpyxl.styles import Font, PatternFill, Alignment OPENPYXL_AVAILABLE = True except ImportError: OPENPYXL_AVAILABLE = False # Verzeichnis dieser Datei, damit das Icon unabhängig vom Startort gefunden wird BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ICON_PATH = os.path.join(BASE_DIR, "Schaefer.ico") # ***************************************************************************** # Farben / Dark-Theme # ***************************************************************************** BG_MAIN = "#1e1f26" BG_PANEL = "#262832" BG_INPUT = "#2f313d" BORDER = "#3c3f4c" ACCENT = "#5b8def" ACCENT_HOVER = "#75a1f5" TEXT_MAIN = "#e6e6ea" TEXT_MUTED = "#9a9cab" COLOR_OK = "#4caf7d" COLOR_ERROR = "#e0645f" COLOR_WARN = "#e0a85f" DARK_STYLESHEET = f""" QMainWindow {{ background-color: {BG_MAIN}; }} QWidget {{ background-color: {BG_MAIN}; color: {TEXT_MAIN}; font-family: "Segoe UI", Arial, sans-serif; font-size: 13px; }} QGroupBox {{ background-color: {BG_PANEL}; border: 1px solid {BORDER}; border-radius: 8px; margin-top: 14px; padding: 14px 10px 10px 10px; font-weight: 600; color: {TEXT_MAIN}; }} QGroupBox::title {{ subcontrol-origin: margin; left: 12px; padding: 0 6px; color: {ACCENT}; }} QLabel {{ background: transparent; color: {TEXT_MAIN}; }} QLabel[role="fieldLabel"] {{ color: {TEXT_MUTED}; }} QLabel[role="outputValue"] {{ font-size: 16px; font-weight: 700; color: {ACCENT}; }} QLabel[role="status"] {{ font-weight: 600; }} QLineEdit {{ background-color: {BG_INPUT}; border: 1px solid {BORDER}; border-radius: 6px; padding: 6px 8px; color: {TEXT_MAIN}; selection-background-color: {ACCENT}; }} QLineEdit:focus {{ border: 1px solid {ACCENT}; }} QComboBox {{ background-color: {BG_INPUT}; border: 1px solid {BORDER}; border-radius: 6px; padding: 6px 8px; color: {TEXT_MAIN}; }} QComboBox:focus {{ border: 1px solid {ACCENT}; }} QComboBox::drop-down {{ border: none; width: 22px; }} QComboBox QAbstractItemView {{ background-color: {BG_INPUT}; border: 1px solid {BORDER}; selection-background-color: {ACCENT}; selection-color: #12131a; color: {TEXT_MAIN}; outline: none; }} QPushButton {{ background-color: {ACCENT}; color: #12131a; border: none; border-radius: 6px; padding: 9px 18px; font-weight: 700; }} QPushButton[role="secondary"] {{ background-color: {BG_INPUT}; color: {TEXT_MAIN}; border: 1px solid {BORDER}; }} QPushButton[role="secondary"]:hover {{ background-color: {BORDER}; }} QPushButton:hover {{ background-color: {ACCENT_HOVER}; }} QPushButton:pressed {{ background-color: #4472c4; }} QTableWidget {{ background-color: {BG_INPUT}; alternate-background-color: #35374296; gridline-color: {BORDER}; border: 1px solid {BORDER}; border-radius: 6px; color: {TEXT_MAIN}; }} QHeaderView::section {{ background-color: {BG_PANEL}; color: {TEXT_MUTED}; padding: 6px; border: none; border-bottom: 1px solid {BORDER}; font-weight: 600; }} QTableWidget::item:selected {{ background-color: {ACCENT}; color: #12131a; }} QScrollBar:vertical {{ background: {BG_PANEL}; width: 12px; margin: 0; }} QScrollBar::handle:vertical {{ background: {BORDER}; min-height: 20px; border-radius: 6px; }} QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{ height: 0; }} QFrame[role="divider"] {{ background-color: {BORDER}; max-height: 1px; min-height: 1px; }} """ # ***************************************************************************** # Wandelt Text (mit Komma oder Punkt) in float um. None bei leerem Feld. # ***************************************************************************** def parse_float(text: str): text = text.strip().replace(".", "").replace(",", ".") if "," in text else text.strip() if text == "": return None return float(text) # ***************************************************************************** # Hauptfenster # ***************************************************************************** class LoanCalculator(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Darlehensrechner") self.resize(1180, 760) # Icon if os.path.exists(ICON_PATH): self.setWindowIcon(QIcon(ICON_PATH)) self.setStyleSheet(DARK_STYLESHEET) self._last_rows = None self._last_meta = None central = QWidget() self.setCentralWidget(central) root = QVBoxLayout(central) root.setContentsMargins(16, 16, 16, 16) root.setSpacing(12) root.addWidget(self._build_input_group()) root.addWidget(self._build_output_group()) splitter = QSplitter(Qt.Orientation.Horizontal) splitter.addWidget(self._build_table_group()) splitter.addWidget(self._build_chart_group()) splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 1) root.addWidget(splitter, stretch=1) # ***************************************************************************** # UI-Aufbau # ***************************************************************************** def _build_input_group(self) -> QGroupBox: box = QGroupBox("Darlehensdaten") layout = QHBoxLayout() layout.setSpacing(20) box.setLayout(layout) validator = QDoubleValidator(0.0, 999999999.0, 2) validator.setNotation(QDoubleValidator.Notation.StandardNotation) def make_field(label_text, placeholder=""): v_layout = QVBoxLayout() lbl = QLabel(label_text) lbl.setProperty("role", "fieldLabel") edit = QLineEdit() edit.setPlaceholderText(placeholder) v_layout.addWidget(lbl) v_layout.addWidget(edit) return v_layout, edit l1, self.input_darlehen = make_field("Darlehen (EUR)", "z. B. 250000") l3, self.input_zins = make_field("Zinssatz (% p. a.)", "z. B. 3,5") l4, self.input_tilgung = make_field("Tilgung (% p. a.) – optional", "leer = Verbraucherkredit") # Laufzeit-Feld mit Einheiten-Umschaltung (Jahre / Monate) l2 = QVBoxLayout() laufzeit_lbl = QLabel("Laufzeit") laufzeit_lbl.setProperty("role", "fieldLabel") laufzeit_row = QHBoxLayout() self.input_laufzeit = QLineEdit() self.input_laufzeit.setPlaceholderText("z. B. 20") self.laufzeit_unit = QComboBox() self.laufzeit_unit.addItems(["Jahre", "Monate"]) self.laufzeit_unit.currentTextChanged.connect(self._on_laufzeit_unit_changed) laufzeit_row.addWidget(self.input_laufzeit, stretch=2) laufzeit_row.addWidget(self.laufzeit_unit, stretch=1) l2.addWidget(laufzeit_lbl) l2.addLayout(laufzeit_row) for l in (l1, l2, l3, l4): layout.addLayout(l) btn_layout = QVBoxLayout() btn_layout.addWidget(QLabel("")) # Ausrichtung zur Feldhöhe self.btn_calc = QPushButton("Berechnen") self.btn_calc.clicked.connect(self.calculate) btn_layout.addWidget(self.btn_calc) layout.addLayout(btn_layout) return box def _on_laufzeit_unit_changed(self, unit_text: str): if unit_text == "Jahre": self.input_laufzeit.setPlaceholderText("z. B. 20") else: self.input_laufzeit.setPlaceholderText("z. B. 240") def _build_output_group(self) -> QGroupBox: box = QGroupBox("Ergebnis") layout = QHBoxLayout() layout.setSpacing(30) box.setLayout(layout) def make_output(title): v = QVBoxLayout() lbl_title = QLabel(title) lbl_title.setProperty("role", "fieldLabel") lbl_value = QLabel("–") lbl_value.setProperty("role", "outputValue") v.addWidget(lbl_title) v.addWidget(lbl_value) return v, lbl_value v1, self.out_rate = make_output("Monatliche Zahlung") v2, self.out_gesamt = make_output("Gesamt bezahlt") v3, self.out_zinsen = make_output("Bezahlte Zinsen") v4, self.out_restschuld = make_output("Restschuld nach Laufzeit") for v in (v1, v2, v3, v4): layout.addLayout(v) layout.addStretch(1) status_layout = QVBoxLayout() status_title = QLabel("Status") status_title.setProperty("role", "fieldLabel") self.out_status = QLabel("Bereit.") self.out_status.setProperty("role", "status") self.out_status.setStyleSheet(f"color: {TEXT_MUTED};") self.out_status.setWordWrap(True) status_layout.addWidget(status_title) status_layout.addWidget(self.out_status) layout.addLayout(status_layout, stretch=1) return box def _build_table_group(self) -> QGroupBox: box = QGroupBox("Tilgungsplan") layout = QVBoxLayout() box.setLayout(layout) self.table = QTableWidget(0, 5) self.table.setHorizontalHeaderLabels( ["Monat", "Rate (EUR)", "Zinsanteil (EUR)", "Tilgungsanteil (EUR)", "Restschuld (EUR)"] ) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.table.verticalHeader().setVisible(False) self.table.setAlternatingRowColors(True) self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) layout.addWidget(self.table) export_row = QHBoxLayout() self.btn_export_csv = QPushButton("Als CSV exportieren") self.btn_export_csv.setProperty("role", "secondary") self.btn_export_csv.clicked.connect(self.export_csv) self.btn_export_excel = QPushButton("Als Excel exportieren") self.btn_export_excel.setProperty("role", "secondary") self.btn_export_excel.clicked.connect(self.export_excel) if not OPENPYXL_AVAILABLE: self.btn_export_excel.setToolTip("Benötigt 'openpyxl' (pip install openpyxl)") export_row.addWidget(self.btn_export_csv) export_row.addWidget(self.btn_export_excel) export_row.addStretch(1) layout.addLayout(export_row) return box def _build_chart_group(self) -> QGroupBox: box = QGroupBox("Saldo- und Rückzahlungsverlauf") layout = QVBoxLayout() box.setLayout(layout) self.figure = Figure(figsize=(5, 4), facecolor=BG_PANEL) self.canvas = FigureCanvasQTAgg(self.figure) layout.addWidget(self.canvas) self._draw_empty_chart() return box def _style_axes(self, ax): ax.set_facecolor(BG_INPUT) ax.tick_params(colors=TEXT_MUTED) for spine in ax.spines.values(): spine.set_color(BORDER) ax.xaxis.label.set_color(TEXT_MUTED) ax.yaxis.label.set_color(TEXT_MUTED) ax.title.set_color(TEXT_MAIN) ax.grid(True, color=BORDER, linewidth=0.6, alpha=0.5) def _draw_empty_chart(self): self.figure.clear() ax = self.figure.add_subplot(111) self._style_axes(ax) ax.set_title("Noch keine Berechnung") ax.set_xlabel("Monat") ax.set_ylabel("EUR") self.figure.tight_layout() self.canvas.draw() # ***************************************************************************** # Berechnung # ***************************************************************************** def _set_status(self, text: str, kind: str = "info"): color = {"info": TEXT_MUTED, "ok": COLOR_OK, "error": COLOR_ERROR, "warn": COLOR_WARN}[kind] self.out_status.setStyleSheet(f"color: {color};") self.out_status.setText(text) def _reset_outputs(self): for lbl in (self.out_rate, self.out_gesamt, self.out_zinsen, self.out_restschuld): lbl.setText("–") self.table.setRowCount(0) self._draw_empty_chart() self._last_rows = None self._last_meta = None def calculate(self): self._reset_outputs() # ---------- Eingaben einlesen & validieren ---------- try: darlehen = parse_float(self.input_darlehen.text()) laufzeit_wert = parse_float(self.input_laufzeit.text()) zins_prozent = parse_float(self.input_zins.text()) tilgung_prozent = parse_float(self.input_tilgung.text()) except ValueError: self._set_status("Bitte gültige Zahlen eingeben (z. B. 3,5 oder 3.5).", "error") return laufzeit_einheit = self.laufzeit_unit.currentText() # "Jahre" oder "Monate" if darlehen is None or darlehen <= 0: self._set_status("Bitte ein Darlehen größer 0 EUR eingeben.", "error") return if laufzeit_wert is None or laufzeit_wert <= 0: self._set_status(f"Bitte eine Laufzeit größer 0 {laufzeit_einheit} eingeben.", "error") return if zins_prozent is None or zins_prozent < 0: self._set_status("Bitte einen gültigen Zinssatz (>= 0 %) eingeben.", "error") return if tilgung_prozent is not None and tilgung_prozent < 0: self._set_status("Die Tilgung darf nicht negativ sein.", "error") return if laufzeit_einheit == "Jahre": n_monate = int(round(laufzeit_wert * 12)) else: n_monate = int(round(laufzeit_wert)) if n_monate <= 0: self._set_status("Die Laufzeit muss mindestens 1 Monat betragen.", "error") return zins_monat = zins_prozent / 100 / 12 # ---------- Monatsrate ermitteln ---------- auto_tilgung = tilgung_prozent is None if auto_tilgung: # Klassischer Verbraucherkredit: Rate so, dass am Laufzeitende exakt 0 EUR if zins_monat > 0: rate = darlehen * zins_monat / (1 - (1 + zins_monat) ** (-n_monate)) else: rate = darlehen / n_monate planungs_monate = n_monate else: # Annuitätendarlehen mit vorgegebenem Tilgungssatz rate = darlehen * (zins_prozent / 100 + tilgung_prozent / 100) / 12 if rate <= darlehen * zins_monat and zins_monat > 0: self._set_status( "Die Rate aus Zins + Tilgung deckt nicht einmal die Zinsen. " "Bitte Tilgungssatz erhöhen.", "error" ) return planungs_monate = n_monate # Tabelle zeigt max. die eingegebene Laufzeit # ---------- Tilgungsplan berechnen ---------- rows = [] # (monat, rate, zins, tilgung, restschuld) saldo = darlehen gesamt_gezahlt = 0.0 gesamt_zinsen = 0.0 monat = 0 vollstaendig_getilgt = False while monat < planungs_monate and saldo > 0.01: monat += 1 zins_anteil = saldo * zins_monat tilgung_anteil = rate - zins_anteil # Letzte Rate ggf. kappen, falls Restschuld kleiner als Tilgungsanteil if tilgung_anteil >= saldo: tilgung_anteil = saldo akt_rate = zins_anteil + tilgung_anteil saldo = 0.0 vollstaendig_getilgt = True else: akt_rate = rate saldo -= tilgung_anteil gesamt_gezahlt += akt_rate gesamt_zinsen += zins_anteil rows.append((monat, akt_rate, zins_anteil, tilgung_anteil, saldo)) if auto_tilgung: vollstaendig_getilgt = True # per Konstruktion exakt 0 restschuld_final = rows[-1][4] if rows else darlehen # ---------- Tabelle füllen ---------- self.table.setRowCount(len(rows)) for i, (m, r_, z_, t_, s_) in enumerate(rows): values = [ str(m), f"{r_:,.2f}", f"{z_:,.2f}", f"{t_:,.2f}", f"{s_:,.2f}", ] for col, val in enumerate(values): item = QTableWidgetItem(val) if col > 0: item.setTextAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) self.table.setItem(i, col, item) # ---------- Ausgabefelder ---------- self.out_rate.setText(f"{rate:,.2f} EUR") self.out_gesamt.setText(f"{gesamt_gezahlt:,.2f} EUR") self.out_zinsen.setText(f"{gesamt_zinsen:,.2f} EUR") if vollstaendig_getilgt: self.out_restschuld.setText("0,00 EUR") else: self.out_restschuld.setText(f"{restschuld_final:,.2f} EUR") if vollstaendig_getilgt: self._set_status( f"Berechnung erfolgreich. Das Darlehen ist nach {len(rows)} Monaten vollständig getilgt.", "ok", ) else: self._set_status( "Berechnung erfolgreich. Mit dem gewählten Tilgungssatz ist das Darlehen " "innerhalb der Laufzeit nicht vollständig getilgt (siehe Restschuld).", "warn", ) # ---------- Chart zeichnen ---------- self._draw_chart(darlehen, rows) # ---------- Für Export zwischenspeichern ---------- self._last_rows = rows self._last_meta = { "darlehen": darlehen, "laufzeit_wert": laufzeit_wert, "laufzeit_einheit": laufzeit_einheit, "zins_prozent": zins_prozent, "tilgung_prozent": tilgung_prozent, "auto_tilgung": auto_tilgung, "rate": rate, "gesamt_gezahlt": gesamt_gezahlt, "gesamt_zinsen": gesamt_zinsen, "restschuld": 0.0 if vollstaendig_getilgt else restschuld_final, "vollstaendig_getilgt": vollstaendig_getilgt, } def _draw_chart(self, darlehen: float, rows): self.figure.clear() ax = self.figure.add_subplot(111) self._style_axes(ax) monate = [r[0] for r in rows] restschuld = [r[4] for r in rows] kumuliert_gezahlt = [] laufsumme = 0.0 for r in rows: laufsumme += r[1] kumuliert_gezahlt.append(laufsumme) ax.plot(monate, restschuld, color=ACCENT, linewidth=2.2, label="Kredit-Saldo (Restschuld)") ax.plot(monate, kumuliert_gezahlt, color=COLOR_WARN, linewidth=2.0, linestyle="--", label="Gesamtrückzahlung (kumuliert)") ax.axhline(darlehen, color=TEXT_MUTED, linewidth=1.0, linestyle=":", label="Ursprüngliches Darlehen") ax.set_xlabel("Monat") ax.set_ylabel("EUR") ax.set_title("Darlehen: Saldo- und Rückzahlungsverlauf") legend = ax.legend(facecolor=BG_INPUT, edgecolor=BORDER, labelcolor=TEXT_MAIN, fontsize=9) legend.get_frame().set_alpha(0.9) self.figure.tight_layout() self.canvas.draw() # ***************************************************************************** # Export # ***************************************************************************** def _summary_lines(self) -> list: m = self._last_meta art = "automatisch berechnet" if m["auto_tilgung"] else f"{m['tilgung_prozent']:.2f} %" return [ ("Darlehen (EUR)", f"{m['darlehen']:.2f}"), ("Laufzeit", f"{m['laufzeit_wert']:g} {m['laufzeit_einheit']}"), ("Zinssatz (% p. a.)", f"{m['zins_prozent']:.2f}"), ("Tilgung (% p. a.)", art), ("Monatliche Rate (EUR)", f"{m['rate']:.2f}"), ("Gesamt bezahlt (EUR)", f"{m['gesamt_gezahlt']:.2f}"), ("Bezahlte Zinsen (EUR)", f"{m['gesamt_zinsen']:.2f}"), ("Restschuld nach Laufzeit (EUR)", f"{m['restschuld']:.2f}"), ] def export_csv(self): if not self._last_rows: self._set_status("Bitte zuerst eine Berechnung durchführen.", "error") return path, _ = QFileDialog.getSaveFileName( self, "Tilgungsplan als CSV speichern", "tilgungsplan.csv", "CSV-Dateien (*.csv)" ) if not path: return try: with open(path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f, delimiter=";") writer.writerow(["Darlehensrechner – Zusammenfassung"]) for label, value in self._summary_lines(): writer.writerow([label, value]) writer.writerow([]) writer.writerow( ["Monat", "Rate (EUR)", "Zinsanteil (EUR)", "Tilgungsanteil (EUR)", "Restschuld (EUR)"] ) for m_, r_, z_, t_, s_ in self._last_rows: writer.writerow([m_, f"{r_:.2f}", f"{z_:.2f}", f"{t_:.2f}", f"{s_:.2f}"]) self._set_status(f"CSV erfolgreich gespeichert: {path}", "ok") except OSError as exc: self._set_status(f"CSV konnte nicht gespeichert werden: {exc}", "error") def export_excel(self): if not self._last_rows: self._set_status("Bitte zuerst eine Berechnung durchführen.", "error") return if not OPENPYXL_AVAILABLE: QMessageBox.warning( self, "openpyxl fehlt", "Für den Excel-Export wird das Paket 'openpyxl' benötigt.\n" "Bitte installieren mit: pip install openpyxl", ) return path, _ = QFileDialog.getSaveFileName( self, "Tilgungsplan als Excel speichern", "tilgungsplan.xlsx", "Excel-Dateien (*.xlsx)" ) if not path: return try: wb = Workbook() ws_summary = wb.active ws_summary.title = "Zusammenfassung" header_font = Font(bold=True, color="FFFFFF") header_fill = PatternFill(start_color="5B8DEF", end_color="5B8DEF", fill_type="solid") ws_summary.append(["Darlehensrechner – Zusammenfassung", ""]) ws_summary["A1"].font = Font(bold=True, size=13) ws_summary.append([]) for label, value in self._summary_lines(): ws_summary.append([label, value]) ws_summary.column_dimensions["A"].width = 30 ws_summary.column_dimensions["B"].width = 20 ws_plan = wb.create_sheet("Tilgungsplan") headers = ["Monat", "Rate (EUR)", "Zinsanteil (EUR)", "Tilgungsanteil (EUR)", "Restschuld (EUR)"] ws_plan.append(headers) for col in range(1, len(headers) + 1): cell = ws_plan.cell(row=1, column=col) cell.font = header_font cell.fill = header_fill cell.alignment = Alignment(horizontal="center") for m_, r_, z_, t_, s_ in self._last_rows: ws_plan.append([m_, round(r_, 2), round(z_, 2), round(t_, 2), round(s_, 2)]) for i, width in enumerate([8, 14, 16, 18, 16], start=1): ws_plan.column_dimensions[ws_plan.cell(row=1, column=i).column_letter].width = width wb.save(path) self._set_status(f"Excel-Datei erfolgreich gespeichert: {path}", "ok") except OSError as exc: self._set_status(f"Excel-Datei konnte nicht gespeichert werden: {exc}", "error") def main(): app = QApplication(sys.argv) app.setStyle("Fusion") window = LoanCalculator() window.show() sys.exit(app.exec()) if __name__ == "__main__": main() # ***************************************************************************** # Ende # *****************************************************************************


Dark Mode: Abgedunkelte Oberflächen mit farbigen Akzenten (Blau, Rot, Grün für den Graphen).

Dynamische Berechnung: Wenn Tilgung leer ist, berechnet Python mittels Annuitätenformel die exakte monatliche Rate, sodass am Ende genau Null € Restschuld bleiben. wenn Tilgung ausgefüllt ist, wird die monatliche Rate basierend auf Zins + Tilgung angesetzt und der Tilgungsplan bis zum Erreichen der Restschuld von Null € abgetragen.

Ergebnisse & Validierung: Auswertung von Rate, Gesamtbetrag und Zinsen inklusive Fehlerdialogen bei ungültigen Eingaben.

Tabelle & Graph: Der Verlauf zeigt übersichtlich die kumulierte Rückzahlung sowie der monatlich sinkende Saldo.


Darlehen


Das Eingabefeld „Tilgung“ ist ausgefüllt! Diese Rate bleibt fix, der Zinsanteil sinkt, der Tilgungsanteil steigt automatisch. Ist die Laufzeit kürzer als die tatsächliche Volltilgungsdauer, gibt es eine Restschuld – genau wie bei einem echten Bankdarlehen mit Zinsbindung.

Darlehen berechnen


Wie wird ein Darlehen, mit vorgegebener Tilgung, berechnet? Das obige Bild zeigt die mathematischen Zusammenhänge.

Python Code
kapital = 25000 zinsen = 0.036 tilgen = 0.02 monatliche_rate = (kapital * (zinsen + tilgen)) / 12 aktueller_kontostand = kapital for m in range(1, 344): der_zins = aktueller_kontostand * (zinsen / 12) getilgt = monatliche_rate - der_zins aktueller_kontostand -= getilgt if m == 120: print(f"Nach 120 Monaten (10 Jahre): Restschuld = {aktueller_kontostand:.2f}") if m == 240: print(f"Nach 240 Monaten (20 Jahre): Restschuld = {aktueller_kontostand:.2f}") if m == 343: print(f"Nach 343 Monaten (28 Jahre): Restschuld = {aktueller_kontostand:.2f}") print("Das Programm ist fertig.") # Verhindert das automatische Schließen input("\nDrücke Enter, um das Programm zu beenden...")


Eine Python Konsolen Anwendung für ein Darlehen. Hier kannst du schnell etwas ausprobieren.

Python Code
p = 5000 zinsen = 0.034 n_monate = 24 i_monate = zinsen / 12 mon_rate = p * (i_monate * (1 + i_monate)**n_monate) / ((1 + i_monate)**n_monate - 1) print(f"Monatliche Rate: {mon_rate:.2f}") restschuld = p gesamtzinsen = 0 for m in range(1, n_monate + 1): zinszahlung = restschuld * i_monate rueckzahlung_kapital = mon_rate - zinszahlung restschuld -= rueckzahlung_kapital gesamtzinsen += zinszahlung print(f"Gesamtzinsen: {gesamtzinsen:.2f}") print(f"Gesamtzahlung: {p + gesamtzinsen:.2f}") print("Das Programm ist fertig.") # Verhindert das automatische Schließen input("\nDrücke Enter, um das Programm zu beenden...")


Eine Python Konsolen Anwendung für ein Verbraucherkredit. Hier kannst du schnell etwas ausprobieren.

Zurück