Back to Documentations

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

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

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

template<typename T, typename I = unsigned long>
using ehpf_v = EhlersHighPassFilterVisitor<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 is a transformer visitor. It means the column(s) passed to this visitor is not read-only and its values will change

This functor class applies Ehlers' high-pass smoothing filter to the input. Ehlers suggested a way to improve trend identification using high-pass filters. The basic smoothers like SMA , low-pass filters, have considerable lag in their display. Mr. Ehlers applied the high-pass filter and subtracted the high-pass filter output from the time series input. Doing these steps he removed high-frequency short-wavelength components (the ones causing the wiggles) from the time series.

This works with both scalar and multidimensional (i.e. vector and arrays) datasets.
explicit
EhlersHighPassFilterVisitor(value_type period = 20);
        
T: Column data type.
I: Index type.
#include <DataFrame/DataFrameTransformVisitors.h>

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

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

template<typename T, typename I = unsigned long>
using ebpf_v = EhlersBandPassFilterVisitor<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 is a transformer visitor. It means the column(s) passed to this visitor is not read-only and its values will change

This functor class applies Ehlers' band-pass smoothing filter to the input. The band-pass indicator is an oscillator-type indicator. It makes full use of the digital computational power of your computer and therefore is superior to conventional oscillators such as the relative strength index (RSI) or the stochastic indicator when the market is in a cycle mode. If you were to stop and think about what an oscillator does, you would conclude that it performs two functions: it detrends the price, and it does some smoothing.

This works with both scalar and multidimensional (i.e. vector and arrays) datasets.
explicit
EhlersBandPassFilterVisitor(value_type period = 20,
                            value_type bandwidth = 0.3);
        
T: Column data type.
I: Index type.
static void test_EhlersHighPassFilterVisitor()  {

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

    typedef StdDataFrame<std::string> StrDataFrame;

    StrDataFrame    df;

    try  {
        df.read("SHORT_IBM.csv", io_format::csv2);
        df.load_column<double>("Smooth Close", { df.get_column<double>("IBM_Close").begin(), df.get_column<double>("IBM_Close").end() });

        EhlersHighPassFilterVisitor<double, std::string>    ehpf;

        df.single_act_visit<double>("Smooth Close", ehpf);

        const auto  &smooth_close = df.get_column<double>("Smooth Close");

        assert(smooth_close.size() == 1721);
        assert(std::abs(smooth_close[0] - 185.53) < 0.0001);
        assert(std::abs(smooth_close[1] - 185.3782) < 0.0001);
        assert(std::abs(smooth_close[19] - 179.8812) < 0.0001);
        assert(std::abs(smooth_close[20] - 179.2847) < 0.0001);
        assert(std::abs(smooth_close[24] - 175.4347) < 0.0001);
        assert(std::abs(smooth_close[25] - 174.8728) < 0.0001);
        assert(std::abs(smooth_close[1720] - 111.7708) < 0.0001);
        assert(std::abs(smooth_close[1712] - 126.7447) < 0.0001);
        assert(std::abs(smooth_close[1707] - 126.108) < 0.0001);
    }
    catch (const DataFrameError &ex)  {
        std::cout << ex.what() << std::endl;
        ::exit(-1);
    }

    // Now multidimensional data
    //
    constexpr std::size_t   dim { 3 };

    using ary_col_t = std::array<double, dim>;
    using vec_col_t = std::vector<double>;

    // Note: z = x + 2.0 throughout, so you can sanity-check that all 3
    // dimensions are being filtered independently
    //
    std::vector<ary_col_t>  ary_col  {
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }, { 1.0, 2.0, 3.0 }, { 0.5, 1.5, 2.5 }, { 0.0, 1.0, 2.0 }, { 0.5, 1.5, 2.5 },
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }
    };
    std::vector<vec_col_t>  vec_col  {
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }, { 1.0, 2.0, 3.0 }, { 0.5, 1.5, 2.5 }, { 0.0, 1.0, 2.0 }, { 0.5, 1.5, 2.5 },
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }
    };

    df.load_column<ary_col_t>("ARY COL", std::move(ary_col), nan_policy::dont_pad_with_nans);
    df.load_column<vec_col_t>("VEC COL", std::move(vec_col), nan_policy::dont_pad_with_nans);

    ehpf_v<vec_col_t, std::string>  vec_ehpf(10);
    ehpf_v<ary_col_t, std::string>  ary_ehpf(10);

    df.single_act_visit<vec_col_t>("VEC COL", vec_ehpf);
    df.single_act_visit<ary_col_t>("ARY COL", ary_ehpf);

    const auto  &vec_col_ref = df.get_column<vec_col_t>("VEC COL");
    const auto  &ary_col_ref = df.get_column<ary_col_t>("ARY COL");

    assert(vec_col_ref.size() == 20);
    assert(std::abs(vec_col_ref[0][0] - 1.0) < 0.00001);
    assert(std::abs(vec_col_ref[0][1] - 2.0) < 0.00001);
    assert(std::abs(vec_col_ref[0][2] - 3.0) < 0.00001);
    assert(std::abs(vec_col_ref[10][0] - 1.22649) < 0.00001);
    assert(std::abs(vec_col_ref[10][1] - 2.22649) < 0.00001);
    assert(std::abs(vec_col_ref[10][2] - 3.22649) < 0.00001);
    assert(std::abs(vec_col_ref[19][0] - 2.43936) < 0.00001);
    assert(std::abs(vec_col_ref[19][1] - 3.43936) < 0.00001);
    assert(std::abs(vec_col_ref[19][2] - 4.43936) < 0.00001);

    assert(ary_col_ref.size() == 20);
    assert(std::abs(ary_col_ref[0][0] - 1.0) < 0.00001);
    assert(std::abs(ary_col_ref[0][1] - 2.0) < 0.00001);
    assert(std::abs(ary_col_ref[0][2] - 3.0) < 0.00001);
    assert(std::abs(ary_col_ref[10][0] - 1.22649) < 0.00001);
    assert(std::abs(ary_col_ref[10][1] - 2.22649) < 0.00001);
    assert(std::abs(ary_col_ref[10][2] - 3.22649) < 0.00001);
    assert(std::abs(ary_col_ref[19][0] - 2.43936) < 0.00001);
    assert(std::abs(ary_col_ref[19][1] - 3.43936) < 0.00001);
    assert(std::abs(ary_col_ref[19][2] - 4.43936) < 0.00001);
}

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

static void test_EhlersBandPassFilterVisitor()  {

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

    typedef StdDataFrame<std::string> StrDataFrame;

    StrDataFrame    df;

    try  {
        df.read("SHORT_IBM.csv", io_format::csv2);
        df.load_column<double>("Smooth Close", { df.get_column<double>("IBM_Close").begin(), df.get_column<double>("IBM_Close").end() });

        ebpf_v<double, std::string> ebpf(14, 0.8);

        df.single_act_visit<double>("Smooth Close", ebpf);

        const auto  &smooth_close = df.get_column<double>("Smooth Close");

        assert(smooth_close.size() == 1721);
        assert(std::abs(smooth_close[0] - 185.53) < 0.001);
        assert(std::abs(smooth_close[1] - 186.64) < 0.001);
        assert(std::abs(smooth_close[2] - 185.928) < 0.001);
        assert(std::abs(smooth_close[19] - 180.851) < 0.001);
        assert(std::abs(smooth_close[20] - 178.903) < 0.001);
        assert(std::abs(smooth_close[24] - 174.9) < 0.001);
        assert(std::abs(smooth_close[25] - 176.41) < 0.001);
        assert(std::abs(smooth_close[1720] - 113.541) < 0.001);
        assert(std::abs(smooth_close[1712] - 119.954) < 0.001);
        assert(std::abs(smooth_close[1707] - 123.239) < 0.001);
    }
    catch (const DataFrameError &ex)  {
        std::cout << ex.what() << std::endl;
        ::exit(-1);
    }

    // Now multidimensional data
    //
    constexpr std::size_t   dim { 3 };

    using ary_col_t = std::array<double, dim>;
    using vec_col_t = std::vector<double>;

    // Note: z = x + 2.0 throughout, so you can sanity-check that all 3
    // dimensions are being filtered independently
    //
    std::vector<ary_col_t>  ary_col  {
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }, { 1.0, 2.0, 3.0 }, { 0.5, 1.5, 2.5 }, { 0.0, 1.0, 2.0 }, { 0.5, 1.5, 2.5 },
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }
    };
    std::vector<vec_col_t>  vec_col  {
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }, { 1.0, 2.0, 3.0 }, { 0.5, 1.5, 2.5 }, { 0.0, 1.0, 2.0 }, { 0.5, 1.5, 2.5 },
        { 1.0, 2.0, 3.0 }, { 1.5, 2.5, 3.5 }, { 2.0, 3.0, 4.0 }, { 2.5, 3.5, 4.5 }, { 3.0, 4.0, 5.0 }, { 2.5, 3.5, 4.5 },
        { 2.0, 3.0, 4.0 }, { 1.5, 2.5, 3.5 }
    };

    df.load_column<ary_col_t>("ARY COL", std::move(ary_col), nan_policy::dont_pad_with_nans);
    df.load_column<vec_col_t>("VEC COL", std::move(vec_col), nan_policy::dont_pad_with_nans);

    ebpf_v<vec_col_t, std::string>  vec_ebpf(10);
    ebpf_v<ary_col_t, std::string>  ary_ebpf(10);

    df.single_act_visit<vec_col_t>("VEC COL", vec_ebpf);
    df.single_act_visit<ary_col_t>("ARY COL", ary_ebpf);

    const auto  &vec_col_ref = df.get_column<vec_col_t>("VEC COL");
    const auto  &ary_col_ref = df.get_column<ary_col_t>("ARY COL");

    assert(vec_col_ref.size() == 20);
    assert(std::abs(vec_col_ref[0][0] - 1.0) < 0.00001);
    assert(std::abs(vec_col_ref[0][1] - 2.0) < 0.00001);
    assert(std::abs(vec_col_ref[0][2] - 3.0) < 0.00001);
    assert(std::abs(vec_col_ref[1][0] - 1.5) < 0.00001);
    assert(std::abs(vec_col_ref[1][1] - 2.5) < 0.00001);
    assert(std::abs(vec_col_ref[1][2] - 3.5) < 0.00001);
    assert(std::abs(vec_col_ref[10][0] - 0.59839) < 0.00001);
    assert(std::abs(vec_col_ref[10][1] - 1.59839) < 0.00001);
    assert(std::abs(vec_col_ref[10][2] - 2.59839) < 0.00001);
    assert(std::abs(vec_col_ref[19][0] - 2.07294) < 0.00001);
    assert(std::abs(vec_col_ref[19][1] - 3.07294) < 0.00001);
    assert(std::abs(vec_col_ref[19][2] - 4.07294) < 0.00001);

    assert(ary_col_ref.size() == 20);
    assert(std::abs(ary_col_ref[0][0] - 1.0) < 0.00001);
    assert(std::abs(ary_col_ref[0][1] - 2.0) < 0.00001);
    assert(std::abs(ary_col_ref[0][2] - 3.0) < 0.00001);
    assert(std::abs(ary_col_ref[1][0] - 1.5) < 0.00001);
    assert(std::abs(ary_col_ref[1][1] - 2.5) < 0.00001);
    assert(std::abs(ary_col_ref[1][2] - 3.5) < 0.00001);
    assert(std::abs(ary_col_ref[10][0] - 0.59839) < 0.00001);
    assert(std::abs(ary_col_ref[10][1] - 1.59839) < 0.00001);
    assert(std::abs(ary_col_ref[10][2] - 2.59839) < 0.00001);
    assert(std::abs(ary_col_ref[19][0] - 2.07294) < 0.00001);
    assert(std::abs(ary_col_ref[19][1] - 3.07294) < 0.00001);
    assert(std::abs(ary_col_ref[19][2] - 4.07294) < 0.00001);
}

C++ DataFrame