SAS Documentation
SASĀ® Solution for Stress Testing
Reference manual - version 08.2021
Loading...
Searching...
No Matches
irmst_riskagg_hicopula.sas
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 SAS Institute Inc. Cary, NC, USA
3 */
4
5/**
6 \file
7 \anchor irmst_riskagg_hicopula
8 \anchor irmst_riskagg_hicopula_key
9
10
11
12
13
14
15 \brief hicopula aggregation of risk.
16
17 \param[in] ds_in_configuration_table Input configuration table. It provides information about risk sources to be aggregated and information about the corresponding scenarios.
18 \param[in] ds_in_copula_table Input configuration table of the COPULA functions. It provides information about the copula functions to be used the aggregation.
19 \param[in] ds_in_marginal_table Input configuration table of the marginal distributions. It provides information about the marginal distributions of the hierarchy leaves.
20 \param[in] ds_in_corr_tables List the correlation matrices that have been loaded. These matrices need to match the matrices required by the configuration table <i>DS_IN_COPULA_TABLE</i>.
21 \param[in] working_libname Working LIBNAME. By default, it is set to work. This option is typically used to facilitate debugging.
22 \param[in] in_alpha Probability tail level to evaluate the Value-at-Risk
23 \param[in] in_seed Seed
24 \param[in] in_ndraws Number of simulation draws to be sample from the COPULAs.
25 \param[in] high_agg_level_var Higher level of aggregation variable.
26 \param[in] low_agg_level_var Lower level of aggregation variable.
27 \param[out] ds_out_risk_analysis_table Output table. It contains results about the hicopula aggregation..
28
29
30
31
32
33
34
35
36 \details
37
38 Given an input configuration table (<i>DS_IN_CONFIGURATION_TABLE</i>), a COPULA configuration table (<i>DS_IN_COPULA_TABLE</i>) and marginal distribution configuration table (<i>ds_in_marginal_table</i>),
39 this macro will aggregate the risk sources defined in the configuration table (<i>DS_IN_CONFIGURATION_TABLE</i>) using hierarchical COPULA aggregation.
40 Particular attention is needed when specifying the COPULA configuration table. This table may require COPULA functions which depends on correlation matrices. In order
41 to successfully run this aggregation, the matrices need to be loaded.
42
43 Notice that the aggregation can be performed with respect to different scenarios: the configuration table (<i>DS_IN_CONFIGURATION_TABLE</i>) and the (<i>DS_IN_CORRELATION_TABLE</i>) need to contain a properly specified scenario variable (<i>&lt;SCENARIO_ID&gt;</i>).
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 The structure of the <b><i>DS_IN_CONFIGURATION_TABLE</i></b> table is as follows:
60
61 | Variable | Type | Description |
62 |-----------------------------|-------------------|--------------------------------------------------------------------------|
63 | <i>&lt;SCENARIO_ID&gt;</i> | CHARACTER/NUMERIC | Scenario identifier variable. |
64 | <i>&lt;NAME&gt;</i> | CHARACTER | Name of the risk source. |
65 | <i>&lt;RISK_MEASURE&gt;</i> | CHARACTER | Type of risk measure: VaR, EC, etc.... |
66 | <i>&lt;SUBDIVISION<i>&lt; | CHARACTER | It identifies the business subdivision. |
67 | <i>&lt;RISK_SOURCE<i>&lt; | CHARACTER | It identifies the risk source. |
68 | <i>&lt;COUPULA<i>&lt; | CHARACTER | It specifies the COPULA function. |
69 | <i>&lt;MARGINAL<i>&lt; | CHARACTER | It specifiess the marginal distribution. |
70 | <i>&lt;VAR<i>&lt; | CHARACTER | It specifies the variable on which the Risk Source Measure is measured. |
71 | <i>&lt;GROUP<i>&lt; | CHARACTER | It identifies the group for the COPULA aggregation. |
72 | <i>&lt;REFNAME<i>&lt; | CHARACTER | It identifies the reference name for COPULA aggregation. |
73
74
75
76 The structure of the <b><i>DS_IN_COPULA_TABLE</i></b> table is as follows:
77
78 |Variable |Type | Description |
79 |-----------------------------|--------------|--------------------------------------------------------------------------|
80 | <i>&lt;SCENARIO_ID;</i> | CHARACTER | It identifies the scenario. |
81 | <i>&lt;RISK_MEASURE;</i> | CHARACTER | It identifies the type of risk measurement (VaR, EC, etc..). |
82 | <i>&lt;NAME;</i> | CHARACTER | It identifies the copula function. |
83 | <i>&lt;PARAMETER;</i> | CHARACTER | Parameter type: type, cov, corr, theta, df, .. |
84 | <i>&lt;CVALUE;</i> | CHARACTER | Character value for parameter "type". |
85 | <i>&lt;NVALUE;</i> | NUMERIC | Numeric value for parameters "args" and "parm". |
86
87 It is important to notice that when the <i>&lt;PARAMETER;</i> is either "cov" or "corr", the corresponding value for <i>&lt;CVALUE;</i> contains the name of the
88 correlation matrix needed for the copula aggregation. The structure of these correlation matrix table is as follows:
89
90 | Variable | Type | Description |
91 |-----------------------------|-------------------|--------------------------------------------------------------------------|
92 | <i>&lt;SCENARIO_ID;</i> | CHARACTER | It identifies the scenario. |
93 | <i>&lt;RISK_MEASURE;</i> | CHARACTER | It identifies the type of risk measurement (VaR, EC, etc..). |
94 | <i>&lt;_NAME_;</i> | CHARACTER | It identifies the risk source . |
95 | <i>&lt;_TYPE_;</i> | CHARACTER | type of dependence matrix: corr |
96 | <i>&lt;name 1;</i> | NUMERIC | Risk for Name 1 |
97 | <i>&lt;...;</i> | NUMERIC | Risk for Name 2 |
98 | <i>&lt;name k;</i> | NUMERIC | Risk for Name 3 |
99
100
101
102
103 The structure of the <b><i>DS_IN_MARGINAL_TABLE</i></b> table is as follows:
104
105 | Variable | Type | Description |
106 |-----------------------------|-------------------|--------------------------------------------------------------------------|
107 | <i>&lt;SCENARIO_ID;</i> | CHARACTER | It identifies the scenario. |
108 | <i>&lt;RISK_MEASURE;</i> | CHARACTER | It identifies the type of risk measurement (VaR, EC, etc..). |
109 | <i>&lt;NAME;</i> | CHARACTER | It identifies the marginal distribution. |
110 | <i>&lt;PARAMETER;</i> | CHARACTER | Parameter type: type, args, parm. |
111 | <i>&lt;CVALUE;</i> | CHARACTER | Character value for parameter "type". |
112 | <i>&lt;NVALUE;</i> | NUMERIC | Numeric value for parameters "args" and "parm". |
113
114
115 \ingroup RiskAggregation
116
117 \author SAS Institute INC.
118 \date 2020
119
120*/
121
122
123
124
125
126
127
128/* Sample call:
129 %irmst_riskagg_hicopula( ds_in_configuration_table = RISKAGG_HICOPULA_CONFIG
130 , ds_in_copula_table = RISKAGG_HICOPULA_COPULA
131 , ds_in_marginal_table = RISKAGG_HICOPULA_MARGINAL
132 , ds_in_corr_tables = RISKAGG_HICOPULA_SUB1_CORR &out_libref..RISKAGG_HICOPULA_SUBS_CORR
133 , ds_out_risk_analysis_table = RISKAGG_HICOPULA_SUMMARY
134 , high_agg_level_var = RISK_TYPE
135 , low_agg_level_var = ENTITY_ID
136 );
137*/
138
139
140
141
142
143%macro irmst_riskagg_hicopula( ds_in_configuration_table =
144 , ds_in_copula_table =
145 , ds_in_marginal_table =
146 , ds_in_corr_tables =
147 , working_libname = work
148 , in_alpha = 0.95
149 , in_seed = 1234
150 , in_ndraws = 100000
151 , high_agg_level_var =
152 , low_agg_level_var =
153 , ds_out_risk_analysis_table =
154 );
155
156 %local
157 bygroup_cnt
158 bygroup_list
159 valid_bygroup_cnt
160 valid_bygroup_list
161 valid_bygroup_i
162 valid_bygroup_index
163 config_dim
164 corr_dim
165 corr_matrix_list
166 corr_matrix_cnt
167 ;
168
169 /*Create a look up table containing the distinct combinations of scenario and risk_measure.
170 Notice that in theory the hicopula aggregation can be performed on multiple scenarios and on multiple risk measures at the same time.*/
171 proc sql noprint;
172 create table &working_libname..bygroup_lookup as
173 select distinct reporting_dt, scenario_id, risk_measure
174 from &ds_in_configuration_table.;
175 quit;
176
177 /*Adding a numeric index to the bygroup lookup table. This index makes the bygroup analysis much simpler since PROC AGGREGATION does not support the by group*/
178 data &working_libname..bygroup_lookup;
179 set &working_libname..bygroup_lookup;
180 index=_N_;
181 run;
182
183 /*Create some macro var*/
184 proc sql noprint;
185 select count(index), index into :bygroup_cnt, :bygroup_list separated by " "
186 from &working_libname..bygroup_lookup;
187 quit;
188
189 proc sort data=&ds_in_configuration_table. out=&working_libname..enhanced_configuration_table;
190 by reporting_dt scenario_id group;
191 run;
192
193 /*Enhance the configuration table with a numeric index identifying the bygroups*/
194 data &working_libname..enhanced_configuration_table;
195 set &working_libname..enhanced_configuration_table;
196 if _N_=1 then do;
197 declare hash h(dataset: "&working_libname..bygroup_lookup");
198 h.definekey("reporting_dt","scenario_id","risk_measure");
199 h.definedata("index");
200 h.defineDone();
201 call missing(index);
202 end;
203 rc=h.find();
204 if rc=0 then output;
205 drop rc;
206 run;
207
208 proc sort data=&ds_in_copula_table. out=&working_libname..enhanced_copula_table;
209 by reporting_dt scenario_id NAME;
210 run;
211
212 /*Enhance the COPULA table with a numeric index identifying the bygroups*/
213 data &working_libname..enhanced_copula_table;
214 set &working_libname..enhanced_copula_table;
215 if _N_=1 then do;
216 declare hash h(dataset: "&working_libname..bygroup_lookup");
217 h.definekey("reporting_dt","scenario_id","risk_measure");
218 h.definedata("index");
219 h.defineDone();
220 call missing(index);
221 end;
222 rc=h.find();
223 drop rc ;
224 run;
225
226 /*The copula configuration table contains references to correlation tables*/
227 data &working_libname..enhanced_copula_table;
228 set &working_libname..enhanced_copula_table end=last;
229 length temp_corr_matrix_list $1000.;
230 retain temp_corr_matrix_index;
231 retain temp_corr_matrix_list "";
232 by index;
233 if first.index then temp_corr_matrix_index=0;
234 if upcase(parameter)="COV" or upcase(parameter)="CORR" then do;
235
236 if index=1 then do;
237 cvalue=cat("&working_libname..",cvalue);
238 temp_corr_matrix_list=catx(" ",temp_corr_matrix_list,cvalue);
239 end;
240 temp_corr_matrix_index+1;
241 cvalue=catx("_","&working_libname..temp_corr_mat",temp_corr_matrix_index);
242 end;
243 if last then do;
244 call symputx("corr_matrix_list",temp_corr_matrix_list);
245 call symputx("corr_matrix_cnt",temp_corr_matrix_index);
246 end;
247 run;
248
249
250 /*Verify that all the required correlation matrices by the enhanced_copula_table have been loaded*/
251 %let parsed_corr_matrix_cnt=%sysfunc(countw(&ds_in_corr_tables.,' '));
252 %do i_corr_matrix=1 %to &corr_matrix_cnt.;
253 %let temp_corr_matrix=%scan(&corr_matrix_list., &i_corr_matrix., %str( ));
254 %let corr_matrix = %sysfunc(prxchange(s/\w+\.//i, -1, &temp_corr_matrix.));
255 %let match_found=0;
256 %do j_parsed_corr_matrix=1 %to &parsed_corr_matrix_cnt.;
257 %let temp_parsed_corr_matrix=%scan(&ds_in_corr_tables., &j_parsed_corr_matrix., %str( ));
258 %let parsed_corr_matrix = %sysfunc(prxchange(s/\w+\.//i, -1, &temp_parsed_corr_matrix.));
259 %if %upcase(&corr_matrix.)=%upcase(&parsed_corr_matrix.) %then %do;
260 %let match_found=1;
261 %end;
262 %end;
263 %if match_found=0 %then %do;
264 %put ERROR: The correlation matrix &corr_matrix. has not been loaded.;
265 %end;
266 %end;
267
268
269
270
271 /*Enhance the correlation matrices with a numeric index identifying the bygroups*/
272 %do i_corr_matrix=1 %to &corr_matrix_cnt.;
273 data &working_libname..enhanced_temp_corr_mat_&i_corr_matrix.;
274 set %scan(&corr_matrix_list.,&i_corr_matrix.,%str( ));
275 if _N_=1 then do;
276 declare hash h(dataset: "&working_libname..bygroup_lookup");
277 h.definekey("reporting_dt","scenario_id","risk_measure");
278 h.definedata("index");
279 h.defineDone();
280 call missing(index);
281 end;
282 rc=h.find();
283 if rc=0 then output;
284 drop rc;
285 run;
286 %end;
287
288
289 /*Enhance the marginal table with a numeric index identifying the bygroups*/
290 data &working_libname..enhanced_marginal_table;
291 set &ds_in_marginal_table.;
292 if _N_=1 then do;
293 declare hash h(dataset: "&working_libname..bygroup_lookup");
294 h.definekey("reporting_dt","scenario_id","risk_measure");
295 h.definedata("index");
296 h.defineDone();
297 call missing(index);
298 end;
299 rc=h.find();
300 if rc=0 then output;
301 drop rc;
302 run;
303
304
305
306
307 /*Check the validity of the bygroups*/
308 %let valid_bygroup_list=&bygroup_list.;
309 %let valid_bygroup_cnt=&bygroup_cnt.;
310 /*Ported from the linear aggregation. Currently there is no check in place.
311 Take a look a the linear aggregation to see how to improve the checks.*/
312
313
314
315
316
317 /*Process the valid bygroups*/
318 %do valid_bygroup_i=1 %to &valid_bygroup_cnt.;
319 %let valid_bygroup_index=%scan(&valid_bygroup_list., &valid_bygroup_i., %str( ));
320 %do i_corr_matrix=1 %to &corr_matrix_cnt.;
321 data &working_libname..temp_corr_mat_&i_corr_matrix.;
322 set &working_libname..enhanced_temp_corr_mat_&i_corr_matrix.(where=(index=&valid_bygroup_index.));
323 run;
324 %end;
325 /*create a temporary data set since there seems to be some instability issue when passing a data set with the where clause to PROC AGGREGATION*/
326 data &working_libname..temp_configuration;
327 set &working_libname..enhanced_configuration_table(where=(index=&valid_bygroup_index.));
328 run;
329 /*create a temporary data set since there seems to be some instability issue when passing a data set with the where clause to PROC AGGREGATION*/
330 data &working_libname..temp_copula;
331 set &working_libname..enhanced_copula_table(where=(index=&valid_bygroup_index.));
332 run;
333 /*create a temporary data set since there seems to be some instability issue when passing a data set with the where clause to PROC AGGREGATION*/
334 data &working_libname..temp_marginal;
335 set &working_libname..enhanced_marginal_table(where=(index=&valid_bygroup_index.));
336 run;
337 proc aggregation
338 vars = (PL comptype=PL)
339 ccv = (&high_agg_level_var. &low_agg_level_var.)
340 config_data = &working_libname..temp_configuration
341 copula_spec = &working_libname..temp_copula
342 marginal_spec = &working_libname..temp_marginal
343 alpha = &in_alpha.
344 seed = &in_seed.
345 ndraws = &in_ndraws.
346 out_simdata = &working_libname..simdata_&valid_bygroup_index.
347 out_statdata = &working_libname..statdata_&valid_bygroup_index.
348 ;
349 runaggr;
350 run;
351 %end;
352
353
354 /*It evaluates ValueAtRisk EXPECTEDLOSS ECONOMIC_CAPITAL*/
355 data &ds_out_risk_analysis_table.;
356 %do valid_bygroup_i=1 %to &valid_bygroup_cnt.;
357 %let valid_bygroup_index=%scan(&valid_bygroup_list., &valid_bygroup_i., %str( ));
358 set &working_libname..statdata_&valid_bygroup_index.(rename=(var=ValueAtRisk mean=EXPECTEDLOSS));
359 index=&valid_bygroup_index.;
360 /*NOTICE THAT PROC AGGREGATION FLIPS THE SIGN OF VALUE-aT-RISK!!!
361 HOWEVER THE LOSS DISTRIBUTION IS ALREADY ASSUMED TO HAVE THE SIGN SWITCHED, THEREFORE A LOSS IS ALREADY ASSUMED TO BE REPORTED WITH A POSITIVE SIGN
362 AND THE SIGN SWITCH IMPLEMENTED BY PROC AGGREGATION IS NOT NEEDED!!!*/
363 ValueAtRisk=-ValueAtRisk;
364 if EXPECTEDLOSS <0 then ECONOMIC_CAPITAL=ValueAtRisk;
365 else ECONOMIC_CAPITAL=ValueAtRisk-EXPECTEDLOSS ;
366 if ECONOMIC_CAPITAL<0 then ECONOMIC_CAPITAL=0;
367 VALUEATRISK_ALPHA=&in_alpha.;
368 output;
369 %end;
370 keep &high_agg_level_var. &low_agg_level_var. VALUEATRISK_ALPHA ValueAtRisk EXPECTEDLOSS ECONOMIC_CAPITAL index;
371 run;
372
373
374 /*Use the bygroup lookup table to populate the aggregated risk result table with corresponding values of
375 the scenarios and the risk measures.*/
376 data &ds_out_risk_analysis_table.;
377 set &ds_out_risk_analysis_table. &working_libname..enhanced_configuration_table(obs=0 keep=scenario_id reporting_dt/* workgroup entity_id"*/);
378 if _N_=1 then do;
379 declare hash h(dataset: "&working_libname..bygroup_lookup");
380 h.definekey("index");
381 h.definedata("scenario_id","reporting_dt");
382 h.defineDone();
383 end;
384 rc=h.find();
385 if rc=0 then output;
386 drop rc name NValue table_id project_id load_id processed_dttm index;
387 run;
388
389%mend;
390
391