unit financemath; {$mode objfpc}{$H+} interface uses Classes, SysUtils; { Cash flow equation: FV + PV * q^n + PMT (q^n - 1) / (q - 1) = 0 (1) with q = 1 + interest rate (RATE) PV = present value of an investment FV = future value of an investment PMT = regular payment (per period) n = NPER = number of payment periods This is valid for payments occuring at the end of each period. If payments occur at the start of each period the payments are multiplied by a factor q. This case is indicated by means of the parameter PaymentTime below. The interest rate is considered as "per period" - whatever that is. If the period is 1 year then we use the "usual" interest rate. If the period is 1 month then we use 1/12 of the yearly interest rate. Sign rules: - Money that I receive is to a positive number - Money that I pay is to a negative number. Example 1: Saving account - A saving account has an initial balance of 1000 $ (PV). I paid this money to the bank --> negative number - I deposit 100$ regularly to this account (PMT): I pay this money --> negative number. - At the end of the payments (NPER periods) I get the money back --> positive number. This is the FV. Example 2: Loan - I borrow 1000$ from the bank: I get money --> positive PV - I pay 100$ back to the bank in regular intervals --> negative PMT - At the end, all debts are paid --> FV = 0. The cash flow equation (1) contains 5 parameters (Rate, PV, FV, PMT, NPER). The functions below solve this equation always for one of these parameters. References: - http://en.wikipedia.org/wiki/Time_value_of_money - https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_Derivation_of_Financial_Formulas } type TPaymentTime = (ptEndOfPeriod, ptStartOfPeriod); function FutureValue(ARate: Extended; NPeriods: Integer; APayment, APresentValue: Extended; APaymentTime: TPaymentTime): Extended; function InterestRate(NPeriods: Integer; APayment, APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; function NumberOfPeriods(ARate, APayment, APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; function Payment(ARate: Extended; NPeriods: Integer; APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; function PresentValue(ARate: Extended; NPeriods: Integer; APayment, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; implementation uses math; function FutureValue(ARate: Extended; NPeriods: Integer; APayment, APresentValue: Extended; APaymentTime: TPaymentTime): Extended; var q, qn, factor: Extended; begin if ARate = 0 then Result := -APresentValue - APayment * NPeriods else begin q := 1.0 + ARate; qn := power(q, NPeriods); factor := (qn - 1) / (q - 1); if APaymentTime = ptStartOfPeriod then factor := factor * q; Result := -(APresentValue * qn + APayment*factor); end; end; function InterestRate(NPeriods: Integer; APayment, APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; { The interest rate cannot be calculated analytically. We solve the equation numerically by means of the Newton method: - guess value for the interest reate - calculate at which interest rate the tangent of the curve fv(rate) (straight line!) has the requested future vale. - use this rate for the next iteration. } const DELTA = 0.001; EPS = 1E-9; // required precision of interest rate (after typ. 6 iterations) MAXIT = 20; // max iteration count to protect agains non-convergence var r1, r2, dr: Extended; fv1, fv2: Extended; iteration: Integer; begin iteration := 0; r1 := 0.05; // inital guess repeat r2 := r1 + DELTA; fv1 := FutureValue(r1, NPeriods, APayment, APresentValue, APaymentTime); fv2 := FutureValue(r2, NPeriods, APayment, APresentValue, APaymentTime); dr := (AFutureValue - fv1) / (fv2 - fv1) * delta; // tangent at fv(r) r1 := r1 + dr; // next guess inc(iteration); until (abs(dr) < EPS) or (iteration >= MAXIT); Result := r1; end; function NumberOfPeriods(ARate, APayment, APresentValue, AFutureValue: extended; APaymentTime: TPaymentTime): extended; { Solve the cash flow equation (1) for q^n and take the logarithm } var q, x1, x2: Extended; begin if ARate = 0 then Result := -(APresentValue + AFutureValue) / APayment else begin q := 1.0 + ARate; if APaymentTime = ptStartOfPeriod then APayment := APayment * q; x1 := APayment - AFutureValue * ARate; x2 := APayment + APresentValue * ARate; if (x2 = 0) // we have to divide by x2 or (sign(x1) * sign(x2) < 0) // the argument of the log is negative then Result := Infinity else begin Result := ln(x1/x2) / ln(q); end; end; end; function Payment(ARate: Extended; NPeriods: Integer; APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; var q, qn, factor: Extended; begin if ARate = 0 then Result := -(AFutureValue + APresentValue) / NPeriods else begin q := 1.0 + ARate; qn := power(q, NPeriods); factor := (qn - 1) / (q - 1); if APaymentTime = ptStartOfPeriod then factor := factor * q; Result := -(AFutureValue + APresentValue * qn) / factor; end; end; function PresentValue(ARate: Extended; NPeriods: Integer; APayment, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; var q, qn, factor: Extended; begin if ARate = 0.0 then Result := -AFutureValue - APayment * NPeriods else begin q := 1.0 + ARate; qn := power(q, NPeriods); factor := (qn - 1) / (q - 1); if APaymentTime = ptStartOfPeriod then factor := factor * q; Result := -(AFutureValue + APayment*factor) / qn; end; end; end.