
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3294 8e941d3f-bd1b-0410-a28a-d453659cc2b4
185 lines
5.7 KiB
ObjectPascal
185 lines
5.7 KiB
ObjectPascal
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.
|
|
|