1 |
1 |
44.43561 |
47.37819 |
50.19227 |
2 |
1 |
35.14101 |
41.02716 |
43.90635 |
3 |
1 |
39.52635 |
39.02007 |
47.91918 |
4 |
1 |
26.58301 |
39.81133 |
60.74746 |
5 |
1 |
38.85981 |
58.04062 |
72.14708 |
1 |
2 |
52.22157 |
48.70145 |
48.87465 |
2 |
2 |
55.35908 |
47.04707 |
49.23315 |
3 |
2 |
72.26222 |
54.19582 |
59.61174 |
4 |
2 |
62.37701 |
48.95239 |
58.43747 |
5 |
2 |
44.81756 |
39.95729 |
52.66088 |
1 |
3 |
54.99454 |
50.54797 |
46.82467 |
2 |
3 |
50.03961 |
49.71884 |
46.77868 |
3 |
3 |
54.42857 |
53.05170 |
59.04259 |
4 |
3 |
78.54505 |
73.68312 |
58.45952 |
5 |
3 |
69.31072 |
45.07935 |
31.05223 |
.
.
.
1 |
50 |
49.39549 |
47.92113 |
52.03304 |
2 |
50 |
60.87475 |
49.03707 |
49.85135 |
3 |
50 |
58.19660 |
46.26249 |
48.62918 |
4 |
50 |
56.92681 |
46.32106 |
51.00469 |
5 |
50 |
68.13376 |
39.79373 |
87.88770 |
This section shows the details of how the risk factors are perturbed by using DATA step and SAS/IML. Later, these perturbed risk factors are compared with those that were generated by SAS Risk Dimensions. The process starts by creating a perturbation vector from the random shocks that were generated by SAS Risk Dimensions. The perturbation vector is then transformed into a perturbation matrix that is used to perturb the risk factors.
This step simply drops the columns that are not needed by IML:
* Store the random shocks output data into a vector that is used to create a
perturbation matrix;
data d;
set shocks (keep=_z_);
run;
The number of horizons, number of simulation replications, and number of risk factors are needed in later calculations, so they are stored in macro variables:
* Extract the numbers of horizons and simulation replications, and assign them macro
variables to use later in calculations;
data _null_;
set simstate end=last;
if last then do;
call symput('nhorizon',simulationtime);
call symput('nsimrep',simulationreplication);
end;
run;
* Extract the number of risk factors from the SAS Risk Dimensions output and assign
it to a macro variable to use in later calculations;
proc contents data=states_v(drop=statenumber analysisnumber analysispart
simulationtime simulationreplication _date_) out=rflist(keep=name) noprint;
run;
proc sql noprint;
select count(name) into: nrf from rflist;
quit;
Labels are removed from the covariance matrix so that only the covariance values are stored in a covariance matrix in SAS/IML:
* Remove the labels from the covariance matrix so that only the covariance values remain;
data cv_vals;
set cv;
where upcase(_type_) eq 'COV';
drop _type_ _name_;
run;
This step uses SAS/IML to compute the lower Cholesky root and multiple it by the random shocks in order to produce a perturbation vector:
proc iml;
* Perform a Cholesky decomposition on the covariance matrix;
use cv_vals var _all_;
read all var _all_ into cv_vals;
U=root(cv_vals);
L=U`;
* Store the lower Cholesky root into a block diagonal matrix that is based on the
numbers of simulation horizons and simulation replications. This block diagonal
matrix is needed to create the perturbation vector;
do i=1 to &nhorizon;
do j=1 to &nsimrep;
if i=1 & j=1 then blockL=L;
else blockL=block(L,blockL);
end;
end;
* Read in the shocks vector, which is organized by horizon by simulation replication;
use d var _all_;
read all var _all_ into d;
* Compute the perturbation vector;
p=blockL*d;
* Output the perturbation vector to a data set;
create p from p [colname='p'];
append from p;
quit;
run;
Further transformations are needed in order to restructure the perturbation vector into a perturbation matrix. The perturbation matrix takes into account the number of risk factors, simulation replications, and simulation horizons:
* Restructure the perturbtion vector to have a row for each risk factor and a column
for each simulation horizon and for each simulation replication;
* Set up indexes for the perturbation vector;
data indx;
label rfnum="Risk Factor Number"
simulationtime="Simulation Time"
simulationreplication="Simulation Replication";
do simulationreplication=1 to &nsimrep;
do simulationtime=1 to &nhorizon;
do rfnum=1 to &nrf;
output;
end;
end;
end;
run;
* Merge the indexes with the perturbation vector;
data indxp;
merge indx p;
label p="Perturbation";
underscore="_"; * Underscore is used to label columns in a transpose later;
run;
* Re-sort the peturbation vector by the risk-factor index;
proc sort data=indxp out=indxprf;
by rfnum;
run;
Print the perturbation vector to view its indexing and values:
proc print data=indxprf noobs label;
title1 "Perturbation Vector";
title2 "Indexed for Risk Factors, Simulation Replications, and Horizons";
title3 "Work.SMS";
var rfnum simulationreplication simulationtime p;
run;
1 |
1 |
1 |
-0.11798 |
1 |
1 |
2 |
-0.23467 |
1 |
1 |
3 |
0.11760 |
1 |
1 |
4 |
-0.13223 |
1 |
1 |
5 |
0.08949 |
1 |
2 |
1 |
0.04347 |
1 |
2 |
2 |
0.05835 |
1 |
2 |
3 |
0.26646 |
1 |
2 |
4 |
-0.04903 |
1 |
2 |
5 |
-0.07792 |
.
.
.
2 |
1 |
1 |
-0.03366 |
2 |
1 |
2 |
-0.14393 |
2 |
1 |
3 |
-0.05016 |
2 |
1 |
4 |
0.00669 |
2 |
1 |
5 |
0.08886 |
2 |
2 |
1 |
-0.00611 |
2 |
2 |
2 |
-0.03456 |
2 |
2 |
3 |
0.14146 |
2 |
2 |
4 |
-0.03392 |
2 |
2 |
5 |
-0.04786 |
.
.
.
3 |
1 |
1 |
-0.01596 |
3 |
1 |
2 |
-0.13380 |
3 |
1 |
3 |
0.08746 |
3 |
1 |
4 |
0.07907 |
3 |
1 |
5 |
0.04054 |
3 |
2 |
1 |
-0.04257 |
3 |
2 |
2 |
0.00731 |
3 |
2 |
3 |
0.19129 |
3 |
2 |
4 |
-0.00663 |
3 |
2 |
5 |
-0.02453 |
.
.
.
Use the indexes to transform the perturbation vector into a perturbation matrix that contains a row for each risk factor and a column for each combination of simulation horizons and simulation replications:
* Transpose the perturbation vector into a perturbation matrix that is structured as follows
for later calculations: Row=[risk-factor-index] by Column=[horizon * simrep];
proc transpose data=indxprf out=indxprft prefix=time_rep_;
var p;
by rfnum;
id simulationtime underscore simulationreplication;
run;
* Remove the labels and indexes from the perturbation matrix so that only the
perturbation values remain;
data pmat;
set indxprft (drop=rfnum _name_ _label_);
run;
Print the perturbation matrix that is used in SAS/IML:
proc print data=pmat noobs;
title1 "Perturbation Matrix that is Used by SAS/IML";
title2 "Work.PMat";
run;
-0.11798 |
-0.23467 |
0.11760 |
-0.13223 |
0.089493 |
0.043473 |
0.058345 |
0.26646 |
-0.049035 |
-0.077922 |
-0.03366 |
-0.14393 |
-0.05016 |
0.00669 |
0.088858 |
-0.006111 |
-0.034560 |
0.14146 |
-0.033919 |
-0.047856 |
-0.01596 |
-0.13380 |
0.08746 |
0.07907 |
0.040536 |
-0.042567 |
0.007308 |
0.19129 |
-0.006632 |
-0.024533 |
|
. . .
|
The perturbation matrix is then read into a matrix in SAS/IML in order to be applied to the risk factors. In this step, the base-case risk factors are perturbed, as well as the risk factors at each horizon. The resulting perturbed risk factor values are then outputted to a data set named
SS that is similar in structure to the SimState table that is outputted by SAS Risk Dimensions:
proc iml;
* Read in the perturbation matrix;
use pmat var _all_;
read all var _all_ into pmat;
pmat=pmat`;
* Get base-case values from current market data;
use curr var _all_;
read all var _all_ into b;
* The horizon vector is based on the simulation horizon macro variable;
h={&horizons};
* Initialize output matrices;
ind={0 0}; * the ind matrix holds the simrep and horizon numbers;
rf=b*0; * the rf matrix holds the simulated risk factor values;
do i=1 to &nsimrep*&nhorizon-1;
ind=ind//{0 0};
rf=rf//b*0;
end;
* Perturb risk factors for each horizon and simulation replication;
cntr=0; * row number for risk factor and perturbation matrices;
do i=1 to &nsimrep; * loop through simulation replications;
do j=1 to &nhorizon; * loop through horizon numbers;
cntr=cntr+1; * increment to next row;
ind[cntr,1]=j; * horizon number;
ind[cntr,2]=i; * simulation replication number;
if j=1 then do; * if first horizon, perturb the base-case risk factor value;
rf[cntr,]=b#exp(sqrt(h[j]-0)*pmat[cntr,]);
end;
else do; * otherwise perturb the previous horizon's rf value;
rf[cntr,]=rf[cntr-1,]#exp(sqrt(h[j]-h[j-1])*pmat[cntr,]); * perturb risk factors;
end;
end; * end horizon loop;
end; * end sim rep loop;
* Create an output data set that uses the same data structure as the SimState table;
rfout=ind||rf;
varnames={'simulationtime' 'simulationreplication' 'rf1' 'rf2' 'rf3'};
create ss from rfout [colname=varnames];
append from rfout;
quit;
run;
The SS output data is printed for simulation replications 1, 2, 3, and 50:
proc print data=ss noobs label;
title1 "Perturbed Risk Factor Values from SAS/IML";
title2 "Work.SS";
label simulationtime="Simulation Time"
simulationreplication="Simulation Replication";
var simulationtime simulationreplication rf1 rf2 rf3;
where simulationreplication le 3 or simulationreplication eq 50;
run;
1 |
1 |
44.4356 |
47.3782 |
50.1923 |
2 |
1 |
35.1410 |
41.0272 |
43.9063 |
3 |
1 |
39.5264 |
39.0201 |
47.9192 |
4 |
1 |
26.5830 |
39.8113 |
60.7475 |
5 |
1 |
38.8598 |
58.0406 |
72.1471 |
1 |
2 |
52.2216 |
48.7015 |
48.8746 |
2 |
2 |
55.3591 |
47.0471 |
49.2331 |
3 |
2 |
72.2622 |
54.1958 |
59.6117 |
4 |
2 |
62.3770 |
48.9524 |
58.4375 |
5 |
2 |
44.8176 |
39.9573 |
52.6609 |
1 |
3 |
54.9945 |
50.5480 |
46.8247 |
2 |
3 |
50.0396 |
49.7188 |
46.7787 |
3 |
3 |
54.4286 |
53.0517 |
59.0426 |
4 |
3 |
78.5450 |
73.6831 |
58.4595 |
5 |
3 |
69.3107 |
45.0793 |
31.0522 |
.
.
.
1 |
50 |
49.3955 |
47.9211 |
52.0330 |
2 |
50 |
60.8747 |
49.0371 |
49.8514 |
3 |
50 |
58.1966 |
46.2625 |
48.6292 |
4 |
50 |
56.9268 |
46.3211 |
51.0047 |
5 |
50 |
68.1338 |
39.7937 |
87.8877 |
The following SAS code compares the SS table of perturbed risk factors from the SAS/IML code to the SimState output data set from SAS Risk Dimensions:
* Compare IML results to the SimState table;
proc compare data=simstate compare=ss criterion=1e-12;
var simulationtime simulationreplication rf1 rf2 rf3;
run;
There is a slight variation in the results due to machine-level precision, but they are equal when using a tolerance level of 1e-12:
Observation Summary
Observation Base Compare
First Obs 1 1
Last Obs 250 250
Number of Observations in Common: 250.
Total Number of Observations Read from WORK.SIMSTATE: 250.
Total Number of Observations Read from WORK.SS: 250.
Number of Observations with Some Compared Variables Unequal: 0.
Number of Observations with All Compared Variables Equal: 250.
The COMPARE Procedure
Comparison of WORK.SIMSTATE with WORK.SS
(Method=RELATIVE(0.0222), Criterion=1.0E-12)
Values Comparison Summary
Number of Variables Compared with All Observations Equal: 5.
Number of Variables Compared with Some Observations Unequal: 0.
Total Number of Values which Compare Unequal: 0.
Total Number of Values not EXACTLY Equal: 161.
Maximum Difference Criterion Value: 4.8141E-16.
Additional Documentation
For licensed customers of SAS Risk Dimensions, documentation is available at:
These sample files and code examples are provided by SAS Institute
Inc. "as is" without warranty of any kind, either express or implied, including
but not limited to the implied warranties of merchantability and fitness for a
particular purpose. Recipients acknowledge and agree that SAS Institute shall
not be liable for any damages whatsoever arising out of their use of this material.
In addition, SAS Institute will provide no support for the materials contained herein.
/*
This simple example compares a covariance-based Monte Carlo simulation across multiple
simulation horizons with the same calculation performed in SAS/IML.
See also the "Static Covariance Simulation" section in the SAS Risk Dimensions
Procedures Guide at http://support.sas.com/documentation/onlinedoc/riskdimen/index.html.
*/
* Specify time horizons in a macro variable;
%let horizons=1 2 3 12 30;
* Specify covariance data;
data cv;
format _type_ $3. _name_ $3. rf1 8.6 rf2 8.6 rf3 8.6;
input _type_ $ _name_ $ rf1 rf2 rf3;
datalines;
COV rf1 0.01631 0.00760 0.00901
COV rf2 0.00760 0.00613 0.00732
COV rf3 0.00901 0.00732 0.01370
STD . 0.12770 0.07831 0.11705
;
run;
* Specify current market data (that is, base-case values for the risk factors);
data curr;
rf1=50;
rf2=49;
rf3=51;
run;
* Specify portfolio positions;
data inst;
format instid $12. insttype $6.;
input instid $ insttype $;
datalines;
position01 asset
;
run;
* Create risk environment and register risk factor variables;
proc risk;
env new=env;
declare riskfactors=(rf1 num var label="Risk Factor 1",
rf2 num var label="Risk Factor 2",
rf3 num var label="Risk Factor 3");
env save;
run;
* Define a pricing method to value the portfolio instrument;
proc compile env=env outlib=env;
method asset_price kind=price;
_value_=max(rf1,rf2,rf3);
endmethod;
quit;
* Define and run a covariance simulation analysis project;
proc risk;
env open=env;
instrument asset methods=(price asset_price);
marketdata cv file=cv type=covariance;
marketdata curr file=curr type=current;
instdata inst file=inst format=simple;
sources inst inst;
read sources=inst out=inst;
simulation simcv
data=cv
method=cov
horizon=(&horizons)
ndraw=50;
project simcv
analysis=simcv
data=(curr cv)
portfolio=inst
options=(shocks simstate)
outlib=work;
runproject simcv
endproject=states;
run;
proc print data=simstate noobs;
title1 "Perturbed Risk Factor Values from SAS Risk Dimensions";
title2 "Work.SimState";
var simulationtime simulationreplication rf1 rf2 rf3;
where simulationreplication le 3 or simulationreplication eq 50;
run;
* Store the random shocks output data into a vector that is used to create a
perturbation matrix;
data d;
set shocks (keep=_z_);
run;
* Extract the numbers of horizons and simulation replications, and assign them macro
variables to use later in calculations;
data _null_;
set simstate end=last;
if last then do;
call symput('nhorizon',simulationtime);
call symput('nsimrep',simulationreplication);
end;
run;
* Extract the number of risk factors from the SAS Risk Dimensions output and assign
it to a macro variable to use in later calculations;
proc contents data=states_v(drop=statenumber analysisnumber analysispart
simulationtime simulationreplication _date_) out=rflist(keep=name) noprint;
run;
proc sql noprint;
select count(name) into: nrf from rflist;
quit;
* Remove the labels from the covariance matrix so that only the covariance values remain;
data cv_vals;
set cv;
where upcase(_type_) eq 'COV';
drop _type_ _name_;
run;
proc iml;
* Perform a Cholesky decomposition on the covariance matrix;
use cv_vals var _all_;
read all var _all_ into cv_vals;
U=root(cv_vals);
L=U`;
* Store the lower Cholesky root into a block diagonal matrix that is based on the
numbers of simulation horizons and simulation replications. This block diagonal
matrix is needed to create the perturbation vector;
do i=1 to &nhorizon;
do j=1 to &nsimrep;
if i=1 & j=1 then blockL=L;
else blockL=block(L,blockL);
end;
end;
* Read in the shocks vector, which is organized by horizon by simulation replication;
use d var _all_;
read all var _all_ into d;
* Compute the perturbation vector;
p=blockL*d;
* Output the perturbation vector to a data set;
create p from p [colname='p'];
append from p;
quit;
run;
* Restructure the perturbation vector to have a row for each risk factor and a column
for each simulation horizon and for each simulation replication;
* Set up indexes for the perturbation vector;
data indx;
label rfnum="Risk Factor Number"
simulationtime="Simulation Time"
simulationreplication="Simulation Replication";
do simulationreplication=1 to &nsimrep;
do simulationtime=1 to &nhorizon;
do rfnum=1 to &nrf;
output;
end;
end;
end;
run;
* Merge the indexes with the perturbation vector;
data indxp;
merge indx p;
label p="Perturbation";
underscore="_"; * Underscore is used to label columns in a transpose later;
run;
* Re-sort the peturbation vector by the risk-factor index;
proc sort data=indxp out=indxprf;
by rfnum;
run;
proc print data=indxprf noobs label;
title1 "Perturbation Vector";
title2 "Indexed for Risk Factors, Simulation Replications, and Horizons";
title3 "Work.SMS";
var rfnum simulationreplication simulationtime p;
run;
* Transpose the perturbation vector into a perturbation matrix that is structured as follows
for later calculations: Row=[risk-factor-index] by Column=[horizon * simrep];
proc transpose data=indxprf out=indxprft prefix=time_rep_;
var p;
by rfnum;
id simulationtime underscore simulationreplication;
run;
* Remove the labels and indexes from the perturbation matrix so that only the
perturbation values remain;
data pmat;
set indxprft (drop=rfnum _name_ _label_);
run;
proc print data=pmat noobs;
title1 "Perturbation Matrix that is Used by SAS/IML";
title2 "Work.PMat";
run;
proc iml;
* Read in the perturbation matrix;
use pmat var _all_;
read all var _all_ into pmat;
pmat=pmat`;
* Get base-case values from current market data;
use curr var _all_;
read all var _all_ into b;
* The horizon vector is based on the simulation horizon macro variable;
h={&horizons};
* Initialize output matrices;
ind={0 0}; * the ind matrix holds the simrep and horizon numbers;
rf=b*0; * the rf matrix holds the simulated risk factor values;
do i=1 to &nsimrep*&nhorizon-1;
ind=ind//{0 0};
rf=rf//b*0;
end;
* Perturb risk factors for each horizon and simulation replication;
cntr=0; * row number for risk factor and perturbation matrices;
do i=1 to &nsimrep; * loop through simulation replications;
do j=1 to &nhorizon; * loop through horizon numbers;
cntr=cntr+1; * increment to next row;
ind[cntr,1]=j; * horizon number;
ind[cntr,2]=i; * simulation replication number;
if j=1 then do; * if first horizon, perturb the base-case risk factor value;
rf[cntr,]=b#exp(sqrt(h[j]-0)*pmat[cntr,]);
end;
else do; * otherwise perturb the previous horizon's rf value;
rf[cntr,]=rf[cntr-1,]#exp(sqrt(h[j]-h[j-1])*pmat[cntr,]); * perturb risk factors;
end;
end; * end horizon loop;
end; * end sim rep loop;
* Create an output data set that uses the same data structure as the SimState table;
rfout=ind||rf;
varnames={'simulationtime' 'simulationreplication' 'rf1' 'rf2' 'rf3'};
create ss from rfout [colname=varnames];
append from rfout;
quit;
run;
proc print data=ss noobs label;
title1 "Perturbed Risk Factor Values from SAS/IML";
title2 "Work.SS";
label simulationtime="Simulation Time"
simulationreplication="Simulation Replication";
var simulationtime simulationreplication rf1 rf2 rf3;
where simulationreplication le 3 or simulationreplication eq 50;
run;
* Compare IML results to the SimState table;
proc compare data=simstate compare=ss criterion=1e-12;
var simulationtime simulationreplication rf1 rf2 rf3;
run;
These sample files and code examples are provided by SAS Institute
Inc. "as is" without warranty of any kind, either express or implied, including
but not limited to the implied warranties of merchantability and fitness for a
particular purpose. Recipients acknowledge and agree that SAS Institute shall
not be liable for any damages whatsoever arising out of their use of this material.
In addition, SAS Institute will provide no support for the materials contained herein.