SAS Documentation
SASĀ® Solution for Stress Testing
Reference manual - version 08.2021
Loading...
Searching...
No Matches
irmst_market_risk_funs_def_1.sas
1%macro irmst_market_risk_funs_def_1;
2
3
4
5 function RSK_EUROPEANCALLOPTION_PF(Price,Strike,RiskFreeRate,YieldParam,TimeToExpiration,Volatility)
6 label="European call option pricing by Black and Scholes (1973)";
7/**************************************************************************/
8/* ERROR CHECKING
9 /**************************************************************************/
10 IF TimeToExpiration lt 0 THEN return(0);
11 IF TimeToExpiration eq 0 THEN return (max(Price-Strike,0));
12/* Initialize the return missing flag to zero
13 If we find an error in one of the inputs, we will return missing */
14/* Set the function name for error reporting */
15 ReturnMissingFlg = 0;
16 Fname = 'rsk_europeancalloption_pf';
17/* Make sure some inputs are positive or nonnegative*/
18/* Negative and missing values for those inputs may cause problems
19 if input into another function (e.g. the logarithm function) */
20 ReturnMissingFlg = rsk_check_num_missing_pf( RiskFreeRate, Fname, '3', 'RiskFreeRate', ReturnMissingFlg );
21 ReturnMissingFlg = rsk_check_num_missing_pf( YieldParam, Fname, '4', 'YieldParam', ReturnMissingFlg );
22 ReturnMissingFlg = rsk_check_num_missing_pf( TimeToExpiration, Fname, '5', 'TimeToExpiration', ReturnMissingFlg );
23 ReturnMissingFlg = rsk_check_num_missing_pf( Price, Fname, '1', 'Price', ReturnMissingFlg );
24 ReturnMissingFlg = rsk_check_nonpositive_pf( Strike, Fname, '2', 'Strike', ReturnMissingFlg );
25 ReturnMissingFlg = rsk_check_nonpositive_pf( Volatility, Fname, '6', 'Volatility', ReturnMissingFlg );
26/* Return missing, if errors are found */
27 if ReturnMissingFlg eq 1 then return(.);
28/************************************************************/
29/* RESUME STANDARD PRICING
30 /************************************************************/
31 CostofCarry=RiskFreeRate - YieldParam;
32 g = Volatility*sqrt(TimeToExpiration);
33 d1 = (log(Price / Strike) + CostofCarry * TimeToExpiration )/g + 0.5 * g;
34 d2 = d1 - g;
35 OptionPrice = Price * exp( -YieldParam * TimeToExpiration) * probnorm(d1) - Strike * exp(-RiskFreeRate * TimeToExpiration) * probnorm(d2);
36 return(OptionPrice);
37
38 endsub;
39
40
41
42 function RSK_DAYCOUNT(Convention $,BeginDate,EndDate)
43 label="Calculates the number of years between two dates, given a day counting convention";
44 /* Create a local copy of this pass-through character value */
45 /* This will make the function more efficient */
46 length local_convention $32;
47 local_convention = Convention;
48 /* Default value to ACT/365 */
49 if missing(local_convention) then local_convention = 'ACT/365';
50 /* Return zero if EndDate before BeginDate or missing either date */
51 if EndDate le BeginDate or missing(BeginDate) then return(0);
52 /* Handle cases, for each convention */
53 select(local_convention);
54 /* ACT/360 must be computed by this function */
55 /* Otherwise, use yrdif function */
56 when ('ACT/366')
57 do;
58 num = EndDate - BeginDate ;
59 den = 366;
60 returnval = num / den;
61 end;
62 otherwise
63 do;
64 returnval = yrdif( BeginDate, EndDate, local_convention );
65 end;
66 end;
67 /* Return the value */
68 return(returnval);
69
70 endsub;
71
72
73
74 function RSK_INTPOLATE(refdate , /* reference date */ matdate , /* maturity date */ Convention $, Curve[*], /* yield curve */ CurveMat[*], /* maturity curve */ IntpMethod $)
75 label="Interpolates on a curve, given the reference and maturity dates";
76 dim = dim(Curve);
77 /* Create a local copy of this pass-through character value */
78 /* This will make the function more efficient */
79 length local_convention upIntpMethod $32;
80 local_convention = Convention;
81 upIntpMethod = upcase(IntpMethod);
82 period = rsk_daycount(local_convention,Refdate,matdate);
83 IntVal = rsk_intpolate2( period, Curve, CurveMat, upIntpMethod );
84 return ( IntVal );
85
86 endsub;
87
88
89
90 function RSK_CALC_NEXT_DATE(RefDate, Nmbr_Mth)
91 label="Calculates the next date using a fixed months for the period";
92 BeginMonth = intnx('month',RefDate,Nmbr_Mth);
93 DayMax = day (intnx ('month',BeginMonth ,1)-1);
94 if day(RefDate) le DayMax then
95 Sameday_Next = mdy (month(BeginMonth) , day(RefDate), year(BeginMonth));
96 else
97 sameday_next = mdy (month(BeginMonth) , DayMax, year(BeginMonth));
98 return (sameday_next);
99
100 endsub;
101
102
103 function RSK_IMPLIED_FWD_RATE(RefDate, fwd_date, fwd_dateto, Convention $, Curve [*], CurveMat[*],IntpMethod $)
104 label="Computes a single forward rate, given the spot rates (continuous compounding)";
105 /* Create a local copy of this pass-through character value */
106 /* This will make the function more efficient */
107 length local_convention $32;
108 local_convention = Convention;
109 period1 = rsk_daycount(local_convention,Refdate,fwd_date);
110 *t0-ti;
111 period2 = rsk_daycount(local_convention,Refdate,fwd_dateto);
112 *t0-ti+1;
113 rate1 = rsk_intpolate2( period1, Curve, CurveMat, IntpMethod);
114 *RT1;
115 rate2 = rsk_intpolate2( period2, Curve, CurveMat,IntpMethod );
116 *RT2;
117 forward_period = period2-period1;
118 /* If forward period is positive, use the calculation */
119 /* If forward period is zero, just set it to rate1 */
120 /* Otherwise (negative forward period), set to missing */
121 sqrtmaceps = constant('SQRTMACEPS');
122 if forward_period > sqrtmaceps and refdate le fwd_date then do;
123 fwd_rate = (1+rate2)**(period2/forward_period) / (1+rate1)**(period1/forward_period) - 1;
124 end;
125 else
126 if abs(forward_period) le sqrtmaceps then fwd_rate = rate1;
127 else
128 fwd_rate=.;
129 return (fwd_rate);
130
131 endsub;
132
133
134
135
136
137 function RSK_INTPOLATE2(Period, Curve[*], CurveMat[*], IntpMethod $)
138 label="Interpolates on a curve, given the target maturity in years";
139 /**************************************************************************/
140 /* ERROR CHECKING
141 /**************************************************************************/
142 dim = dim(Curve);
143 /* Initialize the return missing flag to zero
144 If we find an error in one of the inputs, we will return missing */
145 /* Set the function name for error reporting */
146 ReturnMissingFlg = 0;
147 Fname = 'rsk_intpolate2';
148 /* Make sure 'Period' is nonmissing */
149 /* Missing values for 'Period' may cause problems
150 if input into another function (e.g. the logarithm function) */
151 ReturnMissingFlg = rsk_check_num_missing_pf( Period, Fname, '1', 'Period', ReturnMissingFlg );
152 /* Make sure CurveMat array is nonmissing and nondecreasing */
153 ReturnMissingFlg = rsk_check_array_pf( CurveMat, dim, 'MISSING', Fname, '3', 'CurveMat', ReturnMissingFlg );
154 ReturnMissingFlg = rsk_check_array_pf( CurveMat, dim, 'UNORDERED', Fname, '3', 'CurveMat', ReturnMissingFlg );
155 /* Return missing, if errors are found */
156 if ReturnMissingFlg eq 1 then return(.);
157 /************************************************************/
158 /* RESUME INTPOLATION
159 /************************************************************/
160 if IntpMethod eq 'LOG' then do;
161 /* Interpolation method = LOGLINEAR*/
162 j = rsk_find_right( period, CurveMat );
163 if j eq 1 then IntVal = Curve{1};
164 else
165 if j > dim then IntVal = Curve{dim};
166 else
167 IntVal = Curve{j-1} * ( ( Curve{j} / Curve{j-1} ) ** ( ( period - CurveMat{j-1} ) / ( CurveMat{j} - CurveMat{j-1} ) ) );
168 end;
169 else
170 if IntpMethod eq 'CUBIC' then do;
171 /* Interpolation method = CUBIC (calculation of natural cubic spline coefficients)*/
172 /* linear interpolation at endpoints used */
173 if ( period <= CurveMat{1} ) then IntVal = Curve{1};
174 else
175 if ( period >= CurveMat{dim} ) then IntVal = Curve{dim};
176 else
177 do;
178 Array M_temp[1] /nosym;
179 Array N_temp[1] /nosym;
180 Array Q_temp[1] /nosym;
181 Array A_temp[1] /nosym;
182 Array B_temp[1] /nosym;
183 Array D_temp[1] /nosym;
184 Array AA_temp[1] /nosym;
185 Array BB_temp[1] /nosym;
186 Array CC_temp[1] /nosym;
187 CALL DYNAMIC_ARRAY(M_temp,dim);
188 CALL DYNAMIC_ARRAY(N_temp,dim);
189 CALL DYNAMIC_ARRAY(Q_temp,dim);
190 CALL DYNAMIC_ARRAY(A_temp,dim);
191 CALL DYNAMIC_ARRAY(B_temp,dim);
192 CALL DYNAMIC_ARRAY(D_temp,dim);
193 CALL DYNAMIC_ARRAY(AA_temp,dim);
194 CALL DYNAMIC_ARRAY(BB_temp,dim);
195 CALL DYNAMIC_ARRAY(CC_temp,dim);
196 /* Initialize */
197 M_temp[1]=CurveMat[2] - CurveMat[1];
198 N_temp[1]=Curve[2] - Curve[1];
199 A_temp[1]=1;
200 B_temp[1]=0;
201 D_temp[1]=0;
202 /* end initialize */
203 do i=2 to dim-1;
204 M_temp[i]= CurveMat[i+1] - CurveMat[i];
205 N_temp[i]= Curve[i+1] - Curve[i];
206 Q_temp[i]= 3*( (N_temp[i] / M_temp[i] ) - (N_temp[i-1] / M_temp[i-1] ) );
207 A_temp[i]= 2* (M_temp[i-1] + M_temp[i]) - M_temp[i-1] * B_temp[i-1];
208 B_temp[i]= M_temp[i] / A_temp[i];
209 D_temp[i]= ( Q_temp[i] - M_temp[i-1] * D_temp[i-1] ) / A_temp[i];
210 end;
211 /* Initialize */
212 A_temp[dim]=0;
213 BB_temp[dim]=0;
214 D_temp[dim]=0;
215 /* end initialize */
216 do i=(dim-1) to 1 BY -1;
217 BB_temp[i]= D_temp[i] - B_temp[i] * BB_temp[i+1];
218 AA_temp[i]= (N_temp[i] / M_temp[i] ) - ( M_temp[i] / 3* ( BB_temp[i+1] + 2*BB_temp[i] ) );
219 CC_temp[i]= (BB_temp[i+1] - BB_temp[i]) / (3*M_temp[i]);
220 end;
221 j = rsk_find_right( period, CurveMat );
222 if j eq 1 then IntVal = Curve{1};
223 else
224 if j > dim then IntVal = Curve{dim};
225 else
226 do;
227 j = j - 1;
228 IntVal = Curve{j} + ( AA_temp{j} * ( period - CurveMat{j} ) ) + ( BB_temp{j} * ( period - CurveMat{j} )**2 ) + ( CC_temp{j} * ( period - CurveMat{j} )**3 );
229 end;
230 end;
231 end;
232 else
233 if IntpMethod eq 'STEP' then do;
234 j = rsk_find_left( period, CurveMat );
235 j = max(j,1);
236 IntVal = Curve[j];
237 end;
238 else
239 if IntpMethod eq 'FORWARD_RATE' then do;
240 j = rsk_find_left( period, CurveMat );
241 if j < 1 then IntVal = Curve[1];
242 else
243 if j eq dim(CurveMat) then IntVal = Curve[j];
244 else
245 do;
246 timediff = period - CurveMat[j];
247 if abs(timediff) le constant('SQRTMACEPS') then IntVal = Curve[j];
248 else
249 do;
250 fwdperiod = CurveMat[j+1] - CurveMat[j];
251 r1plus1 = 1+Curve[j];
252 forward_rate_plus1 = (1+Curve[j+1])**(CurveMat[j+1]/fwdperiod) / r1plus1**(CurveMat[j]/fwdperiod);
253 IntVal = r1plus1**(CurveMat[j]/period) * forward_rate_plus1 ** (timediff/period)-1;
254 end;
255 end;
256 end;
257 else
258 do;
259 /* Interpolation method = LINEAR*/
260 j = rsk_find_right( period, CurveMat );
261 if j eq 1 then IntVal = Curve{1};
262 else
263 if j > dim then IntVal = Curve{dim};
264 else
265 IntVal = Curve{j-1} + ( ( Curve{j} - Curve{j-1} ) * ( period - CurveMat{j-1} ) / ( CurveMat{j} - CurveMat{j-1} ) );
266 end;
267 return ( IntVal );
268
269 endsub;
270
271
272 function RSK_CHECK_NONPOSITIVE_PF(VariableValue, FunctionName $, VariableNum $, VariableName $, ErrorFoundFlag)
273 label="Checks a value to verify that it is positive";
274 /* Check missing: if found, print error message and return 1 */
275 if missing(VariableValue) then do;
276 call rsk_print_error_msg_and_abort( 'rsk_func_missing_inputs_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
277 return(1);
278 end;
279 /* Check nonpositive (to machine accuracy): if found, print error message and return 1 */
280 if VariableValue le constant('SQRTMACEPS') then do;
281 call rsk_print_error_msg_and_abort( 'rsk_func_nonpos_inputs_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
282 return(1);
283 end;
284 /* If no errors found, return previous error status,
285 or if no previous error status given, then set the status to zero (no error) */
286 return(coalesce(ErrorFoundFlag,0));
287
288 endsub;
289
290
291 function RSK_CHECK_NUM_MISSING_PF(VariableValue, FunctionName $, VariableNum $, VariableName $, ErrorFoundFlag)
292 label="Checks a value to verify that it is nonmissing";
293 /* Check missing: if found, print error message and return 1 */
294 if missing(VariableValue) then do;
295 call rsk_print_error_msg_and_abort( 'rsk_func_missing_inputs_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
296 return(1);
297 end;
298 /* If no errors found, return previous error status,
299 or if no previous error status given, then set the status to zero (no error) */
300 return(coalesce(ErrorFoundFlag,0));
301
302 endsub;
303
304
305
306 subroutine RSK_PRINT_ERROR_MSG_AND_ABORT(key $, s1 $, s2 $, s3 $, s4 $, s5 $, s6 $, s7 $)
307 label="Prints a message to the log, with call stack, and then aborts the current position";
308 /* Set the maximum length of the message */
309 length msg $1024;
310 /* hard code the maximum allowed messages to fifty */
311 maxputs = 50;
312 /* Get how many messages have been printed so far, default to zero */
313 /* Note: although this function will set RSK_LOG_MESSAGE_COUNT to zero, it is strongly recommended
314 that the user initialize RSK_LOG_MESSAGE_COUNT to zero in the init block of a project method */
315 array message_count[1] / nosym;
316 call dynamic_array(message_count,.);
317 if missing(message_count[1]) then message_count[1]=1;
318 else
319 message_count[1]=message_count[1]+1;
320 numputs=message_count[1];
321 /* If we have not exceeded the number of allowed messages, then get and
322 print this message, then abort using "assert" */
323 if numputs le maxputs then do;
324 msg = rsk_get_msg_subr(key, s1, s2, s3, s4, s5, s6, s7);
325 call assert(0, trim(msg));
326 end;
327 /* If we have reached the maximum messages, send the message to let the user know that no more messages will be output. */
328 if numputs eq maxputs+1 then do;
329 msg = rsk_get_msg_subr('rsk_exceed_msg_limit_warning', 'NOQUOTE', '', '', '', '', '', '');
330 file log;
331 put msg;
332 file print;
333 end;
334
335 endsub;
336
337
338
339 function RSK_FIND_LEFT(value,arr[*])
340 label="Finds closest element of a sorted array which is less than or equal to the input value";
341 cap = dim(arr);
342 up = cap+1;
343 lp = 1;
344 do while( up-lp > 0 );
345 mp = floor((up+lp)/2);
346 if arr[mp] > value then up = mp;
347 else
348 if mp eq cap then return(cap);
349 else
350 if arr[mp+1] le value then lp = mp + 1;
351 else
352 return(mp);
353 end;
354 return(0);
355
356 endsub;
357
358
359 function RSK_FIND_RIGHT(value,arr[*])
360 label="Finds closest element of a sorted array which is greater than or equal to the input value";
361 cap1 = dim(arr)+1;
362 up = cap1;
363 lp = 1;
364 do while( up-lp > 0 );
365 mp = floor((up+lp)/2);
366 if arr[mp] < value then lp = mp + 1;
367 else
368 if mp eq 1 then return(1);
369 else
370 if arr[mp-1] ge value then up = mp;
371 else
372 return(mp);
373 end;
374 return(cap1);
375
376 endsub;
377
378 function RSK_CHECK_ARRAY_PF(InputArray[*], ArraySize, CheckType $, FunctionName $, VariableNum $, VariableName $, ErrorFoundFlag)
379 label="Checks the values of a one-dimensional array to verify they meet expected criteria";
380 /* Check array: if offense found, print error message and return 1 */
381 newsize = min(ArraySize,dim(InputArray));
382 i = .;
383 tempvalue = coalesce(InputArray[1],0);
384 if CheckType eq 'NONPOSITIVE' then do i = 1 to newsize while( InputArray[i] > 0 );
385 end;
386 else
387 if CheckType eq 'NEGATIVE' then do i = 1 to newsize while( InputArray[i] ge 0 );
388 end;
389 else
390 if CheckType eq 'MISSING' then do i = 1 to newsize while( not missing(InputArray[i]) );
391 end;
392 else
393 if CheckType eq 'UNORDERED' then do i = 1 to newsize while( tempvalue le InputArray[i] );
394 tempvalue = InputArray[i];
395 end;
396 if i le newsize and not missing(i) then do;
397 if CheckType eq 'NONPOSITIVE' then do;
398 call rsk_print_error_msg_and_abort( 'rsk_func_nonpos_array_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
399 end;
400 else
401 if CheckType eq 'NEGATIVE' then do;
402 call rsk_print_error_msg_and_abort( 'rsk_func_neg_array_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
403 end;
404 else
405 if CheckType eq 'MISSING' then do;
406 call rsk_print_error_msg_and_abort( 'rsk_func_miss_array_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
407 end;
408 else
409 if CheckType eq 'UNORDERED' then do;
410 call rsk_print_error_msg_and_abort( 'rsk_func_order_array_error', FunctionName, VariableNum, VariableName, '', '', '', '' );
411 end;
412 return(1);
413 end;
414 /* If no errors found, return previous error status,
415 or if no previous error status given, then set the status to zero (no error) */
416 return(coalesce(ErrorFoundFlag,0));
417
418 endsub;
419
420
421 function RSK_GET_MSG_SUBR(key $, s1 $, s2 $, s3 $, s4 $, s5 $, s6 $, s7 $)
422 label="Fetches and returns a translated message";
423 /* Set the maximum length of the message */
424 length msg $1024;
425 /* Depending on how many inputs were supplied, call sasmsgl differently to get the message */
426 /* We are assuming that s1-s7 are populated in that order (i.e. s2 is not populated when s1 is missing ) */
427 msgfile = 'sashelp.rmbutilmsg';
428 if not missing(s7) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1), trim(s2), trim(s3), trim(s4), trim(s5), trim(s6), trim(s7) );
429 else
430 if not missing(s6) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1), trim(s2), trim(s3), trim(s4), trim(s5), trim(s6) );
431 else
432 if not missing(s5) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1), trim(s2), trim(s3), trim(s4), trim(s5) );
433 else
434 if not missing(s4) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1), trim(s2), trim(s3), trim(s4) );
435 else
436 if not missing(s3) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1), trim(s2), trim(s3) );
437 else
438 if not missing(s2) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1), trim(s2) );
439 else
440 if not missing(s1) then msg = sasmsgl( msgfile, key, 'en', 'NOQUOTE', trim(s1) );
441 else
442 msg = sasmsgl( msgfile, key, 'en','NOQUOTE' );
443 /* Print the message */
444 return(msg);
445
446 endsub;
447
448
449
450 function RSK_CALC_CF_DUR(PV, CFAmt[*], CFMat[*], CFNum, Yield, START_DT)
451 label="Calculates duration of a series of cash flows";
452 if(PV le 0 or CFNUM le 0) then
453 return (0);
454 /*Convert maturity date array in fraction of year*/
455 array _MAT_DATE_[1]/nosym;
456 call DYNAMIC_ARRAY(_MAT_DATE_,CFNUM);
457 if START_DT ne . then do;
458 do i=1 to CFNUM;
459 _MAT_DATE_[i]=(CFMat[i]-START_DT)/365.25;
460 end;
461 end;
462 /*end if*/
463 else
464 do;
465 do i=1 to CFNUM;
466 _MAT_DATE_[i]=CFMat[i];
467 end;
468 end;
469 /*end else*/
470 CFSum=0;
471 do i=1 to CFNum;
472 CFSum=CFSum+_MAT_DATE_[i]*CFAmt[i]*exp(-1*Yield*_MAT_DATE_[i]);
473 end;
474 Duration=CFSum/PV;
475 return (Duration);
476
477 endsub;
478
479
480 function RSK_CALC_CF_YIELD(PV, CFAmt[*], CFMat[*], CFNum, START_DT)
481 label="Calculates yield of a series of cash flows";
482 /*Convert maturity date array in fraction of year*/
483 if(CFNUM>0) then
484 do;
485 array _MAT_DATE_[1]/nosym;
486 call DYNAMIC_ARRAY(_MAT_DATE_,CFNUM);
487 if START_DT ne . then do;
488 do i=1 to CFNUM;
489 _MAT_DATE_[i]=(CFMat[i]-START_DT)/365.25;
490 end;
491 end;
492 /*end if*/
493 else
494 do;
495 do i=1 to CFNUM;
496 _MAT_DATE_[i]=CFMat[i];
497 end;
498 end;
499 /*end else*/
500 end;
501 /*end if*/
502 else
503 do;
504 return(0);
505 end;
506 /*end else*/
507 array solvopts[1] initial (0.20);
508 Yield=solve("PV_Minus_Cashflow", solvopts, 0, PV, CFAmt, _MAT_DATE_, CFNum, ./*estimated Yield*/);
509 return (Yield);
510
511 endsub;
512
513 function RSK_PV_CSHFLW_LAMBDA(N,ValDate, Date[*], Amount[*], Convention $, ZCCurve[*],ZCCMAT[*],SPREADCurve[*],SPREADMat[*],RHO, LAMBDA, LAMBDA_IDIO, ZCIntMeth $, SPREADIntMeth $)
514 label="Cashflow present value risk premia";
515 PresentVal=0;
516 /* Create a local copy of this pass-through character value */
517 /* This will make the function more efficient */
518 length local_convention $32;
519 local_convention = Convention;
520 do i = 1 to N;
521 p_year=rsk_daycount(local_convention,ValDate,Date[i]);
522 if p_year gt 0 then do;
523 _a1_=probit(rsk_intpolate2(p_year,SPREADCurve,SPREADMat,SPREADIntMeth));
524 _a2_=probnorm( _a1_ + (RHO*LAMBDA+LAMBDA_IDIO)*sqrt(SPREADMat[i]));
525 _a3_=log(1-_a2_);
526 _a_=(-1/SPREADMat[i])*_a3_;
527 r = sum(rsk_intpolate2(p_year,ZCCURVE,ZCCMAT,ZCIntMeth),_a_);
528 d = exp( -p_year*r);
529 PresentVal = PresentVal + d*(Amount[i]);
530 end;
531 end;
532 return (PresentVal);
533
534 endsub;
535
536
537
538
539 function PV_MINUS_CASHFLOW(PV, /* Present Value of cashflow*/ CFAmt[*], /* amount of cashflow*/ CFMat[*], /* maturity of cashflow in fraction of year*/ CFNum, /* number of cashflow*/ EstimatedYield/*estimated yield*/)
540 label="Difference between present value of a series of cash flows and the sum of the cash flows";
541 SumCFAmt=0;
542 do i=1 to CFNum;
543 SumCFAmt=SumCFAmt+CFAmt[i]*exp(-1*EstimatedYield*CFMat[i]);
544 end;
545 diff=PV-SumCFAmt;
546 return (diff);
547
548 endsub;
549
550 function RSK_PV_CSHFLW(N, ValDate, Date[*], Amount[*], Convention $, ZCCurve[*], ZCCMAT[*], Spread, IntpMethod $)
551 label="Calculates the present value of a series of cash flows";
552 /**************************************************************************/
553 /* ERROR CHECKING
554 /**************************************************************************/
555 /* Initialize the return missing flag to zero
556 If we find an error in one of the inputs, we will return missing */
557 /* Set the function name for error reporting */
558 ErrorFoundFlg = 0;
559 Fname = 'rsk_pv_cshflw';
560 /* Check inputs */
561 ErrorFoundFlg = rsk_check_num_missing_pf( N, Fname, '1', 'N', ErrorFoundFlg );
562 ErrorFoundFlg = rsk_check_num_missing_pf( ValDate, Fname, '2', 'ValDate', ErrorFoundFlg );
563 ErrorFoundFlg = rsk_check_array_pf( Date, N, 'MISSING', Fname, '3', 'Date', ErrorFoundFlg );
564 ErrorFoundFlg = rsk_check_array_pf( Amount, N, 'MISSING', Fname, '4', 'Amount', ErrorFoundFlg );
565 ErrorFoundFlg = rsk_check_array_pf( ZCCurve, dim(ZCCurve), 'MISSING', Fname, '6', 'ZCCurve', ErrorFoundFlg );
566 ErrorFoundFlg = rsk_check_array_pf( ZCCMAT, dim(ZCCMAT), 'UNORDERED', Fname, '7', 'ZCCMAT', ErrorFoundFlg );
567 ErrorFoundFlg = rsk_check_num_missing_pf( Spread, Fname, '8', 'Spread', ErrorFoundFlg );
568 /* Return missing, if errors are found */
569 if ErrorFoundFlg eq 1 then return(.);
570 /************************************************************/
571 /* RESUME STANDARD PRICING
572 /************************************************************/
573 /* Create a local copy of this pass-through character value */
574 /* This will make the function more efficient */
575 length local_convention $32;
576 local_convention = Convention;
577 PresentVal=0;
578 do i = 1 to N;
579 p = Date[i] - ValDate;
580 p_year=rsk_daycount(local_convention,ValDate,Date[i]);
581 if p gt 0 then do;
582 r = rsk_intpolate2( p_year,ZCCURVE,ZCCMAT,IntpMethod) + SPREAD;
583 d = exp( -p_year*r);
584 PresentVal = PresentVal + d*(Amount[i]);
585 end;
586 end;
587 return (PresentVal);
588
589 endsub;
590
591
592 function RSK_BINARY_DOUBLE_BARRIEROPT_PF(Binary_barrertype $, Price, Price_Chk_Upper, Price_Chk_Lower, Lower_Barrier, Upper_Barrier, Cash_Amt, RiskFreeRate, YieldParam, TimeToExpiration, Volatility)
593 label="European binary double barrier option pricing by Hui (1996)";
594 IF TimeToexpiration < 0 THEN return(0);
595 /* TimeToexpiration eq 0 is handled in cases below.
596 Since there is no divide by TimeToexpiration, this is okay */
597 /* BarrierType:
598 Internal_Binary_Barrier_Cd = '1': Binary_barrertype='uidi'
599 Knock-in one touch double barrier pays Cash_Amt at maturity if asset
600 price touches one of the barriers, zero else
601 Internal_Binary_Barrier_Cd = '2': Binary_barrertype='uodo'
602 Knock-out one touch double barrier pays Cash_Amt at maturity if asset
603 price does NOT touch one of the barriers, zero else
604 Internal_Binary_Barrier_Cd = '3': Binary_barrertype='uodi'
605 Knock-out asymmetrical double barrier is knocked out if the upper
606 barrier is hit. If the asset price hits lower barrier then
607 it pays Cash_Amount immediately (at hit)
608 Internal_Binary_Barrier_Cd = '4': Binary_barrertype='uido'
609 Reversed Knock-out asymmetrical double barrier is knocked out if the
610 lower barrier is hit. If the asset price hits upper barrier then
611 it pays Cash_Amount immediately (at hit)
612 */
613 length Internal_Binary_Barrier_Cd $1;
614 if lowcase(Binary_barrertype) eq 'uidi' then Internal_Binary_Barrier_Cd = '1';
615 else
616 if lowcase(Binary_barrertype) eq 'uodo' then Internal_Binary_Barrier_Cd = '2';
617 else
618 if lowcase(Binary_barrertype) eq 'uodi' then Internal_Binary_Barrier_Cd = '3';
619 else
620 if lowcase(Binary_barrertype) eq 'uido' then Internal_Binary_Barrier_Cd = '4';
621 else
622 return(.);
623 CostofCarry=RiskFreeRate - YieldParam;
624 _c_=100;
625 /* term in sum */
626 IF Internal_Binary_Barrier_Cd eq '1' or Internal_Binary_Barrier_Cd eq '2' THEN DO;
627 OptionPrice=0;
628 Z=log(Upper_Barrier/Lower_Barrier);
629 _alpha_=-CostofCarry/(volatility*volatility) + 0.5;
630 _beta_=-0.25*( ((2*CostofCarry)/(volatility*volatility) -1 )**2 ) - ( 2*RiskFreeRate)/(Volatility*volatility);
631 DO i=1 TO _c_;
632 _tmp_= (2*constant('PI') *i*Cash_Amt) /(Z*Z);
633 _tmp1_=(Price/Lower_Barrier)**_alpha_ - ((-1)**i ) * ((Price/Upper_Barrier)**_alpha_);
634 _tmp2_=_alpha_*_alpha_ + ((i*constant('PI'))/Z)**2;
635 _tmp3_=sin( ( (i*constant('PI')) / Z ) * log(Price/Lower_Barrier) );
636 _tmp4_= (-0.5*( (i*constant('PI')) / Z )**2 +0.5*_beta_)* ( volatility*volatility*TimeToExpiration);
637 OptionPrice=OptionPrice + _tmp_*(_tmp1_/_tmp2_)* _tmp3_ * exp(_tmp4_);
638 END;
639 IF Internal_Binary_Barrier_Cd eq '1' THEN DO;
640 OptionPrice=Cash_Amt*exp(-RiskFreeRate*TimeToExpiration) - OptionPrice;
641 IF Price_Chk_Lower le Lower_Barrier or Price_Chk_Upper ge Upper_Barrier THEN DO;
642 /* option is knocked in */
643 OptionPrice=Cash_Amt*exp(-RiskFreeRate*TimeToExpiration);
644 END;
645 END;
646 IF Internal_Binary_Barrier_Cd eq '2' THEN DO;
647 IF Price_Chk_Lower le Lower_Barrier or Price_Chk_Upper ge Upper_Barrier THEN DO;
648 /* option is knocked out if touches one of the barriers */
649 OptionPrice=0;
650 END;
651 END;
652 END;
653 ELSE
654 DO;
655 Up_Barrier=Upper_Barrier;
656 Lo_Barrier=Lower_Barrier;
657 IF Internal_Binary_Barrier_Cd eq '4' THEN DO;
658 Up_Barrier=Lower_Barrier;
659 Lo_Barrier=Upper_Barrier;
660 END;
661 OptionPrice=0;
662 Z=log(Up_Barrier/Lo_Barrier);
663 _alpha_=-CostofCarry/(volatility*volatility) + 0.5;
664 _beta_=-0.25*( ((2*CostofCarry)/(volatility*volatility) -1 )**2 ) - ( 2*RiskFreeRate)/(Volatility*volatility);
665 DO i=1 TO _c_;
666 _tmp0_=Cash_Amt*((Price/Lo_Barrier)**_alpha_);
667 _tmp_= (2/(constant('PI')*i));
668 _tmp3_=SIN( ( (i*constant('PI')) / Z ) * log(Price/Lo_Barrier) );
669 _tmp4_= (-0.5*( (i*constant('PI')) / Z )**2 +0.5*_beta_)* ( volatility*volatility*TimeToExpiration);
670 _tmpv_=((i*constant('PI'))/Z)**2;
671 _tmpvv_= exp(_tmp4_);
672 _tmpvvv_=((i*constant('PI'))/Z)**2 - _beta_;
673 _tmpu_=(1- (log(Price/Lo_Barrier)) / Z );
674 OptionPrice=OptionPrice + _tmp3_*_tmp_* ( ( _beta_ - _tmpv_*_tmpvv_ ) / _tmpvvv_ );
675 END;
676 OptionPrice=OptionPrice*_tmp0_ + +_tmpu_*_tmp0_;
677 END;
678 /* These are asymmetrical payoffs and only one of them can happen i.e.,
679 the one that happens first is relevant
680 Hence, we introduce the checks _w01_chk_, _w02_chk_,... to ensure that
681 only the first breach is relevant
682 */
683 IF Internal_Binary_Barrier_Cd eq '3' THEN DO;
684 IF Price_Chk_Lower le Lower_Barrier THEN DO;
685 IF _w02_chk_ ne 1 THEN DO;
686 /* if equals 1 then upper barrier has already been breached and
687 wether lower barrier is breached or not is irrelevant */
688 OptionPrice=Cash_Amt;
689 _w01_chk_=1;
690 END;
691 END;
692 IF Price_Chk_Upper ge Upper_Barrier THEN DO;
693 /* option is knocked out if touches upper barrier */
694 IF _w01_chk_ ne 1 THEN DO;
695 /* if equals 1 then lower barrier has already been breached and
696 wether upper barrier is breached or not is irrelevant */
697 OptionPrice=0;
698 _w02_chk_=1;
699 END;
700 END;
701 END;
702 IF Internal_Binary_Barrier_Cd eq '4' THEN DO;
703 IF Price_Chk_Lower le Lower_Barrier THEN DO;
704 /* option is knocked out if touches lower barrier */
705 IF _w04_chk_ ne 1 THEN DO;
706 /* if equals 1 then upper barrier has already been breached and
707 wether lower barrier is breached or not is irrelevant */
708 OptionPrice=0;
709 _w03_chk_=1;
710 END;
711 END;
712 IF Price_Chk_Upper ge Upper_Barrier THEN DO;
713 IF _w03_chk_ ne 1 THEN DO;
714 /* if equals 1 then lower barrier has already been breached and
715 wether upper barrier is breached or not is irrelevant */
716 OptionPrice=Cash_Amt;
717 _w04_chk_=1;
718 END;
719 END;
720 END;
721 return(OptionPrice);
722
723 endsub;
724
725
726
727 %mend;