def test_frequency_continuous(self): """ Test setting frequency to 'C' and 'None' (equivalent). """ frequency = frequency_conv('C') self.assertEqual(frequency, None) self.assertIsInstance(frequency, type(None)) frequency = frequency_conv(None) self.assertEqual(frequency, None) self.assertIsInstance(frequency, type(None))
def nper(self, value): # Convert strings to either a number or float value = frequency_conv(value) # Convert non-None values to high-precision, if enabled: if value is not None: value = self.precision_convert(value) self._nper = value
def __init__(self, owner=None, balance=0, rate=0, nper=1, default_timing=None, inputs=None, initial_year=None): """ Constructor for `Account`. This constructor receives only values for the first year. Args: owner (Person): The owner of the account. Optional. balance (Money): The balance for the first year rate (Decimal, callable): An object that gives the rate for each year, either as a constant value (e.g. a Decimal) or as a callable object with a signature of the form `rate(year) -> Decimal`. If this callable object relies on `Scenario` or other objects defined in the `forecaster` package, recommend passing an object that stores these objects explicitly as attributes (as opposed to a method/function where these objects are stored in the context), otherwise `Forecaster`'s object-substitution logic will not work. nper (int): The number of compounding periods per year. default_timing (Timing): The usual schedule for transactions to/from this account, used by various methods when no `timing` arg is expressly provided. Optional. initial_year (int): The first year (e.g. 2000) """ # Use the explicitly-provided initial year if available, # otherwise default to the owner's initial year: if initial_year is None: if not hasattr(owner, 'initial_year'): raise TypeError( 'Account: owner must have initial_year attribute.') else: initial_year = owner.initial_year super().__init__(initial_year=initial_year, inputs=inputs) # Set hidden attributes to support properties that need them to # be set in advance: self._owner = None self._transactions = defaultdict(lambda: Money(0)) self._rate_callable = None self._default_timing = None # Set the various property values based on inputs: self.owner = owner self.balance = Money(balance) self.rate_callable = rate self.nper = frequency_conv(nper) if default_timing is None: self.default_timing = Timing() else: self.default_timing = default_timing
def __init__(self, owner=None, balance=None, rate=None, nper=None, default_timing=None, inputs=None, initial_year=None, *, high_precision=None, **kwargs): """ Constructor for `Account`. This constructor receives only values for the first year. Args: owner (Person): The owner of the account. Optional. balance (float): The balance for the first year. Optional, defaults to 0. rate (float, callable): An object that gives the rate for each year, either as a constant value (e.g. 1.0 implies a 100% interest rate) or as a callable object with a signature of the form `rate(year) -> Decimal`. If this callable object relies on `Scenario` or other objects defined in the `forecaster` package, recommend passing an object that stores these objects explicitly as attributes (as opposed to a method/function where these objects are stored in the context), otherwise `Forecaster`'s object-substitution logic will not work. Optional. Defaults to 0 (i.e. no growth/interest/etc.) nper (int): The number of compounding periods per year. Optional. Defaults to 1 (i.e. compounds once annually). default_timing (Timing): The usual schedule for transactions to/from this account, used by various methods when no `timing` arg is expressly provided. Optional. initial_year (int): The first year (e.g. 2000) high_precision (Callable[[float], T]): Takes a single `float` argument and converts it to high-precision numeric type `T`, such as Decimal. """ # Use the explicitly-provided initial year if available, # otherwise default to the owner's initial year: if initial_year is None: if not hasattr(owner, 'initial_year'): raise TypeError( 'Account: owner must have initial_year attribute.') else: initial_year = owner.initial_year # Defer Ledger's init until we can pass initial_year: super().__init__(initial_year=initial_year, inputs=inputs, high_precision=high_precision, **kwargs) # For numerical optional inputs, ensure an appropriately-typed # default value is used. (Do this after __init__!) if balance is None: balance = self.precision_convert(0) if rate is None: rate = self.precision_convert(0) if nper is None: nper = self.precision_convert(1) # Set hidden attributes to support properties that need them to # be set in advance: self._owner = None self._transactions = defaultdict( lambda: self.precision_convert(0)) # Money value self._rate_callable = None self._default_timing = None self._nper = None # Set the various property values based on inputs: self.owner = owner self.balance = balance # Money value self.rate_callable = rate self.nper = frequency_conv(nper) if default_timing is None: self.default_timing = Timing(high_precision=high_precision) else: self.default_timing = default_timing
def test_frequency_invalid_str(self): """ Test setting frequency to 'invalid', an invalid value. """ with self.assertRaises(ValueError): _ = frequency_conv('invalid')
def test_frequency_invalid_fraction(self): """ Test setting frequency to 0.5, an invalid value. """ with self.assertRaises(TypeError): _ = frequency_conv(0.5)
def test_frequency_invalid_negative(self): """ Test setting frequency to -1, an invalid value. """ with self.assertRaises(ValueError): _ = frequency_conv(-1)
def test_frequency_annually(self): """ Test setting frequency to 'A'. """ frequency = frequency_conv('A') self.assertEqual(frequency, 1)
def test_frequency_quarterly(self): """ Test setting frequency to 'Q'. """ frequency = frequency_conv('Q') self.assertEqual(frequency, 4)
def test_frequency_bimonthly(self): """ Test setting frequency to 'BM'. """ frequency = frequency_conv('BM') self.assertEqual(frequency, 6)
def test_frequency_semimonthly(self): """ Test setting frequency to 'SM'. """ frequency = frequency_conv('SM') self.assertEqual(frequency, 24)
def test_frequency_biweekly(self): """ Test setting frequency to 'BW'. """ frequency = frequency_conv('BW') self.assertEqual(frequency, 26)
def test_frequency_daily(self): """ Test setting frequency to 'D'. """ frequency = frequency_conv('D') self.assertEqual(frequency, 365) self.assertIsInstance(frequency, int)