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 CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ add_cpp_test_labeled(BlackScholesTest test/OptionPricing/black_scholes_test.cpp
add_cpp_test_labeled(BinomialOptionPricingTest test/OptionPricing/binomial_option_pricing_test.cpp "OptionPricing;Unit")
add_cpp_test_labeled(RSITest test/TimeSeries/rsi_test.cpp "TimeSeries;Unit")
add_cpp_test_labeled(RollingStdDevTest test/TimeSeries/rolling_std_dev_test.cpp "TimeSeries;Unit")
add_cpp_test_labeled(ReturnsTest test/TimeSeries/returns_test.cpp "TimeSeries;Unit")
add_cpp_test_labeled(RollingZScoreTest test/TimeSeries/rolling_zscore_test.cpp "TimeSeries;Unit")
add_cpp_test_labeled(BellmanArbitrageTest test/GraphAlgos/bellman_arbitrage_test.cpp "GraphAlgos;Unit")
add_cpp_test_labeled(MarkovChainTest test/MarkovChains/C++/markov_chain_test.cpp "MarkovChains;Unit")
add_cpp_test_labeled(BondPricingTest test/FixedIncome/bond_pricing_test.cpp "FixedIncome;Unit")
add_cpp_test_labeled(AnnuityTest test/InterestAndAnnuities/annuity_test.cpp "InterestAndAnnuities;Unit")
add_cpp_test_labeled(CashFlowTest test/InterestAndAnnuities/cash_flow_test.cpp "InterestAndAnnuities;Unit")
Expand Down
17 changes: 17 additions & 0 deletions include/finmath/MarkovChains/markov_chain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef MARKOV_CHAIN_H
#define MARKOV_CHAIN_H

#include <vector>
#include <map>
#include <string>

// TODO: Implement Markov Chain functionality
// Potential features:
// - Transition matrix operations
// - State space modeling
// - Steady-state calculations
// - N-step transition probabilities
// - First passage times
// - Absorbing states analysis

#endif // MARKOV_CHAIN_H
10 changes: 10 additions & 0 deletions include/finmath/TimeSeries/returns.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef FINMATH_TIME_SERIES_RETURNS_H
#define FINMATH_TIME_SERIES_RETURNS_H

#include <vector>

// Element 0 is 0.0; element i is return from prices[i-1] to prices[i].
std::vector<double> log_returns(const std::vector<double>& prices);
std::vector<double> pct_returns(const std::vector<double>& prices);

#endif // FINMATH_TIME_SERIES_RETURNS_H
9 changes: 9 additions & 0 deletions include/finmath/TimeSeries/rolling_zscore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef FINMATH_TIME_SERIES_ROLLING_ZSCORE_H
#define FINMATH_TIME_SERIES_ROLLING_ZSCORE_H

#include <vector>

std::vector<double> rolling_mean(size_t window_size, const std::vector<double>& data);
std::vector<double> rolling_zscore(size_t window_size, const std::vector<double>& data);

#endif // FINMATH_TIME_SERIES_ROLLING_ZSCORE_H
1 change: 1 addition & 0 deletions include/finmath/finmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
#include "finmath/TimeSeries/rsi.h"
#include "finmath/TimeSeries/ema.h"
#include "finmath/TimeSeries/rolling_std_dev.h"
#include "finmath/MarkovChains/markov_chain.h"

#endif // FINMATH_H
12 changes: 12 additions & 0 deletions src/cpp/MarkovChains/markov_chain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "finmath/MarkovChains/markov_chain.h"
#include <stdexcept>
#include <cmath>

// TODO: Implement Markov Chain functions

// Example function signatures (placeholder):
//
// std::vector<std::vector<double>> compute_transition_matrix(const std::vector<int>& states);
// std::vector<double> compute_steady_state(const std::vector<std::vector<double>>& transition_matrix);
// std::vector<std::vector<double>> matrix_power(const std::vector<std::vector<double>>& matrix, int n);
// double compute_first_passage_time(const std::vector<std::vector<double>>& transition_matrix, int from_state, int to_state);
96 changes: 96 additions & 0 deletions src/cpp/TimeSeries/returns.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "finmath/TimeSeries/returns.h"

#include <pybind11/numpy.h>

#include <cmath>
#include <stdexcept>

namespace py = pybind11;

std::vector<double> log_returns(const std::vector<double>& prices)
{
if (prices.empty()) {
return {};
}

std::vector<double> out(prices.size(), 0.0);
for (size_t i = 1; i < prices.size(); ++i) {
const double prev = prices[i - 1];
const double curr = prices[i];
if (prev <= 0.0 || curr <= 0.0) {
throw std::invalid_argument("log_returns requires all prices > 0");
}
out[i] = std::log(curr / prev);
}
return out;
}

std::vector<double> pct_returns(const std::vector<double>& prices)
{
if (prices.empty()) {
return {};
}

std::vector<double> out(prices.size(), 0.0);
for (size_t i = 1; i < prices.size(); ++i) {
const double prev = prices[i - 1];
const double curr = prices[i];
if (prev == 0.0) {
throw std::invalid_argument("pct_returns requires previous price != 0");
}
out[i] = (curr / prev) - 1.0;
}
return out;
}

std::vector<double> log_returns_np(py::array_t<double> prices_arr)
{
py::buffer_info buf_info = prices_arr.request();
if (buf_info.ndim != 1) {
throw std::runtime_error("Input array must be 1-dimensional.");
}

const size_t n = buf_info.size;
const double* prices_ptr = static_cast<const double*>(buf_info.ptr);

if (n == 0) {
return {};
}

std::vector<double> out(n, 0.0);
for (size_t i = 1; i < n; ++i) {
const double prev = prices_ptr[i - 1];
const double curr = prices_ptr[i];
if (prev <= 0.0 || curr <= 0.0) {
throw std::runtime_error("log_returns requires all prices > 0");
}
out[i] = std::log(curr / prev);
}
return out;
}

std::vector<double> pct_returns_np(py::array_t<double> prices_arr)
{
py::buffer_info buf_info = prices_arr.request();
if (buf_info.ndim != 1) {
throw std::runtime_error("Input array must be 1-dimensional.");
}

const size_t n = buf_info.size;
const double* prices_ptr = static_cast<const double*>(buf_info.ptr);

if (n == 0) {
return {};
}

std::vector<double> out(n, 0.0);
for (size_t i = 1; i < n; ++i) {
const double prev = prices_ptr[i - 1];
const double curr = prices_ptr[i];
if (prev == 0.0) {
throw std::runtime_error("pct_returns requires previous price != 0");
}
out[i] = (curr / prev) - 1.0;
}
return out;
}
115 changes: 115 additions & 0 deletions src/cpp/TimeSeries/rolling_zscore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "finmath/TimeSeries/rolling_zscore.h"

#include <pybind11/numpy.h>

#include <algorithm>
#include <cmath>
#include <stdexcept>

namespace py = pybind11;

std::vector<double> rolling_mean(size_t window_size, const std::vector<double>& data)
{
if (window_size == 0) {
throw std::invalid_argument("Window size cannot be zero.");
}

std::vector<double> result(data.size(), 0.0);
if (data.empty()) {
return result;
}

if (window_size > data.size()) {
window_size = data.size();
}

double sum = 0.0;
for (size_t i = 0; i < window_size; ++i) {
sum += data[i];
}
result[window_size - 1] = sum / static_cast<double>(window_size);

for (size_t i = window_size; i < data.size(); ++i) {
sum += data[i] - data[i - window_size];
result[i] = sum / static_cast<double>(window_size);
}

return result;
}

std::vector<double> rolling_zscore(size_t window_size, const std::vector<double>& data)
{
if (window_size == 0) {
throw std::invalid_argument("Window size cannot be zero.");
}

std::vector<double> result(data.size(), 0.0);
if (data.empty()) {
return result;
}

if (window_size > data.size()) {
window_size = data.size();
}

double sum = 0.0;
double sumsq = 0.0;
for (size_t i = 0; i < window_size; ++i) {
sum += data[i];
sumsq += data[i] * data[i];
}

auto write = [&](size_t idx) {
const double mean = sum / static_cast<double>(window_size);
const double ex2 = sumsq / static_cast<double>(window_size);
double var = ex2 - mean * mean;
if (var < 0.0) {
var = 0.0;
}
const double std = std::sqrt(var);
if (std == 0.0) {
result[idx] = 0.0;
return;
}
result[idx] = (data[idx] - mean) / std;
};

write(window_size - 1);
for (size_t i = window_size; i < data.size(); ++i) {
const double out = data[i - window_size];
const double in = data[i];
sum += in - out;
sumsq += in * in - out * out;
write(i);
}

return result;
}

std::vector<double> rolling_mean_np(py::array_t<double> data_arr, size_t window_size)
{
py::buffer_info buf_info = data_arr.request();
if (buf_info.ndim != 1) {
throw std::runtime_error("Input array must be 1-dimensional.");
}

const size_t n = buf_info.size;
const double* ptr = static_cast<const double*>(buf_info.ptr);

std::vector<double> data(ptr, ptr + n);
return rolling_mean(window_size, data);
}

std::vector<double> rolling_zscore_np(py::array_t<double> data_arr, size_t window_size)
{
py::buffer_info buf_info = data_arr.request();
if (buf_info.ndim != 1) {
throw std::runtime_error("Input array must be 1-dimensional.");
}

const size_t n = buf_info.size;
const double* ptr = static_cast<const double*>(buf_info.ptr);

std::vector<double> data(ptr, ptr + n);
return rolling_zscore(window_size, data);
}
16 changes: 16 additions & 0 deletions src/python_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "finmath/TimeSeries/rsi_simd.h"
#include "finmath/TimeSeries/ema.h"
#include "finmath/TimeSeries/ema_simd.h"
#include "finmath/TimeSeries/returns.h"
#include "finmath/TimeSeries/rolling_zscore.h"
#include "finmath/Helper/simd_helper.h"
#include "finmath/GraphAlgos/bellman_arbitrage.h"
#include "finmath/FixedIncome/bond_pricing.h"
Expand All @@ -27,6 +29,10 @@ namespace py = pybind11;
std::vector<double> rolling_volatility_np(py::array_t<double> prices_arr, size_t window_size);
std::vector<double> simple_moving_average_np(py::array_t<double> data_arr, size_t window_size);
std::vector<double> compute_smoothed_rsi_np(py::array_t<double> prices_arr, size_t window_size);
std::vector<double> log_returns_np(py::array_t<double> prices_arr);
std::vector<double> pct_returns_np(py::array_t<double> prices_arr);
std::vector<double> rolling_mean_np(py::array_t<double> data_arr, size_t window_size);
std::vector<double> rolling_zscore_np(py::array_t<double> data_arr, size_t window_size);

PYBIND11_MODULE(finmath, m)
{
Expand Down Expand Up @@ -88,6 +94,16 @@ PYBIND11_MODULE(finmath, m)
m.def("ema_smoothing_simd", &compute_ema_with_smoothing_simd, "Exponential Moving Average - Smoothing Factor (SIMD-optimized, zero-copy NumPy)",
py::arg("prices"), py::arg("smoothing_factor"));

// Returns + z-score utilities
m.def("log_returns", &log_returns, "Log returns (List input)", py::arg("prices"));
m.def("log_returns", &log_returns_np, "Log returns (NumPy/Pandas input)", py::arg("prices"));
m.def("pct_returns", &pct_returns, "Percent returns (List input)", py::arg("prices"));
m.def("pct_returns", &pct_returns_np, "Percent returns (NumPy/Pandas input)", py::arg("prices"));
m.def("rolling_mean", &rolling_mean, "Rolling mean (List input)", py::arg("window_size"), py::arg("data"));
m.def("rolling_mean", &rolling_mean_np, "Rolling mean (NumPy/Pandas input)", py::arg("data"), py::arg("window_size"));
m.def("rolling_zscore", &rolling_zscore, "Rolling z-score (List input)", py::arg("window_size"), py::arg("data"));
m.def("rolling_zscore", &rolling_zscore_np, "Rolling z-score (NumPy/Pandas input)", py::arg("data"), py::arg("window_size"));

// Utility function to get SIMD backend
m.def("get_simd_backend", &finmath::simd::get_simd_backend, "Get the active SIMD backend (AVX, SSE, NEON, or Scalar)");

Expand Down
20 changes: 20 additions & 0 deletions test/MarkovChains/C++/markov_chain_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "finmath/MarkovChains/markov_chain.h"
#include <iostream>
#include <cassert>
#include <cmath>

// TODO: Implement test cases for Markov Chain functionality

int main() {
std::cout << "Markov Chain tests - TODO: Implement" << std::endl;

// Example test structure:
// - Test transition matrix creation
// - Test steady state calculations
// - Test matrix operations
// - Test first passage times
// - Test absorbing states

std::cout << "All Markov Chain tests passed (placeholder)." << std::endl;
return 0;
}
39 changes: 39 additions & 0 deletions test/TimeSeries/returns_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include <cassert>
#include <cmath>
#include <iostream>
#include <vector>

#include "finmath/TimeSeries/returns.h"

static bool almost_equal(double a, double b, double tol)
{
return std::abs(a - b) <= tol * std::max(std::abs(a), std::abs(b));
}

int main()
{
const double tol = 1e-9;

// pct_returns: simple known ratios
{
std::vector<double> prices{100.0, 110.0, 99.0};
auto r = pct_returns(prices);
assert(r.size() == prices.size());
assert(r[0] == 0.0);
assert(almost_equal(r[1], 0.10, tol));
assert(almost_equal(r[2], -0.10, tol));
}

// log_returns: exp/log identity checks
{
std::vector<double> prices{100.0, 110.0, 99.0};
auto r = log_returns(prices);
assert(r.size() == prices.size());
assert(r[0] == 0.0);
assert(almost_equal(std::exp(r[1]), prices[1] / prices[0], tol));
assert(almost_equal(std::exp(r[2]), prices[2] / prices[1], tol));
}

std::cout << "Returns Tests Pass!" << std::endl;
return 0;
}
Loading
Loading