from typing import List from .invest_fee_drag_types import FeeDrag, FeeDragYear from .money_amount import Money, money from .money_apply_rate import apply_rate def _is_int(value: object) -> bool: return isinstance(value, int) and not isinstance(value, bool) def fee_drag( start_value: Money, annual_growth_basis_points: int, annual_fee_basis_points: int, years: int ) -> FeeDrag: """The same investment grown twice, with and without an annual charge taken on the year-end value, rounding to minor units every year as a statement would.""" if not _is_int(start_value.minor) or start_value.minor < 0: raise ValueError("startValue must be whole minor units, 0 or more; received %s" % (start_value.minor,)) g = annual_growth_basis_points if not _is_int(g) or g < -10000: raise ValueError("annualGrowthBasisPoints must be a whole number, -10000 or more; received %s" % (g,)) f = annual_fee_basis_points if not _is_int(f) or f < 0 or f > 10000: raise ValueError("annualFeeBasisPoints must be a whole number from 0 to 10000; received %s" % (f,)) if not _is_int(years) or years < 0 or years > 100: raise ValueError("years must be a whole number from 0 to 100; received %s" % (years,)) currency = start_value.currency without = start_value.minor with_fee = start_value.minor fees = 0 schedule: List[FeeDragYear] = [] for year in range(1, years + 1): without += apply_rate(money(without, currency), g, "half-up").minor grown = with_fee + apply_rate(money(with_fee, currency), g, "half-up").minor fee = apply_rate(money(grown, currency), f, "half-up").minor with_fee = grown - fee fees += fee schedule.append( FeeDragYear( year=year, value_without_fee=money(without, currency), value_with_fee=money(with_fee, currency), fee=money(fee, currency), ) ) drag = without - with_fee drag_bp = 0 if without > 0: n = drag * 10000 q, r = divmod(abs(n), without) if 2 * r >= without: q += 1 drag_bp = -q if n < 0 else q return FeeDrag( value_without_fee=money(without, currency), value_with_fee=money(with_fee, currency), drag=money(drag, currency), drag_basis_points=drag_bp, fees_paid=money(fees, currency), schedule=schedule, )