Skip to content
Merged
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
8 changes: 8 additions & 0 deletions doc/decimal/examples.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,11 @@ This serves as a framework for other calculations for securities.
=== Currency Conversion
In the examples folder there is a file named `currency_conversion.cpp`.
This example shows how to simply convert currencies based off a given exchange rate.

== Boost.Math Integration

=== Bollinger Bands

In the examples folder there is a file named `statistics.cpp`.
This example demonstrates how to parse a file, and then leverage Boost.Math to compute statistics of that data set culminating with the values of the Bollinger Bands.
This example could be extended with the simple moving average to create full bands based on the period of the moving average you would like.
102 changes: 5 additions & 97 deletions examples/moving_average.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt


#include "where_file.hpp"
#include <boost/decimal.hpp>
#include <iostream>
#include <iomanip>
Expand All @@ -13,100 +13,6 @@

using namespace boost::decimal;

auto where_file(const std::string& test_vectors_filename) -> std::string
{
// Try to open the file in each of the known relative paths
// in order to find out where it is located.

// Boost-root
std::string test_vectors_filename_relative = "libs/decimal/examples/" + test_vectors_filename;

std::ifstream in_01(test_vectors_filename_relative.c_str());

const bool file_01_is_open { in_01.is_open() };


if(file_01_is_open)
{
in_01.close();
}
else
{
// Local test directory or IDE
test_vectors_filename_relative = "../examples/" + test_vectors_filename;

std::ifstream in_02(test_vectors_filename_relative.c_str());

const bool file_02_is_open { in_02.is_open() };

if(file_02_is_open)
{
in_02.close();
}
else
{
// test/cover
test_vectors_filename_relative = "../../examples/" + test_vectors_filename;

std::ifstream in_03(test_vectors_filename_relative.c_str());

const bool file_03_is_open { in_03.is_open() };

if(file_03_is_open)
{
in_03.close();
}
else
{
// CMake builds
test_vectors_filename_relative = "../../../../libs/decimal/examples/" + test_vectors_filename;

std::ifstream in_04(test_vectors_filename_relative.c_str());

const bool file_04_is_open { in_04.is_open() };

if(file_04_is_open)
{
in_04.close();
}
else
{
// Try to open the file from the absolute path.
test_vectors_filename_relative = test_vectors_filename;

std::ifstream in_05(test_vectors_filename_relative.c_str());

const bool file_05_is_open { in_05.is_open() };

if(file_05_is_open)
{
in_05.close();
}
else
{
// Clion Cmake builds
test_vectors_filename_relative = "../../../libs/decimal/examples/" + test_vectors_filename;

std::ifstream in_06(test_vectors_filename_relative.c_str());

const bool file_06_is_open { in_06.is_open() };
if (file_06_is_open)
{
in_06.close();
}
else
{
test_vectors_filename_relative = "";
}
}
}
}
}
}

return test_vectors_filename_relative;
}

struct daily_data
{
std::string date;
Expand Down Expand Up @@ -157,7 +63,8 @@ int main()
std::getline(file, line);

// Read data
while (std::getline(file, line)) {
while (std::getline(file, line))
{
stock_data.push_back(parse_csv_line(line));
}

Expand All @@ -170,7 +77,8 @@ int main()
decimal64 sum(0);

// Calculate sum for the window
for (size_t j = 0; j < window_size; ++j) {
for (size_t j = 0; j < window_size; ++j)
{
sum += stock_data[i - j].close;
}

Expand Down
114 changes: 114 additions & 0 deletions examples/statistics.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include "where_file.hpp"
#include <boost/decimal.hpp>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>

#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
# pragma clang diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif

#include <boost/math/statistics/univariate_statistics.hpp>

#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif

using namespace boost::decimal;

struct daily_data
{
std::string date;
decimal64 open;
decimal64 high;
decimal64 low;
decimal64 close;
decimal64 volume;
};

// Function to split a CSV line into daily_data
auto parse_csv_line(const std::string& line) -> daily_data
{
std::stringstream ss(line);
std::string token;
daily_data data;

// Parse each column
std::getline(ss, data.date, ',');
std::getline(ss, token, ',');
from_chars(token, data.open);

std::getline(ss, token, ',');
from_chars(token, data.high);

std::getline(ss, token, ',');
from_chars(token, data.low);

std::getline(ss, token, ',');
from_chars(token, data.close);

std::getline(ss, token, ',');
from_chars(token, data.volume);

return data;
}

int main()
{
std::vector<daily_data> stock_data;

// Open and read the CSV file
std::ifstream file(where_file("AAPL.csv"));
std::string line;

// Skip header line
std::getline(file, line);

// Read data
while (std::getline(file, line))
{
stock_data.push_back(parse_csv_line(line));
}

// Get the closing prices for the entire year
std::vector<decimal64> closing_prices;
for (const auto& day : stock_data)
{
closing_prices.emplace_back(day.close);
}

const auto mean_closing_price = boost::math::statistics::mean(closing_prices);
const auto median_closing_price = boost::math::statistics::median(closing_prices);
const auto variance_closing_price = boost::math::statistics::variance(closing_prices);
const auto std_dev_closing_price = sqrt(variance_closing_price);

// 2-Sigma Bollinger Bands
const auto upper_band = mean_closing_price + 2 * std_dev_closing_price;
const auto lower_band = mean_closing_price - 2 * std_dev_closing_price;

std::cout << std::fixed << std::setprecision(2)
<< " Mean Closing Price: " << mean_closing_price << '\n'
<< " Standard Deviation: " << std_dev_closing_price << '\n'
<< "Upper Bollinger Band: " << upper_band << '\n'
<< "Lower Bollinger Band: " << lower_band << std::endl;

// Mean = 207.21
// Median = 214.27
return mean_closing_price > median_closing_price;
}

113 changes: 113 additions & 0 deletions examples/where_file.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_DECIMAL_EXAMPLE_WHERE_FILE_HPP
#define BOOST_DECIMAL_EXAMPLE_WHERE_FILE_HPP

#include <string>
#include <vector>
#include <fstream>
#include <sstream>

namespace boost {
namespace decimal {

auto where_file(const std::string& test_vectors_filename) -> std::string
{
// Try to open the file in each of the known relative paths
// in order to find out where it is located.

// Boost-root
std::string test_vectors_filename_relative = "libs/decimal/examples/" + test_vectors_filename;

std::ifstream in_01(test_vectors_filename_relative.c_str());

const bool file_01_is_open { in_01.is_open() };


if(file_01_is_open)
{
in_01.close();
}
else
{
// Local test directory or IDE
test_vectors_filename_relative = "../examples/" + test_vectors_filename;

std::ifstream in_02(test_vectors_filename_relative.c_str());

const bool file_02_is_open { in_02.is_open() };

if(file_02_is_open)
{
in_02.close();
}
else
{
// test/cover
test_vectors_filename_relative = "../../examples/" + test_vectors_filename;

std::ifstream in_03(test_vectors_filename_relative.c_str());

const bool file_03_is_open { in_03.is_open() };

if(file_03_is_open)
{
in_03.close();
}
else
{
// CMake builds
test_vectors_filename_relative = "../../../../libs/decimal/examples/" + test_vectors_filename;

std::ifstream in_04(test_vectors_filename_relative.c_str());

const bool file_04_is_open { in_04.is_open() };

if(file_04_is_open)
{
in_04.close();
}
else
{
// Try to open the file from the absolute path.
test_vectors_filename_relative = test_vectors_filename;

std::ifstream in_05(test_vectors_filename_relative.c_str());

const bool file_05_is_open { in_05.is_open() };

if(file_05_is_open)
{
in_05.close();
}
else
{
// Clion Cmake builds
test_vectors_filename_relative = "../../../libs/decimal/examples/" + test_vectors_filename;

std::ifstream in_06(test_vectors_filename_relative.c_str());

const bool file_06_is_open { in_06.is_open() };
if (file_06_is_open)
{
in_06.close();
}
else
{
test_vectors_filename_relative = "";
}
}
}
}
}
}

return test_vectors_filename_relative;
}

} // namespace decimal
} // namespace boost

#endif //BOOST_DECIMAL_EXAMPLE_WHERE_FILE_HPP
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,4 @@ run ../examples/literals.cpp ;
run ../examples/rounding_mode.cpp ;
run ../examples/moving_average.cpp ;
run ../examples/currency_conversion.cpp ;
run ../examples/statistics.cpp ;
Loading