diff options
author | xengineering <me@xengineering.eu> | 2024-09-04 21:09:42 +0200 |
---|---|---|
committer | xengineering <me@xengineering.eu> | 2024-09-08 17:21:32 +0200 |
commit | 226f5afa968ead223baa5a1b0fafb481f9761f0f (patch) | |
tree | ed54889c22ee43596b48085485a6985189d574dd /finance | |
parent | 877b50ef8802f8b59d81ccabcab22fe185e898d7 (diff) | |
download | finance-py-226f5afa968ead223baa5a1b0fafb481f9761f0f.tar finance-py-226f5afa968ead223baa5a1b0fafb481f9761f0f.tar.zst finance-py-226f5afa968ead223baa5a1b0fafb481f9761f0f.zip |
Refactor Flow.integrate()
This refactoring introduces a generator function supporting the
integration. It creates datetime candidates where it can be checked if
they are in the time interval given to the integration method.
Furthermore a numerical improvement was made. The Flow.integrate()
function now checks candidates from the helper function and sums the
matching candidates up as an integer. After checking all candidates the
integer is converted to a decimal and multiplied by Flow.amount.
This is more accurate because in Python decimal calculation not numbers
or calculations are rounded, just the result of a calculation. Looping
over the candidates and adding amount each time thus involves a rounding
operation per candidate. The new algorithm has exactly one rounding
operation.
Diffstat (limited to 'finance')
-rw-r--r-- | finance/flow.py | 31 |
1 files changed, 16 insertions, 15 deletions
diff --git a/finance/flow.py b/finance/flow.py index fb28228..a5817af 100644 --- a/finance/flow.py +++ b/finance/flow.py @@ -1,6 +1,7 @@ import dataclasses from datetime import datetime from decimal import Decimal +from typing import Generator @dataclasses.dataclass(kw_only=True, frozen=True) @@ -12,22 +13,22 @@ class Flow: def integrate(self, start: datetime, end: datetime) -> Decimal: """Integrate the flow between two dates to an amount of money""" - retval = Decimal(0.0) + payments: int = 0 - current = datetime(start.year, start.month, 1) - - if start == current: - retval += self.amount + for candidate in monthly_candidates(start): + if start <= candidate <= end: + payments += 1 + if candidate > end: + break - while True: - if current.month == 12: - current = datetime(current.year + 1, 1, 1) - else: - current = datetime(current.year, current.month + 1, 1) + return self.amount * Decimal(payments) - if current <= end: - retval += self.amount - else: - break - return retval +def monthly_candidates(start: datetime) -> Generator[datetime, None, None]: + current = datetime(start.year, start.month, 1) + while True: + yield current + if current.month == 12: + current = datetime(current.year + 1, 1, 1) + else: + current = datetime(current.year, current.month + 1, 1) |