#!/usr/bin/perl -w use strict; use Getopt::Long; use ConfigFile; require '/home/ila/WORK/IRR/PIA/LIBRARY/lib_bank_irr.pl'; # pia = Peculiar Interest rate Analysis # # This program reads data for a bank, reconstructs a vector of # `gap' cashflows, and computes a few measures of interest rate risk # for the bank. # # It must be supplied 2 files from the commandline: A `config' file # specifying various tuning parameters, and a pipe-delimited data file # containing one record (one bank for one year) per line. # # Ila Patnaik http://openlib.org/home/ila # Ajay Shah http://www.mayin.org/ajayshah # Mon Jan 26 12:52:21 IST 2004 # Handle commandline args -- my($configfile, $datafile); my($error) = GetOptions('data=s' => \$datafile, 'config=s' => \$configfile); if ((!$error) || (!defined $configfile) || (!defined $datafile)) { die "Usage:\n\t$0 --data datafilename --config configfilename The output that comes out is pipe delimited, and has the fields: 1 Name of bank 2 Year 3 Market value impact of the shock to the yield curve of interest to you 4 Duration of equity 5 Dgap. \n"; } die "$0: Config file $configfile does not exist.\n" if (! -f $configfile); my($C) = ConfigFile::read_config_file($configfile); # From here on, config stuff is accessible using syntax like $C->{crr} open(F, $datafile) or die "$0: Data file $datafile does not exist.\n"; # Hardcode some magic numbers -- # Column IDs for the liquidity statement my($b_1_14d, $b_15_28d, $b_29_3m, $b_3_6m, $b_6_1y, $b_1_3y, $b_3_5y, $b_gt_5y) = (0,1,2,3,4,5,6,7); # Column IDs for the IRR statement my($i_b_ns, $i_b_0_1m, $i_b_1_3m, $i_b_3_6m, $i_b_6_1y, $i_b_1_3y, $i_b_3_5y, $i_b_gt_5y) = (0,1,2,3,4,5,6,7); my(@istring) = ('Zero', '0-1mth', '1-3mth', '3-6mth', '6-12mth', '1-3yrs', '3-5yrs', '> 5yrs'); # Hardcode the TTMs associated with the cells of the IRR vector my(@timepoints) = (0, 15.0/365, 60.0/365, 135.0/365, 270.0/365, 2, 4, 10); my(@widths) = (0, 1.0/12, 2.0/12, 3.0/12, 0.5, 2, 2, 5); my($hline) = "-" x 75; # Now get on with the show... if ($C->{verbose}==2) { my($datestring)=`date`; chop $datestring; my($yourname)=`id -un`; chop $yourname; print " $hline pia : A program for some Peculiar Interest rate Analysis Ila Patnaik http://openlib.org/home/ila Ajay Shah http://www.mayin.org/ajayshah Run by : $yourname Config file : $configfile Data file : $datafile Datetime : $datestring $hline\n\n"; &dump_config_params(); } while () { # 1. Pickup information for one bank from the CMIE file -- my(@tmp) = split(/\|/); my($i); for ($i=0; $i<=$#tmp; $i++) {$tmp[$i]=0 if ($tmp[$i] eq "");} my($entity, $year, $month, @loan_advances, @investment_at_bv, @deposits, @borrowings, @frgn_curr_assets, @frgn_curr_liab, $bills_receivable, $short_terms, $term_loans, $cash_in_hand, $balance_with_rbi, $saving_bank_deposits, $demand_deposits, $equity_capital, $reserves_and_surplus, @junk); ($entity, $year, $month, $loan_advances[$b_1_14d], $loan_advances[$b_15_28d], $loan_advances[$b_29_3m], $loan_advances[$b_3_6m], $loan_advances[$b_6_1y], $loan_advances[$b_1_3y], $loan_advances[$b_3_5y], $loan_advances[$b_gt_5y], $investment_at_bv[$b_1_14d], $investment_at_bv[$b_15_28d], $investment_at_bv[$b_29_3m], $investment_at_bv[$b_3_6m], $investment_at_bv[$b_6_1y], $investment_at_bv[$b_1_3y], $investment_at_bv[$b_3_5y], $investment_at_bv[$b_gt_5y], $deposits[$b_1_14d], $deposits[$b_15_28d], $deposits[$b_29_3m], $deposits[$b_3_6m], $deposits[$b_6_1y], $deposits[$b_1_3y], $deposits[$b_3_5y], $deposits[$b_gt_5y], $borrowings[$b_1_14d], $borrowings[$b_15_28d], $borrowings[$b_29_3m], $borrowings[$b_3_6m], $borrowings[$b_6_1y], $borrowings[$b_1_3y], $borrowings[$b_3_5y], $borrowings[$b_gt_5y], $frgn_curr_assets[$b_1_14d], $frgn_curr_assets[$b_15_28d], $frgn_curr_assets[$b_29_3m], $frgn_curr_assets[$b_3_6m], $frgn_curr_assets[$b_6_1y], $frgn_curr_assets[$b_1_3y], $frgn_curr_assets[$b_3_5y], $frgn_curr_assets[$b_gt_5y], $frgn_curr_liab[$b_1_14d], $frgn_curr_liab[$b_15_28d], $frgn_curr_liab[$b_29_3m], $frgn_curr_liab[$b_3_6m], $frgn_curr_liab[$b_6_1y], $frgn_curr_liab[$b_1_3y], $frgn_curr_liab[$b_3_5y], $frgn_curr_liab[$b_gt_5y], $bills_receivable, $short_terms, $term_loans, $cash_in_hand, $balance_with_rbi, $saving_bank_deposits, $demand_deposits, $equity_capital, $reserves_and_surplus, @junk) = @tmp; # my($res_invest_fluctuation, $capital_adequacy_ratio, $Group_name, $net_interest_income, $interest_earned, $interest_expended, $net_profit, $total_assets, $investment_in_government_securities, $Gross_NPA, $Net_NPA, $BSE_mktcap, $BSE_volume, $NSE_mktcap, $NSE_volume, $Govt_holding, $Non-promoter_holding) = @junk; # Is the maturity data really non-existent? my($missingdata)=0; $missingdata=1 if (($loan_advances[$b_1_14d] == 0) && ($loan_advances[$b_15_28d] == 0) && ($loan_advances[$b_29_3m] == 0) && ($loan_advances[$b_3_6m] == 0) && ($loan_advances[$b_6_1y] == 0) && ($loan_advances[$b_1_3y] == 0) && ($loan_advances[$b_3_5y] == 0) && ($loan_advances[$b_gt_5y] == 0)); if ($missingdata) { if ($C->{verbose} == 2) { print "Maturity data is missing; skipping risk calculations\n"; } print "\nSummary of results --\n" if ($C->{verbose}==2); printf "%-30s %d %6s %6s %6s %6s\n", $entity, $year, ".", ".", ".", "."; print "$hline\n" if ($C->{verbose} == 2); next; } # Convert everything into billion rupees. for ($b_1_14d .. $b_gt_5y) {$loan_advances[$_]/=10000;} for ($b_1_14d .. $b_gt_5y) {$investment_at_bv[$_]/=10000;} for ($b_1_14d .. $b_gt_5y) {$deposits[$_]/=10000;} for ($b_1_14d .. $b_gt_5y) {$borrowings[$_]/=10000;} for ($b_1_14d .. $b_gt_5y) {$frgn_curr_assets[$_]/=10000;} for ($b_1_14d .. $b_gt_5y) {$frgn_curr_liab[$_]/=10000;} $bills_receivable/=10000; $short_terms/=10000; $term_loans/=10000; $cash_in_hand/=10000; $balance_with_rbi/=10000; $saving_bank_deposits/=10000; $demand_deposits/=10000; $equity_capital/=10000; $reserves_and_surplus/=10000; &dump_data($entity, $year, \@loan_advances, \@investment_at_bv, \@deposits, \@borrowings, \@frgn_curr_assets, \@frgn_curr_liab, $bills_receivable, $short_terms, $term_loans, $cash_in_hand, $balance_with_rbi, $saving_bank_deposits, $demand_deposits, $equity_capital, $reserves_and_surplus) if ($C->{verbose}==2); # 2. Convert it into vector of cashflows -- my(@A, @L); &make_cashflows(\@A, \@L, $entity, $year, \@loan_advances, \@investment_at_bv, \@deposits, \@borrowings, \@frgn_curr_assets, \@frgn_curr_liab, $bills_receivable, $short_terms, $term_loans, $cash_in_hand, $balance_with_rbi, $saving_bank_deposits, $demand_deposits, $equity_capital, $reserves_and_surplus); # 3. Compute risk measures -- my(@nstable, $t, $N, $Aimpact, $Limpact, $mve_impact, $duration_of_equity); # Manually fill out a value for t=0 # The Nelson-Siegel function blows up if you directly put t=0 into # it. $nstable[0][0]=0; $nstable[0][1]=nelson_siegel($C->{b0}, $C->{b1}, $C->{b2}, $C->{tau}, 0.0001); for ($N=1, $t=0.001; $t<25; $t+=0.01, $N++) { $nstable[$N][0] = $t; $nstable[$N][1] = nelson_siegel($C->{b0}, $C->{b1}, $C->{b2}, $C->{tau}, $t); } # Risk measure #1: Impact of stated parallel shift (as % of equity) # Note that this uses a size of shock from the config file # $C->{shock} is supposed to portray a statistically meaningful # notion of what the IR shock could be. $Aimpact = irr_mve_parallel_shift($#A+1, \@A, \@timepoints, $C->{shock}/1.0e4, \@nstable, $N); $Limpact = irr_mve_parallel_shift($#L+1, \@L, \@timepoints, $C->{shock}/1.0e4, \@nstable, $N); $mve_impact = 100*($Aimpact - $Limpact) / ($equity_capital + $reserves_and_surplus); if ($C->{verbose} == 2) { printf "\nRisk measure 1: Impact of a %s bps parallel shift:\n", $C->{shock}; printf " Impact on assets : %6.0f\n", $Aimpact; printf " Impact on liabilities: %6.0f\n", $Limpact; printf " Impact on A-L : %6.0f\n", ($Aimpact-$Limpact); printf " Impact on A-L : %6.2f%% of equity\n", $mve_impact; } # Risk measure #2: `Duration of equity' = -(% change in eqcap for 100 # bps change in ZCYC.) # Analogy: If a bond drops by 5% for a 100 bps shock, it's # a bond of duration=5; if equity drops by 5% for a 100 bps # shock, it's like a "duration of 5". $Aimpact = irr_mve_parallel_shift($#A+1, \@A, \@timepoints, 0.01, \@nstable, $N); $Limpact = irr_mve_parallel_shift($#L+1, \@L, \@timepoints, 0.01, \@nstable, $N); $duration_of_equity = -100*($Aimpact - $Limpact) / ($equity_capital + $reserves_and_surplus); if ($C->{verbose} == 2) { printf "\nRisk measure 2: Duration of equity:\n"; printf " Impact on A : %6.0f\n", $Aimpact; printf " Impact on L : %6.0f\n", $Limpact; printf " Duration of equity : %6.2f\n", $duration_of_equity; } # Risk measure #3: Duration gap my($Aduration) = fw_duration($#A+1, \@A, \@timepoints, \@nstable, $N); my($Lduration) = fw_duration($#L+1, \@L, \@timepoints, \@nstable, $N); my($dgap) = $Aduration - $Lduration; if ($C->{verbose} == 2) { printf "\nRisk measure 3: Duration gap:\n"; printf " Duration of A : %6.2f\n", $Aduration; printf " Duration of L : %6.2f\n", $Lduration; printf " Duration gap : %6.2f\n", $dgap; } # Print out results -- print "\nSummary of results --\n" if ($C->{verbose}==2); printf "%s|%d|%f|%f|%f\n", $entity, $year, $mve_impact, $duration_of_equity, $dgap; print "$hline\n" if ($C->{verbose} == 2); } sub make_cashflows { my($pA, $pL, $entity, $year, $ploan_advances, $pinv_at_bv, $pdeposits, $pborrowings, $pfrgn_curr_assets, $pfrgn_curr_liab, $bills_receivable, $short_terms, $term_loans, $cash_in_hand, $balance_with_rbi, $saving_bank_deposits, $demand_deposits, $equity_capital, $reserves_and_surplus) = @_; # Okay, ready for work. for $b ($i_b_ns .. $i_b_gt_5y) {$pA->[$b] = $pL->[$b] = 0;} # Handle equity capital $pL->[$i_b_ns] += $equity_capital + $reserves_and_surplus; # Handle "demand deposits" which are actually current accounts. my($tmp1, $tmp2); $tmp1 = $demand_deposits * $C->{short_current_fraction}; $tmp2 = $demand_deposits - $tmp1; &putcell($tmp1, $C->{short_current_bucket}, 0, $pL); &putcell($tmp2, $C->{long_current_bucket}, 0, $pL); # Handle savings deposits $tmp1 = $saving_bank_deposits * $C->{short_savings_fraction}; $tmp2 = $saving_bank_deposits - $tmp1; &putcell($tmp1, $C->{short_savings_bucket}, $C->{savings_bank_rate}, $pL); &putcell($tmp2, $C->{long_savings_bucket}, $C->{savings_bank_rate}, $pL); # Imputation procedure for time deposits # Problem - to disentable time deposits from the liquidity # statement that came out of the AR. my($volatile) = 0.1*$saving_bank_deposits + 0.15*$demand_deposits; my($core) = $saving_bank_deposits + $demand_deposits - $volatile; # To populate time deposits: Take out volatile & core (which are # all savings or current) from the deposits (as supplied in the AR). my(@tmp_time); for $b ($i_b_ns .. $i_b_gt_5y) {$tmp_time[$b] = 0;} $tmp_time[$i_b_0_1m] = $pdeposits->[$b_1_14d] + $pdeposits->[$b_15_28d] - $volatile; # WARNING - this check has been disabled. # if ($tmp_time[$i_b_0_1m] < 0) { # print "$0: ERROR 0-1 months became negative\n"; # } for ($b=2; $b<=$#$pdeposits; $b++) {$tmp_time[$b] = $pdeposits->[$b];} $tmp_time[$i_b_1_3y] -= $core; # WARNING - this check has been disabled. # if ($tmp_time[$i_b_1_3y] < 0) { # print "$0: ERROR 1-3 years became negative\n"; # } # Now we're ready to putcell time deposits for $b ($i_b_ns .. $i_b_gt_5y) { &putcell($tmp_time[$b], $b, $C->{time_deposits_rate}, $pL); } # Borrowings - # No real hassle here - just put them all in their correct places. &putcell($pborrowings->[$b_1_14d] + $pborrowings->[$b_15_28d], $i_b_0_1m, $C->{bank_borrowing_rate}, $pL); for $b ($b_29_3m .. $b_gt_5y) { &putcell($pborrowings->[$b], $b, $C->{bank_borrowing_rate}, $pL); } # End of liabilities. :-) # Now we turn to assets. # First deal with bills. my(@tmp_bills); if ($bills_receivable == 0) { for $b ($b_1_14d .. $b_gt_5y) { $tmp_bills[$b] = 0; } } else { my($bills_frac); my($sum) = $bills_receivable + $short_terms + $term_loans; $bills_frac = $bills_receivable/$sum; # @loan_advances contains a mix of bills, demand, term. # We will make @tmp_bills, WHICH IS IN THE OLD COORDINATE SYSTEM. for $b ($b_1_14d .. $b_gt_5y) { $tmp_bills[$b] = $bills_frac * $ploan_advances->[$b]; } # Push these values i.e. bills info, into @A &putcell($tmp_bills[$b_1_14d], $i_b_0_1m, $C->{bills_rate}, $pA); &putcell($tmp_bills[$b_15_28d], $i_b_0_1m, $C->{bills_rate}, $pA); for $b ($b_29_3m .. $b_gt_5y) { # Assuming that the coordinate systems are perfect. if ($b > $C->{plr_time_bucket}) { my($x1) = $C->{plr_linked_bills_frac} * $tmp_bills[$b]; my($x2) = $tmp_bills[$b] - $x1; &putcell($x2, $b, $C->{bills_rate}, $pA); &putcell($x1, $C->{plr_time_bucket}, $C->{bills_rate}, $pA); } else { &putcell($tmp_bills[$b], $b, $C->{bills_rate}, $pA); } } } # Now deal with demand loans and term loans. # The code here assumes that $plr_time_bucket is always BEYOND 1m. die "$0: I DO NOT LIKE WHAT YOU ARE DOING\n" if ($C->{plr_time_bucket} <= $i_b_0_1m); my($residual_demand_term) = $short_terms+$term_loans; my($demand_term_0_1m) = $ploan_advances->[$b_1_14d] + $ploan_advances->[$b_15_28d] - $tmp_bills[$b_1_14d] - $tmp_bills[$b_15_28d]; &putcell($demand_term_0_1m, $i_b_0_1m, $C->{plr}, $pA); $residual_demand_term -= $demand_term_0_1m; # At this point we finished with demand&term for 0-1m. if ($C->{plr_time_bucket} > $i_b_1_3m) { # a few more putcells to go for ($b=$i_b_1_3m; $b<$C->{plr_time_bucket}; $b++) { my($demand_term_element) = $ploan_advances->[$b] - $tmp_bills[$b]; # Careful here - @$ploan_advances & @tmp_bills are old # coordinate system, but the loop runs over new coordinate system. &putcell($demand_term_element, $b, $C->{plr}, $pA); $residual_demand_term -= $demand_term_element; } } # What is left is all remaining maturities - just put into # $C->{plr_time_bucket} since they're all floating rate. &putcell($residual_demand_term, $C->{plr_time_bucket}, $C->{plr}, $pA); # Cash on the asset side is dumped into NS. &putcell($cash_in_hand, $i_b_ns, 0, $pA); # Handling CRR # Here's the deal. Suppose CRR is 5.5%. RBI's ruleset is: The first 3 # percentage points of CRR are interest free. Beyond that, you earn # interest at the rate $C->{crr_interest_rate}. # So we need to break up the RBI balance into 2 parts: interested and # interest_free my($interest_free) = 3.0*$balance_with_rbi/$C->{crr}; my($interested) = $balance_with_rbi - $interest_free; &putcell($interest_free, $i_b_ns, 0, $pA); &putcell($interested, $i_b_3_6m, $C->{crr_interest_rate}, $pA); # Investments # No real hassle here - just put them all in their correct places. &putcell($pinv_at_bv->[$b_1_14d], $i_b_0_1m, $C->{investment_rate}, $pA); &putcell($pinv_at_bv->[$b_15_28d], $i_b_0_1m, $C->{investment_rate}, $pA); for $b ($b_29_3m .. $b_gt_5y) { &putcell($pinv_at_bv->[$b], $b, $C->{investment_rate}, $pA); } if ($C->{verbose}==2) { print "Converted into cashflows --\n"; printf " %-12s %12s %12s\n", "Bucket", "Assets", "Liabilities"; for $b ($i_b_ns .. $i_b_gt_5y) { printf " %-12s %12.0f %12.0f\n", $istring[$b], $pA->[$b], $pL->[$b]; } } } # This function ACCRETES a cashflow into a vector. # Suppose I know that Rs.100 will come in with T=4 and # carries a coupon of 10%. # Then I need to first stick 100 into $i_b_3_5_y # I also need to put Rs.10 each into all the years 1,2,3,4. # This needs to be done carefully, to reflect the fact that the buckets # in the interest rate risk statement are not necessarily 1 year wide. # # In short : this function is told that a cashflow of $value is # coming with $ttm and $couponrate. It does 'whatever is needed' # in accreting values into the vector $vp. sub putcell { my($value, $target, $couponrate, $vp) = @_; my($b); # WARNING - it is VERY odd when someone tries to give putcell # a negative cashflow. There seem to be some small cases of this # that happen from time to time. We are just tolerating this # for now. # if ($value < 0) { # print " ERROR Ow ow ow you gave me a negative number\n"; # print " value=$value, target=$istring[$target], coupon=$couponrate\n"; # print " vector is ", join(", ", @$vp), "\n"; # } for ($b=1; $b<=$target; $b++) { $vp->[$b] += $value * ($couponrate/100) * $widths[$b]; } $vp->[$target] += $value; } # A function which prints out everything in the config file... sub dump_config_params { printf "This run uses configuration parameters --\n"; printf " %-64s %8.2f\n", "Fraction of savings deposits which are classified as short", $C->{short_savings_fraction}; printf " %-64s %8s\n", "Bucket where we place short savings deposits", $istring[$C->{short_savings_bucket}]; printf " %-64s %8s\n", "Bucket where we place long savings deposits", $istring[$C->{long_savings_bucket}]; printf " %-64s %8.2f\n", "Fraction of current deposits which are classified as short", $C->{short_current_fraction}; printf " %-64s %8s\n", "Bucket where we place short current deposits", $istring[$C->{short_current_bucket}]; printf " %-64s %8s\n", "Bucket where we place long current deposits", $istring[$C->{long_current_bucket}]; printf " %-64s %8.2f\n", "Interest rate on savings bank deposits", $C->{savings_bank_rate}; printf " %-64s %8.2f\n", "Interest rate on time deposits", $C->{time_deposits_rate}; printf " %-64s %8.2f\n", "Interest rate suffered in borrowing from banks", $C->{bank_borrowing_rate}; printf " %-64s %8.2f\n", "Interest rate earned on bills purchased by the bank", $C->{bills_rate}; printf " %-64s %8.2f\n", "CRR (in percent)", $C->{crr}; printf " %-64s %8.2f\n", "Interest paid by RBI on CRR beyond 3 percentage points", $C->{crr_interest_rate}; printf " %-64s %8.2f\n", "Interest earned on investments", $C->{investment_rate}; printf " %-64s %8.2f\n", "PLR", $C->{plr}; printf " %-64s %8s\n", "Time bucket for placing PLR-linked assets", $istring[$C->{plr_time_bucket}]; printf " %-64s %8.2f\n", "Fraction of bills (in higher buckets) which are PLR-linked", $C->{plr_linked_bills_frac}; printf " %-64s %8.2f\n", "Parallel shift to simulate", $C->{shock}; printf " NS parameters : %s %8.5f %8.5f %8.5f %8.5f\n", " " x 21, $C->{b0}, $C->{b1}, $C->{b2}, $C->{tau}; print "-" x 75, "\n\n"; } sub dump_data { my($entity, $year, $ploan_advances, $pinvestment_at_bv, $pdeposits, $pborrowings, $pfrgn_curr_assets, $pfrgn_curr_liab, $bills_receivable, $short_terms, $term_loans, $cash_in_hand, $balance_with_rbi, $saving_bank_deposits, $demand_deposits, $equity_capital, $reserves_and_surplus) = @_; print "The raw data for $entity for $year --\n\n"; printf "%20s", ""; printf "%6s %6s %6s %6s %6s %6s %6s %6s\n", "1-14d", "15-28d", "29d-3m", "3-6m", "6-12m", "1-3y", "3-5y", ">5y"; printf "\n%-20s", "Loans, Advances"; for ($b_1_14d .. $b_gt_5y) {printf "%6.0f ", $ploan_advances->[$_];} printf "\n%-20s", "Investments"; for ($b_1_14d .. $b_gt_5y) {printf "%6.0f ", $pinvestment_at_bv->[$_];} printf "\n%-20s", "Deposits"; for ($b_1_14d .. $b_gt_5y) {printf "%6.0f ", $pdeposits->[$_];} printf "\n%-20s", "Borrowings"; for ($b_1_14d .. $b_gt_5y) {printf "%6.0f ", $pborrowings->[$_];} printf "\n%-20s", "Foreign A"; for ($b_1_14d .. $b_gt_5y) {printf "%6.0f ", $pfrgn_curr_assets->[$_];} printf "\n%-20s", "Foreign L"; for ($b_1_14d .. $b_gt_5y) {printf "%6.0f ", $pfrgn_curr_liab->[$_];} print "\n\n"; printf "%-20s %6.0f %-20s %6.0f\n", "Bills receivable", $bills_receivable, "Short terms", $short_terms; printf "%-20s %6.0f %-20s %6.0f\n", "Term loans", $term_loans, "Cash in hand", $cash_in_hand; printf "%-20s %6.0f %-20s %6.0f\n", "Balance with RBI", $balance_with_rbi, "Saving bank dep", $saving_bank_deposits; printf "%-20s %6.0f\n", "Demand dep", $demand_deposits; printf "%-20s %6.0f %-20s %6.0f\n", "Equity capital", $equity_capital, "Reserves", $reserves_and_surplus; print "\n"; }