RATS 10.1
RATS 10.1

Instructions /

FUNCTION Instruction

Home Page

← Previous Next →

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