*! Version: 1.0.1 *! Author: Gregorio Impavido, International Monetry Fund, Email: gimpavido@imf.org * * 1.0.1 2/26/2024. cleans up the help file. Adds notification in details, * when betacap option is used. Adds error message when betcap is * used with AR(2). * program define mvfilter, rclass version 16.0 syntax varlist(min=1 ts) [if] [in], /// [Trend(string) Cycle(string) TYpe(string) Smooth(numlist max=1 >0) /// OPTimal DETails AR(numlist max=1 integer >=0 <=2) ADJust Loop BETAcap] tempvar /// cy_hp /// cycle from static HP filter (needed for estimating beta(s)) tr_hp /// trend from static HP filter (needed for estimating beta(s)) cy_ss /// cycle from SS before labeling tr_ss // trend from SS before labeling tempname /// lambda /// smoothing parameter filtertype /// one-sided or two-sided p /// dimension of the AR(p) process in the cycle b /// OLS estimates from AR regression in the cyc beta1 /// AR parameter imposed in the SS when AR(1) or AR(2) beta2 /// AR parameter imposed in the SS when AR(2) var1_hp /// sample variance of the cycle from the static filter var0_hp /// sample variance of the slope in the trend in the static filter vratio_smpl_hp /// sample variance ratio in the static filter factor /// adjustment factor for AR(1) (1-beta1^2) or AR(2) (1-beta1^2-beta2^2) hi lo av adjustor /// parmeters for the sample variance adjustment loop var1_ss /// sample variance of the cycle from the SS var0_ss /// sample variance of the slope in the trend from the SS vratio_smpl_ss /// sample variance ratio in the SS vratio_ss // variance ratio in the SS ******************************************************************************** ********************************* start checks ********************************* ******************************************************************************** quietly tsset *local timevar "`r(timevar)'" local unit "`r(unit)'" *local time_max_string "`r(tmaxs)'" local panel "`r(panelvar)'" if ("`panel'" != "") { display as error "Panel data not supported" error 198 } // check that options have been formulated correctly * check that either trend or cycle have been formulated if ("`trend'"!="" | "`cycle'"!="") confirm new variable `trend' `cycle' else { display as error "At least one of {bf:trend()} and {bf:cycle()} options must be specified" error 198 } * check if the filter is one-sided or two-sided if ("`type'"=="" | "`type'"=="twosided") scalar `filtertype' = "twosided" // this is the default option unless you specify -type(onesided)- else if ("`type'"=="onesided") scalar `filtertype' = "onesided" else { display as error "The type of filter can only be only specified as: either {bf:type(onesided)} or {bf:type(twosided)}" error 198 } * check what lambda to use (4 cases: 4th = endogenous lambda when "`optimal'"!="") if ("`smooth'"!="" & "`optimal'"!="") { // case 1: inconsistent options display as error "You cannot specify both {bf:smooth()} and {bf:optimal} options" error 198 } else if ("`smooth'"=="" & "`optimal'"=="") { // case 2: set lambda following Ravn-Uhlig (l=1600p^4, where p is the number of periods per quarter) if ("`unit'"=="daily") scalar `lambda' = 1600*(365/4)^4 if ("`unit'"=="weekly") scalar `lambda' = 1600*(12)^4 if ("`unit'"=="monthly") scalar `lambda' = 1600*(3)^4 if ("`unit'"=="quarterly") scalar `lambda' = 1600 if ("`unit'"=="halfyearly") scalar `lambda' = 1600*(1/2)^4 if ("`unit'"=="yearly") scalar `lambda' = 1600*(1/4)^4 } else if ("`smooth'"!="" & "`optimal'"=="") { // case 3: user-specified smoothing parameter scalar `lambda' = `smooth' } * check AR() if ("`ar'"=="") scalar `p' = 0 else scalar `p' = `: di `ar'' if (("`ar'"=="0" | "`ar'"=="") & "`adjust'"!="") { display as error "There is nothing to adjust if you are not running the dynamic filter. Please drop the option {bf:adjust}" error 198 } if ("`ar'">="1" & "`optimal'"!="") { // case 1: inconsistent options display as error "The AR parameter(s) are estimated ex-ante for a given lambda. Hence, you cannot specify both {bf:AR(p)} with p>=1 and {bf:optimal}" error 198 } if ("`ar'">="1" & "`type'"=="onsided") { // case 1: inconsistent options display as error "Since the autoregressive parameters will be estimated from the cycle stemming from a two-sided filter, you cannot specify both {bf:AR(p)} with p>=1 and {bf:type(onesided}" error 198 } if ("`adjust'"=="" & "`loop'"!="") { display as error "There is nothing to loop as you have not spefied {bf:adjust}. Please drop {bf:loop}" error 198 } if ("`ar'"=="2" & "`betacap'"!="") { // case 1: inconsistent options display as error "The autoregressive parameter can be capped to min(beta,0.85) only with AR(1)" error 198 } ******************************************************************************** ********************************** end checks ********************************** ******************************************************************************** ******************************************************************************** ********************************* start model ********************************** ******************************************************************************** **** get observed variable and exogenous variable(s) for the observed equation tokenize `varlist' local lhsvar "`1'" // this is the observed variable macro shift 1 local rhsvars "`*'" // this is the exogenous variable(s) for the observed equation marksample touse markout `touse' `lhsvar' `rhsvar' // further trim the sample to match what SS will do **** case p==0. if (`p'==0) { *** set constraints constraint drop _all * State transition matrix A constraint define 1 [st1]l.st1 = 2 constraint define 2 [st1]l.st2 = -1 constraint define 4 [st2]l.st1 = 1 * State exogenous variables matrix B * State errors matrix C constraint define 10 [st1]e.st1 = 1 * Observation matrix D constraint define 13 [`lhsvar']st1 = 1 * Observation exogenous variables matrix F * Observation errors matrix G constraint define 18 [`lhsvar']e.`lhsvar' = 1 * Constraint on the variance ratio of state and observed error variances if ("`optimal'"=="") { // with optimal you don't restrict the variance ratio and let the Kalman filter estimate lambda constraint define 19 `lambda'*[/state]state_sigma2=([/observable]obser_sigma2) } quietly sspace /// (st1 l.st1 l.st2 e.st1, state noconstant) /// (st2 l.st1 , state noconstant) /// (`lhsvar' st1 `rhsvars' e.`lhsvar', noconstant) if `touse', /// constraints(1/19) covstate(dscalar) covobserved(dscalar) scalar `vratio_ss' = [/observable]obser_sigma2/[/state]state_sigma2 // lambda splits the weights in front of the 2 error terms if (`filtertype'=="onesided") { quietly predict `tr_ss' if `touse', smethod(filter) equation(st1) states quietly gen `cy_ss' = `lhsvar' - `tr_ss' if `touse' } if (`filtertype'=="twosided") { quietly predict `tr_ss' if `touse', smethod(smooth) equation(st1) states quietly gen `cy_ss' = `lhsvar' - `tr_ss' if `touse' } } **** case p==1. if (`p'==1) { **** estimation of the autoregressive parameter qui tsfilter hp `cy_hp' = `lhsvar' if `touse', smooth(`: di `lambda'') qui gen `tr_hp' = `lhsvar' - `cy_hp' if `touse' qui arima `cy_hp' if `touse', ar(1) mat `b' = e(b) scalar `beta1' = `b'[1,2] if ("`betacap'"!="") scalar `beta1' = min(`b'[1,2], 0.85) // Borio's prior (?) qui summ `cy_hp' if `touse' scalar `var1_hp' = r(sd)^2 qui summ D2.`tr_hp' if `touse' scalar `var0_hp' = r(sd)^2 scalar `vratio_smpl_hp' = `var1_hp'/`var0_hp' *** set constraints constraint drop _all * State transition matrix A constraint define 1 [st1]l.st1 = 2 constraint define 2 [st1]l.st2 = -1 constraint define 4 [st2]l.st1 = 1 * State exogenous variables matrix B * State errors matrix C constraint define 10 [st1]e.st1 = 1 * Observation matrix D constraint define 13 [`lhsvar']st1 = 1 constraint define 14 [`lhsvar']st2 = -`beta1' * Observation exogenous variables matrix F constraint define 16 [`lhsvar']l.`lhsvar' = `beta1' * Observation errors matrix G constraint define 18 [`lhsvar']e.`lhsvar' = 1 * Constraint on the variance ratio of state and observed error variances scalar `adjustor' = 1 // needed for the loop scalar `factor' = (1-`beta1'^2) constraint define 19 (`lambda'*`factor'*`adjustor')*[/state]state_sigma2=([/observable]obser_sigma2) if ("`adjust'"=="") { quietly sspace /// (st1 l.st1 l.st2 e.st1, state noconstant) /// (st2 l.st1 , state noconstant) /// (`lhsvar' st1 st2 l.`lhsvar' `rhsvars' e.`lhsvar', noconstant) if `touse', /// constraints(1/19) covstate(dscalar) covobserved(dscalar) scalar `vratio_ss' = [/observable]obser_sigma2/[/state]state_sigma2 quietly predict `tr_ss' if `touse', smethod(smooth) equation(st1) states quietly gen `cy_ss' = `lhsvar' - `tr_ss' if `touse' } if ("`adjust'"!="") { * initialize the loop scalar `hi' = 2 //we need to endogenize the max scalar `lo' = 0 scalar `av' = (`hi'+`lo')/2 noisily di " " if ("`loop'"=="") noisily _dots 0, title(Adjusting the SS variance ratio for small sample problem) local rep 1 // this is just a counter while (`hi'-`lo') > 0.00001 { capture drop `cy_ss' `tr_ss' scalar `adjustor' = `av' *constraint define 19 (`lambda'*`factor'*`adjustor')*[/state]state_sigma2=([/observable]obser_sigma2) quietly sspace /// (st1 l.st1 l.st2 e.st1, state noconstant) /// (st2 l.st1 , state noconstant) /// (`lhsvar' st1 st2 l.`lhsvar' `rhsvars' e.`lhsvar', noconstant) if `touse', /// constraints(1/19) covstate(dscalar) covobserved(dscalar) scalar `vratio_ss' = [/observable]obser_sigma2/[/state]state_sigma2 predict `tr_ss' if `touse', smethod(smooth) equation(st1) states quietly gen `cy_ss' = `lhsvar' - `tr_ss' if `touse' quietly summ `cy_ss' if `touse' scalar `var1_ss'=r(sd)^2 quietly summ D2.`tr_ss' if `touse' scalar `var0_ss'=r(sd)^2 scalar `vratio_smpl_ss' = `var1_ss'/`var0_ss' if (`vratio_smpl_ss'>=`vratio_smpl_hp') scalar `hi'=`av' else scalar `lo'=`av' scalar `av' = (`hi'+`lo')/2 if ("`loop'"!="") { noisily di as text "Rep. No: " `rep' noisily di as text "AR(1) Var ratio = " `vratio_ss' " noisily di as text "AR(1) Var ratio adjustor = " `adjustor' noisily di as text "AR(1) smpl Var ratio = " `vratio_smpl_ss' noisily di as text "AR(0) smpl Var ratio = " `vratio_smpl_hp' noisily di as text " " } local ++rep if ("`loop'"=="") noisily _dots `rep' 0 } // end of while } // end of if } // end of p=1 **** case p==2. if (`p'==2) { **** estimation of the autoregressive parameter qui tsfilter hp `cy_hp' = `lhsvar' if `touse', smooth(`: di `lambda'') qui gen `tr_hp' = `lhsvar' - `cy_hp' if `touse' qui arima `cy_hp' if `touse', ar(1/2) mat `b' = e(b) scalar `beta1' = `b'[1,2] scalar `beta2' = `b'[1,3] qui summ `cy_hp' if `touse' scalar `var1_hp' = r(sd)^2 qui summ D2.`tr_hp' if `touse' scalar `var0_hp' = r(sd)^2 scalar `vratio_smpl_hp' = `var1_hp'/`var0_hp' *** set constraints constraint drop _all * State transition matrix A constraint define 1 [st1]l.st1 = 2 constraint define 2 [st1]l.st2 = -1 constraint define 4 [st2]l.st1 = 1 constraint define 8 [st3]l.st2 = 1 * State exogenous variables matrix B * State errors matrix C constraint define 10 [st1]e.st1 = 1 * Observation matrix D constraint define 13 [`lhsvar']st1 = 1 constraint define 14 [`lhsvar']st2 = -`beta1' constraint define 15 [`lhsvar']st3 = -`beta2' * Observation exogenous variables matrix F constraint define 16 [`lhsvar']l.`lhsvar' = `beta1' constraint define 17 [`lhsvar']l2.`lhsvar' = `beta2' * Observation errors matrix G constraint define 18 [`lhsvar']e.`lhsvar' = 1 * Constraint on the variance ratio of state and observed error variances scalar `adjustor' = 1 // needed for the loop scalar `factor' = (1-`beta1'^2-`beta2'^2) constraint define 19 (`lambda'*`factor'*`adjustor')*[/state]state_sigma2=([/observable]obser_sigma2) if ("`adjust'"=="") { quietly sspace /// (st1 l.st1 l.st2 e.st1, state noconstant) /// (st2 l.st1 , state noconstant) /// (st3 l.st2 , state noconstant) /// (`lhsvar' st1 st2 st3 l.`lhsvar' l2.`lhsvar' `rhsvars' e.`lhsvar', noconstant) if `touse', /// constraints(1/19) covstate(dscalar) covobserved(dscalar) scalar `vratio_ss' = [/observable]obser_sigma2/[/state]state_sigma2 quietly predict `tr_ss' if `touse', smethod(smooth) equation(st1) states quietly gen `cy_ss' = `lhsvar' - `tr_ss' if `touse' } if ("`adjust'"!="") { * initialize the loop scalar `hi' = 2 //we need to endogenize the max scalar `lo' = 0 scalar `av' = (`hi'+`lo')/2 noisily di " " if ("`loop'"=="") noisily _dots 0, title(Adjusting the SS variance ratio for small sample problem) local rep 1 // this is just a counter while (`hi'-`lo') > 0.00001 { capture drop `cy_ss' `tr_ss' scalar `adjustor' = `av' *constraint define 19 (`lambda'*`factor'*`adjustor')*[/state]state_sigma2=([/observable]obser_sigma2) quietly sspace /// (st1 l.st1 l.st2 e.st1, state noconstant) /// (st2 l.st1 , state noconstant) /// (st3 l.st2 , state noconstant) /// (`lhsvar' st1 st2 st3 l.`lhsvar' l2.`lhsvar' `rhsvars' e.`lhsvar', noconstant) if `touse', /// constraints(1/19) covstate(dscalar) covobserved(dscalar) scalar `vratio_ss' = [/observable]obser_sigma2/[/state]state_sigma2 predict `tr_ss' if `touse', smethod(smooth) equation(st1) states quietly gen `cy_ss' = `lhsvar' - `tr_ss' if `touse' quietly summ `cy_ss' if `touse' scalar `var1_ss'=r(sd)^2 quietly summ D2.`tr_ss' if `touse' scalar `var0_ss'=r(sd)^2 scalar `vratio_smpl_ss' = `var1_ss'/`var0_ss' if (`vratio_smpl_ss'>=`vratio_smpl_hp') scalar `hi'=`av' else scalar `lo'=`av' scalar `av' = (`hi'+`lo')/2 if ("`loop'"!="") { noisily di as text "Rep. No: " `rep' noisily di as text "AR(2) Var ratio = " `vratio_ss' " noisily di as text "AR(2) Var ratio adjustor = " `adjustor' noisily di as text "AR(2) smpl Var ratio = " `vratio_smpl_ss' noisily di as text "AR(0) smpl Var ratio = " `vratio_smpl_hp' noisily di as text " " } local ++rep if ("`loop'"=="") noisily _dots `rep' 0 } // end of while } // end of if } // end of p=2 ******************************************************************************** *********************************** end model ********************************** ******************************************************************************** ******************************************************************************** ****************************** label the estimates ***************************** ******************************************************************************** if ("`details'"!="") { sspace, noomitted if ("`betacap'"!="") di "NB: Autoregressive parameter capped at min(beta,0.85)" } if (`filtertype'=="onesided") { if ("`trend'"!="") { quietly generate `trend' = `tr_ss' if `touse' label var `trend' "`lhsvar' trend component from SS (one-sided, Var ratio = `: di `vratio_ss'', AR(`: di `p'')" } if ("`cycle'"!="") { quietly generate `cycle' = `cy_ss' if `touse' label var `cycle' "`lhsvar' cyclical component from SS (one-sided, Var ratio = `: di `vratio_ss'', AR(`: di `p''))" } } if (`filtertype'=="twosided") { // this is the default option unless you specify -type(onesided)- if ("`trend'"!="") { quietly generate `trend' = `tr_ss' if `touse' label var `trend' "`lhsvar' trend component from SS (two-sided, Var ratio = `: di `vratio_ss'', AR(`: di `p'')" } if ("`cycle'"!="") { quietly generate `cycle' = `cy_ss' if `touse' label var `cycle' "`lhsvar' cyclical component from SS (two-sided, Var ratio = `: di `vratio_ss'', AR(`: di `p''))" } } ******************************************************************************** ******************************** end estimations ******************************* ******************************************************************************** di as text "Trend and cycle estimated" ******************************************************************************** ********************************** start return ******************************** ******************************************************************************** return scalar lambda = `lambda' return scalar var_ratio = `vratio_ss' return scalar ar_p = `p' if (`filtertype'=="onesided") { return local type = `"MV filter (one-sided)"' } if (`filtertype'=="twosided") { return local type = `"MV filter (two-sided)"' } return local unit = "`unit'" return local observed = "`lhsvar'" return local exogenous = "`rhsvars'" return local cycle = "`cycle'" return local trend = "`trend'" ******************************************************************************** *********************************** end return ********************************* ******************************************************************************** end