*! version 4.3  22jul2020  Cliff Zhu
capture program drop grsftest
program define grsftest, rclass
	version 13
	syntax varlist(min=1 numeric) [if], factor(varlist min=1 numeric) [Details]

** Get Parameters
	qui count `if'
	local T= `r(N)'
	local N: word count `varlist'
	local K: word count `factor'
	matrix b_Alpha  =J(`N',1,.)
	matrix se_Alpha =J(`N',1,.)
	matrix R2 =J(`N',1,.)
	matrix R2_adj =J(`N',1,.)
** Fit Factor Models
	local i=1
	quietly foreach var of varlist `varlist' {
		tempvar res`i'
		reg `var' `factor' `if'
		matrix b_Alpha[`i',1] = _b[_cons]
		matrix se_Alpha[`i',1]= _se[_cons]
		matrix R2[`i',1]= e(r2)
		matrix R2_adj[`i',1]= e(r2_a)
		predict double `res`i'' if e(sample), residuals
		local reslist `reslist' `res`i''
		local i=`i'+1
	}
** Prepare Factor Models Results
	matrix ALPHA= (b_Alpha,se_Alpha,R2,R2_adj)
	matrix U =J(rowsof(ALPHA),1,1)
	matrix Amean= U'*ALPHA /rowsof(ALPHA)
	matrix colnames ALPHA = "Estimate" "Std.Err." "R2" "R2_adj"
	matrix rownames ALPHA = `varlist'
	matrix colnames Amean = "Mean alpha" "Mean SE" "Mean R2" "Mean R2_adj"
	matrix rownames Amean = "`N' Assets"
	qui tabstat `factor' `if', stat(mean) col(stat) save
	matrix Fmean= r(StatTotal)'
	qui matrix accum RR = `reslist' ,deviations noconstant
	matrix RCOV= RR/(`T'-`K'-1)
	qui mkmat `factor' `if', matrix(rp)
	matrix FCOV= (rp'*rp)/(`T')
	matrix LAMBDA= FCOV - Fmean * Fmean'
	qui tabstat `varlist' `factor' `if', stat(n mean sd min median max) col(stat) save
	matrix STATS= r(StatTotal)'

** GRS test Formula (Kamstra and Shi 2020, eq8)
	matrix tm1= (`T'*(`T'-`N'-`K')) / (`N'*(`T'-`K'-1))
	matrix tm2= (1 + Fmean' * invsym(LAMBDA) * Fmean)
	matrix tm3= (b_Alpha' * invsym(RCOV) * b_Alpha)
	local ftest_KS= tm1[1,1] * tm3[1,1] / tm2[1,1]
	local pval_KS= 1 - F(`N',`T'-`N'-`K', `ftest_KS')

** Results
	display " "
	display "{title: GRS Test Results}"
	display "{hline 30}"
	display " Number of Obs.  : " %9.0f `T'
	display " Number of Asset : " %9.0f `N'
	display " Number of Factor: " %9.0f `K'
	display " Results"
	display "       GRS_F-test =" %9.6f `ftest_KS'
	display "       GRS_pvalue =" %9.6f `pval_KS'
	display "{hline 30}"
	if ~missing("`details'"){
		matrix list Amean , format(%9.6f) title("Factor Model Results (On Average)")
		matrix list ALPHA , format(%9.6f) title("Factor Model Results (By Asset)")
		tabstat `varlist' `factor' `if', stat(n mean sd min median max) col(stat) long
	}
	return matrix summary STATS
	return matrix alphas ALPHA
	matrix drop b_Alpha se_Alpha R2 R2_adj U Amean Fmean RR RCOV rp FCOV LAMBDA tm1 tm2 tm3

end