From 325a5376feef0bbae03fc3c4ac0a6ce9710a7c94 Mon Sep 17 00:00:00 2001 From: Hossein Moein Date: Fri, 3 May 2024 10:14:18 -0400 Subject: [PATCH] Implemented PeaksAndValleysVisitor visitor --- docs/HTML/DataFrame.html | 4 + docs/HTML/MACDVisitor.html | 2 +- docs/HTML/PeaksAndValleysVisitor.html | 163 ++++++++++++++++++ .../DataFrame/DataFrameFinancialVisitors.h | 86 ++++----- include/DataFrame/DataFrameStatsVisitors.h | 2 +- test/dataframe_tester_3.cc | 75 +++++++- 6 files changed, 285 insertions(+), 47 deletions(-) create mode 100644 docs/HTML/PeaksAndValleysVisitor.html diff --git a/docs/HTML/DataFrame.html b/docs/HTML/DataFrame.html index 827992e0..5a9d6d11 100644 --- a/docs/HTML/DataFrame.html +++ b/docs/HTML/DataFrame.html @@ -956,6 +956,10 @@

API Reference with code samples

struct ParkinsonVolVisitor{} + + struct PeaksAndValleysVisitor{} + + struct PercentPriceOSCIVisitor{} diff --git a/docs/HTML/MACDVisitor.html b/docs/HTML/MACDVisitor.html index 21c7934d..c0623ff4 100644 --- a/docs/HTML/MACDVisitor.html +++ b/docs/HTML/MACDVisitor.html @@ -41,7 +41,7 @@ -
#include <DataFrame/DataFrameStatsVisitors.h>
+        
#include <DataFrame/DataFrameFinancialVisitors.h>
 
 template<typename T, typename I = unsigned long,
          std::size_t A = 0>
diff --git a/docs/HTML/PeaksAndValleysVisitor.html b/docs/HTML/PeaksAndValleysVisitor.html
new file mode 100644
index 00000000..c8e60571
--- /dev/null
+++ b/docs/HTML/PeaksAndValleysVisitor.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+  
+
+    
+      
+    
+    
+      
+      
+      
+    
+
+  
Signature Description Parameters
+
#include <DataFrame/DataFrameFinancialVisitors.h>
+
+template<typename T, typename I = unsigned long,
+         std::size_t A = 0>
+struct PeaksAndValleysVisitor;
+
+// -------------------------------------
+
+template<typename T, typename I = unsigned long,
+         std::size_t A = 0>
+using pav_v = PeaksAndValleysVisitor<T, I, A>;
+        
+
+ This calculates peaks and valleys in a time-series. A peak is a datapoint that is higher than the previous and next datapoints. Similarly, a valley is a datapoint that is lower than the previous and next datapoints. If these conditions are not met, the previous values of peaks and valleys are copied over.
+ The results are two vectors, namely peaks and valleys with the same size as the input time-series.
+ get_result() returns the vector of peaks.
+ get_peaks() returns the vector of peaks.
+ get_valleys() returns the vector of valleys.

+ + Result vectors are vectors of this struct: +

+  struct ResultItem  {
+
+      T   value { };  // Peak or valley
+      I   index { };  // Index corresponding to the value
+
+      // Convenient print function
+      template<typename S>
+      friend S &operator << (S &stream, const ResultItem &ri)  {
+
+          stream << ri.value << '@' << ri.index;
+          return (stream);
+      }
+  };
+        
+
+ T: Column data type
+ I: Index type
+ A: Memory alignment boundary for vectors. Default is system default alignment
+
+ +
static void test_PeaksAndValleysVisitor()  {
+
+    std::cout << "\nTesting PeaksAndValleysVisitor{ } ..." << std::endl;
+
+    typedef StdDataFrame64<std::string> StrDataFrame;
+
+    StrDataFrame    df;
+
+    try  {
+        df.read("SHORT_IBM.csv", io_format::csv2);
+
+        pav_v<double, std::string, 64>  pav;
+
+        df.single_act_visit<double>("IBM_Close", pav);
+
+        assert(pav.get_peaks().size() == 1721);
+        assert(std::abs(pav.get_peaks()[0].value - 185.53) < 0.0001);
+        assert(pav.get_peaks()[0].index == "2014-01-02");
+        assert(std::abs(pav.get_peaks()[1].value - 186.64) < 0.0001);
+        assert(pav.get_peaks()[1].index == "2014-01-03");
+        assert(std::abs(pav.get_peaks()[19].value - 182.73) < 0.0001);
+        assert(pav.get_peaks()[19].index == "2014-01-23");
+        assert(std::abs(pav.get_peaks()[20].value - 177.36) < 0.0001);
+        assert(pav.get_peaks()[20].index == "2014-01-30");
+        assert(std::abs(pav.get_peaks()[24].value - 177.36) < 0.0001);
+        assert(pav.get_peaks()[24].index == "2014-01-30");
+        assert(std::abs(pav.get_peaks()[25].value - 177.36) < 0.0001);
+        assert(pav.get_peaks()[25].index == "2014-01-30");
+        assert(std::abs(pav.get_peaks()[1720].value - 116.0) < 0.0001);
+        assert(pav.get_peaks()[1720].index == "2020-10-23");
+        assert(std::abs(pav.get_peaks()[1712].value - 125.93) < 0.0001);
+        assert(pav.get_peaks()[1712].index == "2020-10-16");
+        assert(std::abs(pav.get_peaks()[1707].value - 131.49) < 0.0001);
+        assert(pav.get_peaks()[1707].index == "2020-10-08");
+
+        assert(pav.get_valleys().size() == 1721);
+        assert(std::abs(pav.get_valleys()[0].value - 185.53) < 0.0001);
+        assert(pav.get_valleys()[0].index == "2014-01-02");
+        assert(std::abs(pav.get_valleys()[1].value - 186.64) < 0.0001);
+        assert(pav.get_valleys()[1].index == "2014-01-03");
+        assert(std::abs(pav.get_valleys()[19].value - 176.4) < 0.0001);
+        assert(pav.get_valleys()[19].index == "2014-01-29");
+        assert(std::abs(pav.get_valleys()[20].value - 176.4) < 0.0001);
+        assert(pav.get_valleys()[20].index == "2014-01-29");
+        assert(std::abs(pav.get_valleys()[24].value - 172.84) < 0.0001);
+        assert(pav.get_valleys()[24].index == "2014-02-04");
+        assert(std::abs(pav.get_valleys()[25].value - 172.84) < 0.0001);
+        assert(pav.get_valleys()[25].index == "2014-02-04");
+        assert(std::abs(pav.get_valleys()[1720].value - 106.65) < 0.0001);
+        assert(pav.get_valleys()[1720].index == "2020-10-28");
+        assert(std::abs(pav.get_valleys()[1712].value - 124.89) < 0.0001);
+        assert(pav.get_valleys()[1712].index == "2020-10-15");
+        assert(std::abs(pav.get_valleys()[1707].value - 121.97) < 0.0001);
+        assert(pav.get_valleys()[1707].index == "2020-10-06");
+    }
+    catch (const DataFrameError &ex)  {
+        std::cout << ex.what() << std::endl;
+    }
+}
+
+ +
C++ DataFrame + + + + + diff --git a/include/DataFrame/DataFrameFinancialVisitors.h b/include/DataFrame/DataFrameFinancialVisitors.h index 9d9d68ce..d0392a82 100644 --- a/include/DataFrame/DataFrameFinancialVisitors.h +++ b/include/DataFrame/DataFrameFinancialVisitors.h @@ -8225,62 +8225,70 @@ struct QuantQualEstimationVisitor { template using qqe_v = QuantQualEstimationVisitor; +// ---------------------------------------------------------------------------- +template +struct PeaksAndValleysVisitor { + struct ResultItem { + T value { }; + I index { }; + template + friend S &operator << (S &stream, const ResultItem &ri) { + stream << ri.value << '@' << ri.index; + return (stream); + } + }; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ---------------------------------------------------------------------------- - -template -struct PeaksAndValleysVisitor { - - DEFINE_VISIT_BASIC_TYPES_3 + using value_type = T; + using index_type = I; + using size_type = std::size_t; + using result_type = + std::vector::type>; template inline void - operator() (const K &/*idx_begin*/, const K &/*idx_end*/, + operator() (const K &idx_begin, const K &/*idx_end*/, const H &prices_begin, const H &prices_end) { const size_type col_s = std::distance(prices_begin, prices_end); #ifdef HMDF_SANITY_EXCEPTIONS if (col_s < 4) - throw DataFrameError("PeaksAndValleysVisitor: column size must " - "> 3"); + throw DataFrameError( + "PeaksAndValleysVisitor: column size must > 3"); #endif // HMDF_SANITY_EXCEPTIONS + result_type peaks (col_s); + result_type valleys (col_s); + + peaks[0] = valleys[0] = ResultItem { *prices_begin, *idx_begin }; + peaks[1] = valleys[1] = + ResultItem { *(prices_begin + 1), *(idx_begin + 1) }; + for (size_type i = 2; i < col_s; ++i) { + auto &curr_peak = peaks[i]; + auto &curr_valley = valleys[i]; + + curr_peak = peaks[i - 1]; + curr_valley = valleys[i - 1]; + + const auto val = *(prices_begin + i); + const auto val_1 = *(prices_begin + (i - 1)); + const auto idx_1 = *(idx_begin + (i - 1)); + const auto val_2 = *(prices_begin + (i - 2)); + + if (val_1 > val && val_1 > val_2) + curr_peak = ResultItem { val_1, idx_1 }; + else if (val_1 < val && val_1 < val_2) + curr_valley = ResultItem { val_1, idx_1 }; + } + + peaks_.swap(peaks); + valleys_.swap(valleys); } inline void pre () { diff --git a/include/DataFrame/DataFrameStatsVisitors.h b/include/DataFrame/DataFrameStatsVisitors.h index 6752b31c..6c69290c 100644 --- a/include/DataFrame/DataFrameStatsVisitors.h +++ b/include/DataFrame/DataFrameStatsVisitors.h @@ -58,7 +58,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -// 2.0 * PI +// 2PI // #define TAU 6.28318530717958647692528676655900576 diff --git a/test/dataframe_tester_3.cc b/test/dataframe_tester_3.cc index 64831f4b..2335e22c 100644 --- a/test/dataframe_tester_3.cc +++ b/test/dataframe_tester_3.cc @@ -3053,7 +3053,7 @@ static void test_get_data_by_rand_from_view() { } } -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- static void test_get_data_by_like_from_view() { @@ -3203,7 +3203,7 @@ static void test_get_reindexed_from_view() { assert(result.get_column("dbl_col_2")[10] == 112.0); } -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- static void test_concat_from_view() { @@ -3261,7 +3261,7 @@ static void test_concat_from_view() { assert(result.get_column("int_col")[15] == 1); } -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- static void test_get_view_by_sel_from_view() { @@ -3393,7 +3393,7 @@ static void test_get_view_by_rand_from_view() { } } -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- static void test_get_view_by_like_from_view() { @@ -3543,7 +3543,7 @@ static void test_get_reindexed_view_from_view() { assert(result.get_column("dbl_col_2")[10] == 112.0); } -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- static void test_concat_view_from_view() { @@ -3603,7 +3603,7 @@ static void test_concat_view_from_view() { assert(result2.get_index()[14] == 14); } -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- static void test_client_csv_read_test() { @@ -3632,6 +3632,68 @@ static void test_client_csv_read_test() { // ---------------------------------------------------------------------------- +static void test_PeaksAndValleysVisitor() { + + std::cout << "\nTesting PeaksAndValleysVisitor{ } ..." << std::endl; + + typedef StdDataFrame64 StrDataFrame; + + StrDataFrame df; + + try { + df.read("SHORT_IBM.csv", io_format::csv2); + + pav_v pav; + + df.single_act_visit("IBM_Close", pav); + + assert(pav.get_peaks().size() == 1721); + assert(std::abs(pav.get_peaks()[0].value - 185.53) < 0.0001); + assert(pav.get_peaks()[0].index == "2014-01-02"); + assert(std::abs(pav.get_peaks()[1].value - 186.64) < 0.0001); + assert(pav.get_peaks()[1].index == "2014-01-03"); + assert(std::abs(pav.get_peaks()[19].value - 182.73) < 0.0001); + assert(pav.get_peaks()[19].index == "2014-01-23"); + assert(std::abs(pav.get_peaks()[20].value - 177.36) < 0.0001); + assert(pav.get_peaks()[20].index == "2014-01-30"); + assert(std::abs(pav.get_peaks()[24].value - 177.36) < 0.0001); + assert(pav.get_peaks()[24].index == "2014-01-30"); + assert(std::abs(pav.get_peaks()[25].value - 177.36) < 0.0001); + assert(pav.get_peaks()[25].index == "2014-01-30"); + assert(std::abs(pav.get_peaks()[1720].value - 116.0) < 0.0001); + assert(pav.get_peaks()[1720].index == "2020-10-23"); + assert(std::abs(pav.get_peaks()[1712].value - 125.93) < 0.0001); + assert(pav.get_peaks()[1712].index == "2020-10-16"); + assert(std::abs(pav.get_peaks()[1707].value - 131.49) < 0.0001); + assert(pav.get_peaks()[1707].index == "2020-10-08"); + + assert(pav.get_valleys().size() == 1721); + assert(std::abs(pav.get_valleys()[0].value - 185.53) < 0.0001); + assert(pav.get_valleys()[0].index == "2014-01-02"); + assert(std::abs(pav.get_valleys()[1].value - 186.64) < 0.0001); + assert(pav.get_valleys()[1].index == "2014-01-03"); + assert(std::abs(pav.get_valleys()[19].value - 176.4) < 0.0001); + assert(pav.get_valleys()[19].index == "2014-01-29"); + assert(std::abs(pav.get_valleys()[20].value - 176.4) < 0.0001); + assert(pav.get_valleys()[20].index == "2014-01-29"); + assert(std::abs(pav.get_valleys()[24].value - 172.84) < 0.0001); + assert(pav.get_valleys()[24].index == "2014-02-04"); + assert(std::abs(pav.get_valleys()[25].value - 172.84) < 0.0001); + assert(pav.get_valleys()[25].index == "2014-02-04"); + assert(std::abs(pav.get_valleys()[1720].value - 106.65) < 0.0001); + assert(pav.get_valleys()[1720].index == "2020-10-28"); + assert(std::abs(pav.get_valleys()[1712].value - 124.89) < 0.0001); + assert(pav.get_valleys()[1712].index == "2020-10-15"); + assert(std::abs(pav.get_valleys()[1707].value - 121.97) < 0.0001); + assert(pav.get_valleys()[1707].index == "2020-10-06"); + } + catch (const DataFrameError &ex) { + std::cout << ex.what() << std::endl; + } +} + +// ---------------------------------------------------------------------------- + int main(int, char *[]) { MyDataFrame::set_optimum_thread_level(); @@ -3708,6 +3770,7 @@ int main(int, char *[]) { test_get_reindexed_view_from_view(); test_concat_view_from_view(); test_client_csv_read_test(); + test_PeaksAndValleysVisitor(); return (0); }