# --------------------------------------------------------------------------+ # lib_bank_irr | # --------------------------------------------------------------------------+ # | # A library of perl functions which do useful computations about | # interest rate risk. In particular, about the interest rate risk of | # banks. | # | # Ajay Shah, http://www.mayin.org/ajayshah | # Ila Patnaik, http://openlib.org/home/ila | # Sun Nov 16 19:58:25 IST 2003 | # Gurgaon, Haryana, India. | # --------------------------------------------------------------------------+ # A general design comment concerns how we handle the spot yield curve. # In principle, one could have something like a Nelson-Siegel yield # curve, and these functions could just accept the parameters that # define the Nelson-Siegel spot yield curve. # However, that would be limiting; it would hardcode the Nelson-Siegel # into these functions. # Hence, we have instead taken the path of asking the user to supply # a TABLE of (t,r) pairs that make the spot yield curve. Linear # interpolation is done in order to know any interest rate of interest. # # Depending on your spot yield curve model, you should populate out # a simple table where each row shows a timepoint and an interest rate, # and supply this table to us. # If the table has (say) 1000 elements, then the loss of accuracy is # not economically significant. # # This approach is general, in that users are freed up to use splines, # or augmented Nelson Siegel, or whatever functional form for the spot # yield curve that fits your fancy. # # HOWEVER, we are presently doing a simple walk up that table # in locating the two points, between which linear interpolation will # be done. This is slow. # # In short, our implementation is nice in that it's general (you can use # any yield curve you want), but it's slow. Generally, in IRR calculations # in banking, this is a good tradeoff. # --------------------------------------------------------------------------- # spot_yield: function does lookups in a table depicting the spot yield curve. # It accepts 3 inputs: # 1. t, the time (in years) at which the spot yield is desired. # 2. pSYC, pointer to the table supplying the yield curve # 3. T, the number of rows in the yield curve table. # # The yield curve table is a table containing many rows, each with # a timepoint and the corresponding spot yield. This program will do # linear interpolation to locate the correct interest rate within # this table. # # The algorithm here is dumb - just walk up the table and look for a point # where we are happy. This will be SLOW with bulky tables. Hopefully, you # won't call this function very often. sub spot_yield { my($t, $pSYC, $tablesize) = @_; my($i); for ($i=0; $i<=($tablesize-2); $i++) { # Is the desired $t sitting in the yield curve table # between element i and i+1? if (($pSYC->[$i][0] <= $t) && ($t <= $pSYC->[$i+1][0])) { # Got it! # Interpolate for $t between the points # (SYC[i][0], SYC[i][1]) and (SYC[i+1][0], SYC[i+1][1]) my($m) = ($pSYC->[$i+1][1] - $pSYC->[$i][1]) / ($pSYC->[$i+1][0] - $pSYC->[$i][0]); return $pSYC->[$i][1] + $m*($t - $pSYC->[$i][0]); } } die "$0: Died in function spot_yield - fell off the edge for $t (N=$tablesize).\n"; } # --------------------------------------------------------------------------- # make_npv: This function makes the NPV of a vector of cashflows. # # Inputs: # 1. N, The number of cashflows # 2. pC, pointer to a vector of cashflows # 3. pT, pointer to corresponding vector of timepoints # 4. pshift, parallel shift of the yield curve is needed. # 5. pSYC, pointer to table supplying the yield curve # 6. T, Number of rows in the yield curve table. # # The last 2 args are merely handed over to the function spot_yield, # which uses linear interpolation in order to do lookups of the spot # yield curve. # # WARNING - we assume that you are representing the yield curve so that # an interest rate of 5% shows up as a value of 5. And, this is discrete # compounding, not continuous compounding. sub make_npv { my($N, $pC, $pT, $pshift, $pSYC, $tablesize) = @_; # No parallel shift, in case the user didn't bother to give us one. $pshift=0 if (!defined $pshift); my($i, $npv); for ($i=0, $npv=0; $i<$N; $i++) { my($piece); if ($pT->[$i] == 0) { $piece = $pC->[$i]; } else { my($rate) = &spot_yield($pT->[$i], $pSYC, $tablesize); my($dr) = ($pshift + 1 + ($rate/100.0))**$pT->[$i]; $piece = $pC->[$i] / $dr; } $npv += $piece; } return $npv; } # --------------------------------------------------------------------------- # irr_mve_parallel_shift : function computes the interest rate risk of # a bank by measuring the impact upon market value of equity of a # parallel shift of the yield curve. # Inputs: # N - number of elements in the pG and pT vectors # pG - pointer to gap cashflows # pT - pointer to timepoints of gap cashflows # pshift - size of the parallel shift # pSYC, T - the spot yield curve # # Note that the raw material we supply here is the "gap" cashflows: # i.e. in each time bucket, we report the difference between the # assets and liabilities. sub irr_mve_parallel_shift { my($N, $pG, $pT, $pshift, $pSYC, $tablesize) = @_; return &make_npv($N, $pG, $pT, $pshift, $pSYC, $tablesize) - &make_npv($N, $pG, $pT, 0, $pSYC, $tablesize); } # --------------------------------------------------------------------------- # This function computes the Fisher-Weil duration of a stated # vector of cashflows, for a stated spot yield curve. # Inputs - # N, the number of cashflows # pC, the vector of cashflows # pT, the vector of corresponding timepoints. # pSYC, the spot yield curve table # T, the number of rows in the spot yield curve table. sub fw_duration { my($N, $pC, $pT, $pSYC, $tablesize) = @_; my(@pieces, $i, $sum); $sum=0; for ($i=0; $i<$N; $i++) { my($rate) = &spot_yield($pT->[$i], $pSYC, $tablesize); my($dr) = (1 + ($rate/100.0))**$pT->[$i]; $pieces[$i] = $pC->[$i] / $dr; $sum += $pieces[$i]; } my($D)=0; for ($i=0; $i<$N; $i++) { $D += $pT->[$i] * ($pieces[$i]/$sum); } return $D; } # Function nsz # Inputs - beta0, beta1,beta2, tau (Nelson-Siegel parameters), # and t (the timepoint at which you want z(t) evaluated. # Returns - the interest rate z(t) sub nelson_siegel { my($b0, $b1, $b2, $tau, $t) = @_; my($tmp) = $t/$tau; my($tmp2) = exp(-$tmp); return $b0 + (($b1+$b2)*(1-$tmp2)/($tmp)) - ($b2*$tmp2); } 1;