Financial calculator
From source code
Clone, build and publish library to local repository.
git clone https://github.com/char16t/calc
cd calc
sbt publishLocalAdd to your build.sbt (Scala 2.13.1 or higher):
libraryDependencies += "com.manenkov" %% "calc" % "0.2"Library provides methods:
futureValueto calculate future valuepurchasingPowerto calculate purchasing power of amount after given number of monthsfixedMonthlyPaymentsto calculate monthly payments to save given amount in today's value in given monthsvariableMonthlyPaymentsto calculate monthly payments to save given amount in today's value in given months (with compensating inflation)savingsPerMonthto calculate stream of monthly amount of savingsinflationRateto calculate inflation rate by CPIs on current and previous periodsconsumerPriceIndexto calculate consumer price index for single and multiple itemsxirrto 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
nis the number of monthsris the monthly rate of return
So if Y% return is 10% nominal interest compounded monthly
val r = 0.10/12For 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.03or using library
val monthly = 100
val months = 3
val returnValue = 0.1
val actual = Money.futureValue(monthly, months, returnValue) // = 305.03Checking 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.03The 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.20Discounting for inflation at, say, 2% per annum
fv / Math.pow(1 + 0.02, 10) = 16944.46or using library
val amount = 20655.20
val months = 120
val inflationValue = 0.02
val actual = Money.purchasingPower(amount, months, inflationValue) // 16944.46Your 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.16or 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.16You 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.00165158and 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.53or 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.84The 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

