FUNCTION Instruction |
FUNCTION funcname parameters
LOCAL,TYPE instructions if any
executable instructions
END
The FUNCTION statement begins the definition of a user-defined function. It specifies the name of the function and the parameter list, if any.
Parameters
funcname |
The name you want to give this function. funcname must be distinct from any other function, procedure, or variable name in the program. By default, the function will return a real value. You can change that by using a TYPE command to define funcname as a different type. |
parameters |
Names of the parameters. These names are local to the function—they will not conflict with variables elsewhere in the program. By default, parameters are INTEGER passed by value. You can change this using TYPE statements. |
Description
User-defined functions are very similar to RATS procedures. However, procedures are designed to mimic RATS instructions, and thus cannot be called from within an expression. Functions, on the other hand, are designed to be used in expressions, and thus make it possible to embed complicated computations in FRMLs and other expressions. As a result, functions make it possible to handle a wide range of optimization problems that would be very difficult or impossible to handle without them.
A function definition begins with a FUNCTION statement and ends with a matching END. The FUNCTION statement itself names the function and lists the formal parameters, if any.
You need to use function instructions in the following order:
FUNCTION statement
TYPE statements, if any, to define the type of the function and any parameters
other instructions
END
If possible, you should try to write the function using only parameters, local variables and global variables which RATS itself defines. That way, you don’t have to worry about accidentally changing a global variable elsewhere in the program.
You can use any executable instructions within the FUNCTION. However, you need to assign a value to the funcname before either the END or an alternate RETURN, as that is how the return value is given to the function.
Using SOURCE
As with procedures, you may find it convenient to save functions that you’ve written on separate files, so that they can be used in different applications. If you have a function stored on a separate file, bring it into your current RATS program using the instruction SOURCE. The typical instruction is
SOURCE "file with function"
If you have a collection of functions which you use regularly, you can include them in a “procedure library” that gets brought in right at the start of your program (set on the File-Preferences operation).
Indirect References
You can set up a PROCEDURE or FUNCTION to accept a FUNCTION as an option or parameter, and then pass through to it when you execute a specific function. The type specification for such as indirect function reference is
FUNCTION[returntype](argtype1,argtype2,...,argtypeN)
if it has N arguments. For instance,
procedure MCVARDoDraws
*
option model model
option integer steps 48
option integer draws 1000
option vect[int] accum
option function[rect](symm,model) ffunction
This adds an option called FFUNCTION (for Factor FUNCTION) to the procedure. This takes a SYMMETRIC and MODEL as its parameters. Within the procedure, this is used as:
if %defined(ffunction)
compute factor=ffunction(%outerxx(fsigmad),model)
else
compute factor=fsigmad
This checks to see if FFUNCTION has been defined. If FFUNCTION isn't used in executing @MCVARDODRAWS, it won't be. If it is defined, it gets used to compute FACTOR; if not FSIGMAD (which in this case is the Cholesky factor) gets used instead.
function bqfactor sigma model
type rect bqfactor
type symm sigma
type model model
*
compute bqfactor=%bqfactor(sigma,%modellagsums(model))
end
Examples
This defines a VECTOR of probabilities in a regime-switching model, return a 2-VECTOR with the density functions in the two regimes, where the regimes differ based upon the values PHI.
function RegimeF time
type vector RegimeF
type integer time
local integer i
*
dim RegimeF(2)
ewise RegimeF(i)=exp(%logdensity(sigsq,%eqnrvalue(xeq,time,phi(i))))
end
This computes the value of a cubic spline (whose coefficients were just estimated and are in %BETA) for an arbitrary input value M. This is then used to compute and graph the spline function. This is part of BONDSPLINE.RPF.
function cspline m
type real cspline m
*
compute cspline=1.0+%beta(1)*m+%beta(2)*m^2+%beta(3)*m^3
do k=1,nknots
compute cspline=cspline+%beta(3+k)*%max(m-knots(k),0.0)^3
end do k
end cspline
*
set testm 1 50 = .20*t
set pvalue 1 50 = cspline(testm)
*
* Convert the discount factor into an annualized yield curve
*
set testm 1 50 = testm/2
set yield 1 50 = -100.0*log(pvalue)/testm
scatter(style=line,window="Yield Curve",hlabel="Years",vlabel="Yield")
# testm yield
This computes the tri-gamma function (second derivative of log gamma) of its argument. (Note: the function %TRIGAMMA is now built-in, but this offers a good example of writing a general purpose function). The function checks for an out-of-range argument and returns the missing value in that case. Note also that the function name is used repeatedly in the body of the function for the intermediate calculations. This is permitted; the only thing special about the function name (compared with any other local variables) is that the final value assigned to it before the return will be the value returned by the function.
function TriGamma z
type real TriGamma z
local real zp zpr
if z<=0 {
compute TriGamma=%NA
return
}
compute zp=z
compute TriGamma=0.0
while (zp<30) {
compute zpr=1/zp
compute TriGamma=TriGamma+zpr*zpr
compute zp=zp+1
}
compute zpr=1/zp
compute TriGamma=TriGamma+zpr*(1+zpr*(.5+zpr*(1.0/6.0+zpr^2*$
(-1.0/30.0+zpr^2/42.0))))
end
The example program BONDS.RPF fits a yield curve to a set of bonds. The function for evaluating the present value isn't closed form since it requires a sum across all coupon payments. That's most easily done using a FUNCTION.
nonlin a0 a1 a2
compute a0=.030,a1=a2=0.0
compute cusp=2.0
function BondPV bond
type real BondPV
type integer bond
local real mdate cdate
compute mdate=maturity(bond)
compute BondPV=100.0 * $
exp(-mdate*(a0+mdate*a1+$
%max(mdate-cusp,0.0)*a2))
*
* Walk backwards through the coupons.
*
compute cdate=mdate
while (cdate > 0.0) {
compute BondPV=BondPV + coupon(bond) * $
exp(-cdate*(a0+cdate*a1+$
%max(cdate-cusp,0.0)*a2))
compute cdate=cdate-1
}
*
* Adjust for simple interest payable by
* the purchaser for the initial coupon.
* cdate will be -(fraction of period).
*
compute BondPV=BondPV+coupon(bond)*cdate
end
With the evaluating function defined, this set up for using NLLS is quite simple:
frml BondPrice value = BondPV(t)
nlls(robust,trace,frml=BondPrice) value
Copyright © 2025 Thomas A. Doan