*! xtqsh v1.0.0 27feb2026 Dr Merwan Roudane merwanroudane920@gmail.com *! Quantile Regression Slope Homogeneity Test for Panel Data *! Based on: Galvao, Juhl, Montes-Rojas & Olmo (2017) *! "Testing Slope Homogeneity in Quantile Regression Panel Data *! with an Application to the Cross-Section of Stock Returns" *! Journal of Financial Econometrics, 2017, 1–33 capture program drop xtqsh program define xtqsh, eclass sortpreserve version 15.1 if replay() { if "`e(cmd)'" != "xtqsh" error 301 Display `0' } else Estimate `0' end * ===================================================================== * MAIN ESTIMATION PROGRAM * ===================================================================== capture program drop Estimate program define Estimate, eclass sortpreserve * ================================================================ * SYNTAX * ================================================================ syntax varlist(min=2 ts fv) [if] [in], TAU(numlist >0 <1 sort) /// [BW(string) HAC(integer 0) MARGinal /// LEVel(integer `c(level)') /// GRaph NOTAble] * Parse variables tokenize `varlist' local depvar "`1'" mac shift local indepvars "`*'" local k : word count `indepvars' local ntau : word count `tau' if `k' < 1 { di as err "at least one independent variable required" exit 198 } if `ntau' < 1 { di as err "at least one quantile required in tau()" exit 198 } * Default bandwidth if "`bw'" == "" local bw "hallsheather" if !inlist("`bw'", "bofinger", "hallsheather") { di as err "bw() must be {bf:bofinger} or {bf:hallsheather}" exit 198 } * ================================================================ * PANEL SETUP * ================================================================ _xt local ivar "`r(ivar)'" local tvar "`r(tvar)'" marksample touse markout `touse' `ivar' `tvar' qui count if `touse' local nobs = r(N) if `nobs' == 0 error 2000 * Count panels tempvar grp qui egen `grp' = group(`ivar') if `touse' qui summ `grp' if `touse', meanonly local npanels = r(max) * ================================================================ * HEADER * ================================================================ di di in smcl in gr "{hline 78}" di in smcl in gr " {bf:Quantile Regression Slope Homogeneity Test}" di in smcl in gr "{hline 78}" di in gr " H₀: β₁(τ) = β₂(τ) = ··· = βₙ(τ)" /// in gr " (slope homogeneity)" di in gr " H₁: βᵢ(τ) ≠ βⱼ(τ) for some i ≠ j" /// in gr " (heterogeneity)" di in smcl in gr "{hline 78}" di in gr " Dep. variable : " in ye "`depvar'" di in gr " Covariates : " in ye "`indepvars'" di in gr " Observations : " in ye "`nobs'" di in smcl in gr "{hline 78}" di * ================================================================ * CALL ESTIMATION ENGINE * ================================================================ local marg_opt "" if "`marginal'" != "" local marg_opt "marginal" _xtqsh_estimate, dep(`depvar') indep(`indepvars') /// ivar(`ivar') tvar(`tvar') touse(`touse') /// tau(`tau') bw(`bw') hac(`hac') level(`level') /// `marg_opt' local valid_panels = r(valid_panels) local ng = r(n) local kk = r(k) * Retrieve results tempname S_mat D_mat pS_mat pD_mat beta_md beta_md_se beta_all tempname beta_ols_all matrix `S_mat' = r(S) matrix `D_mat' = r(D) matrix `pS_mat' = r(pval_S) matrix `pD_mat' = r(pval_D) matrix `beta_md' = r(beta_md) matrix `beta_md_se' = r(beta_md_se) matrix `beta_all' = r(beta_all) matrix `beta_ols_all' = r(beta_ols_all) local S_ols = r(S_ols) local D_ols = r(D_ols) local pS_ols = r(pval_S_ols) local pD_ols = r(pval_D_ols) if "`marginal'" != "" { tempname S_marg D_marg pS_marg pD_marg matrix `S_marg' = r(S_marginal) matrix `D_marg' = r(D_marginal) matrix `pS_marg' = r(pval_S_marginal) matrix `pD_marg' = r(pval_D_marginal) } * ================================================================ * DISPLAY RESULTS * ================================================================ if "`notable'" == "" { di di in smcl in gr "{hline 78}" di in smcl in gr /// " {bf:JOINT SLOPE HOMOGENEITY TEST}" /// in gr " (H₀: all slopes equal across panels)" di in smcl in gr "{hline 78}" di in gr /// " {ralign 8:Quantile}" /// " {ralign 14:Ŝ statistic}" /// " {ralign 10:p(Ŝ)}" /// " {ralign 14:D̂ statistic}" /// " {ralign 10:p(D̂)}" /// " {ralign 6:}" di in smcl in gr "{hline 78}" * OLS/Mean row di in ye " {ralign 8: Mean }" _c di in ye " {ralign 14:" %12.2f `S_ols' "}" _c _xtqsh_disp_pval `pS_ols' di in ye " {ralign 14:" %12.3f `D_ols' "}" _c _xtqsh_disp_pval `pD_ols' _xtqsh_disp_stars `pD_ols' di in smcl in gr " {hline 76}" * QR rows local ti = 0 foreach tauval of local tau { local ++ti local S_val = `S_mat'[1, `ti'] local D_val = `D_mat'[1, `ti'] local pS_val = `pS_mat'[1, `ti'] local pD_val = `pD_mat'[1, `ti'] di in ye " {ralign 8:τ = " %4.2f `tauval' "}" _c if `S_val' == . { di in gr " {ralign 14: ---}" /// " {ralign 10: ---}" /// " {ralign 14: ---}" /// " {ralign 10: ---}" continue } di in ye " {ralign 14:" %12.2f `S_val' "}" _c _xtqsh_disp_pval `pS_val' di in ye " {ralign 14:" %12.3f `D_val' "}" _c _xtqsh_disp_pval `pD_val' _xtqsh_disp_stars `pD_val' } di in smcl in gr "{hline 78}" di in gr " *** p<0.01, ** p<0.05, * p<0.10" di in gr " Ŝ ~ χ²(" in ye "`=`kk'*(`valid_panels'-1)'" in gr /// ") under H₀ (T→∞, n fixed)" di in gr " D̂ ~ N(0,1) under H₀ (T,n→∞)" di in gr " Bandwidth: " in ye "`bw'" /// in gr " | Panels: n = " in ye "`ng'" /// in gr " | k = " in ye "`kk'" * ============================================================ * MARGINAL TESTS TABLE * ============================================================ if "`marginal'" != "" { di di in smcl in gr "{hline 78}" di in smcl in gr /// " {bf:MARGINAL SLOPE HOMOGENEITY TESTS}" /// in gr " (per-variable, H₀: β_j same ∀ i)" di in smcl in gr "{hline 78}" forvalues j = 1/`kk' { local xvar : word `j' of `indepvars' di di in ye " ── Variable: " in ye "{bf:`xvar'}" /// in gr " {hline 50}" di in gr /// " {ralign 8:Quantile}" /// " {ralign 14:Ŝ statistic}" /// " {ralign 10:p(Ŝ)}" /// " {ralign 14:D̂ statistic}" /// " {ralign 10:p(D̂)}" /// " {ralign 6:}" di in smcl in gr " {hline 74}" local ti = 0 foreach tauval of local tau { local ++ti local S_val = `S_marg'[`j', `ti'] local D_val = `D_marg'[`j', `ti'] local pS_val = `pS_marg'[`j', `ti'] local pD_val = `pD_marg'[`j', `ti'] di in ye " {ralign 8:τ = " %4.2f `tauval' "}" _c if `S_val' == . { di in gr " {ralign 14: ---}" /// " {ralign 10: ---}" /// " {ralign 14: ---}" /// " {ralign 10: ---}" continue } di in ye " {ralign 14:" %12.2f `S_val' "}" _c _xtqsh_disp_pval `pS_val' di in ye " {ralign 14:" %12.3f `D_val' "}" _c _xtqsh_disp_pval `pD_val' _xtqsh_disp_stars `pD_val' } } di in smcl in gr "{hline 78}" di in gr " *** p<0.01, ** p<0.05, * p<0.10" di in gr " Marginal test: k=1, Ŝ ~ χ²(n−1), D̂ ~ N(0,1)" } * ============================================================ * MD-QR COEFFICIENT TABLE * ============================================================ di di in smcl in gr "{hline 78}" di in smcl in gr /// " {bf:MINIMUM DISTANCE QR ESTIMATES}" /// in gr " β̂_MD(τ) = (ΣV̂ᵢ⁻¹)⁻¹ΣV̂ᵢ⁻¹β̂ᵢ" di in smcl in gr "{hline 78}" di in gr /// " {ralign 8:Quantile}" _c forvalues j = 1/`kk' { local xvar : word `j' of `indepvars' local xvar = abbrev("`xvar'", 12) di in gr " {ralign 12:`xvar'}" _c } di di in smcl in gr "{hline 78}" local ti = 0 foreach tauval of local tau { local ++ti di in ye " {ralign 8:τ = " %4.2f `tauval' "}" _c forvalues j = 1/`kk' { local est = `beta_md'[`ti', `j'] if `est' == . { di in gr " {ralign 12:---}" _c } else { di in ye " {ralign 12:" %10.4f `est' "}" _c } } di * SE row di in gr " {ralign 8: }" _c forvalues j = 1/`kk' { local se = `beta_md_se'[`ti', `j'] if `se' == . { di in gr " {ralign 12: }" _c } else { di in gr " {ralign 12:(" %8.4f `se' ")}" _c } } di } di in smcl in gr "{hline 78}" di in gr " Standard errors in parentheses" } * ================================================================ * ERETURN * ================================================================ tempname b_post V_post * Post the MD estimates for the median (or first quantile) as b/V local mid_tau = ceil(`ntau'/2) matrix `b_post' = `beta_md'[`mid_tau', 1...] matrix colnames `b_post' = `indepvars' matrix `V_post' = I(`kk') matrix colnames `V_post' = `indepvars' matrix rownames `V_post' = `indepvars' ereturn post `b_post' `V_post', obs(`nobs') esample(`touse') ereturn matrix S = `S_mat' ereturn matrix D = `D_mat' ereturn matrix pval_S = `pS_mat' ereturn matrix pval_D = `pD_mat' ereturn matrix beta_md = `beta_md' ereturn matrix beta_md_se = `beta_md_se' ereturn matrix beta_all = `beta_all' ereturn matrix beta_ols = `beta_ols_all' ereturn scalar S_ols = `S_ols' ereturn scalar D_ols = `D_ols' ereturn scalar pval_S_ols = `pS_ols' ereturn scalar pval_D_ols = `pD_ols' ereturn scalar N_g = `ng' ereturn scalar k = `kk' ereturn scalar ntau = `ntau' ereturn scalar valid_panels = `valid_panels' ereturn local tau "`tau'" ereturn local depvar "`depvar'" ereturn local indepvars "`indepvars'" ereturn local ivar "`ivar'" ereturn local tvar "`tvar'" ereturn local bw "`bw'" ereturn local cmdline "xtqsh `0'" ereturn local cmd "xtqsh" ereturn local title "QR Slope Homogeneity Test (Galvao et al. 2017)" if "`marginal'" != "" { ereturn matrix S_marginal = `S_marg' ereturn matrix D_marginal = `D_marg' ereturn matrix pval_S_marginal = `pS_marg' ereturn matrix pval_D_marginal = `pD_marg' } * ================================================================ * GRAPHS * ================================================================ if "`graph'" != "" { xtqsh_graph, tau(`tau') k(`kk') /// depvar(`depvar') indepvars(`indepvars') /// npanels(`ng') `marginal' } end * ===================================================================== * DISPLAY HELPERS * ===================================================================== capture program drop _xtqsh_disp_pval program define _xtqsh_disp_pval args pval if `pval' == . { di in gr " {ralign 10: ---}" _c } else if `pval' < 0.01 { di as err " {ralign 10:" %8.4f `pval' "}" _c } else if `pval' < 0.05 { di as res " {ralign 10:" %8.4f `pval' "}" _c } else { di in gr " {ralign 10:" %8.4f `pval' "}" _c } end capture program drop _xtqsh_disp_stars program define _xtqsh_disp_stars args pval if `pval' == . { di "" } else if `pval' < 0.01 { di in ye " ***" } else if `pval' < 0.05 { di in ye " **" } else if `pval' < 0.10 { di in ye " *" } else { di "" } end * ===================================================================== * REPLAY DISPLAY * ===================================================================== capture program drop Display program define Display syntax [, LEVel(integer `c(level)')] di di in smcl in gr "{hline 78}" di in smcl in gr " {bf:Quantile Regression Slope Homogeneity Test}" di in smcl in gr "{hline 78}" * Re-display the test results from stored matrices local ntau = e(ntau) local kk = e(k) local ng = e(N_g) local tau "`e(tau)'" local indepvars "`e(indepvars)'" tempname S_mat D_mat pS_mat pD_mat matrix `S_mat' = e(S) matrix `D_mat' = e(D) matrix `pS_mat' = e(pval_S) matrix `pD_mat' = e(pval_D) di in gr /// " {ralign 8:Quantile}" /// " {ralign 14:Ŝ statistic}" /// " {ralign 10:p(Ŝ)}" /// " {ralign 14:D̂ statistic}" /// " {ralign 10:p(D̂)}" /// " {ralign 6:}" di in smcl in gr "{hline 78}" * Mean di in ye " {ralign 8: Mean }" _c di in ye " {ralign 14:" %12.2f e(S_ols) "}" _c _xtqsh_disp_pval `=e(pval_S_ols)' di in ye " {ralign 14:" %12.3f e(D_ols) "}" _c _xtqsh_disp_pval `=e(pval_D_ols)' _xtqsh_disp_stars `=e(pval_D_ols)' di in smcl in gr " {hline 76}" local ti = 0 foreach tauval of local tau { local ++ti local S_val = `S_mat'[1, `ti'] local D_val = `D_mat'[1, `ti'] local pS_val = `pS_mat'[1, `ti'] local pD_val = `pD_mat'[1, `ti'] di in ye " {ralign 8:τ = " %4.2f `tauval' "}" _c if `S_val' == . { di in gr " {ralign 14: ---}" /// " {ralign 10: ---}" /// " {ralign 14: ---}" /// " {ralign 10: ---}" continue } di in ye " {ralign 14:" %12.2f `S_val' "}" _c _xtqsh_disp_pval `pS_val' di in ye " {ralign 14:" %12.3f `D_val' "}" _c _xtqsh_disp_pval `pD_val' _xtqsh_disp_stars `pD_val' } di in smcl in gr "{hline 78}" di in gr " *** p<0.01, ** p<0.05, * p<0.10" di in gr " Panels: n = " in ye "`ng'" /// in gr " | k = " in ye "`kk'" /// in gr " | Bandwidth: " in ye "`e(bw)'" end exit