Для этого я написал этот код на Python, который пытается улучшить работу другого человека:
Код: Выделить всё
import datetime as dt
from dateutil.relativedelta import relativedelta
import matplotlib.pyplot as plt
import numpy_financial as npf
import pandas as pd
import sqlite3
class Loan:
# Definiamo i parametri del prestito e scriviamo una funzione che consente di vedere quale prestito è attivo
loans = []
def __init__(self, rate, term, loan_amount, amortization_type, frequency, start=dt.date.today().isoformat()):
self.rate = rate
self.term = term
self.loan_amount = loan_amount
self.start = dt.datetime.fromisoformat(start).replace(day=1)
self.frequency = frequency
self.periods = self.calculate_periods()
self.period_rate = self.calculate_period_rate()
self.pmt = npf.pmt(self.period_rate, self.periods, -self.loan_amount)
self.pmt_str = f"€ {self.pmt:,.2f}"
self.amortization_type = amortization_type
self.table = self.loan_table()
self.active = False # Add an attribute to track if this loan is active
Loan.loans.append(self)
def calculate_periods(self):
if self.frequency == 'monthly':
return self.term * 12
elif self.frequency == 'quarterly':
return self.term * 4
elif self.frequency == 'semi-annual':
return self.term * 2
elif self.frequency == 'annual':
return self.term
else:
raise ValueError("Unsupported frequency")
def calculate_period_rate(self):
if self.frequency == 'monthly':
return self.rate / 12
elif self.frequency == 'quarterly':
return self.rate / 4
elif self.frequency == 'semi-annual':
return self.rate / 2
elif self.frequency == 'annual':
return self.rate
else:
raise ValueError("Unsupported frequency")
def set_as_active_loan(self):
for loan in Loan.loans:
loan.active = False # Set all loans to inactive
self.active = True # Set this loan as active
# Definiamo la struttura del piano d'ammortamento in base ai tre metodi principali (Italiano, Francese e Tedesco)
def loan_table(self):
if self.frequency == 'monthly':
periods = [self.start + relativedelta(months=x) for x in range(self.periods)]
elif self.frequency == 'quarterly':
periods = [self.start + relativedelta(months=3*x) for x in range(self.periods)]
elif self.frequency == 'semi-annual':
periods = [self.start + relativedelta(months=6*x) for x in range(self.periods)]
elif self.frequency == 'annual':
periods = [self.start + relativedelta(years=x) for x in range(self.periods)]
else:
raise ValueError("Unsupported frequency")
# Differenziamo i due tipi di ammortamento
if self.amortization_type == "French":
interest = [npf.ipmt(self.period_rate, month, self.periods, -self.loan_amount, when="end")
for month in range(1, self.periods + 1)]
principal = [npf.ppmt(self.period_rate, month, self.periods, -self.loan_amount)
for month in range(1, self.periods + 1)]
table = pd.DataFrame({'Payment': self.pmt,
'Interest': interest,
'Principal': principal}, index=pd.to_datetime(periods))
table['Balance'] = self.loan_amount - table['Principal'].cumsum()
elif self.amortization_type == "Italian":
interest = [self.loan_amount * self.period_rate]
principal_payment = self.loan_amount / self.periods
principal = [principal_payment]
payment = [interest[0] + principal[0]]
for month in range(1, self.periods):
interest_payment = (self.loan_amount - (month) * principal_payment) * self.period_rate
interest.append(interest_payment)
principal.append(principal_payment)
payment.append(interest_payment + principal_payment)
principal[-1] = self.loan_amount - sum(principal[:-1])
payment[-1] = interest[-1] + principal[-1]
table = pd.DataFrame({'Payment': payment,
'Interest': interest,
'Principal': principal}, index=pd.to_datetime(periods))
table['Balance'] = self.loan_amount - table['Principal'].cumsum()
else:
raise ValueError("Unsupported amortization type")
return table.round(2)
# Potrebbe essere utile pure rappresentare graficamente i risultati
def plot_balances(self):
amort = self.loan_table()
if self.amortization_type == "French":
plt.title("French Amortization Interest and Balance")
elif self.amortization_type == "Italian":
plt.title("Italian amortization Interest and Balance")
else:
plt.title("Unknown Amortization")
plt.plot(amort.Balance, label='Balance (€)')
plt.plot(amort.Interest.cumsum(), label='Interest Paid (€)')
plt.grid(axis='y', alpha=.5)
plt.legend(loc=8)
plt.show()
# Così come è utile riassumere le informazioni principali del mutuo sulla base - anche - del tipo di ammortamento utilizzato
def summary(self):
print("Summary")
print("-" * 60)
if self.amortization_type == "French":
print(f'Payment (French amortization): {self.pmt_str:>21}')
elif self.amortization_type == "Italian":
italian_payment = self.table['Payment'].iloc[0]
print(f'Payment (Italian Amortization): €{italian_payment:,.2f}')
print(f'{"Payoff Date:":19s} {self.table.index.date[-1]}')
print(f'Interest Paid: €{self.table["Interest"].cumsum()[-1]:,.2f}')
print("-" * 60)
# Aggiungiamo qualche bonus: 1: vediamo cosa succede quando paghiamo di più
def pay_early(self, extra_amt):
return f'{round(npf.nper(self.period_rate, self.pmt + extra_amt, -self.loan_amount) / self.calculate_periods(), 2)}'
# 2: Vediamo cosa succede quando impostiamo un tempo specifico per essere liberi dal mutuo
def retire_debt(self, years_to_debt_free):
extra_pmt = 1
while npf.nper(self.period_rate, self.pmt + extra_pmt, -self.loan_amount) / self.calculate_periods() > years_to_debt_free:
extra_pmt += 1
return extra_pmt, self.pmt + extra_pmt
# 3: aggiungiamo la possibilità di modificare i parametri del prestito
def edit_loan(self, new_rate, new_term, new_loan_amount, new_amortization_type, new_frequency):
self.rate = new_rate
self.term = new_term
self.loan_amount = new_loan_amount
self.amortization_type = new_amortization_type
self.frequency = new_frequency
self.periods = self.calculate_periods()
self.period_rate = self.calculate_period_rate()
self.pmt = npf.pmt(self.period_rate, self.periods, -self.loan_amount)
self.pmt_str = f"€ {self.pmt:,.2f}"
self.table = self.loan_table()
# 4: aggiungiamo la possibilità di confrontare due prestiti diversi
@classmethod
def compare_loans(cls, loans):
if len(loans) < 2:
print("Please set at least two loans for comparison.")
return
print("Comparison of loans:")
print("-" * 60)
for i, loan in enumerate(loans):
if loan.amortization_type == "French":
monthly_payment = loan.pmt
elif loan.amortization_type == "Italian":
monthly_payment = loan.table['Payment'].iloc[0]
print(f"Loan {i + 1} - Monthly Payment: €{monthly_payment:,.2f}")
print("-" * 60)
min_payment_loan = min(loans, key=lambda loan: loan.pmt)
max_payment_loan = max(loans, key=lambda loan: loan.pmt)
print(f"Loan with the lowest monthly payment: Loan {loans.index(min_payment_loan) + 1}")
print(f"Loan with the highest monthly payment: Loan {loans.index(max_payment_loan) + 1}")
for i, loan in enumerate(loans):
print(f"Loan {i + 1} - Payoff Date: {loan.table.index.date[-1]}, Interest Paid: €{loan.table['Interest'].cumsum().iloc[-1]:,.2f}")
min_interest_loan = min(loans, key=lambda loan: loan.table['Interest'].cumsum().iloc[-1])
max_interest_loan = max(loans, key=lambda loan: loan.table['Interest'].cumsum().iloc[-1])
print("-" * 60)
print(f"Loan that paid the least interest: Loan {loans.index(min_interest_loan) + 1}")
print(f"Loan that paid the most interest: Loan {loans.index(max_interest_loan) + 1}")
# Save results in an SQL database
def save_to_db(self, db_name='loans.db', table_name='loan_table'):
conn = sqlite3.connect(db_name)
self.table.to_sql(table_name, conn, if_exists='replace', index_label='Date')
conn.close()
print(f"Loan table saved to {db_name} in table {table_name}")
Подробнее здесь: https://stackoverflow.com/questions/787 ... -in-python