use crate::{CovarianceType, GreenersError, OLS};
use ndarray::{s, Array1, Array2};
#[derive(Debug)]
pub struct AdfResult {
pub test_statistic: f64,
pub p_value: Option<f64>,
pub critical_values: (f64, f64, f64), pub is_stationary: bool,
pub lags_used: usize,
pub n_obs: usize,
}
pub struct TimeSeries;
impl TimeSeries {
pub fn adf(series: &Array1<f64>, max_lags: Option<usize>) -> Result<AdfResult, GreenersError> {
let n = series.len();
if n < 10 {
return Err(GreenersError::ShapeMismatch(
"Série muito curta para ADF".into(),
));
}
let lags = match max_lags {
Some(l) => l,
None => ((n - 1) as f64).powf(1.0 / 3.0) as usize,
};
let y_diff = diff(series, 1);
let effective_n = n - 1 - lags;
let target_y = y_diff.slice(s![lags..]).to_owned();
let mut x_mat = Array2::<f64>::zeros((effective_n, 2 + lags));
x_mat.column_mut(0).fill(1.0);
for i in 0..effective_n {
x_mat[[i, 1]] = series[lags + i];
}
for l in 0..lags {
for i in 0..effective_n {
x_mat[[i, 2 + l]] = y_diff[lags + i - 1 - l];
}
}
let ols_res = OLS::fit(&target_y, &x_mat, CovarianceType::NonRobust)?;
let adf_stat = ols_res.t_values[1];
let crit_1pct = -3.43;
let crit_5pct = -2.86;
let crit_10pct = -2.57;
Ok(AdfResult {
test_statistic: adf_stat,
p_value: None,
critical_values: (crit_1pct, crit_5pct, crit_10pct),
is_stationary: adf_stat < crit_5pct,
lags_used: lags,
n_obs: effective_n,
})
}
}
fn diff(arr: &Array1<f64>, lag: usize) -> Array1<f64> {
let len = arr.len();
let mut out = Vec::with_capacity(len - lag);
for i in lag..len {
out.push(arr[i] - arr[i - lag]);
}
Array1::from(out)
}