From 8d529ffacc3227b9af2863f2b9d425b2e23ff219 Mon Sep 17 00:00:00 2001
From: Andy Pack <andy@sarsoo.xyz>
Date: Fri, 21 Feb 2025 21:51:19 +0000
Subject: [PATCH] adding rsi indicator

---
 FinLib.NET/FinLib/FinLib.Indicators.cs    |  7 +++++
 FinLib.NET/FinLib/NativeMethods.g.cs      |  6 ++++
 finlib-cpp/CMakeLists.txt                 |  1 +
 finlib-cpp/include/finlib/finlib-native.h |  8 +++++
 finlib-cpp/src/CMakeLists.txt             |  3 +-
 finlib-cpp/src/finlib/CMakeLists.txt      |  3 +-
 finlib-ffi/build.rs                       |  1 +
 finlib-ffi/src/indicators.rs              | 10 ++++++
 finlib-ffi/src/lib.rs                     |  3 +-
 finlib-wasm/src/lib.rs                    | 15 +++++++++
 finlib/src/indicators/mod.rs              |  1 +
 finlib/src/indicators/rsi/mod.rs          | 38 +++++++++++++++++++++++
 finlib/src/lib.rs                         |  3 +-
 pyfinlib/src/lib.rs                       | 20 ++++++++++++
 14 files changed, 115 insertions(+), 4 deletions(-)
 create mode 100644 FinLib.NET/FinLib/FinLib.Indicators.cs
 create mode 100644 finlib-ffi/src/indicators.rs
 create mode 100644 finlib/src/indicators/mod.rs
 create mode 100644 finlib/src/indicators/rsi/mod.rs

diff --git a/FinLib.NET/FinLib/FinLib.Indicators.cs b/FinLib.NET/FinLib/FinLib.Indicators.cs
new file mode 100644
index 0000000..ff196c0
--- /dev/null
+++ b/FinLib.NET/FinLib/FinLib.Indicators.cs
@@ -0,0 +1,7 @@
+namespace FinLib;
+
+public static class Indicators
+{
+    public static double RelativeStrengthIndicator(double timePeriod, double averageGain, double averageLoss) => NativeMethods.relative_strength_indicator(timePeriod, averageGain, averageLoss);
+    public static double RelativeStrengthIndicatorSmoothed(double timePeriod, double previousAverageGain, double currentGain, double previousAverageLoss, double currentLoss) => NativeMethods.relative_strength_indicator_smoothed(timePeriod, previousAverageGain, currentGain, previousAverageLoss, currentLoss);
+}
\ No newline at end of file
diff --git a/FinLib.NET/FinLib/NativeMethods.g.cs b/FinLib.NET/FinLib/NativeMethods.g.cs
index 07f8c5b..247a555 100644
--- a/FinLib.NET/FinLib/NativeMethods.g.cs
+++ b/FinLib.NET/FinLib/NativeMethods.g.cs
@@ -76,6 +76,12 @@ namespace FinLib
         [DllImport(__DllName, EntryPoint = "portfolio_value_at_risk_percent", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
         internal static extern NullableFloat portfolio_value_at_risk_percent(Portfolio_native* portfolio, double confidence);
 
+        [DllImport(__DllName, EntryPoint = "relative_strength_indicator", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+        internal static extern double relative_strength_indicator(double time_period, double average_gain, double average_loss);
+
+        [DllImport(__DllName, EntryPoint = "relative_strength_indicator_smoothed", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+        internal static extern double relative_strength_indicator_smoothed(double time_period, double previous_average_gain, double current_gain, double previous_average_loss, double current_loss);
+
 
     }
 
diff --git a/finlib-cpp/CMakeLists.txt b/finlib-cpp/CMakeLists.txt
index e013ffc..cc1bce3 100644
--- a/finlib-cpp/CMakeLists.txt
+++ b/finlib-cpp/CMakeLists.txt
@@ -11,6 +11,7 @@ project(finlib-cpp)
 
 set(CMAKE_CXX_STANDARD 23)
 
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
 
 set(FINLIB_INSTALL_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
diff --git a/finlib-cpp/include/finlib/finlib-native.h b/finlib-cpp/include/finlib/finlib-native.h
index 4358da3..ddf760d 100644
--- a/finlib-cpp/include/finlib/finlib-native.h
+++ b/finlib-cpp/include/finlib/finlib-native.h
@@ -76,6 +76,14 @@ NullableFloat portfolio_value_at_risk(Portfolio *portfolio,
 
 NullableFloat portfolio_value_at_risk_percent(Portfolio *portfolio, double confidence);
 
+double relative_strength_indicator(double time_period, double average_gain, double average_loss);
+
+double relative_strength_indicator_smoothed(double time_period,
+                                            double previous_average_gain,
+                                            double current_gain,
+                                            double previous_average_loss,
+                                            double current_loss);
+
 double scale_value_at_risk(double initial_value, ptrdiff_t time_cycles);
 
 NullableFloat varcovar_value_at_risk(const double *arr, size_t len, double confidence);
diff --git a/finlib-cpp/src/CMakeLists.txt b/finlib-cpp/src/CMakeLists.txt
index cbfe99f..622186c 100644
--- a/finlib-cpp/src/CMakeLists.txt
+++ b/finlib-cpp/src/CMakeLists.txt
@@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.19)
 project(finexe)
 
 add_subdirectory(finlib)
-set(SOURCE_FILES main.cpp
+set(SOURCE_FILES
+        main.cpp
 )
 
 add_executable(finexe ${SOURCE_FILES})
diff --git a/finlib-cpp/src/finlib/CMakeLists.txt b/finlib-cpp/src/finlib/CMakeLists.txt
index cbf731b..168329f 100644
--- a/finlib-cpp/src/finlib/CMakeLists.txt
+++ b/finlib-cpp/src/finlib/CMakeLists.txt
@@ -7,6 +7,7 @@ set(SOURCE_FILES
 )
 
 add_library(finlib SHARED STATIC ${SOURCE_FILES})
+add_library(finlib::finlib ALIAS finlib)
 
 ExternalProject_Add(
         finlibrs
@@ -25,4 +26,4 @@ target_link_libraries(finlib
 )
 
 install(TARGETS finlib DESTINATION ${FINLIB_INSTALL_LIB_DIR})
-install(FILES finlib.h DESTINATION ${FINLIB_INSTALL_INCLUDE_DIR})
\ No newline at end of file
+install(FILES finlib.hpp DESTINATION ${FINLIB_INSTALL_INCLUDE_DIR})
diff --git a/finlib-ffi/build.rs b/finlib-ffi/build.rs
index 3eff927..ada66eb 100644
--- a/finlib-ffi/build.rs
+++ b/finlib-ffi/build.rs
@@ -19,6 +19,7 @@ fn main() {
     csbindgen::Builder::default()
         .input_extern_file("src/lib.rs")
         .input_extern_file("src/portfolio.rs")
+        .input_extern_file("src/indicators.rs")
         .input_extern_file("../finlib/src/risk/portfolio.rs")
         .csharp_dll_name("libfinlib_ffi")
         .csharp_namespace("FinLib")
diff --git a/finlib-ffi/src/indicators.rs b/finlib-ffi/src/indicators.rs
new file mode 100644
index 0000000..4a80b83
--- /dev/null
+++ b/finlib-ffi/src/indicators.rs
@@ -0,0 +1,10 @@
+
+#[no_mangle]
+pub extern "C" fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64)  -> f64 {
+    finlib::indicators::rsi::relative_strength_indicator(time_period, average_gain, average_loss)
+}
+
+#[no_mangle]
+pub extern "C" fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64)  -> f64 {
+    finlib::indicators::rsi::relative_strength_indicator_smoothed(time_period, previous_average_gain, current_gain, previous_average_loss, current_loss)
+}
\ No newline at end of file
diff --git a/finlib-ffi/src/lib.rs b/finlib-ffi/src/lib.rs
index fb4f934..811c091 100644
--- a/finlib-ffi/src/lib.rs
+++ b/finlib-ffi/src/lib.rs
@@ -1,4 +1,5 @@
-mod portfolio;
+pub mod portfolio;
+pub mod indicators;
 
 use std::slice;
 
diff --git a/finlib-wasm/src/lib.rs b/finlib-wasm/src/lib.rs
index 109bffb..11d0ae7 100644
--- a/finlib-wasm/src/lib.rs
+++ b/finlib-wasm/src/lib.rs
@@ -14,6 +14,21 @@ pub fn init_logging() {
     }
 }
 
+#[wasm_bindgen]
+pub struct RelativeStrengthIndicator { }
+
+#[wasm_bindgen]
+impl RelativeStrengthIndicator {
+    pub fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64)  -> f64 {
+        finlib::indicators::rsi::relative_strength_indicator(time_period, average_gain, average_loss)
+    }
+
+    pub fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64)  -> f64 {
+        finlib::indicators::rsi::relative_strength_indicator_smoothed(time_period, previous_average_gain, current_gain, previous_average_loss, current_loss)
+    }
+}
+
+
 #[wasm_bindgen]
 pub struct Interest { }
 
diff --git a/finlib/src/indicators/mod.rs b/finlib/src/indicators/mod.rs
new file mode 100644
index 0000000..1481c55
--- /dev/null
+++ b/finlib/src/indicators/mod.rs
@@ -0,0 +1 @@
+pub mod rsi;
\ No newline at end of file
diff --git a/finlib/src/indicators/rsi/mod.rs b/finlib/src/indicators/rsi/mod.rs
new file mode 100644
index 0000000..eb2a8c4
--- /dev/null
+++ b/finlib/src/indicators/rsi/mod.rs
@@ -0,0 +1,38 @@
+
+
+pub fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64) -> f64 {
+    100. - (
+        100. /
+            (1. + (
+                (average_gain / time_period)
+                    /
+                (average_loss / time_period)
+                )
+            )
+    )
+}
+
+pub fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64) -> f64 {
+    100. - (100. /
+        (1. +
+            (
+                ((previous_average_gain * time_period) + current_gain)
+
+                /
+
+                ((previous_average_loss * time_period) + current_loss)
+            )
+        )
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn relative_strength_indicator_test() {
+        let result = relative_strength_indicator(14., 1., 0.8);
+        assert_eq!(f64::floor(result * 100.) / 100., 55.55);
+    }
+}
diff --git a/finlib/src/lib.rs b/finlib/src/lib.rs
index 1e01328..e4b4544 100644
--- a/finlib/src/lib.rs
+++ b/finlib/src/lib.rs
@@ -5,4 +5,5 @@ pub mod stats;
 pub mod util;
 pub mod risk;
 pub mod ffi;
-pub mod options;
\ No newline at end of file
+pub mod options;
+pub mod indicators;
\ No newline at end of file
diff --git a/pyfinlib/src/lib.rs b/pyfinlib/src/lib.rs
index 285e481..a6ce2fc 100644
--- a/pyfinlib/src/lib.rs
+++ b/pyfinlib/src/lib.rs
@@ -15,6 +15,26 @@ mod pyfinlib {
         Ok(())
     }
 
+    #[pymodule]
+    mod indicators {
+        use super::*;
+
+        #[pymodule]
+        mod rsi {
+            use super::*;
+
+            #[pyfunction]
+            pub fn relative_strength_indicator(time_period: f64, average_gain: f64, average_loss: f64)  -> PyResult<f64> {
+                Ok(finlib::indicators::rsi::relative_strength_indicator(time_period, average_gain, average_loss))
+            }
+
+            #[pyfunction]
+            pub fn relative_strength_indicator_smoothed(time_period: f64, previous_average_gain: f64, current_gain: f64, previous_average_loss: f64, current_loss: f64)  -> PyResult<f64> {
+                Ok(finlib::indicators::rsi::relative_strength_indicator_smoothed(time_period, previous_average_gain, current_gain, previous_average_loss, current_loss))
+            }
+        }
+    }
+
     #[pymodule]
     mod interest {
         use super::*;