home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
xbase
/
library
/
dbase
/
duflp
/
finance.prg
< prev
next >
Wrap
Text File
|
1992-07-01
|
22KB
|
504 lines
*-------------------------------------------------------------------------------
*-- Program...: FINANCE.PRG
*-- Programmer: Ken Mayer (KENMAYER)
*-- Date......: 06/25/1992
*-- Notes.....: These finance functions are for use with interest rates and
*-- such. See the file README.TXT for details about the use of this
*-- library file.
*--
*-- NOTES ABOUT THESE ROUTINES
*-- The functions that use (1+nRate)^nPeriods require that the
*-- rate be stated in the same terms as the compounding period.
*-- That is, for monthly compounding the nRate should be the annual
*-- rate / 12, and the nPeriods the number of months, and so forth.
*--
*-- If the situation involves continuous compounding, state the
*-- rate as the exponent of the annual rate, less 1, and state the
*-- periods in years. Accordingly, to find the value in 30 months
*-- of a $1000 investment continuously compounded at 6%, use:
*-- FuturVal(1000,exp(.06)-1,30/12)
*--
*-- These functions (except NPV(), which sums a series of equal
*- or unequal cash flows), are designed for use with a single
*-- "investment", one payment or receipt. If the problem involves
*-- a series of equal payments or receipts like a mortgage loan,
*-- a Holiday Club or an annuity, the fv() and pv() functions
*-- built in to dBASE IV should be used instead where possible.
*-------------------------------------------------------------------------------
FUNCTION Discount
*-------------------------------------------------------------------------------
*-- Programmer..: Jay Parsons (JPARSONS)
*-- Date........: 03/01/1992
*-- Notes.......: Compute the present value of an amount to be received at the
*-- end of a number of periods given a periodic interest rate.
*-- Written for.: dBASE IV, 1.1
*-- Rev. History: None
*-- Calls.......: None
*-- Called by...: Any
*-- Usage.......: Discount(<nFuturVal>,<nRate>,<nPeriods>)
*-- Example.....: ?Discount(1000,.08,6)
*-- Returns.....: Numeric
*-- Parameters..: nFuturVal = the amount to be received/paid in the future
*-- nRate = the periodic rate of interest
*-- nPeriods = the number of periods
*-------------------------------------------------------------------------------
parameters nFuturVal, nRate, nPeriods
RETURN nFuturVal / ( 1 + nRate ) ^ nPeriods
*-- EoF: Discount()
FUNCTION FuturVal
*-------------------------------------------------------------------------------
*-- Programmer..: Jay Parsons (JPARSONS)
*-- Date........: 03/01/1992
*-- Notes.......: Compute the future value of an initial amount at compound
*-- interest received at a given periodic rate for a number of
*-- periods.
*-- Written for.: dBASE IV, 1.0
*-- Rev. History: None
*-- Calls.......: None
*-- Called by...: Any
*-- Usage.......: FuturVal(<nPresVal>,<nRate>,<nPeriods>)
*-- Example.....: ?FuturVal(10000,.06,48)
*-- Returns.....: Numeric
*-- Parameters..: nPresVal = Present Value
*-- nRate = Periodic interest rate
*-- nPeriods = Number of periods to calculate for
*-------------------------------------------------------------------------------
parameters nPresVal, nRate, nPeriods
RETURN nPresVal * ( 1 + nRate ) ^ nPeriods
*-- EoF: FuturVal()
FUNCTION Rate
*-------------------------------------------------------------------------------
*-- Programmer..: Jay Parsons (JPARSONS)
*-- Date........: 03/01/1992
*-- Notes.......: Compute rate of periodic interest needed to produce a future
*-- value from a present value in a given number of periods. If
*-- the periods are not years, you'll probably want to multiply
*-- the rate returned by the number of periods in a year to
*-- obtain the equivalent annual rate.
*-- Written for.: dBASE IV, 1.1
*-- Rev. History: None
*-- Calls.......: None
*-- Called by...: Any
*-- Usage.......: Rate(<nFutVal>,<nPresVal>,<nPeriods>)
*-- Example.....: ?Rate(50000,10000,48)
*-- Returns.....: Numeric
*-- Parameters..: nFutVal = Future Value
*-- nPresVal = Present Value
*-- nPeriods = Number of periods to calculate for
*-------------------------------------------------------------------------------
parameters nFutVal, nPresVal, nPeriods
RETURN ( nFutVal / nPresVal ) ^ ( 1 / nPeriods ) - 1
*-- EoF: Rate()
FUNCTION ContRate
*-------------------------------------------------------------------------------
*-- Programmer..: Jay Parsons (JPARSONS)
*-- Date........: 03/01/1992
*-- Notes.......: Rate if compounding is continuous. Periods must be years.
*-- Written for.: dBASE IV, 1.1
*-- Rev. History: None
*-- Calls.......: RATE() Function in FINANCE.PRG
*-- Called by...: Any
*-- Usage.......: ContRate(<nFutVal>,<nPresVal>,<nYears>)
*-- Example.....: ?ContRate(50000,10000,4)
*-- Returns.....: Numeric
*-- Parameters..: nFutVal = Future Value
*-- nPresVal = Present Value
*-- nYears = Number of years to calculate for
*-------------------------------------------------------------------------------
parameters nFutVal, nPresVal, nYears
RETURN log( 1 + Rate( nFutval, nPresval, nYears ) )
*-- EoF: ContRate()
FUNCTION NPV
*-------------------------------------------------------------------------------
*-- Programmer..: Tony Lima (TONYLIMA) and Jay Parsons (JPARSONS)
*-- Date........: 03/01/1992
*-- Notes.......: Net present value of array aCashflow[ nPeriods ]
*-- Calculates npv given assumed rate and # periods.
*-- See "Other inputs" below for instructions/details ...
*-- Written for.: dBASE IV, 1.1
*-- Rev. History: None
*-- Calls.......: None
*-- Called by...: Any
*-- Usage.......: NPV(<nRate>,<nPeriods>)
*-- Example.....: ? NPV( .06, 6 )
*-- Returns.....: Float = value of the project at given rate
*-- Parameters..: nRate = Interest Rate
*-- : nPeriods = Number of Periods to calculate for
*-- Other inputs: Requires the array aCashflow[ ] set up before calling.
*-- : Each of its elements [n] holds the cash flow at the
*-- : beginning of period n, with a negative amount indicating
*-- : a cash outflow. Elements of value 0 must be included for
*-- : all periods with no cash flow, and all periods must be of
*-- : equal length.
*-- : If the project is expected to require an immediate outlay
*-- : of $6,000 and to return $2,000 at the end of each of the
*-- : first five years thereafter, the array will be:
*-- : aCashflow[1] = -6000
*-- : aCashflow[2] = 2000
*-- : aCashflow[3] = 2000
*-- : * * *
*-- : aCashflow[6] = 2000
*-- :
*-- : If the cash flows are at the end of the periods, rather
*-- : than at the beginning, assign 0 to aCashFlow[1], then
*-- : assign cash flows successively. aCashFlow[2] will then
*-- : represent the cash flow at the end of period 1, rather
*-- : than at the beginning of period 2, which is the same thing.
*-- :
*-- : Rewriting the function to have array name passed as a
*-- : parameter is possible, but will slow down execution to an
*-- : extent that will be very noticeable if this function is being
*-- : repeatedly executed, as by Zeroin() to find an Internal Rate
*-- : of Return.
*-------------------------------------------------------------------------------
parameters nRate, nPeriods
private nDiscount, nFactor, nPeriod, nNpv
nPeriod = 1
nNpv = aCashflow[ 1 ]
nDiscount = float( 1 )
nFactor = 1 / ( 1 + nRate )
do while nPeriod < nPeriods
nPeriod = nPeriod + 1
nDiscount = nDiscount * nFactor
nNpv = nNpv + aCashflow[ nPeriod ] * nDiscount
enddo
RETURN nNpv
*-- EoF: NPV()
FUNCTION Irr
*-------------------------------------------------------------------------------
*-- Programmer..: Jay Parsons (Jparsons)
*-- : Based on code by Tony Lima (Tonylima), 1990.
*-- Date........: 4/13/1992
*-- Notes.......: Finds internal rate of return using Zeroin().
*-- : An internal rate of return is an interest rate at
*-- : which the net present value of a series of cash flows
*-- : is zero. In the normal case of an investment, where
*-- : cash flows out at first, then comes back in later periods,
*-- : the IRR gives the interest rate for an equally-good deal, and
*-- : investments with higher IRR should be considered first.
*-- :
*-- : As this function uses the Npv() function to evaluate the
*-- : cash flows at each assumed rate, and Npv() requires for
*-- : speed that the cash flows be placed in the array aCashflow[],
*-- : the cash flows must be placed there before calling this
*-- : function. The number of rows in aCashflow[] is a parameter
*-- : passed through by Zeroin() to Npv().
*-- :
*-- Written for.: dBASE IV Version 1.5
*-- Rev. History: Original function 1990.
*-- : Modified to match Zeroin(), Npv(), 4/13/1992
*-- Calls : Zeroin() Function in STATS.PRG
*-- : Arrayrows() Function in ARRAYS.PRG
*-- Called by...: Any
*-- Usage.......: ? Irr( <fX1>, <fX2>, n_Flag )
*-- Example.....: nRate = Irr( 11, 0, 200, n_Flag )
*-- Returns : a float value representing Irr, if n_Flag < 3.
*-- Parameters..: fX1, lowest plausible rate of return from this project.
*-- : fX2, highest plausible rate of return, ditto.
*-- : n_Flag, an integer to signal success ( < 3 ) or failure.
*-- Other input : Requires advance setup of array to be called by Npv,
*-- : as furnished "aCashflow[]", to hold cash flows.
*-- Side effects: Uses and alters a global numeric variable, here called
*-- : "n_Flag", to report error conditions resulting in value
*-- : returned being meaningless.
*-------------------------------------------------------------------------------
PARAMETERS fX1, fX2, n_Flag
RETURN Zeroin( "Npv", fX1, fX2, float( 1 / 10 ^ 6 ), 100, ;
n_Flag, arrayrows( "aCashflow" ) )
*-- EoF: Irr()
*-------------------------------------------------------------------------------
*-- Note: The following functions are here as a courtesy, as they are used in at
*-- least one of the functions above.
*-------------------------------------------------------------------------------
FUNCTION Zeroin
*-------------------------------------------------------------------------------
*-- Programmer..: Tony Lima (Tonylima) and Jay Parsons (Jparsons)
*-- Date........: 4/13/1992
*-- Notes.......: Finds a zero of a continuous function.
*-- : In substance, what this function does is close in on a
*-- : solution to a function that cannot otherwise be solved.
*-- : Assuming Y = f(X), if Y1 and Y2, the values of the function
*-- : for X1 and X2, have different signs, there must be at least
*-- : one value of X between X1 and X2 for which Y = 0, if the
*-- : function is continuous. This function closes in on such a
*-- : value of X by a trial-and-error process.
*-- :
*-- : This function is very slow, so a maximum number of iterations
*-- : is passed as a parameter. If the number of iterations is
*-- : exceeded, the function will fail to find a root. If this
*-- : occurs, pick different original "X" values, increase the
*-- : number of iterations or increase the errors allowed. Once
*-- : an approximate root is found, you can use values of X close
*-- : on either side and reduce the error allowed to find an
*-- : improved solution. Also, of course, the signs of Y must be
*-- : different for the starting X values for the function to
*-- : proceed at all.
*-- :
*-- : NOTE ESPECIALLY - There is NO guarantee that a root returned
*-- : by this function is the only one, or the most meaningful.
*-- : It depends on the function that this function calls, but if
*-- : that function has several roots, any of them may be returned.
*-- : This can easily happen with such called functions as net
*-- : present value where the cash flows alternate from positive
*-- : to negative and back, and in many other "real life" cases.
*-- : See the discussion of @IRR in the documentation of a good
*-- : spreadsheet program such as Quattro Pro for further
*-- : information.
*-- :
*-- : The method used by this function is a "secant and bisect"
*-- : search. The "secant" is the line connecting two X,Y
*-- : points on a graph using standard Cartesian coordinates.
*-- : Where the secant crosses the X axis is the best guess for
*-- : the value of X that will have Y = 0, and will be correct
*-- : if the function is linear between the two points. The
*-- : basic strategy is to calculate Y at that value of X, then
*-- : keep the new X and that one of the old X values that had
*-- : a Y-value of opposite sign, and reiterate to close in.
*-- :
*-- : If the function is a simple curve with most of the change
*-- : in Y close to one of the X-values, as often occurs if the
*-- : initial values of X are poorly chosen, repeated secants
*-- : will do little to find a Y-value close to zero and will
*-- : reduce the difference in X-values only slightly. In this
*-- : case the function shifts to choosing the new X halfway
*-- : between the old ones, bisecting the difference and always
*-- : reducing the bracket by half, for a while.
*-- :
*-- : While this function finds a "zero", it may be used to
*-- : find an X corresponding to any other value of Y. Suppose
*-- : the function of X is FUNCTION Blackbox( X ) and it is
*-- : desired to find a value of X for which f(X) = 7. The trick
*-- : is to interpose a function between Zeroin() and Blackbox()
*-- : that will return a 0 to Zeroin() whenever Blackbox() returns
*-- : 7. By calling that function, Zeroin() finds a value of
*-- : X for which Blackbox( X ) = 7, as required:
*-- : Result = Zeroin( "Temp", <other parameters omitted> )
*-- :
*-- : FUNCTION Temp
*-- : parameters nQ
*-- : RETURN Blackbox( nQ ) - 7
*-- :
*-- Written for.: dBASE IV Version 1.5
*-- Rev. History: Original function 1990.
*-- : Modified to take optional parameters, 4/13/1992
*-- Calls : The function whose name is first parameter.
*-- : NPV() Function in FINANCE.PRG
*-- Called by...: Any
*-- Usage.......: Zeroin( <cFunction>, <fX1>, <fX2>, <fAbserror>, ;
*-- : <nMaxiter>, <n_Flag> ;
*-- : [, xPass1 [, xPass2 [, xPass3 ] ] ] )
*-- Example.....: ? Zeroin( "Npv", 0, 200, .000001, 200, n_Flag, 11 )
*-- Returns : a float value representing a root, if n_Flag < 3.
*-- Parameters..: cFunction, the name of the function to solve for a root.
*-- fX1, one of the X-values between which the root is sought.
*-- fX2, the second of these values.
*-- Note: These MUST be chosen so the f( X ) values for the two
*-- of them have opposite signs (they must bracket the result).
*-- fAbserror, the absolute error allowed in the result.
*-- nMaxiter, the maximum number of times to iterate.
*-- n_Flag, an integer to signal success ( < 3 ) or failure.
*-- xPass1 . . . 3, arguments to be passed through to cFunction.
*-- The parameter "n_Flag" should be passed as a variable so it
*-- may be accessed on return. The limit of 9 literal parameters
*-- may require passing others as variables. The "xPass"
*-- parameters are optional and the fact there are three of them
*-- is arbitrary; they exist to hold whatever parameters may be
*-- needed by the function cFunction being called aside from
*-- the value of X for which it is being evaluated. Add more
*-- and change the 3 "&cFunc." lines below if you need more.
*-- Side effects: Uses and alters a global numeric variable, here called
*-- "n_Flag", to report error conditions resulting in value
*-- returned being meaningless. Possible n_Flag values are:
*-- 1 success - root found within error allowed
*-- 2 success - root was found exactly
*-- 3 error - function value not converging
*-- 4 error - original values do not bracket a root
*-- 5 error - maximum iterations exceeded
*-------------------------------------------------------------------------------
parameters cFunc, fNearx, fFarx, fAbserr, nMaxiter, ;
n_Flag, xPass1, xPass2, xPass3
private nSplits, fBracket, fFary, fNeary, nIters
private fMaxabs, fOldx, fOldy, fDiffx, fAbsdiff, fSecant
store 0 to nSplits, nIters
fBracket = abs ( fNearx - fFarx )
fFary = &cFunc.( fFarx, xPass1, xPass2, xPass3 )
fNeary = &cFunc.( fNearx, xPass1, xPass2, xPass3 )
if sign( fNeary ) = sign( fFary )
n_Flag = 4
return float(0)
endif
fMaxabs = max( abs( fNeary ), abs( fFary ) )
n_Flag = 0
* Main iteration loop
do while .t.
if abs( fFary ) < abs( fNeary )
* Interchange fNearx and fFarx so that
* fNearx is closer to a solution--
* abs( fNeary ) <= abs( fFary )
fOldx = fNearx
fOldy = fNeary
fNearx = fFarx
fNeary = fFary
fFarx = fOldx
fFary = fOldy
endif
fDiffx = fFarx - fNearx
fAbsdiff = abs( fDiffx )
* Test whether interval is too small to continue
if fAbsdiff <= 2 * fAbserr
if abs( fNeary ) > fMaxabs
* Yes, but we are out of bounds
n_Flag = 3
fNearx = float(0)
else
* Yes, and we have a solution!
n_Flag = 1
endif
exit
endif
* Save the last approximation to x and y
fOldx = fNearx
fOldy = fNeary
* Check if reduction in the size of
* bracketing interval is satisfactory.
* If not, bisect until it is.
nSplits = nSplits + 1
if nSplits >= 4
if 4 * fAbsdiff >= fBracket
fNearx = fNearx + fDiffx / 2
else
nSplits = 0
fBracket = fAbsdiff / 2
* Calculate secant
fSecant = ( fNearx - fFarx ) * fNeary ;
/ ( fFary - fNeary )
* But not less than error allowed
if abs( fSecant ) < fAbserr
fNearx = fnearx + fAbserr * sign( fDiffx )
else
fNearx = fNearx + fSecant
endif
endif
endif
* Evaluate the function at the new approximation
fNeary = &cFunc.( fNearx, xPass1, xPass2, xPass3 )
* If it's exactly zero, we win! Run with it
if fNeary = 0.00
n_Flag = 2
exit
endif
* Else adjust iteration count and quit if too
* many iterations with no solution
nIters = nIters + 1
if nIters > nMaxiter
n_Flag = 5
fNearx = float( 0 )
exit
endif
* And finally keep as the new fFarx that one
* of the previous approximations, fFarx and
* fOldx, at which the function has a sign opposite
* to that at the new approximation, fNearx.
if sign( fNeary ) = sign( fFary )
fFarx = fOldx
fFary = fOldy
endif
enddo
RETURN fNearx
*-- EoF: Zeroin()
FUNCTION ArrayRows
*-------------------------------------------------------------------------------
*-- Programmer..: Jay Parsons (JPARSONS)
*-- Date........: 03/01/1992
*-- Notes.......: Number of Rows in an array
*-- Written for.: dBASE IV, 1.1
*-- Rev. History: None
*-- Calls.......: None
*-- Called by...: Any
*-- Usage.......: ArrayRows("<aArray>")
*-- Example.....: n = ArrayRows("aTest")
*-- Returns.....: numeric
*-- Parameters..: aArray = Name of array
*-------------------------------------------------------------------------------
parameters aArray
private nHi, nLo, nTrial, nDims
nLo = 1
nHi = 1170
if type( "&aArray[ 1, 1 ]" ) = "U"
nDims = 1
else
nDims = 2
endif
do while .T.
nTrial = int( ( nHi + nLo ) / 2 )
if nHi < nLo
exit
endif
if nDims = 1 .and. type( "&aArray[ nTrial ]" ) = "U" .or. ;
nDims = 2 .and. type( "&aArray[ nTrial, 1 ]" ) = "U"
nHi = nTrial - 1
else
nLo = nTrial + 1
endif
enddo
RETURN nTrial
*-- EoF: ArrayRows()
*-------------------------------------------------------------------------------
*-- EoP: FINANCE.PRG
*-------------------------------------------------------------------------------