You've already forked lazarus-ccr
1313 lines
43 KiB
ObjectPascal
1313 lines
43 KiB
ObjectPascal
![]() |
// Upgraded to Delphi 2009: Sebastian Zierer
|
||
|
|
||
|
(* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is TurboPower SysTools
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* TurboPower Software
|
||
|
*
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** *)
|
||
|
|
||
|
{*********************************************************}
|
||
|
{* SysTools: StFIN.pas 4.04 *}
|
||
|
{*********************************************************}
|
||
|
{* SysTools: Financial math functions modeled on *}
|
||
|
{* those in Excel *}
|
||
|
{*********************************************************}
|
||
|
|
||
|
{$IFDEF FPC}
|
||
|
{$mode DELPHI}
|
||
|
{$ENDIF}
|
||
|
|
||
|
//{$I StDefine.inc}
|
||
|
|
||
|
unit StFIN;
|
||
|
|
||
|
interface
|
||
|
|
||
|
uses
|
||
|
{$IFNDEF FPC}
|
||
|
Windows,
|
||
|
{$ENDIF}
|
||
|
{$IFDEF UseMathUnit}
|
||
|
Math,
|
||
|
{$ELSE}
|
||
|
StMath,
|
||
|
{$ENDIF}
|
||
|
SysUtils,
|
||
|
StBase,
|
||
|
StConst,
|
||
|
StDate;
|
||
|
|
||
|
type
|
||
|
TStPaymentTime = (ptEndOfPeriod, ptStartOfPeriod);
|
||
|
TStFrequency = (fqAnnual, fqSemiAnnual, fqQuarterly, fqMonthly);
|
||
|
TStBasis = (BasisNASD, {US (NASD) 30/360}
|
||
|
BasisActAct, {Actual/actual}
|
||
|
BasisAct360, {Actual/360}
|
||
|
BasisAct365, {Actual/365}
|
||
|
BasisEur30360); {European 30/360}
|
||
|
|
||
|
TStDateArray = array[0..(StMaxBlockSize div SizeOf(TStDate))-1] of TStDate;
|
||
|
|
||
|
|
||
|
const
|
||
|
StDelta : Extended = 0.00001; {delta for difference equations}
|
||
|
StEpsilon : Extended = 0.00001; {epsilon for difference equations}
|
||
|
StMaxIterations : Integer = 100; {max attempts for convergence}
|
||
|
|
||
|
|
||
|
function AccruedInterestMaturity(Issue, Maturity : TStDate;
|
||
|
Rate, Par : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the accrued interest for a security that pays interest at maturity}
|
||
|
|
||
|
function AccruedInterestPeriodic(Issue, Settlement, Maturity : TStDate;
|
||
|
Rate, Par : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the accrued interest for a security that pays periodic interest}
|
||
|
|
||
|
function BondDuration(Settlement, Maturity : TStDate;
|
||
|
Rate, Yield : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the Macauley duration for an assumed par value of $100}
|
||
|
|
||
|
function BondPrice(Settlement, Maturity : TStDate;
|
||
|
Rate, Yield, Redemption : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the "clean" bond price per $100 face value of a security}
|
||
|
|
||
|
function CumulativeInterest(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
PV : Extended;
|
||
|
StartPeriod, EndPeriod : Integer;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
{-Returns the cumulative interest paid on a loan in specified periods}
|
||
|
|
||
|
function CumulativePrincipal(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
PV : Extended;
|
||
|
StartPeriod, EndPeriod : Integer;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
{-Returns the cumulative principal paid on a loan in specified periods}
|
||
|
|
||
|
function DayCount(Day1, Day2 : TStDate; Basis : TStBasis) : LongInt;
|
||
|
{-Returns the number of days from Day1 to Day2 according to day count basis}
|
||
|
|
||
|
function DecliningBalance(Cost, Salvage : Extended;
|
||
|
Life, Period, Month : Integer) : Extended;
|
||
|
{-Fixed rate declining balance depreciation}
|
||
|
|
||
|
function DiscountRate(Settlement, Maturity : TStDate;
|
||
|
Price, Redemption : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the discount Rate for a security}
|
||
|
|
||
|
function DollarToDecimal(FracDollar : Extended;
|
||
|
Fraction : Integer) : Extended;
|
||
|
{-Converts a fractional dollar value to decimal dollar value}
|
||
|
|
||
|
function DollarToDecimalText(DecDollar : Extended) : string;
|
||
|
{-Converts a decimal dollar value into an English text string}
|
||
|
|
||
|
function DollarToFraction(DecDollar : Extended;
|
||
|
Fraction : Integer) : Extended;
|
||
|
{-Converts a decimal dollar value to fractional dollar value}
|
||
|
|
||
|
function DollarToFractionStr(FracDollar : Extended;
|
||
|
Fraction : Integer) : string;
|
||
|
{-Converts a fractional dollar value to number string}
|
||
|
|
||
|
function EffectiveInterestRate(NominalRate : Extended;
|
||
|
Frequency : TStFrequency) : Extended;
|
||
|
{-Converts nominal annual interest Rate to effective Rate}
|
||
|
|
||
|
function FutureValue(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
Pmt, PV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing: TStPaymentTime) : Extended;
|
||
|
{-Returns the future value of an annuity}
|
||
|
|
||
|
function FutureValueSchedule(Principal : Extended;
|
||
|
const Schedule : array of Double) : Extended;
|
||
|
|
||
|
function FutureValueSchedule16(Principal : Extended;
|
||
|
const Schedule; NRates : Integer) : Extended;
|
||
|
{-Returns the future value of investment with variable interest rates}
|
||
|
|
||
|
function InterestRate(NPeriods : Integer;
|
||
|
Pmt, PV, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime;
|
||
|
Guess : Extended) : Extended;
|
||
|
{-Returns the interest Rate per period of an annuity}
|
||
|
|
||
|
function InternalRateOfReturn(const Values : array of Double;
|
||
|
Guess : Extended) : Extended;
|
||
|
|
||
|
function InternalRateOfReturn16(const Values; NValues : Integer;
|
||
|
Guess : Extended) : Extended;
|
||
|
{-Returns internal rate of return of a series of periodic cash flows}
|
||
|
|
||
|
function IsCardValid(const S : string) : Boolean;
|
||
|
{-Checks for valid credit card number (MasterCard, Visa, AMEX, Discover)}
|
||
|
|
||
|
function ModifiedDuration(Settlement, Maturity : TStDate;
|
||
|
Rate, Yield : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the modified duration for bond with an assumed par value of $100}
|
||
|
|
||
|
function ModifiedIRR(const Values : array of Double;
|
||
|
FinanceRate, ReinvestRate : Extended) : Extended;
|
||
|
|
||
|
function ModifiedIRR16(const Values; NValues : Integer;
|
||
|
FinanceRate, ReinvestRate : Extended) : Extended;
|
||
|
{-Returns the MIRR for a series of periodic cash flows}
|
||
|
|
||
|
function NetPresentValue(Rate : Extended;
|
||
|
const Values : array of Double) : Extended;
|
||
|
|
||
|
function NetPresentValue16(Rate : Extended;
|
||
|
const Values; NValues : Integer) : Extended;
|
||
|
{-Returns the net present value of a series of periodic cash flows}
|
||
|
|
||
|
function NominalInterestRate(EffectRate : Extended;
|
||
|
Frequency : TStFrequency) : Extended;
|
||
|
{-Converts effective annual interest Rate to nominal Rate}
|
||
|
|
||
|
function NonperiodicIRR(const Values : array of Double;
|
||
|
const Dates : array of TStDate;
|
||
|
Guess : Extended) : Extended;
|
||
|
|
||
|
function NonperiodicIRR16(const Values;
|
||
|
const Dates; NValues : Integer;
|
||
|
Guess : Extended) : Extended;
|
||
|
{-Returns the IRR for a series of irregular cash flows}
|
||
|
|
||
|
function NonperiodicNPV(Rate : Extended;
|
||
|
const Values : array of Double;
|
||
|
const Dates : array of TStDate) : Extended;
|
||
|
|
||
|
function NonperiodicNPV16(Rate : Extended;
|
||
|
const Values;
|
||
|
const Dates;
|
||
|
NValues : Integer) : Extended;
|
||
|
{-Returns the net present value for a series of irregular cash flows}
|
||
|
|
||
|
function Payment(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
PV, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
{-Returns the interest payment per period in an annuity}
|
||
|
|
||
|
function Periods(Rate : Extended;
|
||
|
Pmt, PV, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing: TStPaymentTime) : Integer;
|
||
|
{-Returns the number of periods for an annuity}
|
||
|
|
||
|
function PresentValue(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
Pmt, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
{-Returns present value of an annity}
|
||
|
|
||
|
function ReceivedAtMaturity(Settlement, Maturity : TStDate;
|
||
|
Investment, Discount : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the amount received at Maturity for a fully invested security}
|
||
|
|
||
|
function RoundToDecimal(Value : Extended;
|
||
|
Places : Integer;
|
||
|
Bankers : Boolean) : Extended;
|
||
|
{-Rounds a real value to the specified number of decimal places}
|
||
|
|
||
|
function TBillEquivYield(Settlement, Maturity : TStDate;
|
||
|
Discount : Extended) : Extended;
|
||
|
{-Returns the bond-equivalent yield for a treasury bill}
|
||
|
|
||
|
function TBillPrice(Settlement, Maturity : TStDate;
|
||
|
Discount : Extended) : Extended;
|
||
|
{-Returns the price per $100 face value for a treasury bill}
|
||
|
|
||
|
function TBillYield(Settlement, Maturity : TStDate;
|
||
|
Price : Extended) : Extended;
|
||
|
{-Returns the yield for a treasury bill}
|
||
|
|
||
|
function VariableDecliningBalance(Cost, Salvage : Extended;
|
||
|
Life : Integer;
|
||
|
StartPeriod, EndPeriod, Factor : Extended;
|
||
|
NoSwitch : boolean) : Extended;
|
||
|
{-Variable rate declining balance depreciation}
|
||
|
|
||
|
function YieldDiscounted(Settlement, Maturity : TStDate;
|
||
|
Price, Redemption : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the annual yield for a discounted security}
|
||
|
|
||
|
function YieldPeriodic(Settlement, Maturity : TStDate;
|
||
|
Rate, Price, Redemption : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the yield on a security that pays periodicinterest}
|
||
|
|
||
|
function YieldMaturity(Issue, Settlement, Maturity : TStDate;
|
||
|
Rate, Price : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Returns the annual yield of a security that pays interest at Maturity}
|
||
|
|
||
|
|
||
|
{========================================================================}
|
||
|
|
||
|
implementation
|
||
|
|
||
|
const
|
||
|
PaymentType : array[TStPaymentTime] of Integer = (0, 1);
|
||
|
{Used for converting Timing to integer 0 or 1}
|
||
|
|
||
|
CouponsPerYear : array[TStFrequency] of Integer = (1, 2, 4, 12);
|
||
|
{Used for converting Frequency to integer 1, 2, 4, or 12}
|
||
|
|
||
|
CouponPeriod : array[TStFrequency] of Integer = (12, 6, 3, 1);
|
||
|
{Used for converting Frequency to duration}
|
||
|
|
||
|
DefaultGuess : Extended = 0.1;
|
||
|
{Starting point for rate approximation routines}
|
||
|
|
||
|
var
|
||
|
RecipLn10 : Extended;
|
||
|
{Used for common log computation}
|
||
|
|
||
|
|
||
|
{================= Local routines used by this unit ==================}
|
||
|
|
||
|
procedure RaiseStFinError(Code : Longint);
|
||
|
begin
|
||
|
Raise EStFinError.CreateResTP(Code, 0);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function Exp10(Exponent : Extended) : Extended;
|
||
|
{-Returns 10^Exponent}
|
||
|
begin
|
||
|
Result := Power(10.0, Exponent);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function Log10(Value : Extended) : Extended;
|
||
|
{-Returns common log of Value}
|
||
|
begin
|
||
|
Result := Ln(Value) * RecipLn10;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DayCount(Day1, Day2 : TStDate; Basis : TStBasis) : LongInt;
|
||
|
{-The number of days from Day1 to Day2 according to day count basis}
|
||
|
var
|
||
|
BDT : TStBondDateType;
|
||
|
begin
|
||
|
case Basis of
|
||
|
BasisNASD : BDT := bdt30360PSA;
|
||
|
BasisEur30360 : BDT := bdt30E360;
|
||
|
else
|
||
|
BDT := bdtActual;
|
||
|
end;
|
||
|
Result := Longint(BondDateDiff(Day1, Day2, BDT));
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function LastCoupon(Settlement, Maturity : TStDate;
|
||
|
Frequency : TStFrequency) : TStDate;
|
||
|
{-The last coupon date prior to settlement}
|
||
|
var
|
||
|
Last : TStDate;
|
||
|
Months : Integer;
|
||
|
begin
|
||
|
Last := Maturity;
|
||
|
Months := 0;
|
||
|
while (Last >= Settlement) do begin
|
||
|
Months := Months + CouponPeriod[Frequency];
|
||
|
Last := IncDateTrunc(Maturity, -Months, 0);
|
||
|
end;
|
||
|
Result := Last;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function NextCoupon(Settlement, Maturity : TStDate;
|
||
|
Frequency : TStFrequency) : TStDate;
|
||
|
{-The next coupon date after settlement}
|
||
|
var
|
||
|
Next : TStDate;
|
||
|
begin
|
||
|
Next := LastCoupon(Settlement, Maturity, Frequency);
|
||
|
Result := IncDateTrunc(Next, CouponPeriod[Frequency], 0);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function CouponsToMaturity(Settlement, Maturity : TStDate;
|
||
|
Frequency : TStFrequency) : Integer;
|
||
|
{-The number of coupons remaining after settlement}
|
||
|
var
|
||
|
CouponDate : TStDate;
|
||
|
Months : Integer;
|
||
|
Coupons : Integer;
|
||
|
begin
|
||
|
CouponDate := Maturity;
|
||
|
Coupons := 0;
|
||
|
Months := 0;
|
||
|
while (CouponDate > Settlement) do begin
|
||
|
Months := Months + CouponPeriod[Frequency];
|
||
|
CouponDate := IncDateTrunc(Maturity, -Months, 0);
|
||
|
Coupons := Coupons + 1;
|
||
|
end;
|
||
|
Result := Coupons;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DayCountFraction(Day1, Day2, Settlement, Maturity : TStDate;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-The number of days from Day1 to Day2 divided by days/year
|
||
|
except for Act/Act which uses actual coupon period x frequency}
|
||
|
var
|
||
|
Last, Next : TStDate;
|
||
|
DPY : Integer;
|
||
|
begin
|
||
|
if (Basis = BasisActAct) then begin
|
||
|
Last := LastCoupon(Settlement, Maturity, Frequency);
|
||
|
Next := NextCoupon(Settlement, Maturity, Frequency);
|
||
|
DPY := DayCount(Last, Next, Basis) * CouponsPerYear[Frequency];
|
||
|
end else if (Basis = BasisAct365) then
|
||
|
DPY := 365
|
||
|
else
|
||
|
DPY := 360;
|
||
|
Result := DayCount(Day1, Day2, Basis) / DPY;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function BondDirtyPrice(Settlement, Maturity : TStDate;
|
||
|
Rate, Yield, Redemption : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
{-Bond Price including interest accrued in current coupon period}
|
||
|
var
|
||
|
C, DCF, Yw : Extended;
|
||
|
Vn, Vdcf : Extended;
|
||
|
Next : TStDate;
|
||
|
N, W : Integer;
|
||
|
begin
|
||
|
W := CouponsPerYear[Frequency];
|
||
|
C := Redemption * (Rate / W);
|
||
|
Yw := Yield / W;
|
||
|
N := CouponsToMaturity(Settlement, Maturity, Frequency);
|
||
|
Next := NextCoupon(Settlement, Maturity, Frequency);
|
||
|
DCF := DayCountFraction(Settlement, Next, Settlement, Maturity,
|
||
|
Frequency, Basis);
|
||
|
Vdcf := Power(1.0 / (1.0 + Yw), DCF * W);
|
||
|
Vn := Power(1.0 / (1.0 + Yw), N - 1.0);
|
||
|
Result := Vdcf * (( C * (1.0 - Vn) / Yw) + Redemption * Vn + C);
|
||
|
end;
|
||
|
|
||
|
|
||
|
|
||
|
{====================== Public Routines ============================}
|
||
|
|
||
|
|
||
|
function AccruedInterestMaturity(Issue, Maturity : TStDate;
|
||
|
Rate, Par : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
If (Rate <= 0.0) or (Par <= 0.0) or (Issue >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Issue, Maturity, Issue, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
Result := Par * Rate * DCF;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function AccruedInterestPeriodic(Issue, Settlement, Maturity : TStDate;
|
||
|
Rate, Par : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
Last : TStDate;
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
if (Rate <= 0.0) or (Par <= 0.0) or (Issue >= Settlement) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Last := LastCoupon(Settlement, Maturity, Frequency);
|
||
|
if (Issue > Last) then
|
||
|
Last := Issue;
|
||
|
DCF := DayCountFraction(Last, Settlement, Settlement, Maturity,
|
||
|
Frequency, Basis);
|
||
|
Result := Par * Rate * DCF;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function BondDuration(Settlement,Maturity : TStDate;
|
||
|
Rate, Yield : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
B, dB : Extended;
|
||
|
Yw : Extended;
|
||
|
begin
|
||
|
if (Rate < 0.0) or (Yield < 0.0) or (Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Yw := Yield / CouponsPerYear[Frequency];
|
||
|
B := BondDirtyPrice(Settlement, Maturity, Rate, Yield, 100.0,
|
||
|
Frequency, Basis);
|
||
|
if (B <> 0.0) then begin
|
||
|
dB := BondDirtyPrice(Settlement, Maturity, Rate, Yield + StDelta, 100.0,
|
||
|
Frequency, Basis) - B;
|
||
|
Result := -((1.0 + Yw) / B) * (dB / StDelta);
|
||
|
end else
|
||
|
Result := 0;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function BondPrice(Settlement, Maturity : TStDate;
|
||
|
Rate, Yield, Redemption : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
B, DCF : Extended;
|
||
|
Last : TStDate;
|
||
|
begin
|
||
|
if (Yield < 0.0) or (Rate < 0.0) or (Redemption <= 0) or
|
||
|
(Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
B := BondDirtyPrice(Settlement, Maturity, Rate, Yield, Redemption,
|
||
|
Frequency, Basis);
|
||
|
Last := LastCoupon(Settlement, Maturity, Frequency);
|
||
|
DCF := DayCountFraction(Last, Settlement, Settlement, Maturity,
|
||
|
Frequency, Basis);
|
||
|
Result := B - Redemption * Rate * DCF;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function CumulativeInterest(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
PV : Extended;
|
||
|
StartPeriod, EndPeriod : Integer;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
var
|
||
|
P, CP : Extended;
|
||
|
begin
|
||
|
if (Rate <=0.0) or (NPeriods <= 0) or (PV <= 0.0) or (StartPeriod < 1) or
|
||
|
(EndPeriod < 1) or (StartPeriod > EndPeriod) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
P := Payment(Rate, NPeriods, PV, 0.0, Frequency, Timing);
|
||
|
CP := CumulativePrincipal(Rate, NPeriods, PV, StartPeriod, EndPeriod,
|
||
|
Frequency, Timing);
|
||
|
Result := P * (EndPeriod - (StartPeriod - 1.0)) - CP;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function CumulativePrincipal(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
PV : Extended;
|
||
|
StartPeriod, EndPeriod : Integer;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
var
|
||
|
P : Extended;
|
||
|
begin
|
||
|
if (Rate <=0.0) or (NPeriods <= 0) or (PV <= 0.0) or (StartPeriod < 1) or
|
||
|
(EndPeriod < 1) or (StartPeriod > EndPeriod) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
P := Payment(Rate, NPeriods, PV, 0.0, Frequency, Timing);
|
||
|
Result := FutureValue(Rate, StartPeriod - 1, P, PV, Frequency, Timing) -
|
||
|
FutureValue(Rate, EndPeriod, P, PV, Frequency, Timing);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DecliningBalance(Cost, Salvage : Extended;
|
||
|
Life, Period, Month : Integer) : Extended;
|
||
|
var
|
||
|
Rate : Extended;
|
||
|
DPv : Extended;
|
||
|
TDPv : Extended;
|
||
|
I : Integer;
|
||
|
begin
|
||
|
if (Cost <= 0.0) or (Cost < Salvage) or (Period < 1) or (Life < 2) or
|
||
|
(Period > (Life + 1)) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DPv := 0.0;
|
||
|
TDPv := 0.0;
|
||
|
if (Salvage = 0) then
|
||
|
Salvage := 0.001;
|
||
|
if (Month = 0) then
|
||
|
Month := 12;
|
||
|
Rate := RoundToDecimal(1.0 - Power(Salvage / Cost, 1.0 / Life), 3, false);
|
||
|
for I := 1 to Period do begin
|
||
|
if (I = 1) then
|
||
|
DPv := (Cost * Rate * Month) / 12.0 {1st Period}
|
||
|
else if (I = (Life + 1)) then
|
||
|
DPv := (Cost - TDPv) * Rate * (12.0 - Month) / 12.0 {Last Period}
|
||
|
else
|
||
|
DPv := (Cost - TDPv) * Rate; {All the rest}
|
||
|
TDpv := TDpv + Dpv
|
||
|
end;
|
||
|
Result := RoundToDecimal(Dpv, 3, False);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DiscountRate(Settlement, Maturity : TStDate;
|
||
|
Price, Redemption : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
If (Price <= 0.0) or (Redemption <= 0.0) or (Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
Result := (Redemption - Price) / (Redemption * DCF);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DollarToDecimal(FracDollar : Extended;
|
||
|
Fraction : Integer) : Extended;
|
||
|
var
|
||
|
I, F, N : Extended;
|
||
|
begin
|
||
|
if (Fraction < 1) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
I := Int(FracDollar); {Integral part}
|
||
|
N := Int(Log10(Fraction) + 1.0); {Number of decimal places}
|
||
|
F := Frac(FracDollar); {Fractional part}
|
||
|
Result := I + (F * Exp10(N) / Fraction);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DollarToDecimalText(DecDollar : Extended) : string;
|
||
|
var
|
||
|
A, P : Extended;
|
||
|
N, I : Integer;
|
||
|
Str : string;
|
||
|
T : Longint;
|
||
|
CentVal : Integer;
|
||
|
const
|
||
|
Orders : array[0..5] of string = ('', 'Thousand ', 'Million ',
|
||
|
'Billion ', 'Trillion ', 'Quadrillion ');
|
||
|
|
||
|
function Text100(Num: Longint) : string;
|
||
|
{formats an integer in the range 0 to 999}
|
||
|
var
|
||
|
I, J : Integer;
|
||
|
A, T : Longint;
|
||
|
S : string;
|
||
|
const
|
||
|
Tens : array[0..9] of string =
|
||
|
('', '', 'Twenty', 'Thirty', 'Forty', 'Fifty',
|
||
|
'Sixty', 'Seventy', 'Eighty', 'Ninety');
|
||
|
Ones : array[0..19] of string =
|
||
|
('', 'One', 'Two', 'Three', 'Four', 'Five',
|
||
|
'Six', 'Seven', 'Eight', 'Nine', 'Ten',
|
||
|
'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen',
|
||
|
'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen');
|
||
|
begin
|
||
|
S := '';
|
||
|
I := 0;
|
||
|
J := 0;
|
||
|
Result := S;
|
||
|
if (Num = 0) then
|
||
|
Exit;
|
||
|
A := Num;
|
||
|
T := A div 100;
|
||
|
if (T > 0) then begin
|
||
|
I := T; {I = Hundreds digit}
|
||
|
A := A - (T * 100);
|
||
|
end;
|
||
|
T := A div 10;
|
||
|
if (T > 1) then begin
|
||
|
J := T; {J = Tens digit}
|
||
|
A := A - (T * 10); {A = Ones digit}
|
||
|
end;
|
||
|
if (I > 0) then
|
||
|
S := Ones[I] + ' Hundred';
|
||
|
if (J > 0) then begin
|
||
|
if (I > 0) then
|
||
|
S := S + ' ' + Tens[J]
|
||
|
else
|
||
|
S := S + Tens[J];
|
||
|
end;
|
||
|
if (A > 0) then begin
|
||
|
if (J > 0) then
|
||
|
S := S + '-';
|
||
|
if (I > 0) and (J = 0) then
|
||
|
S := S + ' ' + Ones[A]
|
||
|
else
|
||
|
S := S + Ones[A];
|
||
|
end;
|
||
|
Result := S;
|
||
|
end;
|
||
|
|
||
|
begin
|
||
|
Str := '';
|
||
|
if (DecDollar < 0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
if (DecDollar > 0) then begin
|
||
|
N := Trunc(Log10(DecDollar));
|
||
|
if (N > 17) then {DecDollar too large}
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
A := DecDollar;
|
||
|
for I := N downto 0 do begin
|
||
|
P := Int(Exp10(I * 3));
|
||
|
T := Trunc(A / P);
|
||
|
if (T > 0) then
|
||
|
Str := Str + {' ' +} Text100(T) + ' ' + Orders[I];
|
||
|
A := A - (T * P);
|
||
|
end;
|
||
|
end;
|
||
|
if (Str = '') then
|
||
|
Str := 'Zero ';
|
||
|
Str := Str + 'and ';
|
||
|
CentVal := Round(Frac(DecDollar) * 100);
|
||
|
if (CentVal < 10) then
|
||
|
Str := Str + '0';
|
||
|
Result := Str + IntToStr(CentVal) + '/100';
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DollarToFraction(DecDollar : Extended;
|
||
|
Fraction : Integer) : Extended;
|
||
|
var
|
||
|
I, F, N : Extended;
|
||
|
begin
|
||
|
if (Fraction < 1) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
I := Int(DecDollar); {Integral part}
|
||
|
N := Int(Log10(Fraction) + 1.0); {Number of decimal places}
|
||
|
F := Frac(DecDollar); {Fractional part}
|
||
|
Result := I + (F * Fraction / Exp10(N));
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function DollarToFractionStr(FracDollar : Extended;
|
||
|
Fraction : Integer) : string;
|
||
|
var
|
||
|
I, F, N : Extended;
|
||
|
begin
|
||
|
Result := '';
|
||
|
if (Fraction < 1) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
I := Int(FracDollar); {Integral part}
|
||
|
N := Int(Log10(Fraction) + 1.0); {Number of decimal places}
|
||
|
F := Frac(FracDollar) * Exp10(N); {Fractional part}
|
||
|
Result := IntToStr(Trunc(I));
|
||
|
if (F > 0) then
|
||
|
Result := Result + ' ' + FloatToStrF(F, ffNumber, Trunc(N), 0) +
|
||
|
'/' + IntToStr(Fraction);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function EffectiveInterestRate(NominalRate : Extended;
|
||
|
Frequency : TStFrequency) : Extended;
|
||
|
var
|
||
|
W : Integer;
|
||
|
begin
|
||
|
if (NominalRate <= 0.0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
W := CouponsPerYear[Frequency];
|
||
|
Result := Power(1.0 + NominalRate / W, W) - 1.0;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function FutureValue(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
Pmt, PV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing: TStPaymentTime) : Extended;
|
||
|
var
|
||
|
S, Rw : Extended;
|
||
|
PT : Integer;
|
||
|
|
||
|
begin
|
||
|
PT := PaymentType[Timing];
|
||
|
Rw := Rate / CouponsPerYear[Frequency];
|
||
|
S := Power(1.0 + Rw, NPeriods);
|
||
|
Result := -((PV * S) + Pmt * (S - 1.0) * (1.0 + Rw * PT) / Rw);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function FutureValueSchedule(Principal : Extended;
|
||
|
const Schedule : array of Double) : Extended;
|
||
|
begin
|
||
|
Result := FutureValueSchedule16(Principal, Schedule,
|
||
|
High(Schedule) + 1);
|
||
|
end;
|
||
|
|
||
|
function FutureValueSchedule16(Principal : Extended;
|
||
|
const Schedule; NRates : Integer) : Extended;
|
||
|
var
|
||
|
I : Integer;
|
||
|
begin
|
||
|
Result := Principal;
|
||
|
for I := 0 to (NRates - 1) do
|
||
|
Result := Result * (1.0 + TDoubleArray(Schedule)[I]);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function InterestRate(NPeriods : Integer;
|
||
|
Pmt, PV, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime;
|
||
|
Guess : Extended) : Extended;
|
||
|
var
|
||
|
Rate : Extended;
|
||
|
NextRate : Extended;
|
||
|
T, dT : Extended;
|
||
|
Count : Integer;
|
||
|
begin
|
||
|
Count := 0;
|
||
|
NextRate := Guess;
|
||
|
if (Guess = 0.0) then
|
||
|
NextRate := DefaultGuess;
|
||
|
{Solve FV(rate) = FV for rate by Newton's method}
|
||
|
repeat
|
||
|
Rate := NextRate;
|
||
|
if (Rate <= - CouponsPerYear[Frequency]) then
|
||
|
Rate := -0.999 * CouponsPerYear[Frequency];
|
||
|
T := FutureValue(Rate, NPeriods, Pmt, PV, Frequency, Timing) - FV;
|
||
|
dT := FutureValue(Rate + StDelta, NPeriods, Pmt, PV, Frequency,
|
||
|
Timing) - FV - T;
|
||
|
if (dT = 0.0) then
|
||
|
Count := StMaxIterations
|
||
|
else
|
||
|
NextRate := Rate - StDelta * T / dT;
|
||
|
Inc(Count);
|
||
|
until (Abs(NextRate - Rate) < StEpsilon) or (Count > StMaxIterations);
|
||
|
if (Count > StMaxIterations) then
|
||
|
RaiseStFinError(stscFinNoConverge);
|
||
|
Result := NextRate;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function InternalRateOfReturn(const Values : array of Double;
|
||
|
Guess : Extended) : Extended;
|
||
|
begin
|
||
|
Result := InternalRateOfReturn16(Values, High(Values) + 1, Guess);
|
||
|
end;
|
||
|
|
||
|
function InternalRateOfReturn16(const Values;
|
||
|
NValues : Integer;
|
||
|
Guess : Extended) : Extended;
|
||
|
var
|
||
|
Rate : Extended;
|
||
|
NextRate : Extended;
|
||
|
PV : Extended;
|
||
|
dPV : Extended;
|
||
|
Count : Integer;
|
||
|
begin
|
||
|
Count := 0;
|
||
|
NextRate := Guess;
|
||
|
if (Guess = 0.0) then
|
||
|
NextRate := DefaultGuess;
|
||
|
{Solve NPV(Rate) = 0 for rate by Newton's method}
|
||
|
repeat
|
||
|
Rate := NextRate;
|
||
|
if (Rate <= -1.0) then
|
||
|
Rate := -0.999;
|
||
|
PV := NetPresentValue16(Rate, Values, NValues);
|
||
|
dPV := NetPresentValue16(Rate + StDelta, Values, NValues) - PV;
|
||
|
if (dPV = 0.0) then
|
||
|
Count := StMaxIterations
|
||
|
else
|
||
|
NextRate := Rate - (StDelta * PV) / dPV;
|
||
|
Inc(Count);
|
||
|
until (Abs(NextRate - Rate) < StEpsilon) or (Count > StMaxIterations);
|
||
|
if (Count > StMaxIterations) then
|
||
|
RaiseStFinError(stscFinNoConverge);
|
||
|
Result := NextRate;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function IsCardValid(const S : string) : Boolean;
|
||
|
const
|
||
|
Ord0 = Ord('0');
|
||
|
var
|
||
|
Temp : string;
|
||
|
I, J, K : Integer;
|
||
|
begin
|
||
|
Result := False;
|
||
|
Temp := '';
|
||
|
for I := 1 to Length(S) do
|
||
|
if (S[I] in ['0'..'9']) then
|
||
|
Temp := Temp + S[I];
|
||
|
if Temp = '' then
|
||
|
Exit;
|
||
|
K := 0;
|
||
|
I := 1;
|
||
|
if not Odd(Length(Temp)) then begin
|
||
|
J := Ord(Temp[I]) - Ord0;
|
||
|
J := J shl 1;
|
||
|
if J > 9 then
|
||
|
J := J - 9;
|
||
|
K := K + J;
|
||
|
Inc(I);
|
||
|
end;
|
||
|
while I <= Length(Temp) do begin
|
||
|
K := K + Ord(Temp[I]) - Ord0;
|
||
|
Inc(I);
|
||
|
if I > Length(Temp) then
|
||
|
Break;
|
||
|
J := Ord(Temp[I]) - Ord0;
|
||
|
J := J shl 1;
|
||
|
if J > 9 then
|
||
|
J := J - 9;
|
||
|
K := K + J;
|
||
|
Inc(I);
|
||
|
end;
|
||
|
Result := (K mod 10 = 0);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function ModifiedDuration(Settlement, Maturity : TStDate;
|
||
|
Rate, Yield : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
begin
|
||
|
if (Rate < 0.0) or (Yield < 0.0) or (Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Result := BondDuration(Settlement, Maturity, Rate, Yield,
|
||
|
Frequency, Basis)/ (1.0 + Yield / CouponsPerYear[Frequency]);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function ModifiedIRR(const Values : array of Double;
|
||
|
FinanceRate, ReinvestRate : Extended) : Extended;
|
||
|
begin
|
||
|
Result := ModifiedIRR16(Values, High(Values) + 1, FinanceRate,
|
||
|
ReinvestRate);
|
||
|
end;
|
||
|
|
||
|
function ModifiedIRR16(const Values;
|
||
|
NValues : Integer;
|
||
|
FinanceRate, ReinvestRate : Extended) : Extended;
|
||
|
var
|
||
|
NPVPos : Extended;
|
||
|
NPVNeg : Extended;
|
||
|
Val : Extended;
|
||
|
Rn, Fn : Extended;
|
||
|
I : Integer;
|
||
|
begin
|
||
|
NPVPos := 0.0;
|
||
|
NPVNeg := 0.0;
|
||
|
for I := 0 to (NValues - 1) do begin
|
||
|
Val := TDoubleArray(Values)[I];
|
||
|
if (Val > 0.0) then
|
||
|
NPVPos := NPVPos + Val / Power(1.0 + ReinvestRate, I + 1.0)
|
||
|
else
|
||
|
NPVNeg := NPVNeg + Val / Power(1.0 + FinanceRate, I + 1.0);
|
||
|
end;
|
||
|
Rn := Power(1.0 + ReInvestRate, NValues);
|
||
|
Fn := 1.0 + FinanceRate;
|
||
|
Result := Power(-NPVPos * Rn / (NPVNeg * Fn), 1.0 / (NValues - 1.0)) - 1.0;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function NetPresentValue(Rate : Extended;
|
||
|
const Values : array of Double) : Extended;
|
||
|
begin
|
||
|
Result := NetPresentValue16(Rate, Values, High(Values) + 1);
|
||
|
end;
|
||
|
|
||
|
function NetPresentValue16(Rate : Extended;
|
||
|
const Values;
|
||
|
NValues : Integer) : Extended;
|
||
|
var
|
||
|
I : Integer;
|
||
|
begin
|
||
|
Result := 0;
|
||
|
for I := 0 to (NValues - 1) do
|
||
|
Result := Result + TDoubleArray(Values)[I] / Power(1.0 + Rate, I + 1.0);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function NominalInterestRate(EffectRate : Extended;
|
||
|
Frequency : TStFrequency) : Extended;
|
||
|
var
|
||
|
W : Extended;
|
||
|
begin
|
||
|
if (EffectRate <= 0.0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
W := CouponsPerYear[Frequency];
|
||
|
Result := W * (Power(EffectRate + 1.0, 1.0 / W) - 1.0);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function NonperiodicIRR(const Values : array of Double;
|
||
|
const Dates : array of TStDate;
|
||
|
Guess : Extended) : Extended;
|
||
|
begin
|
||
|
Result := NonPeriodicIRR16(Values, Dates, High(Values) + 1, Guess);
|
||
|
end;
|
||
|
|
||
|
function NonperiodicIRR16(const Values;
|
||
|
const Dates;
|
||
|
NValues : Integer;
|
||
|
Guess : Extended) : Extended;
|
||
|
var
|
||
|
Rate : Extended;
|
||
|
NextRate : Extended;
|
||
|
PV, dPV : Extended;
|
||
|
Count : Integer;
|
||
|
begin
|
||
|
Count := 0;
|
||
|
NextRate := Guess;
|
||
|
if (Guess = 0.0) then
|
||
|
NextRate := DefaultGuess;
|
||
|
{Solve XNPV(Rate) = 0 for rate by Newton's method}
|
||
|
repeat
|
||
|
Rate := NextRate;
|
||
|
if (Rate <= -1.0) then
|
||
|
Rate := -0.999;
|
||
|
PV := NonPeriodicNPV16(Rate, Values, Dates, NValues);
|
||
|
dPV := NonPeriodicNPV16(Rate + StDelta, Values, Dates, NValues) - PV;
|
||
|
if (dPV = 0.0) then
|
||
|
Count := StMaxIterations
|
||
|
else
|
||
|
NextRate := Rate - (StDelta * PV) / dPV;
|
||
|
Inc(Count);
|
||
|
until (Abs(NextRate - Rate) < StEpsilon) or (Count > StMaxIterations);
|
||
|
if (Count > StMaxIterations) then
|
||
|
RaiseStFinError(stscFinNoConverge);
|
||
|
Result := NextRate;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function NonperiodicNPV(Rate : Extended;
|
||
|
const Values : array of Double;
|
||
|
const Dates : array of TStDate) : Extended;
|
||
|
begin
|
||
|
Result := NonperiodicNPV16(Rate, Values, Dates, High(Values) + 1);
|
||
|
end;
|
||
|
|
||
|
function NonperiodicNPV16(Rate : Extended;
|
||
|
const Values;
|
||
|
const Dates;
|
||
|
NValues : Integer) : Extended;
|
||
|
var
|
||
|
Day1 : TStDate;
|
||
|
Diff : Double;
|
||
|
I : Integer;
|
||
|
begin
|
||
|
Result := 0.0;
|
||
|
Day1 := TStDateArray(Dates)[0];
|
||
|
for I := 0 to (NValues - 1) do begin
|
||
|
Diff := TStDateArray(Dates)[I] - Day1;
|
||
|
if (Diff < 0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Result := Result + TDoubleArray(Values)[I] / Power(1.0 + Rate, Diff / 365.0);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function Payment(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
PV, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
var
|
||
|
PT, Rw, S : Extended;
|
||
|
begin
|
||
|
PT := PaymentType[Timing];
|
||
|
Rw := Rate / CouponsPerYear[Frequency];
|
||
|
S := Power(1.0 + Rw, NPeriods);
|
||
|
Result := Rw * (FV - PV * S) / ((S - 1.0) * (1.0 + Rw * PT));
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
function Periods(Rate : Extended;
|
||
|
Pmt, PV, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing: TStPaymentTime) : Integer;
|
||
|
var
|
||
|
S, Rw : Extended;
|
||
|
|
||
|
begin
|
||
|
Rw := Rate / CouponsPerYear[Frequency];
|
||
|
S := Pmt * (1.0 + Rw * PaymentType[Timing]);
|
||
|
Result := Round(Ln((Rw*FV + S) / (Rw*PV + S)) / Ln(1.0 + Rw));
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function PresentValue(Rate : Extended;
|
||
|
NPeriods : Integer;
|
||
|
Pmt, FV : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Timing : TStPaymentTime) : Extended;
|
||
|
var
|
||
|
PT, Rw, S : Extended;
|
||
|
begin
|
||
|
PT := PaymentType[Timing];
|
||
|
Rw := Rate / CouponsPerYear[Frequency];
|
||
|
S := Power(1.0 + Rw, -NPeriods);
|
||
|
Result := (FV * S) + Pmt * (S - 1.0) * (1.0 + Rw * PT) / Rw;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function ReceivedAtMaturity(Settlement, Maturity : TStDate;
|
||
|
Investment, Discount : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
if (Investment <= 0.0) or (Discount <= 0.0) or (Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
Result := Investment / (1.0 - Discount * DCF);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
{revised}
|
||
|
function RoundToDecimal(Value : Extended;
|
||
|
Places : Integer;
|
||
|
Bankers : Boolean) : Extended;
|
||
|
var
|
||
|
Val, IV, N, F : Extended;
|
||
|
T : Integer;
|
||
|
begin
|
||
|
IV := 0;
|
||
|
N := Exp10(Places);
|
||
|
if (Places > 0) then
|
||
|
IV := Int(Value);
|
||
|
Val := (Value - IV) * N;
|
||
|
T := Trunc(Val);
|
||
|
F := (Val - T);
|
||
|
if Bankers then
|
||
|
Val := Round(Val) / N {Delphi's Round does Bankers}
|
||
|
else begin
|
||
|
if Abs(Round(10.0 * F)) >= 5 then begin
|
||
|
if (F > 0) then
|
||
|
Val := (T + 1.0) / N
|
||
|
else
|
||
|
Val := (T - 1.0) / N;
|
||
|
end else
|
||
|
Val := T / N;
|
||
|
end;
|
||
|
Result := Val + IV;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function TBillEquivYield(Settlement, Maturity : TStDate;
|
||
|
Discount : Extended) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
if (Discount <= 0.0) or (Settlement > Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, BasisAct360);
|
||
|
if (DCF > 1.0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Result := (365.0 / 360.0) * Discount / (1.0 - Discount * DCF);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function TBillPrice(Settlement, Maturity : TStDate;
|
||
|
Discount : Extended) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
if (Discount <= 0.0) or (Settlement > Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, BasisAct360);
|
||
|
if (DCF > 1.0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Result := 100.0 * ( 1.0 - Discount * DCF);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function TBillYield(Settlement, Maturity : TStDate;
|
||
|
Price : Extended) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
if (Price <= 0.0) or (Settlement > Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, BasisAct360);
|
||
|
if (DCF > 1.0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Result := ((100.0 - Price) / Price) * (1.0 / DCF);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function VariableDecliningBalance(Cost, Salvage : Extended;
|
||
|
Life : Integer;
|
||
|
StartPeriod, EndPeriod, Factor : Extended;
|
||
|
NoSwitch : Boolean) : Extended;
|
||
|
var
|
||
|
VDB : Extended;
|
||
|
SLD : Extended;
|
||
|
Rate : Extended;
|
||
|
begin
|
||
|
if (Cost <= 0.0) or (Cost < Salvage) or (Life < 2) or (EndPeriod > Life) or
|
||
|
(StartPeriod > EndPeriod) or (StartPeriod < 0) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
if (Factor = 0.0) then
|
||
|
Rate := 2.0 / Life
|
||
|
else
|
||
|
Rate := Factor / Life;
|
||
|
SLD := (Cost - Salvage) * (EndPeriod - StartPeriod) / Life;
|
||
|
VDB := Cost * (Power(1.0 - Rate, StartPeriod) - Power(1.0 - Rate, EndPeriod));
|
||
|
if (not NoSwitch) and (SLD > VDB) then
|
||
|
Result := SLD
|
||
|
else
|
||
|
Result := VDB;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function YieldDiscounted(Settlement, Maturity : TStDate;
|
||
|
Price, Redemption : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
DCF : Extended;
|
||
|
begin
|
||
|
if (Price <= 0.0) or (Redemption <= 0.0) or (Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCF := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
Result := (Redemption - Price) / (Price * DCF);
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function YieldPeriodic(Settlement, Maturity : TStDate;
|
||
|
Rate, Price, Redemption : Extended;
|
||
|
Frequency : TStFrequency;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
Yield : Extended;
|
||
|
NextYield : Extended;
|
||
|
P, dP : Extended;
|
||
|
Count : Integer;
|
||
|
begin
|
||
|
if (Price <= 0.0) or (Rate < 0.0) or (Redemption <= 0.0) or
|
||
|
(Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
Count := 0;
|
||
|
NextYield := Rate;
|
||
|
repeat {Solve B = BondPrice(yield) - Price = 0 by Newton's method}
|
||
|
if (NextYield > 0) then
|
||
|
Yield := NextYield
|
||
|
else
|
||
|
Yield := 0.001;
|
||
|
P := BondPrice(Settlement, Maturity, Rate, Yield, Redemption,
|
||
|
Frequency, Basis) - Price;
|
||
|
dP := BondPrice(Settlement, Maturity, Rate, Yield + StDelta,
|
||
|
Redemption, Frequency, Basis) - Price - P;
|
||
|
if (dP = 0.0) then
|
||
|
Count := StMaxIterations
|
||
|
else
|
||
|
NextYield := Yield - StDelta * P / dP;
|
||
|
Inc(Count);
|
||
|
until (Abs(NextYield - Yield) < StEpsilon) or (Count > StMaxIterations);
|
||
|
if (Count > StMaxIterations) then
|
||
|
RaiseStFinError(stscFinNoConverge);
|
||
|
Result := NextYield;
|
||
|
end;
|
||
|
|
||
|
{-------------------------------------------------------}
|
||
|
|
||
|
function YieldMaturity(Issue, Settlement, Maturity : TStDate;
|
||
|
Rate, Price : Extended;
|
||
|
Basis : TStBasis) : Extended;
|
||
|
var
|
||
|
DCFim, DCFsm, DCFis : Extended;
|
||
|
begin
|
||
|
if (Price <= 0.0) or (Rate < 0.0) or (Settlement < Issue) or
|
||
|
(Settlement >= Maturity) then
|
||
|
RaiseStFinError(stscFinBadArg);
|
||
|
DCFim := DayCountFraction(Issue, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
DCFsm := DayCountFraction(Settlement, Maturity, Settlement, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
DCFis := DayCountFraction(Issue, Settlement, Settlement, Maturity,
|
||
|
fqAnnual, Basis);
|
||
|
Result := 100.0 * (1.0 + Rate * DCFim);
|
||
|
Result := Result / (Price + 100.0 * Rate * DCFis);
|
||
|
Result := (Result - 1.0) / DCFsm;
|
||
|
end;
|
||
|
|
||
|
|
||
|
|
||
|
initialization
|
||
|
RecipLn10 := 1.0 / Ln(10.0);
|
||
|
end.
|
||
|
|