Back to Documentations

Signature Description
enum class  stationary_test : unsigned char  {

    // Kwiatkowski-Phillips-Schmidt–Shin (KPSS)
    // In econometrics, Kwiatkowski–Phillips–Schmidt–Shin (KPSS) tests are
    // used for testing a null hypothesis that an observable time series is stationary around a deterministic
    // trend (i.e. trend-stationary) against the alternative of a unit root. Contrary to most unit root tests,
    // the presence of a unit root is not the null hypothesis but the alternative. Additionally, in the KPSS
    // test, the absence of a unit root is not a proof of stationarity but, by design, of trend-stationarity.
    // This is an important distinction since it is possible for a time series to be non-stationary, have no
    // unit root yet be trend-stationary.
    //
    // In a KPSS test, a higher test statistic value (meaning a larger calculated KPSS statistic) indicates a
    // greater likelihood that the time series is not stationary around a deterministic trend, while a lower
    // value suggests stationarity; essentially, you want a low KPSS test value to conclude stationarity.
    //
    kpss = 1,

    // Augmented Dickey-Fuller (ADF)
    // In statistics, an augmented Dickey–Fuller test (ADF) tests the null hypothesis that a unit root is
    // present in a time series sample. The alternative hypothesis depends on which version of the test is
    // used, but is usually stationarity or trend-stationarity. It is an augmented version of the
    // Dickey–Fuller test for a larger and more complicated set of time series models. The augmented
    // Dickey–Fuller (ADF) statistic, used in the test, is a negative number. The more negative it is, the
    // stronger the rejection of the hypothesis that there is a unit root at some level of confidence.
    //
    // To interpret an ADF test statistic, compare its value to the critical value at a chosen significance
    // level (usually 0.05): if the test statistic is less than the critical value, you reject the null
    // hypothesis and conclude that the time series is stationary; if it's greater than the critical value,
    // you fail to reject the null hypothesis, indicating non-stationarity; a more negative ADF statistic
    // signifies stronger evidence against the null hypothesis (i.e., more likely stationary).
    //
    adf = 2,
};


struct  StationaryTestParams  {

    // Only considered for KPSS test
    //
    double  critical_values[4] { 0.347, 0.463, 0.574, 0.739 };

    // Only considered for ADF test
    //
    std::size_t adf_lag { 1 };
    bool        adf_with_trend { false };
};
Methods to test if a time-series is stationary
Also, a struct to contain the necessary parameters to StationaryCheckVisitor constructor

Signature Description Parameters
#include <DataFrame/DataFrameStatsVisitors.h>

template<arithmetic T, typename I = unsigned long>
struct StationaryCheckVisitor;

// -------------------------------------

template<typename T, typename I = unsigned long>
using stac_v = StationaryCheckVisitor<T, I>;
This is a "single action visitor", meaning it is passed the whole data vector in one call and you must use the single_act_visit() interface.

This visitor uses the specified method to test if the given time-series (i.e., column) is stationary.

This visitor has the following methods to get results:
get_kpss_value(): Returns the KPSS calcuated value.
get_kpss_statistic(): Returns the percentage calculated by comparing KPSS value against critical_values.
get_adf_statistic(): Returns the ADF calcuated statistics.

    explicit
    StationaryCheckVisitor(stationary_test method,
                           const StationaryTestParams params = { })
        
method: One of the above methods.
params: Necessary parameters depending what method is being used.
T: Column data type.
I: Index type.
static void test_StationaryCheckVisitor()  {

    std::cout << "\nTesting StationaryCheckVisitor{  } ..." << std::endl;

    StrDataFrame    df;

    try  {
        df.read("IBM.csv", io_format::csv2);
    }
    catch (const DataFrameError &ex)  {
        std::cout << ex.what() << std::endl;
    }

    RandGenParams<double>   p;

    p.mean = 0;
    p.std = 1;
    p.seed = 123;

    df.load_column("normal_col", gen_normal_dist<double>(df.get_index().size(), p));

    p.max_value = 1000;
    p.min_value = -1000;
    df.load_column("uniform col", gen_uniform_real_dist<double>(df.get_index().size(), p));

    std::vector<double> log_close;

    log_close.reserve(df.get_index().size());
    for (const auto val : df.get_column<double>("IBM_Close"))
        log_close.push_back(std::log(val));
    df.load_column("log close", std::move(log_close));

    DecomposeVisitor<double, std::string>   d_v (280, 0.6, 0.01);

    df.single_act_visit<double>("IBM_Close", d_v);
    df.load_column("residual close", std::move(d_v.get_residual()));

    // KPSS tests
    //
    StationaryCheckVisitor<double, std::string> sc { stationary_test::kpss };

    df.single_act_visit<double>("IBM_Close", sc);
    assert(std::fabs(sc.get_kpss_value() - 63.5831) < 0.0001);
    assert(sc.get_kpss_statistic() == 0);

    df.single_act_visit<double>("normal_col", sc);
    assert(sc.get_kpss_value() < 0.078);
    assert(sc.get_kpss_statistic() == 0.1);

    df.single_act_visit<double>("uniform col", sc);
    assert(sc.get_kpss_value() < 0.08);
    assert(sc.get_kpss_statistic() == 0.1);

    df.single_act_visit<double>("log close", sc);
    assert(sc.get_kpss_value() < 62.7013);
    assert(sc.get_kpss_statistic() == 0);

    df.single_act_visit<double>("residual close", sc);
    assert(sc.get_kpss_value() < 46.41);
    assert(sc.get_kpss_statistic() == 0);

    // ADF tests
    //
    StationaryCheckVisitor<double, std::string> sc2 { stationary_test::adf, { .adf_lag = 10, .adf_with_trend = false } };

    df.single_act_visit<double>("IBM_Close", sc2);
    assert(std::fabs(sc2.get_adf_statistic() - 0.989687) < 0.00001);

    StationaryCheckVisitor<double, std::string> sc3 { stationary_test::adf, { .adf_lag = 25, .adf_with_trend = false } };

    df.single_act_visit<double>("IBM_Close", sc3);
    assert(std::fabs(sc3.get_adf_statistic() - 0.974531) < 0.0000001);

    StationaryCheckVisitor<double, std::string> sc4 { stationary_test::adf, { .adf_lag = 10, .adf_with_trend = false } };

    df.single_act_visit<double>("normal_col", sc4);
    assert(std::fabs(sc4.get_adf_statistic() - 0.0286854) < 0.0000001);

    StationaryCheckVisitor<double, std::string> sc5 { stationary_test::adf, { .adf_lag = 25, .adf_with_trend = false } };

    df.single_act_visit<double>("normal_col", sc5);
    assert(std::fabs(sc5.get_adf_statistic() - -0.0017814) < 0.0000001);

    // ADF tests with trend
    //
    StationaryCheckVisitor<double, std::string> sc6 { stationary_test::adf, { .adf_lag = 10, .adf_with_trend = true } };

    df.single_act_visit<double>("IBM_Close", sc6);
    assert(std::fabs(sc6.get_adf_statistic() - 0.977705) < 0.000001);

    StationaryCheckVisitor<double, std::string> sc7 { stationary_test::adf, { .adf_lag = 25, .adf_with_trend = true } };

    df.single_act_visit<double>("IBM_Close", sc7);
    assert(std::fabs(sc7.get_adf_statistic() - 0.946614) < 0.000001);

    StationaryCheckVisitor<double, std::string> sc8 { stationary_test::adf, { .adf_lag = 10, .adf_with_trend = true } };

    df.single_act_visit<double>("normal_col", sc8);
    assert(std::fabs(sc8.get_adf_statistic() - 0.0286834) < 0.0000001);

    StationaryCheckVisitor<double, std::string> sc9 { stationary_test::adf, { .adf_lag = 25, .adf_with_trend = true } };

    df.single_act_visit<double>("normal_col", sc9);
    assert(std::fabs(sc9.get_adf_statistic() - -0.00178569) < 0.0000001);

    StationaryCheckVisitor<double, std::string> sc10 { stationary_test::adf, { .adf_lag = 10, .adf_with_trend = true } };

    df.single_act_visit<double>("log close", sc10);
    assert(std::fabs(sc10.get_adf_statistic() - 0.972062) < 0.000001);

    StationaryCheckVisitor<double, std::string> sc11 { stationary_test::adf, { .adf_lag = 10, .adf_with_trend = true } };

    df.single_act_visit<double>("residual close", sc11);
    assert(std::fabs(sc11.get_adf_statistic() - 0.679027) < 0.000001);
}

C++ DataFrame