diff options
Diffstat (limited to 'finance/simulate.py')
-rw-r--r-- | finance/simulate.py | 59 |
1 files changed, 59 insertions, 0 deletions
diff --git a/finance/simulate.py b/finance/simulate.py new file mode 100644 index 0000000..3f8e3ad --- /dev/null +++ b/finance/simulate.py @@ -0,0 +1,59 @@ +from datetime import datetime +from decimal import Decimal +from typing import Generator + +from finance.model import Flow + + +def integrate(flow: Flow, start: datetime, end: datetime) -> Decimal: + """Integrate the flow between two dates to an amount of money""" + + payments: int = 0 + + if flow.since is not None: + if start < flow.since: + start = flow.since + + if flow.until is not None: + if end > flow.until: + end = flow.until + + for candidate in monthly_candidates(start): + if start <= candidate <= end: + payments += 1 + if candidate > end: + break + + return flow.amount * Decimal(payments) + + +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) + + +def simulate( + start: datetime, end: datetime, flows: tuple[Flow, ...] +) -> list[tuple[datetime, Decimal]]: + dates: list[datetime] = [] + values: list[Decimal] = [] + + for candidate in monthly_candidates(start): + if start <= candidate <= end: + dates.append(candidate) + + if candidate > end: + break + + for date in dates: + value = Decimal(0.0) + for flow in flows: + value += integrate(flow, start, date) + values.append(value) + + return [(date, values[index]) for index, date in enumerate(dates)] |