Date: Fri, 20 Feb 2004 14:27:24 -0600
Reply-To: "Busby, Phil" <Phil.Busby@GMACERM.COM>
Sender: "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From: "Busby, Phil" <Phil.Busby@GMACERM.COM>
Subject: How to tell SET KEY= to repoint to the top of the subset?
In the valuation module of a VaR Monte-Carlo simulation we have to
generate a forward price curve into an array from within a data step.
To do this we have a lookup data set of commodity forward prices
indexed by currency and risk factor group. There are multiple
observations for a given key, ordered by future delivery date.
The first time the macro is invoked, the SET statement reads all of the
observations matching the key and correctly returns _IORC_=DSENOM
when there are no more matches. Subsequent invocations for the same
currency and risk factor group do not work, however, because the key
has not changed. How do we tell the SET statement to point back to
the top of the subset?
Multiple SET statements do not work; they are processed independently.
The /UNIQUE option always returns just the first matching observation.
The POINT option is not allowed with KEY=. My workaround, as shown in
the code below, is to do an extra execution of the SET statement with
a forced key change, but this seems so clunky and slow.
Can't find anything in the archives on this. No answer from support@sas
<mailto:support@sas> .
Anybody see a better way?
%macro get_commodity_price(currency, group, avgmeth, horizon, shock, spot,
forward);
****************************************************************************
*******;
* get_commodity_price
* Given a currency, group, averaging method, horizon, and shock,
* return a spot price and a forward curve for the commodity. The spot
price
* is returned in the scalar variable the caller supplies the name of, and
* the forward curve is returned in a Fincad array, also named by the
caller.
*
* This macro is invoked from within a data step.
*
* For a given horizon=0 and shock=0, the price comes from the
risk_factor_value in
* the sim_parameters data set, and the forward curve comes from the
commodity forward
* data set. If the horizon or shock is not zero, the price comes from
sim_expected_value
* data set, and the forward curve comes from the commodity forward data
set but with each
* forward price adjusted by the damping factor. The damping factor is
given in the
* commodity forward data set and the forward price is adjusted by the
formula
* forward_price + (damping_factor * (MuX + (shock * sigmaX)) - spot_price)
where the
* spot price is the risk_factor_value from the sim_parms data set.
*
* Input:
* currency - 3-character currency code for the commodity
* group - risk_factor_group name
* avgmeth - averaging method to use
* horizon - horizon in months
* shock - number of standard deviations the price is shocked
* Output:
* spot - name of variable to get the resulting spot price
* forward - name of Fincad array to get the resulting forward curve
* of dates and prices.
****************************************************************************
*********;
length _irisk_factor_group $ 20;
length _icurrency $ 3;
if (&horizon=0 and &shock=0) then do;
* Determine spot price for Zero-horizon, Zero-shock commodity;
* Index Averaging Rule: 'Non-Averaging', '3 days', '1 Month';
* In sim_parameters, choose risk_factor_value, day3_avg, or month_avg;
%get_one_indexed(parm_rf_groups, ¤cy, &group, &horizon, 1, found);
if "&avgmeth"="Non-Averaging" then &spot = _irisk_factor_value;
else if "&avgmeth"="3 Days" then &spot = _iday3_avg;
else if "&avgmeth"="1 Month" then &spot = _imonth_avg;
else do;
%data_error(-1,Invalid averaging method: &avgmeth);
end;
%fc_makea(&forward, &max_curve, 2); * Make fincad array to store
forward prices;
gcp_n = 0; * number of curve points;
_iorc_= 0; * _iorc_ is reloaded by the SET
statement;
first = 1; * First pass through loop below;
* Read sim_commodity_fwd data set for currency and group;
do while(_iorc_ = 0);
* Force key change to make SAS start reading at beginning of subset;
if first then _icurrency = ''; * the values we are NOT
looking for;
else do;
_icurrency= "¤cy"; * the values we are
looking for;
_irisk_factor_group = "&group";
end;
set _icommodity_fwd_rf_groups
(keep=_icurrency _irisk_factor_group _iterm
_ifuture_last_deliv_date
_irisk_factor_value _idamping_factor
_imonth_avg_deliv_date
_imonth_avg_fwd_rate)
key=_icommodity_fwd_rf_groupsi;
if first then do;
first = 0; * Not first time through this loop now;
_iorc_= 0; * Loop again to read the terms we want;
end;
else do;
if _iorc_ = 0 then do; * another term
found;
gcp_n = gcp_n + 1;
%if "&avgmeth" = "1 Month" %then %do;
%fc_seta(&forward, gcp_n, 1,
%fc_to_date(_imonth_avg_deliv_date));
%fc_seta(&forward, gcp_n, 2, _imonth_avg_fwd_rate);
%end;
%else %do; * 3 Days or Non-Averaging, some values may be
missing;
if _irisk_factor_value ne . then do;
%fc_seta(&forward, gcp_n, 1,
%fc_to_date(_ifuture_last_deliv_date));
%fc_seta(&forward, gcp_n, 2, _irisk_factor_value);
end;
%end;
end;
else if _iorc_ = %sysrc(_DSENOM) then do; * ran out of terms;
_error_ = 0;
end;
else do;
%data_error(_iorc_, There was error reading
_icommodity_fwd_rf_groups);
end;
end; * not first;
end; * do while more_terms _iorc_=0;
if gcp_n = 0 then do;
%data_error(-1, Could not find zero horizon or shock forward
terms.);
end;
%fc_makea(&forward, gcp_n, 2); * now we know how many rows;
end;
else do;
* Determine spot price for Non-zero horizon or non-zero shock commodity;
%get_one_indexed(expect_rf_groups,¤cy,&group,&horizon,1,found);
&spot = _imux + (_isigmax * &shock);
* Determine spot price for Zero-horizon, Zero-shock commodity, ignoring
averaging;
%get_one_indexed(parm_rf_groups,¤cy,&group,&horizon,1,found);
zzspot = _irisk_factor_value;
spotdiff = &spot-zzspot; * compute (expected - spot);
* Read sim_commodity_fwd data set for currency and group;
%fc_makea(&forward, &max_curve, 2); * make fincad array to store
forward prices;
gcp_n = 0; * number of curve points;
_iorc_= 0; * Automatic SAS variable;
first = 1; * Priming read needed;
do while(_iorc_ = 0);
* Force key change to make SAS start reading at
beginning of list;
if first then _icurrency = ''; * the values we are NOT
looking for;
else do;
_icurrency= "¤cy"; * the values we are
looking for;
_irisk_factor_group = "&group";
end;
set _icommodity_fwd_rf_groups
(keep=_icurrency _irisk_factor_group _iterm
_ifuture_last_deliv_date
_irisk_factor_value _idamping_factor
_imonth_avg_deliv_date
_imonth_avg_fwd_rate)
key=_icommodity_fwd_rf_groupsi;
if first then do;
first =0; * Not first time through this loop now;
_iorc_=0; * Loop again to read the terms we want;
end;
else do;
if _iorc_ = 0 then do; * another term
found;
gcp_n = gcp_n + 1;
if _irisk_factor_value ne . then do; * Ignore missing
value obs;
%fc_seta(&forward, gcp_n, 1,
%fc_to_date(_ifuture_last_deliv_date));
* Apply damping factor to forward curve value;
dampedvalue = _irisk_factor_value + (_idamping_factor *
spotdiff);
%fc_seta(&forward, gcp_n, 2, dampedvalue);
end;
end;
else if _iorc_ = %sysrc(_DSENOM) then do; * ran out of terms;
_error_ = 0; * NO More, not an
error;
end;
else do;
%data_error(_iorc_, There was error reading
_icommodity_fwd_rf_groups);
end;
end; * not first;
end; * do while more terms: _iorc_=0;
if gcp_n = 0 then do;
%data_error(-1, Could not find non-zero horizon or shock forward
terms.);
end;
%fc_makea(&forward, gcp_n, 2); * now we know how many rows;
end; *NON-zero horizon and/or shock;
%mend get_commodity_price;
|