From 78892b4b6bdd32264d9498eae8bf3f02430aec28 Mon Sep 17 00:00:00 2001 From: Roger Oriol Date: Sat, 28 Dec 2024 19:32:44 +0100 Subject: [PATCH] 28 desembre 2024 --- commands/investments | 124 ++++++++++++++++++++++++++ ledger/trading/prices.beancount | 9 +- ledger/transactions/2024/12.beancount | 66 ++++++++++---- 3 files changed, 182 insertions(+), 17 deletions(-) create mode 100755 commands/investments diff --git a/commands/investments b/commands/investments new file mode 100755 index 0000000..a10f59f --- /dev/null +++ b/commands/investments @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +from beancount import loader +from beancount.query import query +from beancount.parser import printer +import argparse +from tabulate import tabulate +from decimal import Decimal +from beancount.core.amount import Amount, add, sub, mul +from math import floor +from datetime import datetime, timedelta + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +def draw_line(): + print('─' * 30) + +def get_amount(inventory): + if inventory.get_only_position() == None: + return Amount(Decimal(0), "EUR").to_string() + result = inventory.get_only_position().units + return Amount(Decimal(round(result.number, 2)), result.currency).to_string() + +def print_capital_gains(inventory): + if inventory.get_only_position() == None: + return Amount(Decimal(0), "EUR").to_string() + result = inventory.get_only_position().units + num = Amount(Decimal(round(result.number * -1, 2)), result.currency).to_string() + return f"{bcolors.OKGREEN if result.number <= 0 else bcolors.FAIL}{num}{bcolors.ENDC}" + +def print_perc_capital_gains(inventory, init_invest): + if inventory.get_only_position() == None: + return Amount(Decimal(0), "EUR").to_string() + result = inventory.get_only_position().units + invest = init_invest.get_only_position().units + num = round((result.number * -1 / invest.number) * 100, 2) + return f"{bcolors.OKGREEN if result.number <= 0 else bcolors.FAIL}{num} %{bcolors.ENDC}" + +def calc_contributions(contributions): + total = Decimal(0) + for c in contributions: + amount = c.position.units.number + if amount > 0: + total += amount + return total + +def get_returns(init_invest, end_invest, contributions): + init = init_invest.get_only_position().units + end = end_invest.get_only_position().units + contr = calc_contributions(contributions) + result = sub(sub(end, init), Amount(contr, "EUR")) + return Amount(Decimal(round(result.number, 2)), result.currency).to_string() + +def print_contributions(contributions): + total = calc_contributions(contributions) + return Amount(Decimal(round(total, 2)), "EUR").to_string() + +def print_report(date, end_date, init_invest, end_invest, contributions, capital_gains): + print(f"{bcolors.BOLD}Investment returns (date={date}){bcolors.ENDC}") + draw_line() + print(tabulate([ + [date, get_amount(init_invest)], + [end_date, get_amount(end_invest)] + ], headers=['Date', 'Balance'])) + + print(tabulate([ + ["Contributions", print_contributions(contributions)], + ["Returns", get_returns(init_invest, end_invest, contributions)], + ["Capital gains", print_capital_gains(capital_gains)], + ["Capital gains %", print_perc_capital_gains(capital_gains, init_invest)] + ])) + +def get_investments(entries, options, date, end_date): + initial_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {date} WHERE account ~ '^Assets:Invest'" + rtypes, rrows = query.run_query( + entries, options, initial_query) + end_query = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {end_date} WHERE account ~ '^Assets:Invest'" + rtypes, erows = query.run_query( + entries, options, end_query) + return rrows[0].position, erows[0].position + +def get_contributions(entries, options, start_date, end_date): + contributions_query = f"SELECT position FROM date <= {end_date} WHERE account ~ '^Assets:Liquid:R4:EUR' AND date >= {start_date}" + rtypes, rrows = query.run_query( + entries, options, contributions_query) + return rrows + +def get_capital_gains(entries, options, date, end_date): + q = f"SELECT convert(sum(position), \"EUR\") as position FROM date <= {end_date} WHERE account ~ '^Income:Invest:R4:CapitalGains' AND date >= {date}" + rtypes, rrows = query.run_query( + entries, options, q) + return rrows[0].position + +def main(): + parser = argparse.ArgumentParser(description='Generate investments report') + parser.add_argument('date', metavar='date', type=str, nargs=1, + help='Report date in ISO format (e.g. 1970-01-01)') + + args = parser.parse_args() + date = args.date[0] + + filename = "ledger/main.beancount" + entries, errors, options = loader.load_file(filename) + + if errors: + printer.print_errors(errors) + + d = date.split("-") + end_date = datetime(int(d[0]) + 1, int(d[1]), int(d[2])) + end_date = end_date.strftime("%Y-%m-%d") + init_invest, end_invest = get_investments(entries, options, date, end_date) + contributions = get_contributions(entries, options, date, end_date) + capital_gains = get_capital_gains(entries, options, date, end_date) + print_report(date, end_date, init_invest, end_invest, contributions, capital_gains) + +main() \ No newline at end of file diff --git a/ledger/trading/prices.beancount b/ledger/trading/prices.beancount index fd5b471..20bace8 100644 --- a/ledger/trading/prices.beancount +++ b/ledger/trading/prices.beancount @@ -23,4 +23,11 @@ 2024-06-24 price FIGLTECH 48.37 EUR 2024-06-24 price IWVL 48.37 EUR 2024-06-24 price PLTR 24.21 USD -2024-06-24 price MSFT 448.325 USD \ No newline at end of file +2024-06-24 price MSFT 448.325 USD + +2024-12-28 price VANGL 52.621 EUR +2024-12-28 price VANSMCAP 360.4489 EUR +2024-12-28 price VANEMMK 210.0581 EUR +2024-12-28 price R4RF 15.229469 EUR +2024-12-28 price IWVL 41.3 EUR +2024-12-28 price XDEQ 68.24 EUR \ No newline at end of file diff --git a/ledger/transactions/2024/12.beancount b/ledger/transactions/2024/12.beancount index 7b091d4..e263545 100644 --- a/ledger/transactions/2024/12.beancount +++ b/ledger/transactions/2024/12.beancount @@ -1,11 +1,11 @@ 2024-12-01 balance Assets:Liquid:Caixabank:Corrent 12580.17 EUR 2024-12-01 balance Assets:Liquid:R4:EUR 1035.41 EUR -2024-12-01 balance Assets:Invest:Fund:Vanguard:EMMK 14.99 VANEMMK -2024-12-01 balance Assets:Invest:Fund:Vanguard:GL 688.32 VANGL -2024-12-01 balance Assets:Invest:Fund:Vanguard:SMCAP 48.28 VANSMCAP -2024-12-01 balance Assets:Invest:ETF:IWVL 117 IWVL -2024-12-01 balance Assets:Invest:Fixed:R4RF 1505.44135 R4RF -2024-12-01 balance Assets:Invest:ETF:XDEQ 70 XDEQ +2024-12-01 balance Assets:Invest:Fund:Vanguard:EMMK 14.99 VANEMMK +2024-12-01 balance Assets:Invest:Fund:Vanguard:GL 688.32 VANGL +2024-12-01 balance Assets:Invest:Fund:Vanguard:SMCAP 48.28 VANSMCAP +2024-12-01 balance Assets:Invest:ETF:IWVL 117 IWVL +2024-12-01 balance Assets:Invest:Fixed:R4RF 1505.44135 R4RF +2024-12-01 balance Assets:Invest:ETF:XDEQ 70 XDEQ 2024-12-01 balance Assets:Benefits:Edenred:TicketsRestaurant 37.68 EUR 2024-12-01 balance Assets:Benefits:Edenred:TargetaTransport 94.50 EUR 2024-12-01 balance Assets:Benefits:DZP:PPEZurich 2971.29 EUR @@ -40,7 +40,7 @@ Expenses:MarcaPersonal 1.43 EUR Assets:Liquid:Caixabank:Corrent 2024-12-02 * "Renta 4" "Compra de VANGUARD GLOBAL STOCK INDEX (EUR) ACC" - Assets:Invest:Fund:Vanguard:GL 18.93 VANGL {52.8324 EUR} + Assets:Invest:Fund:Vanguard:GL 18.93 VANGL {52.8324 EUR} Assets:Liquid:R4:EUR -1000 EUR Income:Invest:R4:CapitalGains 2024-12-03 * "Aigues de Barcelona" "Factura aigua" @@ -190,17 +190,51 @@ 2024-12-20 * "Carrefour" "Paper d'embolicar" Expenses:Supermercat 1.50 EUR Assets:Liquid:Caixabank:Corrent +2024-12-21 * "Mercadona" "Compra super" + Expenses:Supermercat 7.20 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-22 * "Mr Ji Sushi" "Dinar sushi" + Expenses:MenjarFora 49.30 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-24 * "Zurich" "Nòmina desembre 2024" + Income:Work:Zurich:Salari -3474.41 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-24 * "R4" "Enviament de diners R4" + Assets:Liquid:Caixabank:Corrent -1200 EUR + Assets:Liquid:R4:EUR +2024-12-25 * "Farmacia Pou" "Mayesta Laura" + Expenses:Medic 17.09 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-25 * "City Arms" "Ratafia" + Expenses:MenjarFora 16 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-27 * "Renta 4" "Compra de ISHARES EDGE MSCI WORLD VALUE" + Assets:Invest:ETF:IWVL 24 IWVL {41.35 EUR} + Assets:Liquid:R4:EUR -1007.40 EUR + Expenses:R4:Comissions 15 EUR +2024-12-27 * "EsclatOil" "Gasolina" + Expenses:Gasolina 25 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-28 * "Aerobus" "Anada i tornada aeroport" + Expenses:Mobilitat 25 EUR + Assets:Benefits:Edenred:TargetaTransport +2024-12-28 * "L'Obrador" "Esmorzar" + Expenses:MenjarFora 13.30 EUR + Assets:Liquid:Caixabank:Corrent +2024-12-28 * "Eroski" "Gilette" + Expenses:Supermercat 18.84 EUR + Assets:Liquid:Caixabank:Corrent -2025-01-01 balance Assets:Liquid:Caixabank:Corrent 11078.39 EUR -2025-01-01 balance Assets:Liquid:R4:EUR 35.41 EUR -2025-01-01 balance Assets:Invest:Fund:Vanguard:EMMK 14.99 VANEMMK -2025-01-01 balance Assets:Invest:Fund:Vanguard:GL 707.25 VANGL -2025-01-01 balance Assets:Invest:Fund:Vanguard:SMCAP 48.28 VANSMCAP -2025-01-01 balance Assets:Invest:ETF:IWVL 117 IWVL -2025-01-01 balance Assets:Invest:Fixed:R4RF 1505.44135 R4RF -2025-01-01 balance Assets:Invest:ETF:XDEQ 70 XDEQ +2025-01-01 balance Assets:Liquid:Caixabank:Corrent 13206.07 EUR +2025-01-01 balance Assets:Liquid:R4:EUR 228.01 EUR +2025-01-01 balance Assets:Invest:Fund:Vanguard:EMMK 14.99 VANEMMK +2025-01-01 balance Assets:Invest:Fund:Vanguard:GL 707.25 VANGL +2025-01-01 balance Assets:Invest:Fund:Vanguard:SMCAP 48.28 VANSMCAP +2025-01-01 balance Assets:Invest:ETF:IWVL 141 IWVL +2025-01-01 balance Assets:Invest:Fixed:R4RF 1505.44135 R4RF +2025-01-01 balance Assets:Invest:ETF:XDEQ 70 XDEQ 2025-01-01 balance Assets:Benefits:Edenred:TicketsRestaurant 25.92 EUR -2025-01-01 balance Assets:Benefits:Edenred:TargetaTransport 122 EUR +2025-01-01 balance Assets:Benefits:Edenred:TargetaTransport 97 EUR 2025-01-01 balance Assets:Benefits:DZP:PPEZurich 2971.29 EUR 2025-01-01 balance Assets:PersonalProperty:VivendaPrincipal 0 EUR 2025-01-01 balance Assets:PersonalProperty:Cotxe 10000 EUR