Skip to content

Commit 13d1ebe

Browse files
authored
Merge pull request #774 from cppalliance/math_example
Add example using Boost.Math
2 parents c2bf7c5 + fd1f881 commit 13d1ebe

File tree

5 files changed

+241
-97
lines changed

5 files changed

+241
-97
lines changed

doc/decimal/examples.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,11 @@ This serves as a framework for other calculations for securities.
228228
=== Currency Conversion
229229
In the examples folder there is a file named `currency_conversion.cpp`.
230230
This example shows how to simply convert currencies based off a given exchange rate.
231+
232+
== Boost.Math Integration
233+
234+
=== Bollinger Bands
235+
236+
In the examples folder there is a file named `statistics.cpp`.
237+
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.
238+
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.

examples/moving_average.cpp

Lines changed: 5 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Distributed under the Boost Software License, Version 1.0.
33
// https://www.boost.org/LICENSE_1_0.txt
44

5-
5+
#include "where_file.hpp"
66
#include <boost/decimal.hpp>
77
#include <iostream>
88
#include <iomanip>
@@ -13,100 +13,6 @@
1313

1414
using namespace boost::decimal;
1515

16-
auto where_file(const std::string& test_vectors_filename) -> std::string
17-
{
18-
// Try to open the file in each of the known relative paths
19-
// in order to find out where it is located.
20-
21-
// Boost-root
22-
std::string test_vectors_filename_relative = "libs/decimal/examples/" + test_vectors_filename;
23-
24-
std::ifstream in_01(test_vectors_filename_relative.c_str());
25-
26-
const bool file_01_is_open { in_01.is_open() };
27-
28-
29-
if(file_01_is_open)
30-
{
31-
in_01.close();
32-
}
33-
else
34-
{
35-
// Local test directory or IDE
36-
test_vectors_filename_relative = "../examples/" + test_vectors_filename;
37-
38-
std::ifstream in_02(test_vectors_filename_relative.c_str());
39-
40-
const bool file_02_is_open { in_02.is_open() };
41-
42-
if(file_02_is_open)
43-
{
44-
in_02.close();
45-
}
46-
else
47-
{
48-
// test/cover
49-
test_vectors_filename_relative = "../../examples/" + test_vectors_filename;
50-
51-
std::ifstream in_03(test_vectors_filename_relative.c_str());
52-
53-
const bool file_03_is_open { in_03.is_open() };
54-
55-
if(file_03_is_open)
56-
{
57-
in_03.close();
58-
}
59-
else
60-
{
61-
// CMake builds
62-
test_vectors_filename_relative = "../../../../libs/decimal/examples/" + test_vectors_filename;
63-
64-
std::ifstream in_04(test_vectors_filename_relative.c_str());
65-
66-
const bool file_04_is_open { in_04.is_open() };
67-
68-
if(file_04_is_open)
69-
{
70-
in_04.close();
71-
}
72-
else
73-
{
74-
// Try to open the file from the absolute path.
75-
test_vectors_filename_relative = test_vectors_filename;
76-
77-
std::ifstream in_05(test_vectors_filename_relative.c_str());
78-
79-
const bool file_05_is_open { in_05.is_open() };
80-
81-
if(file_05_is_open)
82-
{
83-
in_05.close();
84-
}
85-
else
86-
{
87-
// Clion Cmake builds
88-
test_vectors_filename_relative = "../../../libs/decimal/examples/" + test_vectors_filename;
89-
90-
std::ifstream in_06(test_vectors_filename_relative.c_str());
91-
92-
const bool file_06_is_open { in_06.is_open() };
93-
if (file_06_is_open)
94-
{
95-
in_06.close();
96-
}
97-
else
98-
{
99-
test_vectors_filename_relative = "";
100-
}
101-
}
102-
}
103-
}
104-
}
105-
}
106-
107-
return test_vectors_filename_relative;
108-
}
109-
11016
struct daily_data
11117
{
11218
std::string date;
@@ -157,7 +63,8 @@ int main()
15763
std::getline(file, line);
15864

15965
// Read data
160-
while (std::getline(file, line)) {
66+
while (std::getline(file, line))
67+
{
16168
stock_data.push_back(parse_csv_line(line));
16269
}
16370

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

17279
// Calculate sum for the window
173-
for (size_t j = 0; j < window_size; ++j) {
80+
for (size_t j = 0; j < window_size; ++j)
81+
{
17482
sum += stock_data[i - j].close;
17583
}
17684

examples/statistics.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include "where_file.hpp"
6+
#include <boost/decimal.hpp>
7+
#include <iostream>
8+
#include <iomanip>
9+
#include <string>
10+
#include <vector>
11+
#include <fstream>
12+
#include <sstream>
13+
14+
#if defined(__clang__)
15+
# pragma clang diagnostic push
16+
# pragma clang diagnostic ignored "-Wfloat-equal"
17+
# pragma clang diagnostic ignored "-Wsign-conversion"
18+
#elif defined(__GNUC__)
19+
# pragma GCC diagnostic push
20+
# pragma GCC diagnostic ignored "-Wfloat-equal"
21+
# pragma GCC diagnostic ignored "-Wsign-conversion"
22+
#endif
23+
24+
#include <boost/math/statistics/univariate_statistics.hpp>
25+
26+
#if defined(__clang__)
27+
# pragma clang diagnostic pop
28+
#elif defined(__GNUC__)
29+
# pragma GCC diagnostic pop
30+
#endif
31+
32+
using namespace boost::decimal;
33+
34+
struct daily_data
35+
{
36+
std::string date;
37+
decimal64 open;
38+
decimal64 high;
39+
decimal64 low;
40+
decimal64 close;
41+
decimal64 volume;
42+
};
43+
44+
// Function to split a CSV line into daily_data
45+
auto parse_csv_line(const std::string& line) -> daily_data
46+
{
47+
std::stringstream ss(line);
48+
std::string token;
49+
daily_data data;
50+
51+
// Parse each column
52+
std::getline(ss, data.date, ',');
53+
std::getline(ss, token, ',');
54+
from_chars(token, data.open);
55+
56+
std::getline(ss, token, ',');
57+
from_chars(token, data.high);
58+
59+
std::getline(ss, token, ',');
60+
from_chars(token, data.low);
61+
62+
std::getline(ss, token, ',');
63+
from_chars(token, data.close);
64+
65+
std::getline(ss, token, ',');
66+
from_chars(token, data.volume);
67+
68+
return data;
69+
}
70+
71+
int main()
72+
{
73+
std::vector<daily_data> stock_data;
74+
75+
// Open and read the CSV file
76+
std::ifstream file(where_file("AAPL.csv"));
77+
std::string line;
78+
79+
// Skip header line
80+
std::getline(file, line);
81+
82+
// Read data
83+
while (std::getline(file, line))
84+
{
85+
stock_data.push_back(parse_csv_line(line));
86+
}
87+
88+
// Get the closing prices for the entire year
89+
std::vector<decimal64> closing_prices;
90+
for (const auto& day : stock_data)
91+
{
92+
closing_prices.emplace_back(day.close);
93+
}
94+
95+
const auto mean_closing_price = boost::math::statistics::mean(closing_prices);
96+
const auto median_closing_price = boost::math::statistics::median(closing_prices);
97+
const auto variance_closing_price = boost::math::statistics::variance(closing_prices);
98+
const auto std_dev_closing_price = sqrt(variance_closing_price);
99+
100+
// 2-Sigma Bollinger Bands
101+
const auto upper_band = mean_closing_price + 2 * std_dev_closing_price;
102+
const auto lower_band = mean_closing_price - 2 * std_dev_closing_price;
103+
104+
std::cout << std::fixed << std::setprecision(2)
105+
<< " Mean Closing Price: " << mean_closing_price << '\n'
106+
<< " Standard Deviation: " << std_dev_closing_price << '\n'
107+
<< "Upper Bollinger Band: " << upper_band << '\n'
108+
<< "Lower Bollinger Band: " << lower_band << std::endl;
109+
110+
// Mean = 207.21
111+
// Median = 214.27
112+
return mean_closing_price > median_closing_price;
113+
}
114+

examples/where_file.hpp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_DECIMAL_EXAMPLE_WHERE_FILE_HPP
6+
#define BOOST_DECIMAL_EXAMPLE_WHERE_FILE_HPP
7+
8+
#include <string>
9+
#include <vector>
10+
#include <fstream>
11+
#include <sstream>
12+
13+
namespace boost {
14+
namespace decimal {
15+
16+
auto where_file(const std::string& test_vectors_filename) -> std::string
17+
{
18+
// Try to open the file in each of the known relative paths
19+
// in order to find out where it is located.
20+
21+
// Boost-root
22+
std::string test_vectors_filename_relative = "libs/decimal/examples/" + test_vectors_filename;
23+
24+
std::ifstream in_01(test_vectors_filename_relative.c_str());
25+
26+
const bool file_01_is_open { in_01.is_open() };
27+
28+
29+
if(file_01_is_open)
30+
{
31+
in_01.close();
32+
}
33+
else
34+
{
35+
// Local test directory or IDE
36+
test_vectors_filename_relative = "../examples/" + test_vectors_filename;
37+
38+
std::ifstream in_02(test_vectors_filename_relative.c_str());
39+
40+
const bool file_02_is_open { in_02.is_open() };
41+
42+
if(file_02_is_open)
43+
{
44+
in_02.close();
45+
}
46+
else
47+
{
48+
// test/cover
49+
test_vectors_filename_relative = "../../examples/" + test_vectors_filename;
50+
51+
std::ifstream in_03(test_vectors_filename_relative.c_str());
52+
53+
const bool file_03_is_open { in_03.is_open() };
54+
55+
if(file_03_is_open)
56+
{
57+
in_03.close();
58+
}
59+
else
60+
{
61+
// CMake builds
62+
test_vectors_filename_relative = "../../../../libs/decimal/examples/" + test_vectors_filename;
63+
64+
std::ifstream in_04(test_vectors_filename_relative.c_str());
65+
66+
const bool file_04_is_open { in_04.is_open() };
67+
68+
if(file_04_is_open)
69+
{
70+
in_04.close();
71+
}
72+
else
73+
{
74+
// Try to open the file from the absolute path.
75+
test_vectors_filename_relative = test_vectors_filename;
76+
77+
std::ifstream in_05(test_vectors_filename_relative.c_str());
78+
79+
const bool file_05_is_open { in_05.is_open() };
80+
81+
if(file_05_is_open)
82+
{
83+
in_05.close();
84+
}
85+
else
86+
{
87+
// Clion Cmake builds
88+
test_vectors_filename_relative = "../../../libs/decimal/examples/" + test_vectors_filename;
89+
90+
std::ifstream in_06(test_vectors_filename_relative.c_str());
91+
92+
const bool file_06_is_open { in_06.is_open() };
93+
if (file_06_is_open)
94+
{
95+
in_06.close();
96+
}
97+
else
98+
{
99+
test_vectors_filename_relative = "";
100+
}
101+
}
102+
}
103+
}
104+
}
105+
}
106+
107+
return test_vectors_filename_relative;
108+
}
109+
110+
} // namespace decimal
111+
} // namespace boost
112+
113+
#endif //BOOST_DECIMAL_EXAMPLE_WHERE_FILE_HPP

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,4 @@ run ../examples/literals.cpp ;
152152
run ../examples/rounding_mode.cpp ;
153153
run ../examples/moving_average.cpp ;
154154
run ../examples/currency_conversion.cpp ;
155+
run ../examples/statistics.cpp ;

0 commit comments

Comments
 (0)