コード例 #1
0
def value_at_time(value,
                  rate,
                  now='start',
                  time='end',
                  nper=1,
                  *,
                  high_precision=None):
    """ Returns the present (or future) value.

    Args:
        value (Money): The (nominal) value to be converted.
        rate (Decimal): The rate of growth (e.g. inflation)
        now (Decimal): The time associated with the nominal value,
            expressed using `when_conv` syntax.
        time (Decimal): The time to which the nominal value is to
            be converted, expressed using `when_conv` syntax.
        nper (Decimal): The number of compounding periods for growth.

    Returns:
        A Money object representing the present value
        (if now > time) or the future value (if now < time) of
        `value`.
    """
    return value * accumulation_function(
        when_conv(time, high_precision=high_precision) -
        when_conv(now, high_precision=high_precision),
        rate,
        nper,
        high_precision=high_precision)
コード例 #2
0
ファイル: base.py プロジェクト: dxcv/forecaster
    def time_to_balance(self, value, when=Decimal(0)):
        """ Returns the time required to grow to a given balance.

        If `when` is provided, this method returns the earliest time
        at or after `when` when the balance has reached `value`. This
        method is transaction-aware; a given balance may be reached
        more than once if there are inflows/outflows.

        Args:
            value (Money): The balance to grow to.
            when (Decimal): Only balances reached on or after `when`
                are considered. Optional.
        """
        # Convert `when` to avoid type errors.
        when = when_conv(when)

        # We'll base all calculations at `when`, including the value
        # of `balance`. Do this even for `when=0`, since there may
        # be a transaction at the start of the year that isn't
        # reflected by `balance` but is incorporated in
        # `balance_at_time`.
        balance = self.balance_at_time(when)

        # Determine when we'll reach the desired amount, assuming
        # no further transactions:
        time = when + time_to_value(self.rate, balance, value, nper=self.nper)

        # Now look ahead to the next transaction and, if it happens
        # before `time`, recurse onto that transaction's timing:
        next_transaction = min(
            (key for key in self.transactions if key > when), default=time)
        if next_transaction < time:
            time = self.time_to_balance(value, next_transaction)

        return time
コード例 #3
0
    def balance_at_time(self, time, transactions=None):
        """ Returns the balance at a point in time.

        Args:
            when (Decimal, str): The time at which the account's balance
                is to be determined.
            transactions (dict[Decimal, float]): If provided, the result
                of this method will be determined as if the account
                also had these transactions recorded against it.
        """
        # We need to convert `time` to enable the comparison in the dict
        # comprehension in the for loop below.
        time = when_conv(time, high_precision=self.high_precision)

        # Find the future value (at t=time) of the initial balance.
        # This doesn't include any transactions of their growth.
        balance = value_at_time(self.balance,
                                self.rate,
                                'start',
                                time,
                                nper=self.nper,
                                high_precision=self.high_precision)

        # Combine the recorded and input transactions, if provided:
        if transactions is not None:
            transactions = copy(transactions)
            add_transactions(transactions, self.transactions)
        # Otherwise simply use the account's recorded transactions:
        else:
            transactions = self.transactions
        # Add in the future value of each transaction (except that that
        # happen after `time`).
        for when in [w for w in transactions if w <= time]:
            balance += value_at_time(transactions[when],
                                     self.rate,
                                     when,
                                     time,
                                     nper=self.nper,
                                     high_precision=self.high_precision)

        return balance
コード例 #4
0
    def add_transaction(self, value, when='end'):
        """ Adds a transaction to the account.

        Args:
            value (float): The value of the transaction. Positive values
                are inflows and negative values are outflows.
            when (float, Decimal, str): The timing of the transaction.
                Must be in the range [0,1] or be a suitable str input,
                as described in the documentation for `when_conv`.

        Raises:
            decimal.InvalidOperation: Transactions must be convertible
                to type Money and `when` must be convertible to type
                Decimal.
            ValueError: `when` must be in [0,1]
        """
        when = when_conv(when, high_precision=self.high_precision)

        # NOTE: If `value` is intended to be a special Money type
        # (like PyMoney), attempt conversion here

        # Simultaneous transactions are modelled as one sum,
        self.transactions[when] += value
コード例 #5
0
ファイル: base.py プロジェクト: dxcv/forecaster
    def add_transaction(self, value, when='end'):
        """ Adds a transaction to the account.

        Args:
            value (Money): The value of the transaction. Positive values
                are inflows and negative values are outflows.
            when (float, Decimal, str): The timing of the transaction.
                Must be in the range [0,1] or be a suitable str input,
                as described in the documentation for `when_conv`.

        Raises:
            decimal.InvalidOperation: Transactions must be convertible
                to type Money and `when` must be convertible to type
                Decimal.
            ValueError: `when` must be in [0,1]
        """
        when = when_conv(when)

        # Try to cast non-Money objects to type Money
        if not isinstance(value, Money):
            value = Money(value)

        # Simultaneous transactions are modelled as one sum,
        self.transactions[when] += value
コード例 #6
0
ファイル: base.py プロジェクト: dxcv/forecaster
 def __contains__(self, key):
     when = when_conv(key)
     return when in self._transactions
コード例 #7
0
 def __contains__(self, key):
     when = when_conv(key, high_precision=self.high_precision)
     return when in self._transactions
コード例 #8
0
 def test_when_conv_invalid(self):
     """ Tests `when_conv` on an invalid input. """
     with self.assertRaises(decimal.InvalidOperation):
         _ = when_conv('invalid input')
コード例 #9
0
 def test_when_conv_str(self):
     """ Tests `when_conv` on a non-magic str input. """
     when = when_conv('1')
     self.assertEqual(when, Decimal(1))
コード例 #10
0
 def test_when_conv_end(self):
     """ Tests `when_conv` on 'end'. """
     when = when_conv('end')
     self.assertEqual(when, Decimal(1))
コード例 #11
0
 def test_when_conv_start(self):
     """ Tests `when_conv` on 'start'. """
     when = when_conv('start')
     self.assertEqual(when, Decimal(0))
コード例 #12
0
 def test_when_conv_simple(self):
     """ Tests `when_conv` on a simple, single-valued input. """
     when = when_conv(1)
     self.assertEqual(when, Decimal(1))