All Downloads are FREE. Search and download functionalities are using the official Maven repository.

no.finansportalen.freecalc.freeloan.calc.IntervallengthCalc Maven / Gradle / Ivy

package no.finansportalen.freecalc.freeloan.calc;

import no.finansportalen.freecalc.common.AnnuityLoanPeriod;
import no.finansportalen.freecalc.common.Utils;
import no.finansportalen.freecalc.freeloan.calc.FreeLoanException.FreeLoanExceptionType;

class IntervallengthCalc {

    /**
     * The size of the principal in this segment.
     */
    private double upperlimit;

    /**
     * Nominal, annual interest rate. RATE CANNOT BE ZERO (!= 0)
     */
    private double rate;

    /**
     * Remaining principal after the interval is over
     */
    private double lowerlimit;

    /**
     * The remaining number of periodical payments for the whole loan when the function is called. CANNOT BE ZERO (!= 0)
     */
    private double periods;

    /**
     * NORMAL: Annuities are rounded after normal rules UP: Rounded up DOWN. Rounded down
     */
    private Utils.RoundDirection roundDirection;

    /**
     * false: Payment rounded to nearest 1/100 true: Rounded to nearest integer
     */
    private boolean roundToInteger;

    /**
     * Factor the nominal annual rate in percent is divided by to obtain the rate in decimal fraction
     */
    private int rateDivisor;

    /**
     * The interest from an eventual residual/balloon added to each period payment.
     */
    private double interestAmountRes;

    /**
     * Boolean. If 'false', we use normal annuities (annuity-immediate). Otherwise annuities in advance (annuity-due).
     */
    private boolean advance;

    /**
     * 

* WHAT THE FUNCTION DOES: *

* *

* It computes how many periods it takes to pay the segment's principal 'upperlimit' down to the amount 'lowerlimit' * at the given interest rate and the given number of periods ('periodsRemaining') that are left of the loan period. *

* *

* The number of periods cannot be a decimal number. The answer is thus rounded up to the nearest integer, because * we want to make sure that the principal in the interval is payed down. (When there is subsequent intervals, the * interest rate does not change before the principal limit is surpassed). *

* *

* When the number of periods are rounded up, we don't "hit" the principal limit accurately. Normally, the loan will * be payed further down - the remaining principal is lower than the limit. This remaining principal is computed and * returned in 'answer[3]'. *

* *

* When we compute the number of periods, we don't include periodical fees, because these fees don't affect the * remaining principal in the bank's books. *

* *

* The periodical payment is rounded before the computations begin. *

* * *

DERIVATION OF FORMULAS

* *

* a) ANNUITY-IMMEDIATE *

* *

* Consider a loan of 2.5 millions, where you pay 3.95% as long as the remaining principal is greater than 2 * millions. You pay 4.05% when the loan is between 1 million and 2 millions. You pay 4.15% if the loan is paid off * below 1 million. The example loan runs for 20 years with monthly payments, 240 payments in all: *

* *

* We can easily compute the first annuity, using the annuity formula: *

* ********************************************************************************* *

FORMULA FOR ANNUITY-IMMEDIATE:

* * annuity = loan * (1 - k) / (k - Math.pow(k,termnumber+1));
* where
* k = 1/(1+rate);
********************************************************************************* * *

* It will be 15,083.72 *

* *

* At some point, the first threshold 2,000,000 will be passed, and the rate changes. But how many periods will this * take? *

* *

* We know that if we pay 15.083.72 per month in 240 months, we will have paid the 2,5 million. The present value * over 240 terms is 2,5 millions: *

* *

* PV(240) = 2,5 millons *

* *

* After an unknown number of periods – x –, we will pass the 2 million threshold. Then, the present value of the * remaining loan will be 2 millions. *

* *

* PV(x) = 2 millons *

* * *

* PV(240) - PV(x) = 500,000 *

* * *

* We rearrange the annuity formula with respect to the loan: *

* *

* capital = annuity * (k – k^(termnum+1)) /(1 - k) *

* *

* 2,000,000 = annuity * (k – k^(x+1)) /(1 - k) *

* *

* We call 2,000,000 ‘PV’ and the annuity ‘a’: *

* *

* PV = a * (k – k^(x+1)) /(1 - k) => (PV * (1 – k)/a )= (k – k^(x+1)) => (PV * (1 – k)/a) –k = – k^(x+1) => (PV * * (1 – k)/a) –k = – k^(x+1) *

* *

* We multiply by -1: *

* *

* k – (PV * (1 – k)/a) = k^(1+x) *

* *

* The whole left side consists of constants. We can set *

* *

* II C = k – (PV * (1 – k)/a) I+II C = k^(1+x) *

* *

* Taking the logarithm of both sides: *

* *

* log(C) = (1 + x) * log (k) => log(C) = log (k)+ (x * log (k)) => (log(C) - log (k))/log(k) = x *

* ********************************************************************************* *

TERM NUMBER FORMULA FOR ANNUITY-IMMEDIATE

* * terms = (log(C)- log (k))/log(k)
* where:
* k = 1/(1+rate);
* C = k – (PV * (1 – k)/a)
* a = annuity
* PV = present value
* (capital/loan/principal)
********************************************************************************* * *

* Subsituting the variable names: *

* *

* nomrate = 3.95% r = nomrate/1200 k = 1/(1+r) => k = 0.996719133 *

* *

* PV = 2,000,000 a = 15,083.72 => x = 174.515769 *

* *

* Thus, with an annuity of 15,083.72, the present value is 2,000,000 if the loan runs 174.515769 terms/payments. *

* *

* As term numbers in the real world are always integers, we have to round this number down in order to be certain * that the 2 million threshold is passed, and the interest rate thus can change. *

* *

* 174,515769 => 174 *

* *

* The number of periods in the first interval, where the loan runs with an interest rate of 3.95%, is thus 240 – 174 * = 66. We repeat this procedure with eventual subsequent intervals. *

* * *

* b) ANNUITY-DUE *

* *

* For loans where the annuities are paid in advance, annuity-due, we make a similar derivation: *

********************************************************************************* *

FORMULA FOR ANNUITY-DUE:

* * annuity = loan * (1 - k) / (1 - Math.pow(k,termnumber));
* where
* k = 1/(1+rate);
********************************************************************************* * *

* We use short forms of the variable names: *

* *

* a = PV * (1-k) / (1 - Math.pow(k,x)) => (1 - Math.pow(k,x)) = PV * (1-k) / a => - Math.pow(k,x) = (PV * (1-k) / * a) - 1 => 1 - (PV * (1-k) / a) = Math.pow(k,x) *

* * *

* The whole left side consists of constants. We can set *

* *

* II C = 1 – (PV * (1 – k)/a) I+II C = k^x *

* *

* Taking the logarithm of both sides: *

* *

* log(C) = x * log (k) => x = log(C)/ log(k) *

* ********************************************************************************* *

TERM NUMBER FORMULA FOR ANNUITY-DUE

* * terms = log(C)/ log(k)
* where:
* k = 1/(1+rate);
* C = 1 – (PV * (1 – k)/a)
* a = annuit
* PV = present value
* (capital/loan/principal)
********************************************************************************* * *

* NB! These two formulas don't support negative interest rate, as that would lead to a k < 1 *

* *

* And log(k) is not defines for negative values of 'k'. *

*/ public AnnuityLoanPeriod calculate() throws FreeLoanException { double fullannuity, C, a, remaintime; double k = 1 / (1 + rate / rateDivisor); if (k < 0) { // Negative interest rate not supported throw new FreeLoanException(FreeLoanExceptionType.FAILING_CONVERGENCE); } if (advance) { if (periods != 1) { fullannuity = upperlimit * (1 - k) / (1 - Math.pow(k, periods)); } else { // 'periods' = 1 would give divitions with zero throw new FreeLoanException(FreeLoanExceptionType.FAILING_CONVERGENCE); } // In order to find the correct number of periods, we must use the actually paid - rounded - annuities: a = Utils.roundoff(fullannuity, roundDirection, roundToInteger); C = 1 - (lowerlimit * (1 - k)/a); remaintime = Math.log(C)/Math.log(k); } else { if (periods != 0) { fullannuity = upperlimit * (1 - k) / (k - Math.pow(k, periods + 1)); } else { throw new FreeLoanException(FreeLoanExceptionType.FAILING_CONVERGENCE); } // In order to find the correct number of periods, we must use the actually paid - rounded - annuities: a = Utils.roundoff(fullannuity, roundDirection, roundToInteger); C = k - (lowerlimit * (1 - k)/a); remaintime = (Math.log(C) - Math.log(k))/Math.log(k); } /* As the time elapsed in this interval is 'elapsed' = 'periods' minus 'remaintime', we must round 'remaintime' DOWN to be certain the threshold is really passed: */ remaintime = Math.floor(remaintime); // The difference between the unrounded and rounded annuity double remainder = fullannuity - a; /* * REMAINING PRINCIPAL * * * This is one of the most subtle/tricky parts of FreeLoan. * * We can only pay rounded annuities. One thousandth of a cent does not exist. * * The present value of the rounded payments do not exactly match the present value of the theoretical payments * writtten with an 'infinite' number of decimal fractions. * * But the bank will still deduct the installment part of the annuity from the booked principal. For each term, * these two values will differ. * * This discrepancy will follow us through the whole computation if we apply the annuity formula directly in * order to find the remaining principal. * * We find the remaining principal by: * * o Computing the principal 'upperlimit_adjusted' using the rounded annuity 'annuity_true' o Computing * 'deviation' - the difference between 'upperlimit_adjusted' and 'upperlimit' - the latter a parameter to the * function o Computing 'remainingprincipal' as the sum of the present value at the end of the interval using * 'annuity_true' and the forward discounted deviation. * * * In case the loan has a residual/ballon part that remains at the end of the loan period, the interest amount * for an eventual such part of the loan is found in the parameter 'interestAmountRes'. It must be taken into * account when rounding: */ // Interest on residual + annuity double fullannuityround = Utils.roundoff(fullannuity + interestAmountRes, roundDirection, roundToInteger); // The rounded payment minus the unrounded interest amount double annuity_true = fullannuityround - interestAmountRes; // The number of periods it takes to pay the principal lower than 'lowerlimit' double elapsed = periods - remaintime; // The number of periods it takes to pay the principal lower than 'lowerlimit' // ALTERNATIVELY: var elapsed = Math.ceil(periodsRemaining - remaintime); /* * We apply the annuity formula again: * * annuity = principal * (1 - k) / (k - Math.pow(k,calculation_periods+1)) * * .. with respect to the principal: * * principal = annuity * (k - Math.pow(k,calculation_periods+1))/ (1 - k) * * .. if we got an annuity-due (payment in advance) loan: * * principal = annuity * (1 - Math.pow(k,calculation_periods))/ (1 - k) * * The adjusted principal, 'upperlimit_adjusted' deviates a little from 'upperlimit', as the former is computed * with rounded annuities: */ double upperlimit_adjusted; if (advance) { upperlimit_adjusted = annuity_true * (1 - Math.pow(k, periods)) / (1 - k); } else { upperlimit_adjusted = annuity_true * (k - Math.pow(k, periods + 1)) / (1 - k); } /* * We call our small deviation - the difference between the inital principal computed with rounded and unrounded * payments - 'deviation': */ double deviation = upperlimit - upperlimit_adjusted; /* * As time passes, this initial, small error will be charged with interest and grows. At any point in time, the * remainding debt will be the present value of the rounded, ordinary payments, plus the value of the * rate-incurred deviation: */ double remainingprincipal; if (advance) { remainingprincipal = annuity_true * (1 - Math.pow(k, remaintime)) / (1 - k) + deviation * Math.pow(1 + rate / rateDivisor, elapsed - 1); } else { remainingprincipal = annuity_true * (k - Math.pow(k, remaintime + 1)) / (1 - k) + deviation * Math.pow(1 + rate / rateDivisor, elapsed); } /* * FIVE ANSWERS * * We will return both the annuity and the number of periods it took to pay the loan down to 'lowerlimit' from * 'upperlimit' with that annity. We also wish to keep the new, adjusted value of the "outgoing" principal. */ AnnuityLoanPeriod answer = new AnnuityLoanPeriod(); answer.setPayment(a); // The real result of the function - the number of periods it takes to pay down this segment of the loan answer.setNumberOfTerms(elapsed); // The remaining principal answer.setLowerSegmentLimit(remainingprincipal); // Same as the parameter answer.setUpperSegmentLimit(upperlimit); // Cents/fractions of cents we pay too much or too little at each annuity due to rounding answer.setRemainder(remainder); return answer; } /** * @param upperlimit The size of the principal in this segment. */ public void setUpperlimit(double upperlimit) { this.upperlimit = upperlimit; } /** * @param rate Nominal, annual interest rate. RATE CANNOT BE ZERO (!= 0) */ public void setRate(double rate) { this.rate = rate; } /** * @param lowerlimit Remaining principal after the interval is over */ public void setLowerlimit(double lowerlimit) { this.lowerlimit = lowerlimit; } /** * @param periods_remaining The remaining number of periodical payments for the whole loan when * the function is called. CANNOT BE ZERO (!= 0) */ public void setPeriods(double periods_remaining) { this.periods = periods_remaining; } /** * @param roundDirection NORMAL: Annuities are rounded after normal rules UP: Rounded up DOWN. Rounded down */ public void setRoundDirection(Utils.RoundDirection round_direction) { this.roundDirection = round_direction; } /** * @param roundToInteger false: Payment rounded to nearest 1/100 true: Rounded to nearest integer */ public void setRoundToInteger(boolean roundToInteger) { this.roundToInteger = roundToInteger; } /** * @param rate_divisor Factor the nominal annual rate in percent is divided by to obtain the rate in decimal fraction */ public void setRateDivisor(int rate_divisor) { this.rateDivisor = rate_divisor; } /** * @param interest_amount_res The interest from an eventual residual/balloon added to each period payment. */ public void setInterestAmountRes(double interest_amount_res) { this.interestAmountRes = interest_amount_res; } /** * @param advance Boolean. If 'false', we use normal annuities (annuity-immediate). Otherwise annuities in * advance (annuity-due). */ public void setAdvance(boolean advance) { this.advance = advance; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy