*! version 1.7 16july2006

program define dpredict, rclass
	version 9.0


	_on_colon_parse `0'	// parse the whole input in two macros `s(after)' `s(before)'
					// `s(after)' contains the part after the colon, `s(before)' the rest

	local command `"`s(after)'"'
	local 0 `"`s(before)'"'		//the macro `0' will be parsed by the syntax command below

	_prefix_command dpredictn :`command' //parse the `command' macro into `s(...)' macros

	local cmd		`"`s(command)'"'
	local cmdname   	`"`s(cmdname)'"'
	local cmdargs   	`"`s(anything)'"'
	local cmdif     	`"`s(if)'"'
	local cmdopts   	`"`s(options)'"'

	tokenize `cmdargs'
	local count_arima_args=1		// count the number of variables going to arima (incl. the dependent)
	while "``count_arima_args''" ~= "" {
		local ++count_arima_args
	}
	local --count_arima_args


	if ("`cmdname'"!="arima") {
		di as error "dpredict supports only arima (`s(cmdname)' not supported)"
		exit 199
	}

	syntax [varlist(default=none)], From(numlist >0 max=1 integer) To(numlist >0 max=1 integer) Periods(numlist>0 max=1 integer) [NORecursive] [THReshold(real 0)] [Lvl]

	tokenize `varlist'
	local count_var_args=1		// count the number of variables supplied to dpredict
	while "``count_var_args''" ~= "" {
		local ++count_var_args
	}
	local --count_var_args

	if (`threshold'<0) {
		di as error "The threshold parameter must be non-negative."
		exit 199
	}

	if (`from'>`to') {
		di as error "The from-parameter must be smaller or equal to the to-parameter."
		exit 199
	}

	disp _newline,, as txt"Pre-run summary:"
	disp as input "Running dynamic predictions on: " as txt "`command'"as input "."
	disp as input "Out-of-sample prediction horizon: " as txt "`periods'" as input "."
	if "`norecursive'"=="" {
		disp as input "Subsamples from " as txt "1/`from'" as input " to " as txt "1/" min(`to',_N-`periods') as input" (growing window size; norecursive-option not specified)."
	}
	else {
		disp as input "Subsamples from " as txt "1/`from'" as input " to " as txt  min(`to',_N-`periods')- `from' + 1 "/" min(`to',_N-`periods') as input" (fixed window size; norecursive-option specified)."
	}
	if ("`lvl'"=="") {
		di as error "Warning: " as input "Lvl-option not specified, the forecast statistics will be computed assuming that the dependent variable is in " as txt "`periods'" as input "-period differences (after applying all specified time-series and arima(.,d,.) operators)."
	}


	local use_actual = 0
	if (`count_arima_args'~=`count_var_args') {
		if (`count_var_args'>0) {
			di as error "Warning: " as input "Number of vars supplied to dpredict isn't equal to the number of arima variables. Using actual values."
		}
		local use_actual = 1
	}


	tempvar yhat res dep
	tempname pred_var doc_var res_var

	mat `pred_var' = J(_N,1,.)
	mat `doc_var' = J(_N,1,.)
	mat `res_var' = J(_N,1,.)

	local rmse = 0
	local mae  = 0
	local doc  = 0

	local count=0
	local count_doc=0
	local start = 1
	local j=`from'

	while `j'<=min(`to',_N-`periods') {		//run only until `periods'-ahead predictions can be computed


		if (~`use_actual') {			// replace the last observation of each variable by the supplied predicted var
			preserve				// need to preserve, since variables from the dataset are changed
			tokenize `varlist' `cmdargs'
			local counter=1
			while `counter'<=`count_var_args' {
				local pos = `count_var_args'+`counter'
					tsrevar ``pos'' , list
				qui replace `r(varlist)'=``counter'' in `j'
				local ++counter
			}
		}

		if ("`norecursive'"~="") {
			local start  = `start' + 1
		}

		capture: arima `cmdargs' `cmdif' in `start'/`j', `cmdopts'		//run the arima - estimation

		if _rc{								//if the arima produced an error exit the program
			di as err"Error: "as input"Executing the command " as txt "arima `cmdargs' `cmdif' in 1/`j', `cmdopts'" as input " produced an error (code " _rc "):"
			di as input"See the help for an explanation of the error code and the help for the arima command."
			err _rc
		}

		quietly: predict `res' , residuals dynamic(e(tmax)+1)
 		quietly: predict `yhat',  dynamic(e(tmax)+1)

		local pos = `j' + `periods'					//need to define this, the "in #" doesnt accept expressions in place of #

		mat `pred_var'[`pos',1]=`yhat'[`pos']	// save the current prediction and residual (these will be returned as matrices)
		mat `res_var'[`pos',1] =`res'[`pos']

		if (`yhat'[`pos']>=.)  {							// sometimes the prediction is null (e.g. when the indep vars needed for
			disp as input" - Warning: Null-prediction encountered -.",,_continue	// the prediction are null)
		}
		else if (`res'[`pos']>=.) {							//and sometimes the residual is null
			disp as input " - Warning: Null-residual encountered -.",,_continue		//exclude both of this cases for computing the forecast stats
		}
		else {
			tsrevar `e(depvar)'	//strip the time-series operator from the dependent var, name of the temporary var is saved in r(varlist)

			if ("`lvl'"~="") {	//if the lvl options is given transform the prediction,yhat, and dependent variable,r(varlist), to differences, such that same code can be used as when lvl is not specified
				qui gen `dep' = s`periods'.`r(varlist)'
				qui replace `yhat' = `yhat' - l`periods'.`r(varlist)' in `pos'
			}
			else {
				qui gen `dep' = `r(varlist)'
			}

			if (`dep'[`pos'])*(`yhat'[`pos'])>`threshold'{ // check the direction of change
				local ++doc					// count the correctly predicted directions
				mat `doc_var'[`pos',1]=1			// direction predicted correctly
				local ++count_doc

			}
			else if (`dep'[`pos'])*(`yhat'[`pos'])<(-`threshold') {
				mat `doc_var'[`pos',1]=0
				local ++count_doc
			}

			local mae=`mae' + abs(`res'[`pos'])
			local rmse=`rmse' + (`res'[`pos'])^2

			disp ".",,_continue
			local ++count						// count the non-null predictions

		}

		cap drop `dep'
		cap drop `yhat' `res' 	// need to drop these, otherwise predict complains
		local ++j
		if (~`use_actual') restore

	}


	local rmse=sqrt(`rmse' / `count')
	local mae=`mae' / `count'
	local doc=`doc' / `count_doc'

	di _newline,,as txt"After-run summary:"

	di as input"dpredict `0' : `command'"

	disp "//The RMSE is: " `rmse'
	disp "//The MAE is:  " `mae'
	disp "//The DOC is:  " `doc'

	return matrix Yhat = `pred_var'
	return matrix DOCmat  = `doc_var'
	return matrix ResMat  = `res_var'

	return local RMSE = `rmse'
	return local MAE = `mae'
	return local DOC = `doc'


end