21 desembre 2025

This commit is contained in:
Roger Oriol
2025-12-21 12:21:08 +01:00
parent b581e7590f
commit c0ebdf099a
4 changed files with 160 additions and 291 deletions

View File

@@ -26,7 +26,7 @@ def draw_line():
print('─' * 30)
def get_last_month_timestamps(date):
def get_month_to_date_timestamps(date):
month = int(date.split("-")[1])
year = int(date.split("-")[0])
d = datetime(year, month, 1)
@@ -35,6 +35,14 @@ def get_last_month_timestamps(date):
return start_date, end_date.strftime("%Y-%m-%d")
def get_last_year_timestamps(date):
date_parts = date.split("-")
day = int(date_parts[2])
month = int(date_parts[1])
year = int(date_parts[0])
return f"{year-1}-{month:02d}-{day:02d}", date
def get_sum_balances(balances, account_prefix):
sum = 0
for account, balance in balances.items():
@@ -64,7 +72,8 @@ def get_debt_to_assets_ratio(balances, max):
def get_emergency_fund_ratio(balances, expenses, low, mid):
liquid = 0
living_expenses = expenses[0].position.get_only_position().units.number
living_expenses = expenses[0].position.get_only_position(
).units.number / 12
for account, balance in balances.items():
if account.startswith("Assets:Liquid"):
liquid = balance if liquid == 0 else liquid + balance
@@ -72,7 +81,7 @@ def get_emergency_fund_ratio(balances, expenses, low, mid):
) == None else liquid.get_only_position().units
result = round(total_liquid.number / living_expenses, 2)
color = bcolors.FAIL if result < low else bcolors.OKGREEN if result > mid else bcolors.WARNING
return f"{color}{result}{bcolors.ENDC}"
return f"{color}{result} mth{bcolors.ENDC}"
def get_investment_assets_to_net_worth_ratio(balances, min):
@@ -95,18 +104,19 @@ def get_liquid_assets_to_net_worth_ratio(balances, min):
return f"{bcolors.FAIL if result < min else bcolors.OKGREEN}{result} %{bcolors.ENDC}"
def get_savings_ratio(balances, gross_monthly_income, monthly_savings, min):
result = round((monthly_savings.number / gross_monthly_income) * 100, 2)
def get_savings_ratio(balances, net_yearly_income, yearly_savings, min):
result = round((yearly_savings.number /
net_yearly_income) * 100, 2)
return f"{bcolors.FAIL if result < min else bcolors.OKGREEN}{result} %{bcolors.ENDC}"
def get_debt_service_ratio(balances, gross_monthly_income, debt_payments, max):
result = round((debt_payments.number / gross_monthly_income) * 100, 2)
def get_debt_service_ratio(balances, net_yearly_income, debt_payments, max):
result = round((debt_payments.number / net_yearly_income) * 100, 2)
return f"{bcolors.FAIL if result >= max else bcolors.OKGREEN}{result} %{bcolors.ENDC}"
def get_non_mortgage_debt_service_ratio(balances, gross_monthly_income, mortgage_payments, max):
result = round((mortgage_payments.number / gross_monthly_income) * 100, 2)
def get_non_mortgage_debt_service_ratio(balances, net_yearly_income, mortgage_payments, max):
result = round((mortgage_payments.number / net_yearly_income) * 100, 2)
return f"{bcolors.FAIL if result >= max else bcolors.OKGREEN}{result} %{bcolors.ENDC}"
@@ -117,11 +127,11 @@ def get_solvency_ratio(balances, min):
return f"{bcolors.FAIL if result < min else bcolors.OKGREEN}{result} %{bcolors.ENDC}"
def get_interest_coverage_ratio(gross_monthly_income, expenses, debt_payments, mortgage_payments, min):
def get_interest_coverage_ratio(net_monthly_income, expenses, debt_payments, mortgage_payments, min):
living_expenses = expenses[0].position.get_only_position().units.number
interest = debt_payments.number + mortgage_payments.number
interest = interest if interest > 0 else 1
result = round((gross_monthly_income - living_expenses) / interest, 2)
result = round((net_monthly_income - living_expenses) / interest, 2)
return f"{bcolors.FAIL if result < min else bcolors.OKGREEN}{result}{bcolors.ENDC}"
@@ -138,7 +148,7 @@ def get_position_as_str(inventory):
return Amount(Decimal(round(position.units.number, 2)), position.units.currency).to_string()
def print_report(date, balances, expenses, income, debt_payments, mortgage_payments, savings):
def print_report(date, balances, expenses, net_monthly_income, net_yearly_income, debt_payments, mortgage_payments, savings):
print(f"{bcolors.BOLD}Balance Sheet (date={date}){bcolors.ENDC}")
draw_line()
print(f"{bcolors.BOLD}Assets{bcolors.ENDC}")
@@ -202,8 +212,10 @@ def print_report(date, balances, expenses, income, debt_payments, mortgage_payme
balances["Liabilities:Hipoteca:VivendaPrincipal"] * Decimal(-1))],
["Hipoteques en vivenda d'inversió",
Amount(Decimal(0), "EUR").to_string()],
["Crèdit", get_position_as_str(
["Targetes de crèdit", get_position_as_str(
balances["Liabilities:Credit:Caixabank:TargetaCredit"] * Decimal(-1))],
["Línies de crèdit per inversió", get_position_as_str(
balances["Liabilities:Credit:Renta4:PolissaCredit"] * Decimal(-1))],
["Factures impagades", get_position_as_str(
balances["Liabilities:Factures:FacturesPendents"] * Decimal(-1))],
["Préstecs personals", Amount(Decimal(0), "EUR").to_string()],
@@ -225,19 +237,19 @@ def print_report(date, balances, expenses, income, debt_payments, mortgage_payme
["Debt-to-Assets Ratio",
get_debt_to_assets_ratio(balances, 50), "50 %"],
["Emergency Fund", get_emergency_fund_ratio(
balances, expenses, 3, 6), "3-6"],
balances, expenses, 3, 6), "3-6 mth"],
["Investment Assets to Net Worth Ratio",
get_investment_assets_to_net_worth_ratio(balances, 50), "50 %"],
["Liquid Assets to Net Worth Ratio",
get_liquid_assets_to_net_worth_ratio(balances, 15), "15 %"],
["Savings Ratio", get_savings_ratio(
balances, income, savings, 20), "20 %"],
balances, net_yearly_income, savings, 20), "20 %"],
["Debt-Service Ratio",
get_debt_service_ratio(balances, income, debt_payments, 35), "35 %"],
get_debt_service_ratio(balances, net_yearly_income, debt_payments, 35), "35 %"],
["Non-Mortgage Debt-Service Ratio",
get_non_mortgage_debt_service_ratio(balances, income, mortgage_payments, 15), "15 %"],
get_non_mortgage_debt_service_ratio(balances, net_yearly_income, mortgage_payments, 15), "15 %"],
["Interest Coverage Ratio", get_interest_coverage_ratio(
income, expenses, debt_payments, mortgage_payments, 1.5), "1.5"]
net_yearly_income, expenses, debt_payments, mortgage_payments, 1.5), "1.5"]
]))
@@ -253,7 +265,7 @@ def get_balances(entries, options, date):
def get_expenses(entries, options, date):
start_date, end_date = get_last_month_timestamps(date)
start_date, end_date = get_last_year_timestamps(date)
expenses_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
end_date} WHERE account ~ 'Expenses:' AND date >= {start_date}"
rtypes, rrows = query.run_query(
@@ -262,17 +274,27 @@ def get_expenses(entries, options, date):
def get_income(entries, options, date):
start_date, end_date = get_last_month_timestamps(date)
start_date, end_date = get_month_to_date_timestamps(date)
income_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
end_date} WHERE account ~ '^(Income:Work|Income:Savings|Income:Invest)' AND date >= {start_date}"
rtypes, rrows = query.run_query(
entries, options, income_query)
return rrows
net_monthly_income = rrows[0].position.get_only_position(
).units.number * -1
start_date, end_date = get_last_year_timestamps(date)
income_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
end_date} WHERE account ~ '^(Income:Work|Income:Savings|Income:Invest)' AND date >= {start_date}"
rtypes, rrows = query.run_query(
entries, options, income_query)
net_yearly_income = rrows[0].position.get_only_position(
).units.number * -1
return net_monthly_income, net_yearly_income
def get_debt_payments(entries, options, date):
# FIX: Agafar nomes els pagaments de deute, enlloc de també les addicions de deute
start_date, end_date = get_last_month_timestamps(date)
start_date, end_date = get_last_year_timestamps(date)
debt_payments_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
end_date} WHERE account ~ 'Expenses:R4:Interessos' AND date >= {start_date}"
mortgage_payments_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
@@ -289,13 +311,20 @@ def get_debt_payments(entries, options, date):
def get_savings(entries, options, date):
start_date, end_date = get_last_month_timestamps(date)
savings_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
start_date, end_date = get_last_year_timestamps(date)
investments_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
end_date} WHERE account ~ '^Assets:Invest:' AND date >= {start_date}"
rtypes, rrows = query.run_query(
entries, options, savings_query)
entries, options, investments_query)
result = rrows[0].position.get_only_position().units if len(
rrows) > 0 else Amount(Decimal(0), "EUR")
liabilities_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {
end_date} WHERE account ~ '^Liabilities:Credit:Renta4:' AND date >= {start_date}"
rtypes, rrows = query.run_query(
entries, options, liabilities_query)
liabilities = rrows[0].position.get_only_position().units if len(
rrows) > 0 else Amount(Decimal(0), "EUR")
result = add(result, liabilities)
return result
@@ -316,13 +345,12 @@ def main():
balances = get_balances(entries, options, date)
expenses = get_expenses(entries, options, date)
income = get_income(entries, options, date)
gross_monthly_income = income[0].position.get_only_position(
).units.number * -1
net_monthly_income, net_yearly_income = get_income(
entries, options, date)
debt_payments, mortgage_payments = get_debt_payments(
entries, options, date)
savings = get_savings(entries, options, date)
print_report(date, balances, expenses, gross_monthly_income,
print_report(date, balances, expenses, net_monthly_income, net_yearly_income,
debt_payments, mortgage_payments, savings)