Showing posts with label credit risk. Show all posts
Showing posts with label credit risk. Show all posts

Friday, September 30, 2011

Modeling loss given default (LGD) by finite mixture model

The 'highly skewed' and 'highly irregular' loss data from the insurance and banking world is routinely fitted by a simple beta/ lognormal/gamma/Pareto distribution. While looking at the distribution plot, I bet that many people don’t want to buy this story and are willing to explore better ways. Finite mixture model that incorporates multiple distributions can be a good option in the radar map. For example, Matt Flynn will present how to use PROC NLMIXED to realize finite mixture model for insurance loss data in the incoming SAS ANALYTICS 2011 conference. Finally the revolutionary FMM procedure shipped with SAS 9.3 makes building finite mixture model easy.



For example, I have a sample loss given default dataset with 317 observations: lgd(real loss given default) is the dependent variable; lgd_a(mean default rate by industry), lev(leverage coefficient by firm) and i_def( mean default rate by year) are independent variables. The kernel distribution is plotted and difficult to be estimated by naked eyes.

data lgddata;
informat lgd lev 12.9 lgd_a 6.4 i_def 4.3;
input lgd lev lgd_a i_def;
label lgd = 'Real loss given default'
lev = 'Leverage coefficient by firm'
lgd_a = 'Mean default rate by year'
i_def = 'Mean default rate by industry';
cards;
0.747573451 0.413989786 0.6261 1.415
/* Other data*/
0.748255544 0.607452819 0.3645 3.783
;
run;

proc kde data = lgddata;
univar lgd / plots = all;
run;

data _lgddata01;
set lgddata;
id + 1;
run;
proc transpose data = _lgddata01 out = _lgddata02 ;
by id;
run;
proc sgplot data = _lgddata02;
hbox col1 / category = _LABEL_;
xaxis label = ' ';
run;
What I need PROC FMM to do is to estimate: 1. which distribution is the best from beta, lognormal, and gamma distributions; 2. how many components (ranging from 1 to 10) are the best for each distribution. To automate and visualize the process, I designed a macro. From the plots above, all penalized criterions (AIC, BIC, etc.) indicate that beta distribution is better than the other two. Also the beta distribution has higher Pearson statistic value and less parameter numbers.

ods html style = money;
%macro modselect(data = , depvar = , kmin= , kmax = , modlist = );
%let modcnt=%eval(%sysfunc(count(%cmpres(&modlist),%str( )))+1);
%do i = 1 %to &modcnt;
%let modelnow = %scan(&modlist, &i);
ods output fitstatistics = &modelnow(rename=(value=&modelnow));
ods select densityplot fitstatistics;
proc fmm data = &data;
model &depvar = / kmin=&kmin kmax= &kmax dist=&modelnow;
run;
%end;
data _final;
%do i = 1 %to &modcnt;
set %scan(&modlist, &i);
%end;
run;
proc sgplot data = _tmp01;
%do i = 1 %to &modcnt;
%let modelnow = %scan(&modlist, &i);
series x = descr y = &modelnow;
where descr ne :'E' and descr ne :'P';
%end;
yaxis label = ' ' grid;
run;
proc transpose data = _tmp01 out = _tmp02;
where descr = :'E' or descr = :'P';
id descr;
run;
proc sgplot data = _tmp02;
bubble x = effective_parameters y = effective_components
size = pearson_statistic / datalabel = _name_;
xaxis grid; yaxis grid;
run;
%mend;
%modselect(data = lgddata, depvar = lgd, kmin= 1,
kmax = 10, modlist = beta lognormal gamma);


The optimized component number for the beta distribution is 5 – beautiful matching curve. Lognormal distribution exhausted the maximum 10 components and fits the kernel distribution very awkwardly. Gamma distribution used 9 components and fits relatively well.


Then I chose the 5-compenent Homogeneous beta distribution to model the LGD data. PROC FMM provided all parameter estimates for these 5 components. From the plot above, the intercepts and the scale parameter s are different as expected. Interestingly, the parameters of lgd_a(mean default rate by industry) present big diversity, while the parameters of i_def( mean default rate by year) tend to converge at the zero point.

ods output parameterestimates = parmds;
proc fmm data = lgddata;
model lgd = lev lgd_a i_def / k = 5 dist=beta;
run;

proc sgplot data = parmds;
series x = Effect y = Estimate / group = Component;
xaxis grid label = ' '; yaxis grid;
run;
ods html style = htmlbluecml;
In conclusion, although PROC FMM is still an experimental procedure, its powerful model selection features would significantly change the way how people feel and use the loss data in the risk management industry.

Tuesday, June 21, 2011

Credit default swap pricing by PROC FCMP

Sometimes I feel curious about how running a simple VBA macro in Excel could beat my 8-core desktop to indefinite waiting time with 100% CPU usage. On those occasions, I wish SAS could be a rescue, since I am more familiar and confident with SAS. The good news is that in SAS 9.2, many essential Excel functions were translated by Proc FCMP and stored in a built-in dataset named sashelp.slkwxl. Then it will be more convenient for Proc FCMP to port code from Excel to SAS as a bridge. The sashelp.slkwxl dataset contains 41 functions derived from Excel as below:

Type Function
----------------------------------
Finance Excel ACCRINT
Excel ACCRINTM
Excel AMORDEGRC
Excel AMORLINC
Excel COUPDAYBS
Excel COUPDAYS
Excel COUPDAYSNC
Excel COUPNCD
Excel COUPNUM
Excel COUPPCD
European DATDIF
Excel DB
Excel DISC
Excel DOLLARDE
Excel DOLLARFR
Excel DURATION
Excel EFFECT
Excel MDURATION
Excel ODDFPRICE
Excel ODDFYIELD
Excel ODDLPRICE
Excel ODDLYIELD
Excel PRICE
Excel PRICEDSIC
Excel PRICE
Excel RECEIVED
Excel TBILLEQ
Excel TBILLPRICE
Excel TBILLYIELD
Excel YIELD
Excel YIELDDISC
Excel YIELDMAT
Mathematics Excel EVEN
Excel FACTDOUBLE
Excel FLOOR
Excel MULTINOMIAL
Excel ODD
Excel PRODUCT
Statistics Excel AVEDEV
Excel DEVSQ
Excel VARP

With the help of user-defined function and some financial functions from sashelp.slkwxl, we can probably develop some pretty complicated SAS programs to replace VBA macros in Excel. For example, credit default swap, a popular instrument in credit derivative market, is like a contract to exchange default risk using spread between buyer and seller. Implementing the pricing mechanism may need a number of modules, like what Gunter and Peter showed with fixed risk-neutral probabilities of default [Ref. 1]. SAS macro can hardly fit in the role as a module, since nested macro with leaky macro variables is a big headache for SAS programmers. In the codes below, I used coupdaysnc_slk() and coupncd_slk() functions from sashelp.slkwxl, which correspond to the coupdaysnc() and coupncd() functions in Excel, and another 3 user-defined functions to build a system for CDS pricing. Besides the features of manufacturing home-made function and encapsulating macros, Proc FCMP proves to be a better tool for vector/matrix operations than Data Step array. The result shows that for some financial applications, the migration from Excel to SAS is smoothed by Proc FCMP.

References:
1. Gunter Loeffler and Peter Posch. ‘Credit Risk Modeling using Excel and VBA’. The 2nd edition. Wiley, 2011.

 
/*******************READ ME*********************************************
* - Credit default swap pricing by Proc FCMP -
*
* SAS VERSION: 9.2.2
* DATE: 22jun2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME******************************************/

****************(1) MODULE-BUILDING STEP********************************;
******(1.1) CREATE A FUNCTION FOR YEAR FRACTION*************************;
options cmplib = (sashelp.slkwxl work.myfunclib);
proc fcmp outlib = work.myfunclib.finance;
function yearfrac0(sdate, edate);
return(datdif(sdate, edate, '30/360') / 360);
endsub;
quit;

******(1.2) CREATE A FUNCTION FOR ACCRUED INTEREST AT SETTLEMENT*******;
proc fcmp outlib = work.myfunclib.finance;
function aci(settlement_date, maturity_date, rate, freq);
if settlement_date < maturity_date then
aci = 100 * rate / freq * (1 - coupdaysnc_slk(settlement_date, maturity_date, freq, 0)
/ coupdays_slk(settlement_date, maturity_date, freq, 0));
if aci = 0 or settlement_date = maturity_date then aci = 100 * rate / freq;
return(aci);
endsub;
quit;

******(1.3) CREATE A FUNCTION FOR NON-FLAT INTEREST RATE STRUCTURE******;
option mstored sasmstore = work;
%macro intspot_macro() / store source;
%let data = %sysfunc(dequote(&data));
proc sql noprint;
select count(*) into :nobs from &data;
quit;
%mend;

proc fcmp outlib = work.myfunclib.finance;
function intspot(data $, year);
array spots[1, 2] / nosymbols;
rc1 = run_macro('intspot_macro', data, nobs);
call dynamic_array(spots, nobs, 2);
rc2 = read_array(data, spots, 't', 'spotrate');
if nobs = 1 then intspot = spots[1, 2];
else do;
if year le spots[1, 1] then intspot = spots[1, 2];
else if year ge spots[nobs, 1] then intspot = spots[nobs, 2];
else do;
i = 1;
do until(spots[i, 1] gt year);
i + 1;
intspot = spots[i-1, 2] + (spots[i, 2] - spots[i-1, 2])*(year - spots[i-1, 1])
/ (spots[i, 1] - spots[i-1, 1]) ;
end;
end;
end;
return(intspot);
endsub;
quit;

******(1.4) CREATE A MACRO TO EVALUATE CREDIT DEFAULT SWAP SPREAD******;
%macro cdsprice(n = 20, Settlement_date = '15jul2006'd, Maturity_date = '15jul2013'd,
rate = 0.07125, freq = 2, recovery_rate = 0.4,
compounding = 2, pay_freq = 4, pd = 0.0197,
outfile = );
options mlogic mprint cmplib = (sashelp.slkwxl work.myfunclib)
nocenter mstored sasmstore = work;
proc fcmp;
mixed_date = mdy(month(&settlement_date), day(&settlement_date), year(&maturity_date) + 1);
array default_date[&n] / nosymbols;
default_date[1] = coupncd_slk(&settlement_date, mixed_date, &pay_freq, 0);
do i = 2 to &n;
default_date[i] = coupncd_slk(default_date[i-1], mixed_date, &pay_freq, 0);
end;
rc1 = write_array('_tmp01', default_date, 'default_date');
quit;

data _tmp02;
set _tmp01;
datdif = yearfrac0(&Settlement_date, default_date);
spotrate = intspot('rate', datdif);
aci = aci(default_date, &Maturity_date, &rate, &freq) / 100;
retain sum_pd;
if _n_ = 1 then sum_pd = 0;
else sum_pd = sum_pd + &pd;
fees = 1/&pay_freq * (1 - sum_pd) / (1 + spotrate/&compounding)**(&compounding*datdif);
default_pay = (1 - &recovery_rate - &recovery_rate*aci)*&pd
/ (1 + spotrate/&compounding)**(&compounding*datdif);
run;

proc sql noprint;
select sum(default_pay) / sum(fees) format = percent8.3 into: cds_spread from _tmp02;
select intck('year', min(default_date), max(default_date)) into: period from _tmp02;
quit;

ods html file = "&outfile" style = money;
title; footnote;
proc report data = _tmp02 nowd headline split = "|";
columns default_date aci spotrate fees default_pay ;
define default_date / display format = date9. "Dates of|default";
define aci / format = percent9.2 "Accruted interest|rate";
define spotrate / format = percent9.2 "Non-flat interest|rate";
define fees / format = percent9.2 "Accruted fees";
define default_pay / format = percent9.2 "Default payments";
compute after;
line @2 "The %sysfunc(strip(&period)) year CDS Spread is:&cds_spread";
line " ";
line @2 "Settlement date is :%sysfunc(putn(&Settlement_date, date11.))";
line @2 "Maturity date is :%sysfunc(putn(&Maturity_date, date11.)) ";
line @2 "Payment frequency is :&pay_freq";
line @2 "Reference bond coupon rate is :%sysfunc(putn(&rate, percent9.2)) ";
line @2 "Reference bond coupon freqency is :&freq ";
line @2 "Compounding frequency is :&compounding " ;
line @2 "Risk-neutral probabilities of default is :%sysfunc(putn(&pd, percent9.2))";
line @2 "Recover rate is : %sysfunc(putn(&recovery_rate, percent9.2))";
endcomp;
run;
ods html close;
%mend cdsprice;

****************(2) TESTING STEP****************************************;
******(2.1) INPUT DATA OF A TERM STRUCTURE OF INTEREST RATE*************;
data rate;
format t 6.2 spotrate percent7.2;
input t: SpotRate best32.;
cards;
0.083333333 0.055609
/*To buy Gunter and Peter's book will have complete data*/
10 0.057603
;;;
run;

******(2.2) RUN THE MACRO TO HAVE RESULT*******************************;
%cdsprice(outfile = c:\tmp\result.xls);

****************END OF ALL CODING***************************************;

Wednesday, June 8, 2011

Bootstrap prediction models for probability of default

Not like consumer credit scoring, corporate default study is usually jeopardized by the low-n-low-p data sizes. In the fourth chapter of their book, Gunter and Peter, demonstrated an example about how to construct prediction models for IDR (invesment grade default rate) using VBA and therefore evaluate them by residual sum of squares [Ref. 1]. The only shortcoming is that the variables are limited and the observations are scarce (25 total, 22 valid), which makes me feel awkward to estimate the distribution. In this case, bootstrapping may be a good alternative, since it is a simple and straightforward method to increase predictability. Previously, Wensui showed the logit bootstrapping for credit risk by Proc LOGISTIC and Proc GENMOD [Ref. 2]. Data transformation was conducted by Data Step merge first raised by Liang Xie [Ref. 3].

The GLMSELECT procedure in SAS 9.2.2 harnesses the power of variable selection and bootstrapping together. In the example, the trend of IDR is hard to tell, even with a 3-year moving average chart. Thus, I chose 10000 times of resampling. As the result, 3 of the four predictors remained with their corresponding satisfying parameters.

References:
1. Gunter Loeffler and Peter Posch. ‘Credit Risk Modeling using Excel and VBA’. The 2nd edition. Wiley.
2. Wensui Liu. ‘Improving credit scoring by generalized additive model’. SAS Global 2007.
3. Liang Xie. http://www.sas-programming.com

/*******************READ ME*********************************************
* - Bootstrap prediction models for probability of default -
*
* SAS VERSION: 9.2.2
* DATE: 09jun2011
* AUTHOR: hchao8@gmail.com
****************END OF READ ME******************************************/

****************(1) DATA INTEGRATION/TRANSFORMATION STEP*****************;
data idr;
infile datalines delimiter = ',' missover dsd lrecl=32767;
format year idr prf age bbb spr best12.;
input year idr prf age bbb spr;
datalines;
1981,0,-7.340255411,,27.02456779,2.77
/*To buy Gunter and Peter's book will have the full data*/
2005,0.030637255,3.183410997,6.673717385,45.77112235,1.91
;;;
run;

data idr_t;
merge idr(keep=idr firstobs=2 )
idr(rename=(idr=_idrforward year=_yearforward));
year = _yearforward + 1;
label idr = 'invesment grade default rate'
prf = 'forecasted change in corporate profits'
age = 'fraction of new issuers'
bbb = 'fraction of bbb-rated issuers'
spr = 'spread on baa bonds';
run;

****************(2) MODULE-BUILDING STEP********************************;
%macro idrbs(data =, nsamp =, out =);
/*****************************************************************
* MACRO: idrbs()
* GOAL: build prediction model by variable selection and
* bootstrapping
* PARAMETERS: data = dataset to use
* nsamp = numbers of bootstrapping
* out = name of scored dataset
*****************************************************************/
proc sgplot data = &data;
title 'the invesment grade default rates by years';
series x = year y = idr;
yaxis grid;
run;

ods graphics on;
proc macontrol data = &data;
title 'three year moving average chart for invesment grade default rate';
machart idr*year / span = 3 odstitle = title;
run;
proc glmselect data = &data plots = all;
model idr = prf age bbb spr/selection = stepwise(select = press);
modelaverage nsamples = &nsamp subset(best = 1);
output out = &out(drop = _:) p = pred_idr;
run;
ods graphics off;
%mend idrbs;

%idrbs(data = idr_t, nsamp = 10000, out = idr_scored);

****************END OF ALL CODING***************************************;

Saturday, June 4, 2011

The eve of Lehman Brothers' demise


Recently Moody’s warned the US government to degrade its credit rating if the nation’s debt limit increase is not approved [Ref. 1]. The news came right after Standard & Poor’s lowered US’s sovereign rating from AAA to AA. Those rating changes suggest the accumulation of default risk and may cause some butterfly effect.

Before the start of this Great Recession, without much notice, Lehman Brothers’ default probability increased drastically according to a classic model by Merton [Ref. 2]. And this change failed to be disclosed by either Standard & Poor’s rating or the stock price. Here I translated Gunter and Peter’s VBA code for Merton’s model [Ref. 3] into SAS code and reproduced the trend plot. The result clearly shows that implementation of probability and statistics may catch alert within the narrow 'escape' window, and therefore help avoid or mitigate risks .

References:
1. ‘Moody’s Warns of Downgrade for U.S. Credit’. The New York Times. 02JUN2011
2. Merton, R.C. ‘On the pricing of corporate debt: The risk structure of interest rates’. The Journal of Finance. 29(2): 449-470. 1974
3. Gunter Loeffler and Peter Posch. ‘Credit Risk Modeling using Excel and VBA’. The 2nd edition. Wiley.


/*******************READ ME*********************************************
* - The eve of Lehman Brothers' demise -
*
* SAS VERSION: 9.2.2
* DATE: 04jun2011
* AUTHOR: hchao8@gmail.com
****************END OF READ ME******************************************/

proc fcmp outlib = work.cg.func;
function cg_ps(s, sigma_s, d, lambda, sigma_b, t);
d1 = (s + lambda*d) * exp(sigma_b ** 2) / (lambda *d);
alpha = (((sigma_s*s) / (s + lambda * d))**2 * t + sigma_b**2)**0.5;
x = probnorm(-(alpha/2) + (log(d1)/alpha)) -
d1 * probnorm(-(alpha/2) - (log(d1) / alpha));
return(x);
endsub;
run;

data record;
input @1 date $7. @11 sp 4.2 @18 dps 20.8 @32 Vol30d 4.2 @43 s_p $2.;
cards;
Q4 2003 36.11 69.95612419 25.26 A+
/*To buy Gunter and Peter's book will have the full data*/
Q2 2008 36.81 127.8 99.93 A
;;;
run;

options cmplib = work.cg;
data scored;
set record;
global_rcv = 0.5;
vol_barrier = 0.1;
time = 1;
vol30d = vol30d / 100;
pd = 1 - cg_ps(sp, vol30d, dps, global_rcv, vol_barrier, time);
label sp = 'Lehman Brothers'' stock price'
pd = 'Probability of default';
run;

proc sgplot data = scored;
series x = date y = sp;
series x = date y = pd / y2axis;
yaxis values= (0 to 90 by 10) label = 'stock price($)';
y2axis values= (0 to 0.4 by 0.05) grid
label = 'Default rating by Merton''s model';
refline 'Q4 2005' / axis = x;
inset "Time period when S&P rating as A" / position = topright ;
inset "Time period when S&P rating as A+" / position = topleft;
run;

Thursday, June 2, 2011

Using Proc IML for credit risk validation


Validation step is crucial for a scorecard in credit risk industry. Gunter and Peter mentioned in their fantastic book [Ref. 1] that cumulative accuracy profile (CAP) and receiver operating characteristic (ROC) are two popular methods. Thus, the values of accuracy ratio from CAP (or I refer it as Gini coefficient) and area under curve(AUC) from ROC would be important metrics to evaluate the discriminatory power of the scorecard. And actually they can be derived from each other by their linear relationship.

In the latest post of his blog, Rick Wicklin introduced how to implement the trapezoidal rule or calculate trapezoid areas under curve by a function of Proc IML [Ref. 2]. Although the same methodology can be realized by Data Step array or Proc FCMP, the beauty of Rick’s method is that it avoids the loops through IML’s matrix operation and therefore is more efficient and scalable. In the example below, I built Rick’s function into a macro to calculate AUC and accuracy ratio for a tiny testing dataset. The ‘TrapIntegral’ function can be further applied for validation of large-scale credit risk records.

References:
1. Gunter Löeffler and Peter Posch. ‘Credit Risk Modeling using Excel and VBA’. The 2nd edition. Wiley.
2. Rick Wicklin. ‘The Trapezoidal Rule of Integration’. The Do Loop. 01JUN2011.

/*******************READ ME*********************************************
* - Using Proc IML for credit risk validation -
*
* SAS VERSION: 9.2.2
* EXCEL VERSION: 2007
* DATE: 02jun2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME******************************************/

****************(1) MODULE-BUILDING STEP********************************;
%macro auc(data =, path =, filename =);
/*****************************************************************
* MACRO: auc()
* GOAL: calcuate auc and accuracy ratio for default risk
* PARAMETERS: data = dataset to use
* path = output path
* filename = name for validation card
*****************************************************************/
options mprint mlogic;
ods listing close;
proc sql;
select sum(default) into :totaldef
from &data;
quit;

data _tmp01;
set &data nobs = nobs;
xratio = ifn(default = 0, 1, 0)/(nobs - &totaldef);
yratio = default/&totaldef;
run;
proc sort data = _tmp01 out = _tmp02;
by descending rating descending default;
run;
data _tmp03;
set _tmp02;
by descending rating;
retain x y;
if _n_ = 1 then do; x = 0; y = 0; end;
x + xratio;
y + yratio;
if last.rating;
run;
data _tmp04;
if _n_ = 1 then do; x = 0; y = 0; end; output;
set _tmp03(keep = x y);
run;

proc iml;
use _tmp04;
read all var{x y};
start TrapIntegral(x,y);
N = nrow(x);
dx = x[2:N] - x[1:N-1];
meanY = (y[2:N] + y[1:N-1])/2;
return( dx` * meanY );
finish;
area = TrapIntegral(x,y);
acuratio = 2*area - 1;
call symput('area', left(char(area)));
call symput('acuratio', left(char(acuratio)));
quit;

ods html file = "&path&filename..xls" gpath = "&path" style = harvest;
title; footnote;
proc print data = &data label noobs;
run;

proc sgplot data = _tmp04;
series x = x y = y ;
scatter x = x y = y;
band x =x upper = y lower = 0 / transparency=.5;
xaxis grid;
yaxis grid;
inset "AUC is: %sysfunc(putn(&area, 8.4));
Accuracy Ratio is: %sysfunc(putn(&acuratio, 8.4))"
/ position = bottomright border;
keylegend "scatter";
run;
ods html close;

proc datasets;
delete _:;
quit;
ods listing;
%mend auc;

****************(2) TESTING STEP****************************************;
data test;
input Observation Rating $ Default;
label rating = 'Rating(A is best)'
default = 'Default(1=default)';
cards;
1 A 0
2 A 0
3 A 0
4 B 1
5 B 0
6 B 0
7 C 1
8 C 1
9 C 1
10 C 0
;;;
run;

%auc(data = test, path = h:\, filename = valid);
****************END OF ALL CODING***************************************;

Wednesday, May 25, 2011

Semiparametric methods in predicting loss given default


Sparse data is a big concern in building models for loss given default (LGD) for corporate risk. For LGD, most predictors are instrument-related, firm-specific, macroeconomic and industry-specific variables, while the costs to collect such data may be relatively high. In one example of Gunter and Peter’s book, industry-wise average default rate, yearly average default rate, firm-wise leverage rate were applied to predict LGD. To increase the predictability, the painful transformation of LGD was conducted [Ref. 1]. Actually some non-linear models could be considered.

In a conference paper about consumer risk scoring, Wensui mentioned that generalized additive model (GAM) provides the ability to detect the nonlinear relationship between risk behavior and predictors [Ref. 2]. In this example, we are possibly more interested in estimating the parameter of firm-specific leverage (lev). Thus I used Proc GAM to estimate this variable’s parameter while smoothing other predictors by LOESS functions. In addition, I used Proc LOESS to realize the nonparametric regression. Comparing the two methods in a series plot, their predictions of LGD are pretty close. As the result, Proc GAM may provide us an insightful tool to construct meaningful semiparametric regression to predict LGD.

References:
1. Gunter Loeffler and Peter Posch. ‘Credit Risk Modeling using Excel and VBA’. The 2nd edition. Wiley. 2011
2. Wensui Liu, Chuck Vu, Jimmy Cela.‘Generalizations of Generalized Additive Model (GAM): A Case of Credit Risk Modeling’. SAS Global 2009

data _tmp01;
infile "h:\raw_data.txt" delimiter = '09'x missover dsd firstobs=2;
informat lgd lev lgd_a i_def 8.3;
label lgd = 'Real loss given default'
lev = 'Leverage coefficient by firm'
lgd_a = 'Mean default rate by year'
i_def = 'Mean default rate by industry';
input lgd lev lgd_a i_def;
run;

ods html gpath = 'h:\' style = money;
ods graphics on;
proc loess data=_tmp01;
model lgd = lev lgd_a i_def / scale = sd select = gcv degree = 2;
score;
ods output scoreresults = predloess;
run;

proc gam data= _tmp01 plots = components(clm);
model lgd = loess(i_def) loess(lgd_a) param(lev) / method = gcv;
output out = predgam p = pbygam;
run;
ods graphics off;

data _tmp02;
merge predloess predgam;
keep p_LGD LGD pbygamLGD obs;
label p_LGD = 'Prediction by Proc LOESS'
pbygamLGD = 'Prediction by Proc GAM';
run;

proc sgplot data = _tmp02;
series x = obs y = lgd ;
series x = obs y = p_lgd;
series x = obs y = pbygamlgd;
yaxis label = 'loss given default';
run;
ods html close;