4. Reduced-form VAR Modeling
4.1. Description
\[y_{t}=C\left( r_{t}\right) x_{t}+B_{1}\left( r_{t}\right)
y_{t-1}+...+B_{p}\left( r_{t}\right) y_{t-p}+u_{t}\]
With \(r_{t}=1,2,...,h\) and transition probabilities \(p_{r_{t},r_{t+1}}\left( I_{t}\right)\)
4.2. Quick-start examples
4.2.1. A constant-parameter VAR
Collecting and transforming data
% CLVMNACSCAB1GQNO : GDP Norway
% IR3TIB01NOQ156N : 3-month Interbank interest rate
% NORCPGRLE01IXOBQ : CPI excluding food and energy
% CCUSSP01NOQ650N : Spot Exchange rate : 1NOK = x USD
% POILWTIUSDQ : Global price of WTI Crude
xrange='1990Q1:2022Q3';
rawdb=fetch_fred({'NORCPGRLE01IXOBQ','IR3TIB01NOQ156N','CLVMNACSCAB1GQNO',...
'CCUSSP01NOQ650N','POILWTIUSDQ'});
rawdb1.P=rawdb(1).series(xrange);
rawdb1.INTRATE=rawdb(2).series(xrange);
rawdb1.Y=rawdb(3).series(xrange);
rawdb1.EXRATE=rawdb(4).series(xrange);
rawdb1.POIL=rawdb(5).series(xrange);
rawdb=rawdb1; clear rawdb1
db=struct();
db.PAI=rawdb.P/lag(rawdb.P,1);
db.R=1+rawdb.INTRATE/100;
db.GROWTH=rawdb.Y/lag(rawdb.Y,1);
db.EXRATE=1/rawdb.EXRATE; % so that increase = depreciation of NOK
db.PAIOIL=rawdb.POIL/lag(rawdb.POIL,1);
Setting up the Reduced.form VAR
endog={'PAIOIL','GROWTH','PAI','R','EXRATE'};
exog={};
nlags=4;
const=true;
mdl = rfvar(endog, exog, nlags, const);
Estimating the VAR using classical techniques
data_range={db.GROWTH.start,db.GROWTH.finish};
mdlest=estimate(mdl,db,data_range);
Restrictions on the VAR : Domestic variables do not affect oil prices
linres={
'b1(PAIOIL,PAI)=0'
'b1(PAIOIL,GROWTH)=0'
'b1(PAIOIL,R)=0'
'b1(PAIOIL,EXRATE)=0'
'b2(PAIOIL,PAI)=0'
'b2(PAIOIL,GROWTH)=0'
'b2(PAIOIL,R)=0'
'b2(PAIOIL,EXRATE)=0'
};
% or programmatically
% linres=cell(0,1);
% for ilag=1:nlags
% for iv=2:numel(endog)
% y=endog{iv};
% linres{end+1,1}=sprintf('b%0.0f(PAIOIL,%s)=0',ilag,y); %#ok<SAGROW>
% end
% end
Estimate the restricted VAR
prior=[];
mdlest_restr=estimate(mdl,db,data_range,prior,linres);
Identification
shock_names={'oilp','demand','costpush','mp','forex'};
ident_restr1={
% normalization with sign restrictions
'PAIOIL{0}@oilp','+'
'GROWTH{0}@demand','+'
'PAI{0}@costpush','+'
'R{0}@mp','+'
'EXRATE{0}@forex','+'
% first set
'PAIOIL{0}@demand',0
'PAIOIL{0}@costpush',0
'PAIOIL{0}@mp',0
'PAIOIL{0}@forex',0
% second set
'GROWTH{0}@costpush',0
'GROWTH{0}@mp',0
'GROWTH{0}@forex',0
% third set
'PAI{0}@mp',0
'PAI{0}@forex',0
% fourth set
'R{0}@forex',0
};
agnostic=true;
max_trials=6000;
Rfunc=struct(); ident=struct();
[Rfunc.unrestr,ident.unrestr]=identification(mdlest,ident_restr1,shock_names,...
agnostic,max_trials);
[Rfunc.restr,ident.restr]=identification(mdlest_restr,ident_restr1,shock_names,...
agnostic,max_trials);
disp(ident.restr)
disp(ident.unrestr)
Compare structural shocks
params=[];
sshocks=struct();
sshocks.unrestr=structural_shocks(mdlest,...
params,Rfunc.unrestr,shock_names);
sshocks.restr=structural_shocks(mdlest_restr,...
params,Rfunc.restr,shock_names);
% plots
%-------
titel='Structural shocks';
figure('name',titel);
for ii=1:numel(shock_names)
thisname=shock_names{ii};
subplot(3,2,ii)
d=[sshocks.unrestr.(thisname),sshocks.restr.(thisname)];
plot(d,'linewidth',2)
xtickangle(45)
title(thisname)
if ii==1
legend({'unrestr','restr'})
end
end
% xrotate(45)
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
Compare CHOLESKI irfs
cholShocks=[];
myirfs=irf([mdlest,mdlest_restr],cholShocks,40);
cholShocks=fieldnames(myirfs);
tex=struct();
tex.PAIOIL='Oil price inflation';
tex.GROWTH='GDP growth';
tex.PAI='Inflation';
tex.R = 'Interest rate';
tex.EXRATE='exchange rate';
for ishock=1:numel(cholShocks)
shock=cholShocks{ishock};
titel=['Impulse responses to a ',shock,' shock'];
figure('name',titel);
for ii=1:numel(endog)
subplot(3,2,ii)
plot(myirfs.(shock).(endog{ii}),'linewidth',2)
title(tex.(endog{ii}))
if ii==1
legend({'unrestr','restr'})
end
end
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
end
Compare irfs based on the identification scheme
params=[];
% Note the two models have the same identification !!!
% This is why we can run them together
myirfs=irf([mdlest,mdlest_restr],shock_names,40,params,Rfunc.unrestr);
for ishock=1:numel(shock_names)
shock=shock_names{ishock};
titel=['Impulse responses to a ',shock,' shock'];
figure('name',titel);
for ii=1:numel(endog)
subplot(3,2,ii)
plot(myirfs.(shock).(endog{ii}),'linewidth',2)
title(tex.(endog{ii}))
if ii==1
legend({'unrestr','restr'})
end
end
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
end
Variance decomposition
params=[];
vd=variance_decomposition(mdlest_restr,params,Rfunc.restr);
% plot decompositions
%---------------------
range='0:50'; % pick a range for the plots
figure('name','Variance Decomposition');
for iv=1:numel(endog)
d=vd.conditional.(endog{iv});
subplot(3,2,iv)
plot_decomp(range,d)
if iv==1
legend(shock_names,'location','SE',...
'Orientation','horizontal')
end
title(tex.(endog{iv}))
end
Historical decomposition
params=[];
hd=historical_decomposition(mdlest_restr,params,Rfunc.restr);
% plot decompositions
%----------------------
shock_only=true;
titel='Model with block exogeneity: Historical Decomposition';
figure('name',titel);
for iv=1:numel(endog)
d=hd.(endog{iv});
subplot(3,2,iv)
if shock_only
shock_tex=shock_names;
d=d(shock_names);
else
shock_tex=d.varnames;
end
plot_decomp(d)
if iv==1
legend(shock_tex,'location','SE','Orientation','horizontal')
end
title(tex.(endog{iv}))
end
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
Bootstrap
n=1000;
params=bootstrap(mdlest_restr,n);
Variance decomposition distribution
ci=[30,50,68,90];
vd=variance_decomposition(mdlest_restr,params,Rfunc.restr);
shock_tex=shock_names;
myrange='1:50';
for iv=1:numel(endog)
vname=tex.(endog{iv});
titel=['Variance Decomposition (in %) of ',vname];
figure('name',titel);
d=vd.conditional.(endog{iv});
d.varnames=shock_names;
d=pages2struct(d);
contributors=fieldnames(d); % = shock_names
for ii=1:numel(contributors)
subplot(3,2,ii)
% note we are multiplying by 100, this just by pure convenience
%--------------------------------------------------------------
out=fanchart(100*d.(contributors{ii})(myrange),ci);
plot_fanchart(out)
title(contributors{ii})
axis tight
end
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
end
Historical decomposition distribution
ci=[30,50,68,90];
% compute decompositions
hd=historical_decomposition(mdlest_restr,...
params,Rfunc.restr);
% plot decompositions
for iv=1:numel(endog)
vname=tex.(endog{iv});
titel=['Model with restrictions : Historical Decomposition of ',vname];
figure('name',titel);
d=pages2struct(hd.(endog{iv}));
contributors=fieldnames(d);
for ii=1:numel(contributors)
subplot(5,2,ii)
out=fanchart(d.(contributors{ii}),ci);
plot_fanchart(out)
xtickangle(45)
title(contributors{ii})
axis tight
end
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
end
IRF distribution
myirfs=irf(mdlest_restr,shock_names,...
40,params,Rfunc.restr);
% IRFs plots
%--------------
for ishock=1:numel(shock_names)
shock=shock_names{ishock};
titel=['Model with restrictions : IRFs to a ',shock,' shock'];
figure('name',titel);
for ii=1:numel(endog)
subplot(3,2,ii)
d=myirfs.(shock).(endog{ii});
out=fanchart(d,ci);
plot_fanchart(out)
title(tex.(endog{ii}))
axis tight
end
[~,h]=sup_label(titel,'t');
set(h,'fontsize',12)
end
Bayesian estimation
v=rfvar(endog,exog,nlags,const);
%set prior
var_prior=rfvar.prior_template();
% modify as needed
var_prior.type='sz';
prior=struct('var',var_prior);
% prior.nonvar
% prior.endogenous
% unrestricted model
%--------------------
ve=estimate(v,db,data_range,prior);%,restrictions
% restricted model
%------------------
ve_lr=estimate(v,db,data_range,prior,linres);
Posterior sampling of parameters
params=struct();
params.ve=ve.estim_.sampler(1000);
params.ve_lr=ve_lr.estim_.sampler(1000);
Bayesian forecasting
myfkst=struct();
date_start='2003Q1';
myfkst.ve=forecast(ve,db,date_start,params.ve);
myfkst.ve_lr=forecast(ve_lr,db,date_start,params.ve_lr);
% set environment
ci=[30,50,68,90];
modelnames={'ve','ve_lr'};
% Forecast plots
for jj=1:numel(modelnames)
modname=modelnames{jj};
figure('name',['model (',modname,') Forecasts of Norwegian Data']);
for ii=1:numel(endog)
subplot(3,2,ii)
d=myfkst.(modname).(endog{ii});
out=fanchart(d,ci);
plot_fanchart(out,[244, 122, 66]/244)
title(tex.(endog{ii}))
axis tight
end
xrotate(45)
end
Conditional forecasting
myfkst=struct();
% date_start=[];
date_start='2003Q1';
nsteps=12;
shock_uncertainty=false;
Rfunc=[]; % No need for identification
conditions=struct();
conditions.R={'2003Q1','2004Q4'}; % range over which we want to condition
myfkst.ve=forecast(ve,db,date_start,params.ve,nsteps,...
shock_uncertainty,Rfunc,conditions);
myfkst.ve_lr=forecast(ve_lr,db,date_start,params.ve_lr,nsteps,...
shock_uncertainty,Rfunc,conditions);
% set environment
ci=[30,50,68,90];
% Forecast plots
for jj=1:numel(modelnames)
modname=modelnames{jj};
figure('name',['model (',modname,') Forecasts of Norwegian Data']);
for ii=1:numel(endog)
subplot(3,2,ii)
d=myfkst.(modname).(endog{ii});
out=fanchart(d,ci);
plot_fanchart(out,[244, 122, 66]/255)
title(tex.(endog{ii}))
axis tight
end
xrotate(45)
end
4.2.2. Adding regime switching with constant transition probabilities
%% adding constant transition probabilities
mc=struct();
mc.name='policy';
mc.number_of_states=2;
mc.controlled_parameters={'b(R,:)'};
mc.endogenous_probabilities=[];
mc.probability_parameters=[];
mdl = rfvar(endog, exog, nlags, const,mc);
%% Set priors and Estimate the VAR
switch_prior=struct();
switch_prior.policy_tp_1_2={0.5,0.1,0.3,'beta'};
switch_prior.policy_tp_2_1={0.5,0.1,0.3,'beta'};
prior=struct();
prior.nonvar=switch_prior;
% ___Optional prior on the VAR__
% var_prior=svar.prior_template();
% var_prior.type='sz';
% prior.var=var_prior;
data_range={db.GROWTH.start,db.GROWTH.finish};
mdlest=estimate(mdl,db,data_range,prior);
4.2.3. Adding regime switching with time~varying transition probabilities
%% adding time-varying transition probabilities
mc=struct();
mc.name='policy';
mc.number_of_states=2;
mc.controlled_parameters={'b(R,:)'};
mc.endogenous_probabilities={
'policy_tp_1_2=1/(1+exp(a12*(PAI-c12)))'
'policy_tp_2_1=1/(1+exp(a21*(PAI-c21)))'
};
mc.probability_parameters={'a12','a21','c12','c21'};
mdl = rfvar(endog, exog, nlags, const,mc);
%% Set priors and estimate the RFVAR
switch_prior=struct();
switch_prior.a12={0,0,1,'normal'};
switch_prior.a21={0,0,1,'normal'};
switch_prior.c12={1.05^.25,1.05^.25,0.05,'gamma'};
switch_prior.c21={1.05^.25,1.05^.25,0.05,'gamma'};
prior=struct();
prior.nonvar=switch_prior;
% ___Optional prior on the VAR__
% var_prior=svar.prior_template();
% var_prior.type='sz';
% prior.var=var_prior;
data_range={db.GROWTH.start,db.GROWTH.finish};
mdlest=estimate(mdl,db,data_range,prior);
4.3. Technical documentation for rfvar objects
Contents: