Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ build
cmake-build-debug
venv
.idea
*.so
*.pyc
__pycache__/
68 changes: 37 additions & 31 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,46 @@ add_library(finmath_library SHARED ${SOURCES}
"include/finmath/TimeSeries/rsi.h"
"include/finmath/TimeSeries/ema.h")

# Test executables
add_executable(black_scholes_test test/OptionPricing/black_scholes_test.cpp)
target_link_libraries(black_scholes_test finmath_library)

add_executable(binomial_option_pricing_test test/OptionPricing/binomial_option_pricing_test.cpp)
target_link_libraries(binomial_option_pricing_test finmath_library)
# Link pybind11 headers to the main library (needed for numpy integration in C++ files)
target_link_libraries(finmath_library PUBLIC pybind11::headers)

add_executable(compound_interest_test test/InterestAndAnnuities/compound_interest_test.cpp)
target_link_libraries(compound_interest_test finmath_library)
# Also link Python libraries/headers (needed for Python.h)
find_package(Python COMPONENTS Interpreter Development REQUIRED)
target_link_libraries(finmath_library PUBLIC Python::Python)

add_executable(rsi_test test/TimeSeries/rsi_test.cpp)
target_link_libraries(rsi_test finmath_library)

# Test runner
add_executable(run_all_tests test/test_runner.cpp)
# Test executables
# add_executable(black_scholes_test test/OptionPricing/black_scholes_test.cpp)
# target_link_libraries(black_scholes_test finmath_library)
#
# add_executable(binomial_option_pricing_test test/OptionPricing/binomial_option_pricing_test.cpp)
# target_link_libraries(binomial_option_pricing_test finmath_library)
#
# add_executable(compound_interest_test test/InterestAndAnnuities/compound_interest_test.cpp)
# target_link_libraries(compound_interest_test finmath_library)
#
# add_executable(rsi_test test/TimeSeries/rsi_test.cpp) # This was the problematic one
# target_link_libraries(rsi_test finmath_library)
#
# # Test runner (can be removed if using ctest directly)
# add_executable(run_all_tests test/test_runner.cpp)

# Enable testing
enable_testing()

# Define individual tests
add_test(NAME BlackScholesTest COMMAND black_scholes_test)
add_test(NAME BinomialOptionPricingTest COMMAND binomial_option_pricing_test)
add_test(NAME CompoundInterestTest COMMAND compound_interest_test)
add_test(NAME RSITest COMMAND rsi_test)

# Add a custom target to run all tests
add_custom_target(build_and_test
COMMAND ${CMAKE_COMMAND} --build . --target black_scholes_test
COMMAND ${CMAKE_COMMAND} --build . --target binomial_option_pricing_test
COMMAND ${CMAKE_COMMAND} --build . --target compound_interest_test
COMMAND ${CMAKE_COMMAND} --build . --target rsi_test
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# Make 'build_and_test' the default target
add_custom_target(default ALL DEPENDS build_and_test)
# Helper macro to add a test executable and link it
macro(add_cpp_test test_name source_file)
message(STATUS "Adding C++ test: ${test_name} from ${source_file}")
add_executable(${test_name}_executable ${source_file})
target_link_libraries(${test_name}_executable PRIVATE finmath_library)
add_test(NAME ${test_name} COMMAND ${test_name}_executable)
endmacro()

# Add C++ tests using the macro
add_cpp_test(RSITest test/TimeSeries/RSI/C++/rsi_test.cpp)
add_cpp_test(RollingVolatilityTest test/TimeSeries/RollingVolatility/C++/rolling_volatility_test.cpp)
add_cpp_test(SimpleMovingAverageTest test/TimeSeries/SimpleMovingAverage/C++/simple_moving_average_test.cpp)
add_cpp_test(EMATest test/TimeSeries/EMA/C++/ema_test.cpp)
# Add tests for EMA here later...

# Add pybind11 for Python bindings
include(FetchContent)
Expand All @@ -79,5 +82,8 @@ pybind11_add_module(finmath_bindings src/python_bindings.cpp ${SOURCES})
# Set the output name of the bindings to 'finmath' to match your desired module name
set_target_properties(finmath_bindings PROPERTIES OUTPUT_NAME "finmath")

# Set the library output directory to be alongside the source bindings file
set_target_properties(finmath_bindings PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/src")

# Link the Python bindings target with the C++ library
target_link_libraries(finmath_bindings PRIVATE finmath_library)
11 changes: 9 additions & 2 deletions include/finmath/TimeSeries/ema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
#define EMA_H

#include <vector>
#include <pybind11/numpy.h>

namespace py = pybind11;

// Function to compute the Exponential Moving Average (EMA) using window size
std::vector<double> compute_ema(const std::vector<double>& prices, size_t window);
std::vector<double> compute_ema(const std::vector<double> &prices, size_t window);

// Function to compute the Exponential Moving Average (EMA) using a smoothing factor
std::vector<double> compute_ema_with_smoothing(const std::vector<double>& prices, double smoothing_factor);
std::vector<double> compute_ema_with_smoothing(const std::vector<double> &prices, double smoothing_factor);

// NumPy overloads
std::vector<double> compute_ema_np(py::array_t<double> prices_arr, size_t window);
std::vector<double> compute_ema_with_smoothing_np(py::array_t<double> prices_arr, double smoothing_factor);

#endif // EMA_H
14 changes: 10 additions & 4 deletions include/finmath/TimeSeries/rolling_volatility.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
#define ROLLING_VOLATILITY_H

#include <vector>
#include <pybind11/numpy.h>

namespace py = pybind11;

// Function to compute the logarithmic returns from prices
std::vector<double> compute_log_returns(const std::vector<double>& prices);
std::vector<double> compute_log_returns(const std::vector<double> &prices);

// Function to compute the standard deviation of a vector
double compute_std(const std::vector<double>& data);
double compute_std(const std::vector<double> &data);

// Function to compute the rolling volatility from a time series of prices (vector version)
std::vector<double> rolling_volatility(const std::vector<double> &prices, size_t window_size);

// Function to compute the rolling volatility from a time series of prices
std::vector<double> rolling_volatility(const std::vector<double>& prices, size_t window_size);
// Overloaded function to compute rolling volatility from a NumPy array
std::vector<double> rolling_volatility_np(py::array_t<double> prices_arr, size_t window_size);

#endif // ROLLING_VOLATILITY_H
16 changes: 11 additions & 5 deletions include/finmath/TimeSeries/rsi.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
#ifndef RSI_H
#define RSI_H

#include<vector>
#include <vector>
#include <pybind11/numpy.h>

//function to compute the average gain over a window
double compute_avg_gain(const std::vector<double>& price_changes, size_t window_size);
namespace py = pybind11;

// function to compute the average gain over a window
double compute_avg_gain(const std::vector<double> &price_changes, size_t window_size);

// Function to compute the average loss over a window
double compute_avg_loss(const std::vector<double>& price_changes, size_t window_size);
double compute_avg_loss(const std::vector<double> &price_changes, size_t window_size);

// Function to compute the RSI from a time series of prices
std::vector<double> compute_smoothed_rsi(const std::vector<double>& prices, size_t window_size);
std::vector<double> compute_smoothed_rsi(const std::vector<double> &prices, size_t window_size);

// NumPy overload
std::vector<double> compute_smoothed_rsi_np(py::array_t<double> prices_arr, size_t window_size);

#endif // RSI_H
8 changes: 7 additions & 1 deletion include/finmath/TimeSeries/simple_moving_average.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@
#define SIMPLE_MOVING_AVERAGE_H

#include <vector>
#include <pybind11/numpy.h>

namespace py = pybind11;

// Function to compute the moving average from a time series
std::vector<double> simple_moving_average(const std::vector<double>& data, size_t window_size);
std::vector<double> simple_moving_average(const std::vector<double> &data, size_t window_size);

// NumPy overload
std::vector<double> simple_moving_average_np(py::array_t<double> data_arr, size_t window_size);

#endif // MOVING_AVERAGE_H
80 changes: 71 additions & 9 deletions src/cpp/TimeSeries/ema.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,85 @@
#include "finmath/TimeSeries/ema.h"
#include <pybind11/numpy.h> // Include numpy header
#include <pybind11/pybind11.h> // Include core pybind11 header for exceptions

std::vector<double> compute_ema(const std::vector<double>& prices, size_t window)
// Compute EMA using window size (list version)
std::vector<double> compute_ema(const std::vector<double> &prices, size_t window)
{
std::vector<double> ema(prices.size(), 0.0);
double multiplier = 2.0 / (window + 1);

ema = compute_ema_with_smoothing(prices, multiplier);
return ema;
if (window == 0)
{
throw std::runtime_error("EMA window cannot be zero.");
}
if (prices.empty())
{
return {};
}
double multiplier = 2.0 / (static_cast<double>(window) + 1.0);
return compute_ema_with_smoothing(prices, multiplier);
}

// Compute EMA using a specified smoothing factor
std::vector<double> compute_ema_with_smoothing(const std::vector<double>& prices, double smoothing_factor)
// Compute EMA using a specified smoothing factor (list version)
std::vector<double> compute_ema_with_smoothing(const std::vector<double> &prices, double smoothing_factor)
{
if (smoothing_factor <= 0 || smoothing_factor >= 1)
{
throw std::runtime_error("EMA smoothing factor must be between 0 and 1 (exclusive).");
}
if (prices.empty())
{
return {};
}
std::vector<double> ema(prices.size(), 0.0);
ema[0] = prices[0]; // Initialize the first EMA value

for (size_t i = 1; i < prices.size(); ++i) {
for (size_t i = 1; i < prices.size(); ++i)
{
ema[i] = ((prices[i] - ema[i - 1]) * smoothing_factor) + ema[i - 1];
}

return ema;
}

// --- NumPy Versions ---

// Compute EMA using window size (NumPy version)
std::vector<double> compute_ema_np(py::array_t<double> prices_arr, size_t window)
{
if (window == 0)
{
throw std::runtime_error("EMA window cannot be zero.");
}
double multiplier = 2.0 / (static_cast<double>(window) + 1.0);
// Delegate to the smoothing factor NumPy version
return compute_ema_with_smoothing_np(prices_arr, multiplier);
}

// Compute EMA using a specified smoothing factor (NumPy version)
std::vector<double> compute_ema_with_smoothing_np(py::array_t<double> prices_arr, double smoothing_factor)
{
py::buffer_info buf_info = prices_arr.request();
if (buf_info.ndim != 1)
{
throw std::runtime_error("Input array must be 1-dimensional.");
}
size_t num_prices = buf_info.size;

if (smoothing_factor <= 0 || smoothing_factor >= 1)
{
throw std::runtime_error("EMA smoothing factor must be between 0 and 1 (exclusive).");
}
if (num_prices == 0)
{
return {};
}

const double *prices_ptr = static_cast<const double *>(buf_info.ptr);
std::vector<double> ema(num_prices, 0.0);
ema[0] = prices_ptr[0]; // Initialize the first EMA value

for (size_t i = 1; i < num_prices; ++i)
{
ema[i] = ((prices_ptr[i] - ema[i - 1]) * smoothing_factor) + ema[i - 1];
}

return ema;
}
Loading