char16t / calc

Library for calculating the internal rate of return and cost of money over the time

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Financial calculator

Usage

From source code

Clone, build and publish library to local repository.

git clone https://github.com/char16t/calc
cd calc
sbt publishLocal

Add to your build.sbt (Scala 2.13.1 or higher):

libraryDependencies += "com.manenkov" %% "calc" % "0.2"

Library provides methods:

  • futureValue to calculate future value
  • purchasingPower to calculate purchasing power of amount after given number of months
  • fixedMonthlyPayments to calculate monthly payments to save given amount in today's value in given months
  • variableMonthlyPayments to calculate monthly payments to save given amount in today's value in given months (with compensating inflation)
  • savingsPerMonth to calculate stream of monthly amount of savings
  • inflationRate to calculate inflation rate by CPIs on current and previous periods
  • consumerPriceIndex to calculate consumer price index for single and multiple items
  • xirr to calculate internal rate of return for a schedule of cash flows that is not necessarily periodic

Problem

You have a target amount T (in today's value, before inflation) that you would like to accumulate over N years. Every month you invest X with an expected return of Y%. Expected inflation is Z% per year. How can you calculate whether you investing enough? If that time is not enough, how long do you need to invest the money? How much should you invest monthly to meet the required deadline?

Solution

If you save x every month, the future value (FV) is

where

  • n is the number of months
  • r is the monthly rate of return

So if Y% return is 10% nominal interest compounded monthly

val r = 0.10/12

For example, over 3 months, saving $100 per month

val n  = 3
val x  = 100
val fv = (x * (1 + r) * (Math.pow(1 + r, n) - 1)) / r      // = 305.03

or using library

val monthly = 100
val months = 3
val returnValue = 0.1
val actual = Money.futureValue(monthly, months, returnValue)  // = 305.03

Checking the balance at the end of each month long-hand

val b1 = 100 * (1 + 0.1/12)                     // = 100.83        
val b2 = (b1 + 100) * (1 + 0.1/12)              // = 202.51
val b3 = (b2 + 100) * (1 + 0.1/12)              // = 305.03

The formula checks out.

So after 10 years, saving $100 per month

val n  = 120
val x  = 100 
val fv = (x * (1 + r) * (Math.pow(1 + r, n) - 1))/r        // = 20655.20

Discounting for inflation at, say, 2% per annum

fv / Math.pow(1 + 0.02, 10) = 16944.46

or using library

val amount = 20655.20
val months = 120
val inflationValue = 0.02
val actual = Money.purchasingPower(amount, months, inflationValue) // 16944.46

Your future saving of $20,655 would have the purchasing power of $16,944 today.

To work it backwards, if you want $100,000 in today's value in 10 years

val fv = 100000 * Math.pow(1 + 0.02, 10)                       // = 121899.44
val r  = 0.10/12
val n  = 120
val x  = (fv * r)/((1 + r) * (Math.pow(1 + r, n) - 1))         // = 590.16

or using library

val amount = 100_000.0
val months = 120
val inflationValue = 0.02
val returnValue = 0.1
val actual = Money.fixedMonthlyPayments(amount, months, inflationValue, returnValue) // = 590.16

You would need to save $590.16 each month.

Compensating for inflation

Inflation can be compensated for by increasing the monthly payments at the same rate as inflation. This makes the payments equal in 'value' terms.

Inflation is usually quoted as an effective annual rate, so with 2% (as before) the monthly rate is obtained like so

val i = Math.pow(1 + 0.02, 1/12) - 1                          // = 0.00165158

and the 3 month long-hand calculation becomes

val b1 = 100 * (1 + 0.1/12)                                   // = 100.83
val b2 = (b1 + 100 (1 + i)) * (1 + 0.1/12)                    // = 202.67
val b3 = (b2 + 100 * Math.pow(1 + i, 2)) * (1 + 0.1/12)       // = 305.53

or using library

val monthly: Double = 100.0
val inflationValue: Double = 0.02
val returnValue: Double = 0.1
val savings = Money.savingsPerMonth(monthly, inflationValue, returnValue)
// savings is LazyList(100.83, 202.67, 305.53, ...)

This can be expressed as a formula

Once again, to save $100,000 in today's value over ten years

val fv = 100000 * Math.pow(1 + 0.02, 10)                                       // = 121899.44
val n  = 120
val i  = Math.pow(1 + 0.02, 1/12) - 1                                          // = 0.00165158
val r  = 0.10/12

val x  = (fv * (i - r))/((1 + r) * (Math.pow(1 + i, n) - Math.pow(1 + r, n)))  // = 542.84

The first payment is $542.84, and the payments increase like so

x * Math.pow(1 + i, 0)      // = 542.84 (month 1)
x * Math.pow(1 + i, 1)      // = 543.74 (month 2)
x * Math.pow(1 + i, 2)      // = 544.63 (month 3)
                            // ...
x * Math.pow(1 + i, 119)    // = 660.63 (month 120)

or using library

val amount = 100_000.0
val months = 120
val inflationValue = 0.02
val returnValue = 0.1
val payments = Money.variableMonthlyPayments(amount, months, inflationValue, returnValue, reverse = false)
// payments is List(542.84, 543.74, 544.63, ..., 660.63)

XIRR

XIRR is Excel function that helps calculate internal rate of return for non-periodical payments. Example:

This repo contains Scala implementation for XIRR. Example of calculation like on image:

val cf = Seq(
  (-997.78, LocalDateTime.of(2019, 3, 1, 0, 0)),
  (34.9, LocalDateTime.of(2019, 6, 19, 0, 0)),
  (34.9, LocalDateTime.of(2019, 12, 18, 0, 0)),
  (34.9, LocalDateTime.of(2020, 6, 17, 0, 0)),
  (34.9, LocalDateTime.of(2020, 12, 16, 0, 0)),
  (34.9, LocalDateTime.of(2021, 6, 16, 0, 0)),
  (1034.9, LocalDateTime.of(2021, 12, 15, 0, 0)),
)
val expected = 0.0778696
Money.xirr(cf) == expected                                             // true
Money.xirr(cf, decimals = 8, maxRate = Double.MaxValue) == expected    // true

Thanks

License

Source code licensed under Public Domain. See UNLICENSE file for details

About

Library for calculating the internal rate of return and cost of money over the time

License:The Unlicense


Languages

Language:Scala 100.0%