From 7b9f2a29deaf4bdbda9bb6161476809c6480d80a Mon Sep 17 00:00:00 2001 From: gcoue Date: Mon, 6 Nov 2023 23:15:06 +0100 Subject: [PATCH 1/9] Change credit card to negative amount --- finalynx/fetch/source_finary.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/finalynx/fetch/source_finary.py b/finalynx/fetch/source_finary.py index 4a441c5..accf602 100644 --- a/finalynx/fetch/source_finary.py +++ b/finalynx/fetch/source_finary.py @@ -122,13 +122,7 @@ def _authenticate(self) -> Optional[Session]: # Login to Finary with the existing cookies file or credentials in environment variables and retrieve data if os.environ.get("FINARY_EMAIL") and os.environ.get("FINARY_PASSWORD"): - self._log("Signing in to Finary...") - with console.status( - f"""[bold {TH().ACCENT}]Signing in to Finary...[/] """ - """[dim white](Type your 2FA code if prompted and press [italic]Enter[/], """ - """it will remain invisible while you type)""", - spinner_style=TH().ACCENT, - ): + with console.status(f"[bold {TH().ACCENT}]Signing in to Finary...", spinner_style=TH().ACCENT): result = ff.signin() self._log("Signed in to Finary.") @@ -182,12 +176,17 @@ def _process_account(self, dict_account: Dict[str, Any], tree: Tree) -> None: node = tree.add(account_name) for item in dict_account["fiats"]: + subtype=dict_account["bank_account_type"]["subtype"] + if subtype == "credit": + amount = -item["display_current_value"] + else: + amount = item["display_current_value"] self._register_fetchline( tree_node=node, name=account_name, id=item["id"], account=dict_account["institution"]["name"], - amount=item["display_current_value"], + amount=amount, currency=item["fiat"]["symbol"], ) From 1983b81993f11f71051418bf5d04c4001fdd9e4d Mon Sep 17 00:00:00 2001 From: gcoue Date: Mon, 6 Nov 2023 23:23:36 +0100 Subject: [PATCH 2/9] Rollback source_finary.py --- finalynx/fetch/source_finary.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/finalynx/fetch/source_finary.py b/finalynx/fetch/source_finary.py index accf602..4a441c5 100644 --- a/finalynx/fetch/source_finary.py +++ b/finalynx/fetch/source_finary.py @@ -122,7 +122,13 @@ def _authenticate(self) -> Optional[Session]: # Login to Finary with the existing cookies file or credentials in environment variables and retrieve data if os.environ.get("FINARY_EMAIL") and os.environ.get("FINARY_PASSWORD"): - with console.status(f"[bold {TH().ACCENT}]Signing in to Finary...", spinner_style=TH().ACCENT): + self._log("Signing in to Finary...") + with console.status( + f"""[bold {TH().ACCENT}]Signing in to Finary...[/] """ + """[dim white](Type your 2FA code if prompted and press [italic]Enter[/], """ + """it will remain invisible while you type)""", + spinner_style=TH().ACCENT, + ): result = ff.signin() self._log("Signed in to Finary.") @@ -176,17 +182,12 @@ def _process_account(self, dict_account: Dict[str, Any], tree: Tree) -> None: node = tree.add(account_name) for item in dict_account["fiats"]: - subtype=dict_account["bank_account_type"]["subtype"] - if subtype == "credit": - amount = -item["display_current_value"] - else: - amount = item["display_current_value"] self._register_fetchline( tree_node=node, name=account_name, id=item["id"], account=dict_account["institution"]["name"], - amount=amount, + amount=item["display_current_value"], currency=item["fiat"]["symbol"], ) From f97a7c7c0b801c821deaf9c7cf1b120ac791fd25 Mon Sep 17 00:00:00 2001 From: gcoue Date: Mon, 6 Nov 2023 23:27:03 +0100 Subject: [PATCH 3/9] Change credit card to negative amount --- finalynx/fetch/source_finary.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/finalynx/fetch/source_finary.py b/finalynx/fetch/source_finary.py index 4a441c5..a4e36ca 100644 --- a/finalynx/fetch/source_finary.py +++ b/finalynx/fetch/source_finary.py @@ -182,12 +182,17 @@ def _process_account(self, dict_account: Dict[str, Any], tree: Tree) -> None: node = tree.add(account_name) for item in dict_account["fiats"]: + subtype=dict_account["bank_account_type"]["subtype"] + if subtype == "credit": + amount = -item["display_current_value"] + else: + amount = item["display_current_value"] self._register_fetchline( tree_node=node, name=account_name, id=item["id"], account=dict_account["institution"]["name"], - amount=item["display_current_value"], + amount=amount, currency=item["fiat"]["symbol"], ) From 721e36af9132ff7e03b3dd66d729e679224ba2af Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 21:04:59 +0000 Subject: [PATCH 4/9] [pre-commit.ci lite] apply automatic fixes --- finalynx/fetch/source_finary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finalynx/fetch/source_finary.py b/finalynx/fetch/source_finary.py index a4e36ca..62e5e1c 100644 --- a/finalynx/fetch/source_finary.py +++ b/finalynx/fetch/source_finary.py @@ -182,7 +182,7 @@ def _process_account(self, dict_account: Dict[str, Any], tree: Tree) -> None: node = tree.add(account_name) for item in dict_account["fiats"]: - subtype=dict_account["bank_account_type"]["subtype"] + subtype = dict_account["bank_account_type"]["subtype"] if subtype == "credit": amount = -item["display_current_value"] else: From a079da793d1bea195fbe31cac22f07e930a45dc3 Mon Sep 17 00:00:00 2001 From: gcoue Date: Wed, 8 Nov 2023 10:29:30 +0100 Subject: [PATCH 5/9] Add a flag to allow the potfolio restitution for each simulation step --- finalynx/assistant.py | 17 +++++++++++++++++ finalynx/simulator/timeline.py | 3 +++ finalynx/usage.py | 1 + 3 files changed, 21 insertions(+) diff --git a/finalynx/assistant.py b/finalynx/assistant.py index 6b6c0cd..09c0fd0 100644 --- a/finalynx/assistant.py +++ b/finalynx/assistant.py @@ -93,6 +93,9 @@ def __init__( self.buckets = buckets if buckets else [] self.envelopes = envelopes if envelopes else [] + #Storage for value of portfolio at each intermediate step + self.intermediate_value = [] + # Options that can either be set in the constructor or from the command line options, see --help self.ignore_orphans = ignore_orphans self.clear_cache = clear_cache @@ -190,6 +193,8 @@ def _parse_args(self) -> None: self.active_sources = str(args["--sources"]).split(",") if args["--future"] and self.simulation: self.simulation.print_final = True + if args["--each-step"] and self.simulation: + self.simulation.print_each_step = True if args["--sim-steps"] and self.simulation: self.simulation.step_years = int(args["--sim-steps"]) if args["--theme"]: @@ -234,6 +239,11 @@ def run(self) -> None: # Add the simulation summary to the performance panel in the console dict_panels["performance"].add(self.simulate()) + # If enabled by the user, print the each_step portfolio during the simulation + if self.simulation.print_each_step: + for element in self.intermediate_value: + renders.append(element) + # If enabled by the user, print the final portfolio after the simulation if self.simulation.print_final: renders.append(f"\nYour portfolio in {self.simulation.end_date}:") @@ -301,6 +311,13 @@ def append_worth(year: int, amount: float) -> None: if (year - date.today().year) % self.simulation.step_years == 0: append_worth(year, self.portfolio.get_amount()) + if self.simulation.print_each_step: + #Storage for each intermediate step of simu + #f"[{TH().TEXT}]Current: [bold][{TH().ACCENT}]{perf:.2f} %[/] / year" + #title = "Your portfolio in "+str(year)+"-12-31:" + title = "Your portfolio in [bold]"+str(year)+"-12-31:[/]" + self.intermediate_value.append(Panel(self.render_mainframe(), title=title)) + # Run until the end date and append the final result self._timeline.run() append_worth(self._timeline.current_date.year, self.portfolio.get_amount()) diff --git a/finalynx/simulator/timeline.py b/finalynx/simulator/timeline.py index 9ffe991..c452da2 100644 --- a/finalynx/simulator/timeline.py +++ b/finalynx/simulator/timeline.py @@ -35,6 +35,9 @@ class Simulation: # Whether to print the final portfolio state in the console after the simulation print_final: bool = False + # Whether to print the final portfolio state in the console after the simulation + print_each_step: bool = False + # Display the portfolio's worth in the console every `step` years step_years: int = 5 diff --git a/finalynx/usage.py b/finalynx/usage.py index f431846..fb393be 100644 --- a/finalynx/usage.py +++ b/finalynx/usage.py @@ -56,5 +56,6 @@ def main_filter(message: str) -> str: --sim-steps=int Display the simulated portfolio's worth every X years, defaults to 5 --future Print the portfolio after the simulation has finished + --each-step Print the portfolio for each step of the simulation """ From 2594dd2310e783cf3121d1f3d985113bd75aedb1 Mon Sep 17 00:00:00 2001 From: gcoue Date: Wed, 8 Nov 2023 10:29:30 +0100 Subject: [PATCH 6/9] Add a flag to allow the potfolio restitution for each simulation step --- finalynx/assistant.py | 14 ++++++++++++++ finalynx/simulator/timeline.py | 3 +++ finalynx/usage.py | 1 + 3 files changed, 18 insertions(+) diff --git a/finalynx/assistant.py b/finalynx/assistant.py index 6b6c0cd..ffd62a1 100644 --- a/finalynx/assistant.py +++ b/finalynx/assistant.py @@ -93,6 +93,9 @@ def __init__( self.buckets = buckets if buckets else [] self.envelopes = envelopes if envelopes else [] + #Storage for value of portfolio at each intermediate step + self.intermediate_value = [] + # Options that can either be set in the constructor or from the command line options, see --help self.ignore_orphans = ignore_orphans self.clear_cache = clear_cache @@ -190,6 +193,8 @@ def _parse_args(self) -> None: self.active_sources = str(args["--sources"]).split(",") if args["--future"] and self.simulation: self.simulation.print_final = True + if args["--each-step"] and self.simulation: + self.simulation.print_each_step = True if args["--sim-steps"] and self.simulation: self.simulation.step_years = int(args["--sim-steps"]) if args["--theme"]: @@ -234,6 +239,11 @@ def run(self) -> None: # Add the simulation summary to the performance panel in the console dict_panels["performance"].add(self.simulate()) + # If enabled by the user, print the each_step portfolio during the simulation + if self.simulation.print_each_step: + for element in self.intermediate_value: + renders.append(element) + # If enabled by the user, print the final portfolio after the simulation if self.simulation.print_final: renders.append(f"\nYour portfolio in {self.simulation.end_date}:") @@ -300,6 +310,10 @@ def append_worth(year: int, amount: float) -> None: if (year - date.today().year) % self.simulation.step_years == 0: append_worth(year, self.portfolio.get_amount()) + if self.simulation.print_each_step: + #Storage for each intermediate simulation step + title = "Your portfolio in [bold]"+str(year)+"-12-31:[/]" + self.intermediate_value.append(Panel(self.render_mainframe(), title=title)) # Run until the end date and append the final result self._timeline.run() diff --git a/finalynx/simulator/timeline.py b/finalynx/simulator/timeline.py index 9ffe991..c452da2 100644 --- a/finalynx/simulator/timeline.py +++ b/finalynx/simulator/timeline.py @@ -35,6 +35,9 @@ class Simulation: # Whether to print the final portfolio state in the console after the simulation print_final: bool = False + # Whether to print the final portfolio state in the console after the simulation + print_each_step: bool = False + # Display the portfolio's worth in the console every `step` years step_years: int = 5 diff --git a/finalynx/usage.py b/finalynx/usage.py index f431846..fb393be 100644 --- a/finalynx/usage.py +++ b/finalynx/usage.py @@ -56,5 +56,6 @@ def main_filter(message: str) -> str: --sim-steps=int Display the simulated portfolio's worth every X years, defaults to 5 --future Print the portfolio after the simulation has finished + --each-step Print the portfolio for each step of the simulation """ From de565620724eb92bac2331486c10adb748328751 Mon Sep 17 00:00:00 2001 From: gcoue Date: Sun, 31 Dec 2023 16:50:38 +0100 Subject: [PATCH 7/9] fix: correct the recurrence function to be more precise on Yearly recurrence --- finalynx/simulator/recurrence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finalynx/simulator/recurrence.py b/finalynx/simulator/recurrence.py index a0d372c..de72080 100644 --- a/finalynx/simulator/recurrence.py +++ b/finalynx/simulator/recurrence.py @@ -36,7 +36,7 @@ def __init__( months = months if months is not None else 0 years = years if years is not None else 0 - self._delta = timedelta(days, weeks=4 * months + 52 * years) + self._delta = timedelta(days, weeks=4.3452 * months + 52.1429 * years+0.1429) #Ajout de 0.1429 pour rester au même jour sinon Yearly le 30 au lieu du 31/12 def _next_date(self, current_date: date) -> date: return current_date + self._delta From cf14f28a33b814dcc303b458f3cbfc4b485238e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sat, 13 Jan 2024 15:31:08 +0000 Subject: [PATCH 8/9] [pre-commit.ci lite] apply automatic fixes --- finalynx/assistant.py | 8 ++++---- finalynx/simulator/recurrence.py | 4 +++- finalynx/simulator/timeline.py | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/finalynx/assistant.py b/finalynx/assistant.py index ffd62a1..41d5bfb 100644 --- a/finalynx/assistant.py +++ b/finalynx/assistant.py @@ -93,7 +93,7 @@ def __init__( self.buckets = buckets if buckets else [] self.envelopes = envelopes if envelopes else [] - #Storage for value of portfolio at each intermediate step + # Storage for value of portfolio at each intermediate step self.intermediate_value = [] # Options that can either be set in the constructor or from the command line options, see --help @@ -194,7 +194,7 @@ def _parse_args(self) -> None: if args["--future"] and self.simulation: self.simulation.print_final = True if args["--each-step"] and self.simulation: - self.simulation.print_each_step = True + self.simulation.print_each_step = True if args["--sim-steps"] and self.simulation: self.simulation.step_years = int(args["--sim-steps"]) if args["--theme"]: @@ -311,8 +311,8 @@ def append_worth(year: int, amount: float) -> None: if (year - date.today().year) % self.simulation.step_years == 0: append_worth(year, self.portfolio.get_amount()) if self.simulation.print_each_step: - #Storage for each intermediate simulation step - title = "Your portfolio in [bold]"+str(year)+"-12-31:[/]" + # Storage for each intermediate simulation step + title = "Your portfolio in [bold]" + str(year) + "-12-31:[/]" self.intermediate_value.append(Panel(self.render_mainframe(), title=title)) # Run until the end date and append the final result diff --git a/finalynx/simulator/recurrence.py b/finalynx/simulator/recurrence.py index de72080..0e48a6b 100644 --- a/finalynx/simulator/recurrence.py +++ b/finalynx/simulator/recurrence.py @@ -36,7 +36,9 @@ def __init__( months = months if months is not None else 0 years = years if years is not None else 0 - self._delta = timedelta(days, weeks=4.3452 * months + 52.1429 * years+0.1429) #Ajout de 0.1429 pour rester au même jour sinon Yearly le 30 au lieu du 31/12 + self._delta = timedelta( + days, weeks=4.3452 * months + 52.1429 * years + 0.1429 + ) # Ajout de 0.1429 pour rester au même jour sinon Yearly le 30 au lieu du 31/12 def _next_date(self, current_date: date) -> date: return current_date + self._delta diff --git a/finalynx/simulator/timeline.py b/finalynx/simulator/timeline.py index c452da2..8bce40d 100644 --- a/finalynx/simulator/timeline.py +++ b/finalynx/simulator/timeline.py @@ -37,7 +37,7 @@ class Simulation: # Whether to print the final portfolio state in the console after the simulation print_each_step: bool = False - + # Display the portfolio's worth in the console every `step` years step_years: int = 5 From be0e79ba6bfcc14de3eb795ec9718ea48057c53f Mon Sep 17 00:00:00 2001 From: MadeInPierre Date: Sat, 13 Jan 2024 16:47:47 +0100 Subject: [PATCH 9/9] style: minor refactoring --- finalynx/assistant.py | 16 +++++++++------- finalynx/fetch/source_finary.py | 14 ++++++++++---- finalynx/simulator/recurrence.py | 5 ++--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/finalynx/assistant.py b/finalynx/assistant.py index 41d5bfb..b98970a 100644 --- a/finalynx/assistant.py +++ b/finalynx/assistant.py @@ -93,9 +93,6 @@ def __init__( self.buckets = buckets if buckets else [] self.envelopes = envelopes if envelopes else [] - # Storage for value of portfolio at each intermediate step - self.intermediate_value = [] - # Options that can either be set in the constructor or from the command line options, see --help self.ignore_orphans = ignore_orphans self.clear_cache = clear_cache @@ -128,6 +125,9 @@ def __init__( # Initialize the simulation timeline with the initial user events self._timeline = Timeline(simulation, self.portfolio, self.buckets) if simulation else None + # Store the portfolio renders for each simulation date (if enabled) + self._timeline_renders: List[Any] = [] + def add_source(self, source: SourceBaseLine) -> None: """Register a source, either defined in your own config or from the available Finalynx sources using `from finalynx.fetch.source_any import SourceAny`.""" @@ -239,9 +239,9 @@ def run(self) -> None: # Add the simulation summary to the performance panel in the console dict_panels["performance"].add(self.simulate()) - # If enabled by the user, print the each_step portfolio during the simulation + # If enabled by the user, print the portfolio at each simulation date if self.simulation.print_each_step: - for element in self.intermediate_value: + for element in self._timeline_renders: renders.append(element) # If enabled by the user, print the final portfolio after the simulation @@ -309,11 +309,13 @@ def append_worth(year: int, amount: float) -> None: self._timeline.goto(date(year, 12, 31)) if (year - date.today().year) % self.simulation.step_years == 0: + # Append the portfolio's worth to the Worth tree append_worth(year, self.portfolio.get_amount()) + + # Render each intermediate simulation step if self.simulation.print_each_step: - # Storage for each intermediate simulation step title = "Your portfolio in [bold]" + str(year) + "-12-31:[/]" - self.intermediate_value.append(Panel(self.render_mainframe(), title=title)) + self._timeline_renders.append(Panel(self.render_mainframe(), title=title)) # Run until the end date and append the final result self._timeline.run() diff --git a/finalynx/fetch/source_finary.py b/finalynx/fetch/source_finary.py index 62e5e1c..a3c81d2 100644 --- a/finalynx/fetch/source_finary.py +++ b/finalynx/fetch/source_finary.py @@ -84,7 +84,10 @@ def _authenticate(self) -> Optional[Session]: if os.path.exists(finary_uapi.constants.COOKIE_FILENAME): os.remove(finary_uapi.constants.COOKIE_FILENAME) if os.path.exists(finary_uapi.constants.CREDENTIAL_FILE): - if not Confirm.ask("Reuse saved credentials? Otherwise, they will also be deleted.", default=True): + if not Confirm.ask( + "Reuse saved credentials? Otherwise, they will also be deleted.", + default=True, + ): os.remove(finary_uapi.constants.CREDENTIAL_FILE) # Get the user credentials if there's no session yet (through environment variables or manual input) @@ -171,7 +174,10 @@ def _fetch_data(self, tree: Tree) -> None: raise ValueError("Finary signin failed.") # Call the API and parse the response into `FetchLine` instances - with console.status(f"[bold {TH().ACCENT}]Fetching investments from Finary...", spinner_style=TH().ACCENT): + with console.status( + f"[bold {TH().ACCENT}]Fetching investments from Finary...", + spinner_style=TH().ACCENT, + ): response = ff.get_holdings_accounts(session) if response["message"] == "OK": for dict_account in response["result"]: @@ -182,11 +188,11 @@ def _process_account(self, dict_account: Dict[str, Any], tree: Tree) -> None: node = tree.add(account_name) for item in dict_account["fiats"]: - subtype = dict_account["bank_account_type"]["subtype"] - if subtype == "credit": + if dict_account["bank_account_type"]["subtype"] == "credit": amount = -item["display_current_value"] else: amount = item["display_current_value"] + self._register_fetchline( tree_node=node, name=account_name, diff --git a/finalynx/simulator/recurrence.py b/finalynx/simulator/recurrence.py index 0e48a6b..fd0f747 100644 --- a/finalynx/simulator/recurrence.py +++ b/finalynx/simulator/recurrence.py @@ -36,9 +36,8 @@ def __init__( months = months if months is not None else 0 years = years if years is not None else 0 - self._delta = timedelta( - days, weeks=4.3452 * months + 52.1429 * years + 0.1429 - ) # Ajout de 0.1429 pour rester au même jour sinon Yearly le 30 au lieu du 31/12 + # Add decimals to stay on the same day (otherwise Yearly goes to 30/12) + self._delta = timedelta(days, weeks=4.3452 * months + 52.1429 * years + 0.1429) def _next_date(self, current_date: date) -> date: return current_date + self._delta