Financial calculator
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 valuepurchasingPower
to calculate purchasing power of amount after given number of monthsfixedMonthlyPayments
to calculate monthly payments to save given amount in today's value in given monthsvariableMonthlyPayments
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 savingsinflationRate
to calculate inflation rate by CPIs on current and previous periodsconsumerPriceIndex
to calculate consumer price index for single and multiple itemsxirr
to calculate internal rate of return for a schedule of cash flows that is not necessarily periodic
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?
If you save x
every month, the future value (FV) is
where
n
is the number of monthsr
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
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
- Chris Degnen for answer on Stack Overflow
- Nikolay Stanev for XIRR demystifation
Source code licensed under Public Domain. See UNLICENSE file for details