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)]