*! _boundedur_adf v1.0.0 Dr. Merwan Roudane 02Feb2026
*! ADF test for bounded time series (internal routine)
*! Implements Algorithm 1 from Cavaliere & Xu (2014)

program define _boundedur_adf, rclass
    version 14.0

    syntax varlist(max=1 ts) [if] [in], ///
        lags(integer) ///
        [clower(real -999)] ///
        [cupper(real 999)] ///
        [nsim(integer 499)] ///
        [nstep(integer 0)] ///
        [detrend(string)] ///
        [stat(string)] ///
        [krclag(integer -1)] ///
        [recolor] ///
        [nosimulation]

    marksample touse

    * Defaults
    if "`detrend'" == "" local detrend "constant"
    if "`stat'" == "" local stat "alpha"
    if `krclag' == -1 local krclag = `lags'

    * Get sample size
    qui count if `touse'
    local T = r(N)

    * De-mean or de-trend the data
    tempvar Xhat
    if "`detrend'" == "constant" {
        qui reg `varlist' if `touse'
        qui predict `Xhat' if `touse', residuals
    }
    else {
        qui gen `Xhat' = `varlist' if `touse'
    }

    * Run ADF regression (Equation 3.7)
    * Delta X_t = alpha * X_{t-1} + sum_i alpha_i * Delta X_{t-i} + epsilon_{t,k}

    if `lags' > 0 {
        qui reg D.`Xhat' L.`Xhat' LD(1/`lags').D.`Xhat' if `touse', nocons
    }
    else {
        qui reg D.`Xhat' L.`Xhat' if `touse', nocons
    }

    * Extract alpha_hat
    tempname alpha_hat
    scalar `alpha_hat' = _b[L.`Xhat']

    * Compute alpha(1) = 1 - sum(alpha_i)
    tempname alpha1_hat
    scalar `alpha1_hat' = 1
    if `lags' > 0 {
        forvalues i = 1/`lags' {
            scalar `alpha1_hat' = `alpha1_hat' - _b[LD`i'.D.`Xhat']
        }
    }

    * Standard error of alpha
    tempname se_alpha
    scalar `se_alpha' = _se[L.`Xhat']

    * Compute test statistics
    if "`stat'" == "alpha" {
        * ADF_alpha = T*(alpha_hat - 1) / alpha(1)
        tempname test_stat
        scalar `test_stat' = `T' * (`alpha_hat' - 1) / `alpha1_hat'
        return scalar adf_alpha = `test_stat'
    }
    else if "`stat'" == "t" {
        * ADF_t = (alpha_hat - 1) / se(alpha_hat)
        tempname test_stat
        scalar `test_stat' = (`alpha_hat' - 1) / `se_alpha'
        return scalar adf_t = `test_stat'
    }

    * If simulation requested, compute p-value
    if "`nosimulation'" == "" {

        * Set discretization step
        if `nstep' == 0 {
            local nstep = `T'  // Paper recommendation: n=T
        }

        * Get residuals for re-coloring
        tempvar resid
        qui predict `resid' if `touse', residuals

        * Estimate re-coloring AR polynomial if requested
        tempname krc_coef
        if "`recolor'" != "" & `krclag' > 0 {
            * Fit AR(krc) to residuals for re-coloring (Section 4.3)
            qui reg D.`Xhat' L.`Xhat' LD(1/`krclag').D.`Xhat' if `touse', nocons

            matrix `krc_coef' = J(`krclag', 1, .)
            forvalues i = 1/`krclag' {
                matrix `krc_coef'[`i',1] = _b[LD`i'.D.`Xhat']
            }
        }

        * Monte Carlo simulation (Algorithm 1)
        tempname mc_stat pval count_below
        scalar `count_below' = 0

        forvalues b = 1/`nsim' {
            * Step (i): Generate i.i.d. N(0,1) innovations
            tempvar epsilon_star
            qui gen `epsilon_star' = rnormal() if `touse'

            * Step (ii): Generate re-colored innovations if requested
            tempvar u_star
            if "`recolor'" != "" & `krclag' > 0 {
                * Apply AR filter: u*_t = alpha_krc(L)/alpha_krc(1) * epsilon*_t
                * This implements Equation 4.14

                qui gen `u_star' = `epsilon_star' if `touse'

                * Apply AR polynomial
                local alpha_krc_1 = 1
                forvalues i = 1/`krclag' {
                    local alpha_krc_1 = `alpha_krc_1' - `krc_coef'[`i',1]
                }

                * Initialize
                sort `touse'
                qui replace `u_star' = `epsilon_star' if _n <= `krclag' & `touse'

                * Recursive application
                forvalues i = 1/`krclag' {
                    qui replace `u_star' = `u_star' + ///
                        `krc_coef'[`i',1] * L`i'.`u_star' / `alpha_krc_1' ///
                        if _n > `krclag' & `touse'
                }
            }
            else {
                qui gen `u_star' = `epsilon_star' if `touse'
            }

            * Step (ii): Generate bounded process (Equation 4.11 or 4.13)
            tempvar X_star
            qui gen `X_star' = 0 if _n == 1

            qui count if `touse'
            local n_sim = min(`nstep', r(N))

            * Recursive generation with reflection at bounds
            forvalues t = 2/`n_sim' {
                qui replace `X_star' = ///
                    cond(L.`X_star' + `u_star'/sqrt(`n_sim') > `cupper', `cupper', ///
                    cond(L.`X_star' + `u_star'/sqrt(`n_sim') < `clower', `clower', ///
                    L.`X_star' + `u_star'/sqrt(`n_sim'))) ///
                    if _n == `t'
            }

            * De-mean simulated series
            tempvar Xstar_tilde
            qui gen `Xstar_tilde' = `X_star' - sum(`X_star')/`n_sim'

            * Step (iii): Compute MC statistic
            if `lags' > 0 {
                qui reg D.`Xstar_tilde' L.`Xstar_tilde' LD(1/`lags').D.`Xstar_tilde', nocons
            }
            else {
                qui reg D.`Xstar_tilde' L.`Xstar_tilde', nocons
            }

            tempname alpha_star alpha1_star
            scalar `alpha_star' = _b[L.`Xstar_tilde']
            scalar `alpha1_star' = 1
            if `lags' > 0 {
                forvalues i = 1/`lags' {
                    scalar `alpha1_star' = `alpha1_star' - _b[LD`i'.D.`Xstar_tilde']
                }
            }

            if "`stat'" == "alpha" {
                scalar `mc_stat' = `n_sim' * (`alpha_star' - 1) / `alpha1_star'
            }
            else {
                tempname se_alpha_star
                scalar `se_alpha_star' = _se[L.`Xstar_tilde']
                scalar `mc_stat' = (`alpha_star' - 1) / `se_alpha_star'
            }

            * Count if MC statistic is below observed
            if `mc_stat' < `test_stat' {
                scalar `count_below' = `count_below' + 1
            }

            * Clean up temporary variables for next iteration
            drop `epsilon_star' `u_star' `X_star' `Xstar_tilde'
        }

        * Step (iv): Compute p-value (Theorem 2)
        scalar `pval' = `count_below' / `nsim'
        return scalar pval = `pval'
    }

end