Esempio n. 1
0
    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))
Esempio n. 2
0
 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
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
0
 def test_frequency_invalid_str(self):
     """ Test setting frequency to 'invalid', an invalid value. """
     with self.assertRaises(ValueError):
         _ = frequency_conv('invalid')
Esempio n. 6
0
 def test_frequency_invalid_fraction(self):
     """ Test setting frequency to 0.5, an invalid value. """
     with self.assertRaises(TypeError):
         _ = frequency_conv(0.5)
Esempio n. 7
0
 def test_frequency_invalid_negative(self):
     """ Test setting frequency to -1, an invalid value. """
     with self.assertRaises(ValueError):
         _ = frequency_conv(-1)
Esempio n. 8
0
 def test_frequency_annually(self):
     """ Test setting frequency to 'A'. """
     frequency = frequency_conv('A')
     self.assertEqual(frequency, 1)
Esempio n. 9
0
 def test_frequency_quarterly(self):
     """ Test setting frequency to 'Q'. """
     frequency = frequency_conv('Q')
     self.assertEqual(frequency, 4)
Esempio n. 10
0
 def test_frequency_bimonthly(self):
     """ Test setting frequency to 'BM'. """
     frequency = frequency_conv('BM')
     self.assertEqual(frequency, 6)
Esempio n. 11
0
 def test_frequency_semimonthly(self):
     """ Test setting frequency to 'SM'. """
     frequency = frequency_conv('SM')
     self.assertEqual(frequency, 24)
Esempio n. 12
0
 def test_frequency_biweekly(self):
     """ Test setting frequency to 'BW'. """
     frequency = frequency_conv('BW')
     self.assertEqual(frequency, 26)
Esempio n. 13
0
 def test_frequency_daily(self):
     """ Test setting frequency to 'D'. """
     frequency = frequency_conv('D')
     self.assertEqual(frequency, 365)
     self.assertIsInstance(frequency, int)