SAS Documentation
SASĀ® Solution for Stress Testing
Reference manual - version 08.2021
Loading...
Searching...
No Matches
irmst_market_risk_funs_def_3.sas
1%macro irmst_market_risk_funs_def_3;
2
3 function RSK_BINARY_CASH_OPTION_PF(CallPutType $, Price, Payoff_Cash, Strike, RiskFreeRate, YieldParam, TimeToExpiration, Volatility)
4 label="European binary cash or nothing option pricing by Reiner and Rubinstein (1991)";
5 /***************************************************/
6 /* Error and special case checking */
7 /***************************************************/
8 /* Past maturity, option is worthless */
9 if TimeToExpiration < 0 then return(0);
10 /* Initialize the return missing flag to zero (state: no error found)
11 If we find an error in one of the inputs, we will return missing */
12 /* Set the function name for error reporting */
13 ReturnMissingFlg = 0;
14 Fname = 'rsk_binary_cash_option_pf';
15 /* Check that nonoptional inputs are nonmissing */
16 ReturnMissingFlg = rsk_check_num_missing_pf( Payoff_Cash, Fname, '3', 'Payoff_Cash', ReturnMissingFlg );
17 ReturnMissingFlg = rsk_check_num_missing_pf( RiskFreeRate, Fname, '5', 'RiskFreeRate', ReturnMissingFlg );
18 ReturnMissingFlg = rsk_check_num_missing_pf( YieldParam, Fname, '6', 'YieldParam', ReturnMissingFlg );
19 /* Make sure some input values are positive */
20 /* Nonpositive or missing values for these variables may cause problems
21 if input into another function (e.g. the logarithm function) */
22 ReturnMissingFlg = rsk_check_nonpositive_pf( Price, Fname, '2', 'Price', ReturnMissingFlg );
23 ReturnMissingFlg = rsk_check_nonpositive_pf( Strike, Fname, '4', 'Strike', ReturnMissingFlg );
24 ReturnMissingFlg = rsk_check_nonpositive_pf( Volatility, Fname, '8', 'Volatility', ReturnMissingFlg );
25 /* Return missing, if errors are found */
26 if ReturnMissingFlg eq 1 then return(.);
27 /* At maturity, return payoff */
28 if abs(TimeToExpiration) le constant('SQRTMACEPS') then do;
29 if upcase(CallPutType) eq 'CALL' and Price > Strike then return(Payoff_Cash);
30 if upcase(CallPutType) ne'CALL' and Price < Strike then return(Payoff_Cash);
31 return(0);
32 end;
33 /************************************************************/
34 /* RESUME STANDARD PRICING */
35 /************************************************************/
36 /* Calculate constants used in pricing */
37 CostofCarry=RiskFreeRate - YieldParam;
38 g = Volatility*sqrt(TimeToExpiration);
39 d = (log(Price / Strike) + CostofCarry * TimeToExpiration )/g - 0.5 * g;
40 /* Value option for call/put cases */
41 IF upcase(CallPutType) eq 'CALL' THEN DO;
42 OptionPrice = Payoff_Cash * exp(-RiskFreeRate * TimeToExpiration) * probnorm(d);
43 END;
44 ELSE
45 DO;
46 OptionPrice = Payoff_Cash * exp(-RiskFreeRate * TimeToExpiration) * probnorm(-d);
47 END;
48 return(OptionPrice);
49
50 endsub;
51
52 function RSK_EUROPEANPUTOPTION_PF(Price,Strike,RiskFreeRate,YieldParam,TimeToExpiration,Volatility)
53 label="European put option pricing by Black (1976)";
54/**************************************************************************/
55/* ERROR CHECKING
56 /**************************************************************************/
57 IF TimeToExpiration lt 0 THEN return(0);
58 IF TimeToExpiration eq 0 THEN return (max(Strike-Price,0));
59/* Initialize the return missing flag to zero
60 If we find an error in one of the inputs, we will return missing */
61/* Set the function name for error reporting */
62 ReturnMissingFlg = 0;
63 Fname = 'rsk_europeanputoption_pf';
64/* Make sure some inputs are positive or nonnegative*/
65/* Negative and missing values for those inputs may cause problems
66 if input into another function (e.g. the logarithm function) */
67 ReturnMissingFlg = rsk_check_num_missing_pf( RiskFreeRate, Fname, '3', 'RiskFreeRate', ReturnMissingFlg );
68 ReturnMissingFlg = rsk_check_num_missing_pf( YieldParam, Fname, '4', 'YieldParam', ReturnMissingFlg );
69 ReturnMissingFlg = rsk_check_num_missing_pf( TimeToExpiration, Fname, '5', 'TimeToExpiration', ReturnMissingFlg );
70 ReturnMissingFlg = rsk_check_num_missing_pf( Price, Fname, '1', 'Price', ReturnMissingFlg );
71 ReturnMissingFlg = rsk_check_nonpositive_pf( Strike, Fname, '2', 'Strike', ReturnMissingFlg );
72 ReturnMissingFlg = rsk_check_nonpositive_pf( Volatility, Fname, '6', 'Volatility', ReturnMissingFlg );
73/* Return missing, if errors are found */
74 if ReturnMissingFlg eq 1 then return(.);
75/************************************************************/
76/* RESUME STANDARD PRICING
77 /************************************************************/
78 CostofCarry=RiskFreeRate - YieldParam;
79 g = Volatility * sqrt(TimeToExpiration);
80 d1 = (log(Price / Strike) + CostofCarry * TimeToExpiration )/g + 0.5 * g;
81 d2 = d1 - g;
82 OptionPrice = -Price * exp(-YieldParam* TimeToExpiration) * probnorm(-d1) + Strike * exp(-RiskFreeRate * TimeToExpiration) * probnorm(-d2);
83 return(OptionPrice);
84
85 endsub;
86
87
88
89 function RSK_BARRIEROPTION_PF(CallPutType $, BarrierType $, Price, Price_Chk_Barrier, Strike, Barrier, CashRebate, RiskFreeRate, YieldParam, TimeToExpiration, Volatility)
90 label="European barrier option pricing by Merton (1973) and Reiner and Rubinstein (1991)";
91 /***************************************************/
92 /* Error and special case checking */
93 /***************************************************/
94 /* If the option has expired, it is worthless */
95 if TimeToexpiration < 0 then return(0);
96 /* Initialize the return missing flag to zero
97 If we find an error in one of the inputs, we will return missing */
98 /* Set the function name for error reporting */
99 ReturnMissingFlg = 0;
100 Fname = 'rsk_barrieroption_pf';
101 /* Check that nonoptional inputs are nonmissing */
102 ReturnMissingFlg = rsk_check_num_missing_pf( RiskFreeRate, Fname, '8', 'RiskFreeRate', ReturnMissingFlg );
103 ReturnMissingFlg = rsk_check_num_missing_pf( YieldParam, Fname, '9', 'YieldParam', ReturnMissingFlg );
104 ReturnMissingFlg = rsk_check_num_missing_pf( TimeToExpiration, Fname, '10', 'TimeToExpiration', ReturnMissingFlg );
105 /* Make sure some input values are positive */
106 /* Nonpositive or missing values for these variables may cause problems
107 if input into another function (e.g. the logarithm function) */
108 ReturnMissingFlg = rsk_check_nonpositive_pf( Price, Fname, '3', 'Price', ReturnMissingFlg );
109 ReturnMissingFlg = rsk_check_nonpositive_pf( Strike, Fname, '5', 'Strike', ReturnMissingFlg );
110 ReturnMissingFlg = rsk_check_nonpositive_pf( Barrier, Fname, '6', 'Barrier', ReturnMissingFlg );
111 ReturnMissingFlg = rsk_check_nonpositive_pf( Volatility, Fname, '11', 'Volatility', ReturnMissingFlg );
112 /* Return missing, if errors are found */
113 if ReturnMissingFlg eq 1 then return(.);
114 /* Adjust inputs for casing/missing */
115 BarrType = lowcase(BarrierType);
116 Cash_Rebate = coalesce(CashRebate,0);
117 /* If at maturity, return payoff */
118 if abs(TimeToExpiration) le constant('SQRTMACEPS') then do;
119 if upcase(CallPutType) eq 'CALL' then payoff = max( Price - Strike, 0 );
120 else
121 payoff = max( Strike - Price, 0 );
122 if max(Price_Chk_Barrier,Price) > Barrier then do;
123 if BarrType eq 'ui' then return(payoff);
124 if BarrType eq 'uo' then return(Cash_Rebate);
125 if BarrType eq 'di' then return(Cash_Rebate);
126 if BarrType eq 'do' then return(payoff);
127 end;
128 else
129 if min(Price_Chk_Barrier,Price) < Barrier then do;
130 if BarrType eq 'ui' then return(Cash_Rebate);
131 if BarrType eq 'uo' then return(payoff);
132 if BarrType eq 'di' then return(payoff);
133 if BarrType eq 'do' then return(Cash_Rebate);
134 end;
135 else
136 do;
137 if BarrType eq 'ui' then return(payoff);
138 if BarrType eq 'uo' then return(Cash_Rebate);
139 if BarrType eq 'di' then return(payoff);
140 if BarrType eq 'do' then return(Cash_Rebate);
141 end;
142 end;
143 /* If barrier has been breached, return European value (in) or cash rebate (out) */
144 if ( BarrType in ( 'di', 'do' ) and Price_Chk_Barrier le Barrier and not missing(Price_Chk_Barrier) ) or ( BarrType in ( 'ui', 'uo' ) and Price_Chk_Barrier ge Barrier ) then
145 do;
146 if BarrType in ( 'do', 'uo' ) then OptionPrice = Cash_Rebate;
147 /* "Out" option */
148 else
149 if upcase(CallPutType) eq 'CALL' then do;
150 /* "In" call */
151 OptionPrice =rsk_europeancalloption_pf(Price,Strike,RiskFreeRate, YieldParam,TimeToExpiration,Volatility);
152 end;
153 else
154 do;
155 /* "In" put */
156 OptionPrice =rsk_europeanputoption_pf(Price,Strike,RiskFreeRate, YieldParam,TimeToExpiration,Volatility);
157 end;
158 return( OptionPrice );
159 end;
160 /************************************************************/
161 /* RESUME STANDARD PRICING */
162 /************************************************************/
163 /* Compute pricing constants */
164 CostofCarry=RiskFreeRate - YieldParam;
165 _mu_=(CostofCarry - (volatility*volatility)/2)/(volatility*volatility);
166 _lambda_=sqrt(_mu_*_mu_ + (2*RiskFreeRate)/(volatility*volatility) );
167 x1= (log(Price/Strike))/( volatility*sqrt(TimeToExpiration) ) + (1+_mu_)* volatility*sqrt(TimeToExpiration);
168 x2= (log(Price/Barrier))/( volatility*sqrt(TimeToExpiration) ) + (1+_mu_)* volatility*sqrt(TimeToExpiration);
169 y1= (log((Barrier*Barrier)/(Price*Strike)))/( volatility*sqrt( TimeToExpiration) ) + (1+_mu_)*volatility*sqrt(TimeToExpiration);
170 y2= (log((Barrier)/(Price)))/( volatility*sqrt(TimeToExpiration) ) + (1+ _mu_)*volatility*sqrt(TimeToExpiration);
171 z= (log((Barrier)/(Price)))/( volatility*sqrt(TimeToExpiration) ) + ( _lambda_)*volatility*sqrt(TimeToExpiration);
172 _nn_=(Barrier/Price)**(2*(_mu_+1));
173 _nm_=(Barrier/Price)**(2*_mu_);
174 _mm_=(Barrier/Price)**(_mu_ + _lambda_);
175 _ms_=(Barrier/Price)**(_mu_ - _lambda_);
176 /* Set eta, based on up/down type */
177 if BarrType in ( 'ui', 'uo' ) then _eta_ = -1;
178 /* up */
179 else
180 _eta_ = 1;
181 /* down */
182 /* Set psi, based on call/put type */
183 if upcase(CallPutType) eq 'CALL' then _psi_ = 1;
184 /* call */
185 else
186 _psi_ = -1;
187 /* put */
188 /* Compute constants A-F for pricing */
189 A=_psi_ * Price * exp((CostofCarry - RiskFreeRate)*( TimeToExpiration)) * probnorm(_psi_*x1) - _psi_* Strike * exp(- RiskFreeRate*(TimeToExpiration)) * probnorm(_psi_*x1 - _psi_* volatility*sqrt(TimeToExpiration));
190 B=_psi_ * Price * exp((CostofCarry - RiskFreeRate)*( TimeToExpiration)) * probnorm(_psi_*x2) - _psi_* Strike * exp(- RiskFreeRate*(TimeToExpiration)) * probnorm(_psi_*x2 - _psi_* volatility*sqrt(TimeToExpiration));
191 C=_psi_ * Price * exp((CostofCarry - RiskFreeRate)*( TimeToExpiration)) * _nn_ * probnorm(_eta_*y1) - _psi_* Strike * exp(- RiskFreeRate*(TimeToExpiration)) * _nm_ * probnorm(_eta_*y1 - _eta_*volatility*sqrt(TimeToExpiration));
192 D=_psi_ * Price * exp((CostofCarry - RiskFreeRate)*( TimeToExpiration)) * _nn_ * probnorm(_eta_*y2) - _psi_* Strike * exp(- RiskFreeRate*(TimeToExpiration)) * _nm_ * probnorm(_eta_*y2 - _eta_*volatility*sqrt(TimeToExpiration));
193 E=Cash_Rebate * exp(- RiskFreeRate*(TimeToExpiration)) * ( probnorm(_eta_*x2 - _eta_*volatility*sqrt(TimeToExpiration)) - _nm_* probnorm(_eta_*y2 - _eta_*volatility*sqrt( TimeToExpiration)) ) ;
194 F=Cash_Rebate * ( probnorm(_eta_*z)*_mm_ + _ms_* probnorm(_eta_*z - 2*_eta_*_lambda_*volatility* sqrt(TimeToExpiration)) ) ;
195 /* If not breached, price the option, for the 8 different type cases, each
196 of which have two strike/barrier cases: Strike ge Barrier and Strike < Barrier */
197 IF upcase(CallPutType) eq 'CALL' THEN DO;
198 /************** CALL CASE *************/
199 IF BarrType eq 'di' THEN DO;
200 /* Down and In Call */
201 IF Strike ge Barrier THEN DO;
202 OptionPrice = C + E;
203 END;
204 ELSE
205 DO;
206 OptionPrice = A - B + D + E;
207 END;
208 END;
209 IF BarrType eq 'ui' THEN DO;
210 /* Up and In Call */
211 IF Strike ge Barrier THEN DO;
212 OptionPrice = A + E;
213 END;
214 ELSE
215 DO;
216 OptionPrice = B - C +D + E;
217 END;
218 END;
219 IF BarrType eq 'do' THEN DO;
220 /* Down and Out Call */
221 IF Strike ge Barrier THEN DO;
222 OptionPrice = A -C + F;
223 END;
224 ELSE
225 DO;
226 OptionPrice = B - D + F;
227 END;
228 END;
229 IF BarrType eq 'uo' THEN DO;
230 /* Up and Out Call */
231 IF Strike ge Barrier THEN DO;
232 OptionPrice =F;
233 END;
234 ELSE
235 DO;
236 OptionPrice = A - B + C -D +F;
237 END;
238 END;
239 END;
240 ELSE
241 DO;
242 /***************** PUT CASE *********************/
243 IF BarrType eq 'di' THEN DO;
244 /* Down and In Put */
245 IF Strike ge Barrier THEN DO;
246 OptionPrice = B - C + D + E;
247 END;
248 ELSE
249 DO;
250 OptionPrice = A + E;
251 END;
252 END;
253 IF BarrType eq 'ui' THEN DO;
254 /* Up and In Put */
255 IF Strike ge Barrier THEN DO;
256 OptionPrice = A - B + D + E;
257 END;
258 ELSE
259 DO;
260 OptionPrice = C + E;
261 END;
262 END;
263 IF BarrType eq 'do' THEN DO;
264 /* Down and Out Put */
265 IF Strike ge Barrier THEN DO;
266 OptionPrice = A -B + C - D + F;
267 END;
268 ELSE
269 DO;
270 OptionPrice = F;
271 END;
272 END;
273 IF BarrType eq 'uo' THEN DO;
274 /* Up and Out Put */
275 IF Strike ge Barrier THEN DO;
276 OptionPrice = B - D + F;
277 END;
278 ELSE
279 DO;
280 OptionPrice = A - C + F;
281 END;
282 END;
283 END;
284 return(OptionPrice);
285
286 endsub;
287
288
289 subroutine RSK_COMBINE_TWO_SCHEDULES(aggregation_method $, sched_one_dt[*], sched_one_amt[*], sched_one_num, sched_two_dt[*], sched_two_amt[*], sched_two_num, out_dt[*], out_amt[*], out_num)
290 label="Combines two schedules into one";
291 outargs out_dt, out_amt, out_num;
292 /* Initialize index variables and temporary date tracking */
293 i = 1;
294 j = 1;
295 n = 0;
296 TEMP_DT = min( sched_one_dt[1], sched_two_dt[1] );
297 /* Loop through scenarion and capital schedules, merging them into a complete capital schedule */
298 do while( ( i le sched_one_num or j le sched_two_num ) and not missing(TEMP_DT) );
299 /* Get the next date */
300 if i le sched_one_num then temp_cap_dt = sched_one_dt[i];
301 else
302 temp_cap_dt = .;
303 if j le sched_two_num then temp_scen_dt = sched_two_dt[j];
304 else
305 temp_scen_dt = .;
306 TEMP_DT = min( temp_cap_dt, temp_scen_dt );
307 /* Reset flag marking whether this date exists in schedule one */
308 sched_one_dt_exists = 0;
309 /* If current date is from schedule one, use those values */
310 if i le sched_one_num then do;
311 if TEMP_DT eq sched_one_dt[i] then do;
312 n = n + 1;
313 out_dt[n] = sched_one_dt[i];
314 out_amt[n] = sched_one_amt[i];
315 sched_one_dt_exists = 1;
316 i = i + 1;
317 end;
318 end;
319 /* If current date is from schedule two, use those values */
320 if j le sched_two_num then do;
321 if TEMP_DT eq sched_two_dt[j] then do;
322 /* If this date exists in both schedules, combine their values by method */
323 /* Otherwise, just use schedule two amounts */
324 if sched_one_dt_exists eq 1 then do;
325 if upcase(aggregation_method) eq 'MIN' then out_amt[n] = min( out_amt[n], sched_two_amt[j] );
326 else
327 if upcase(aggregation_method) eq 'MAX' then out_amt[n] = max( out_amt[n], sched_two_amt[j] );
328 else
329 if upcase(aggregation_method) eq 'FIRST' then do;
330 /* Do nothing, leave as-is */
331 end;
332 else
333 if upcase(aggregation_method) eq 'SECOND' then out_amt[n] = sched_two_amt[j];
334 else
335 out_amt[n] = sum( out_amt[n], sched_two_amt[j] );
336 /* default to sum */
337 end;
338 else
339 do;
340 n = n + 1;
341 out_dt[n] = sched_two_dt[j];
342 out_amt[n] = sched_two_amt[j];
343 end;
344 j = j + 1;
345 end;
346 end;
347 end;
348 /* Set the new size of the output array */
349 out_num = n;
350
351 endsub;
352
353
354 subroutine RSK_BOOTSTRAP_HAZARD_RATE(ValuationDate, /*If it is missing valuie, then Mat_CDS_SprdCurve is fraction of year*/ PremiumFrequency $,/*Frequency of Premium: "month","quarter","semiannual" or "annual"*/ CDS_SprdCurve[*],/*CDS spread curve in basic point*/ Mat_CDS_SprdCurve[*],/*date of CDS spread curve. Either real date or the fraction of year */ LGD_PCT,/*Percentage of Loss Given Default*/ DefaultFrequency $,/*Frequency of Default: "month"=1/12,"quarter"=.25,"semiannual"=.5 or "annual"=1*/ DiscountFactorCurve[*], /* dicount factor curve*/ Mat_DiscountFactorCurve[*],/* maturity date of discount factor (in fraction of year*/ DayCountConvention $, /* day count convention*/ IntpMethod $, /* interpolation method*/ IF_ACC_PRE_FLAG, /*1, if include accrual premium. 0, otherwise*/ HazardRateCurve[*] /*Hazard Rate Curve with the same time point as CDS_SprdCurve (output arguments)*/)
355 label="Bootstraps the hazard rate from a credit spread curve";
356 outargs HazardRateCurve;
357 RecoveryRate=1-LGD_PCT/100;
358 Default_Time_Increment=rsk_get_frequency_in_year(1,DefaultFrequency);
359 /*specify the number of days for a year*/
360 DayofYear=rsk_get_day_of_year(DayCountConvention);
361 PrmFreq=rsk_get_frequency_in_year(1,PremiumFrequency);
362 /*calculate the series of day count in CDS spread curve (in fraction of year)*/
363 array CDS_Sprd_DayCount[1]/nosym;
364 call DYNAMIC_ARRAY(CDS_Sprd_DayCount,DIM(CDS_SprdCurve));
365 if (ValuationDate ~= .) then do;
366 do i=1 to DIM(Mat_CDS_SprdCurve);
367 CDS_Sprd_DayCount[i]=rsk_daycount(DayCountConvention,ValuationDate,Mat_CDS_SprdCurve[i]);
368 end;
369 end;
370 else
371 do;
372 do i=1 to DIM(Mat_CDS_SprdCurve);
373 CDS_Sprd_DayCount[i]=Mat_CDS_SprdCurve[i];
374 end;
375 end;
376 /* bootstrap hazard rate from CDS spread spot curve.The hazard rate makes
377 Premium Leg = Protection Leg
378 if implied hazard rate is non-positive, then assign missing value to it*/
379 array solvopts[1] initial (0.20);
380 do i=1 to DIM(Mat_CDS_SprdCurve);
381 HazardRate=solve("PremiumLeg_Minus_ProtectionLeg", solvopts, 0, /*parameters of functions*/ CDS_SprdCurve, /* CDS spread curve in basic point, eg: 140 BP*/ Mat_CDS_SprdCurve, /*date of CDS spread curve(in fraction of year, eg: 1/12 represents one month)*/ RecoveryRate, /*Recovery Rate, eg .4 */ Default_Time_Increment, /*Default Frequency, eg :"month"=1/12, "quater"=.25,"seminannual"=.5,"annual"=1*/ DiscountFactorCurve,/*Discount factor curve*/ Mat_DiscountFactorCurve,/*maturity of discount factor curve*/ PremiumFrequency , DayCountConvention ,/*Day Count Convention*/ IntpMethod ,/*interpolation method*/ IF_ACC_PRE_FLAG, /*if include accrual premium flag, eg: 1 if ture*/ HazardRateCurve,/* hazard rate curve as input*/ i, /* end index of pre-defined hazard rate curve*/ ./*estimated hazard rate*/ );
382 if (HazardRate<0) then do;
383 /*if implied hazard rate is non-positive then assign 0*/
384 HazardRateCurve[i]=0;
385 end;
386 else
387 do;
388 HazardRateCurve[i]=HazardRate;
389 end;
390 end;
391
392 endsub;
393
394 function PREMIUMLEG_MINUS_PROTECTIONLEG(CDS_SprdCurve[*], /* CDS spread curve in basic point, eg: 140 BP*/ Mat_CDS_SprdCurve[*], /*date of CDS spread curve(in fraction of year, eg: 1/12 represents one month)*/ RecoveryRate, /*Recovery Rate, eg .4 */ DefaultFrequency, /*Default Frequency, eg :"month"=1/12, "quater"=.25,"seminannual"=.5,"annual"=1*/ DiscountFactorCurve[*],/*Discount factor curve*/ Mat_DiscountFactorCurve[*],/*maturity of discount factor curve*/ PremiumFrequency $, DayCountConvention $,/*Day Count Convention*/ IntpMethod $,/*interpolation method*/ IF_ACC_PRE_FLAG, /*if include accrual premium flag, eg: 1 if ture*/ bHazardRateCurve[*],/* hazard rate curve as input*/ EndIndexofHazardRateCurve, /* end index of pre-defined hazard rate curve*/ EstimatedHazardRate/*estimated hazard rate*/)
395 label="Computes the difference between premium leg and protection leg values in a credit default swap, given a hazard rate";
396 /*generate the survival probability up to EndIndexofHazardRateCurve*/
397 EndDate=Mat_CDS_SprdCurve[EndIndexofHazardRateCurve];
398 /*date of estimated hazard rate*/
399 /*reallocate the hazard rate curve from input*/
400 array HazardRateCurve[1]/nosym;
401 call DYNAMIC_ARRAY(HazardRateCurve,EndIndexofHazardRateCurve);
402 do i=1 to DIM(HazardRateCurve);
403 HazardRateCurve[i]=bHazardRateCurve[i];
404 end;
405 HazardRateCurve[EndIndexofHazardRateCurve]=EstimatedHazardRate;
406 /*assign estimated hazard rate*/
407 /*reallocate matuirty date of hazard rate curve*/
408 array Mat_HazardRateCurve[1]/nosym;
409 call DYNAMIC_ARRAY(Mat_HazardRateCurve,EndIndexofHazardRateCurve);
410 do i=1 to DIM(Mat_HazardRateCurve);
411 Mat_HazardRateCurve[i]=Mat_CDS_SprdCurve[i];
412 end;
413 /*calculate surivival prbability for protection leg*/
414 szSurvProb=round(EndDate/DefaultFrequency,1);
415 array ProtectionLeg_SurvProb[1]/nosym;
416 call DYNAMIC_ARRAY(ProtectionLeg_SurvProb,szSurvProb+1);
417 ProtectionLeg_SurvProb[1]=1;
418 /*Survival Probability at time zero = 1*/
419 do i=1 to DIM(ProtectionLeg_SurvProb)-1;
420 ProtectionLeg_SurvProb[i+1]=rsk_calc_survival_prob(HazardRateCurve, Mat_HazardRateCurve, i*DefaultFrequency);
421 end;
422 /*calculate the protection leg*/
423 ProtectionLeg= getProtectionLeg(RecoveryRate, DiscountFactorCurve, Mat_DiscountFactorCurve, ProtectionLeg_SurvProb, DefaultFrequency, IntpMethod);
424 /*calculate surivival probability for premium leg*/
425 Premium_Time_Increment=rsk_get_frequency_in_year(1,PremiumFrequency);
426 szSurvProb=round(EndDate/Premium_Time_Increment,1);
427 array PremiumLeg_SurvProb[1]/nosym;
428 call DYNAMIC_ARRAY(PremiumLeg_SurvProb,szSurvProb+1);
429 PremiumLeg_SurvProb[1]=1;
430 /*Survival Probability at time zero = 1*/
431 do i=1 to DIM(PremiumLeg_SurvProb)-1;
432 PremiumLeg_SurvProb[i+1]=rsk_calc_survival_prob(HazardRateCurve, Mat_HazardRateCurve, i*Premium_Time_Increment);
433 end;
434 /*calculate premium leg*/
435 CDS_Sprd=CDS_SprdCurve[EndIndexofHazardRateCurve];
436 PremiumLeg=getPremiumLeg(CDS_Sprd, DiscountFactorCurve, Mat_DiscountFactorCurve, PremiumLeg_SurvProb, PremiumFrequency, IF_ACC_PRE_FLAG, IntpMethod);
437 diff=PremiumLeg-ProtectionLeg;
438 return (diff);
439
440 endsub;
441
442 function RSK_GET_FREQUENCY_IN_YEAR(Frequency_Interval_NO,Frequency_Interval_Unit $)
443 label="Calculates the period length in years, given the period description";
444 freq_int_unit = upcase(Frequency_Interval_Unit);
445 select (freq_int_unit);
446 when ('SECOND', 'SECONDS')
447 do;
448 freq=1/31557600;
449 end;
450 when ('MINUTE', 'MINUTES')
451 do;
452 freq=1/525960;
453 end;
454 when ('HOUR', 'HOURS')
455 do;
456 freq=1/8766;
457 end;
458 when ('DAY', 'DAYS')
459 do;
460 freq=1/365.25;
461 end;
462 when ('WEEKDAY')
463 do;
464 freq=1/260;
465 end;
466 when ('WEEK', 'WEEKS')
467 do;
468 freq=7/365.25;
469 end;
470 when ('SEMIMONTH')
471 do;
472 freq=1/24;
473 end;
474 when ('MONTH', 'MONTHS')
475 do;
476 freq=1/12;
477 end;
478 when ('QUARTER', 'QUARTERS')
479 do;
480 freq=.25;
481 end;
482 when ('SEMIANNUAL')
483 do;
484 freq=.5;
485 end;
486 when ('ANNUAL','YEAR','YEARS')
487 do;
488 freq=1;
489 end;
490 end;
491 freq=freq*Frequency_Interval_NO;
492 return (freq);
493
494 endsub;
495
496
497
498 function RSK_GET_DAY_OF_YEAR(DayCountConvention $)
499 label="Returns the number of days in a year, given the day counting convention";
500 select (DayCountConvention);
501 when ('ACT/360')
502 do;
503 DayofYear=360;
504 end;
505 when ('ACT/366')
506 do;
507 DayofYear=366;
508 end;
509 when ('30/360')
510 do;
511 DayofYear=360;
512 end;
513 when ('ACT/360')
514 do;
515 DayofYear=360;
516 end;
517 otherwise
518 do;
519 DayofYear=365;
520 end;
521 end;
522 return (DayofYear);
523
524 endsub;
525
526 function GETPREMIUMLEG(CDS_Spread, /*CDS spread*/ DiscountFactorCurve[*],/*dicsount factor curve*/ Mat_DiscountFactorCurve[*],/*maturity date of discount factor curve*/ SurvProb[*],/*survival probability*/ PremiumFrequency $,/*premium frequency*/ IF_ACC_PRE_FLAG,/*accrual premium flag. ie 1, if include accrual premium*/ IntpMethod $/*interpolation method*/)
527 label="Values premium leg of a credit default swap";
528 PremiumLeg=0;
529 Premium_Time_Increment=rsk_get_frequency_in_year(1,PremiumFrequency);
530 do i=1 to DIM(SurvProb)-1;
531 /*if allow accrual premium*/
532 TmpProb=0;
533 if (IF_ACC_PRE_FLAG eq 1) then do;
534 TmpProb=0.5*(SurvProb[i]-SurvProb[i+1]);
535 end;
536 TmpProb=TmpProb+SurvProb[i+1];
537 PremiumLeg=PremiumLeg+Premium_Time_Increment*TmpProb*rsk_intpolate2(i*Premium_Time_Increment,DiscountFactorCurve,Mat_DiscountFactorCurve,IntpMethod);
538 end;
539 PremiumLeg=PremiumLeg*CDS_Spread;
540 return (PremiumLeg);
541
542 endsub;
543
544
545 function GETPROTECTIONLEG(RecoveryRate, /*Recovery Rate*/ DiscountFactorCurve[*],/*Discount Factor Curve*/ Mat_DiscountFactorCurve[*],/*Maturity date of discount factor curve*/ SurvProb[*],/* survival probability */ DefaultFrequency, /*default frequency*/ IntpMethod $/*interpolation method*/)
546 label="Values protection leg of a credit default swap";
547 ProtectionLeg=0;
548 do i=1 to DIM(SurvProb)-1;
549 ProtectionLeg=ProtectionLeg+(SurvProb[i]-SurvProb[i+1])*rsk_intpolate2(i*DefaultFrequency,DiscountFactorCurve,Mat_DiscountFactorCurve,IntpMethod);
550 end;
551 ProtectionLeg=ProtectionLeg*(1-RecoveryRate);
552 return (ProtectionLeg);
553
554 endsub;
555
556 function RSK_CALC_SURVIVAL_PROB(HazardRateCurve[*], Mat_HazardRateCurve[*], TimePoint)
557 label="Calculates survival probability from a hazard rate curve";
558 if (TimePoint<=Mat_HazardRateCurve[1]) then do tHazardRate=HazardRateCurve[1]*TimePoint;
559 end;
560 else
561 do tHazardRate=HazardRateCurve[1]*Mat_HazardRateCurve[1];
562 do i=2 to DIM(Mat_HazardRateCurve);
563 if(TimePoint>=Mat_HazardRateCurve[i]) then
564 do;
565 tHazardRate=tHazardRate+HazardRateCurve[i]*(Mat_HazardRateCurve[i]-Mat_HazardRateCurve[i-1]);
566 end;
567 else
568 do;
569 tHazardRate=tHazardRate+HazardRateCurve[i]*(TimePoint-Mat_HazardRateCurve[i-1]);
570 GOTO OUTLOOP;
571 end;
572 end;
573 OUTLOOP: if(TimePoint>Mat_HazardRateCurve[DIM(Mat_HazardRateCurve)]) then
574 do;
575 tHazardRate=tHazardRate+HazardRateCurve[DIM(Mat_HazardRateCurve)]*(TimePoint-Mat_HazardRateCurve[DIM(Mat_HazardRateCurve)]);
576 end;
577 end;
578 SurvProb=exp(-1*tHazardRate);
579 return (SurvProb);
580
581 endsub;
582
583 %mend;