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

template<typename T, typename I = unsigned long>
struct UltimateOSCIVisitor;
        
This visitor calculates the rolling values of Ultimate Oscillator. It requires 3 input columns in the order of low, high, close.
The result is a vector of values with same number of items as the given columns. The first slow_roll items, in the result, will be NAN.

The Ultimate Oscillator is a technical indicator that was developed by Larry Williams in 1976 to measure the price momentum of an asset across multiple timeframes. By using the weighted average of three different timeframes the indicator has less volatility and fewer trade signals compared to other oscillators that rely on a single timeframe. Buy and sell signals are generated following divergences. The Ultimately Oscillator generates fewer divergence signals than other oscillators due to its multi-timeframe construction.
The indicator uses three timeframes in its calculation: 7, 14, and 28 periods.
The shorter timeframe has the most weight in the calculation, while the longer timeframe has the least weight.
Buy signals occur when there is bullish divergence, the divergence low is below 30 on the indicator, and the oscillator then rises above the divergence high.
A sell signal occurs when there is bearish divergence, the divergence high is above 70, and the oscillator then falls below the divergence low.
    explicit
    UltimateOSCIVisitor(size_type slow_roll = 28,
                        size_type fast_roll = 7,
                        size_type medium_roll = 14,
                        value_type slow_weight = 1.0,
                        value_type fast_weight = 4.0,
                        value_type medium_weight = 2.0);
        
T: Column data type
I: Index type
static void test_UltimateOSCIVisitor()  {

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

    typedef StdDataFrame<std::string> StrDataFrame;

    StrDataFrame    df;

    try  {
        df.read("IBM.csv", io_format::csv2);

        UltimateOSCIVisitor<double, std::string> uo_v;

        df.single_act_visit<double, double, double>("IBM_Low", "IBM_High", "IBM_Close", uo_v);

        assert(uo_v.get_result().size() == 5031);
        assert(std::isnan(uo_v.get_result()[0]));
        assert(std::isnan(uo_v.get_result()[26]));
        assert(std::abs(uo_v.get_result()[27] - 41.3509) < 0.0001);
        assert(std::abs(uo_v.get_result()[31] - 32.1768) < 0.0001);
        assert(std::abs(uo_v.get_result()[5030] - 45.076) < 0.001);
        assert(std::abs(uo_v.get_result()[5026] - 31.3935) < 0.0001);
    }
    catch (const DataFrameError &ex)  {
        std::cout << ex.what() << std::endl;
    }
}