Financial Examples
Below are a few additional examples as to how the Decimal library can be used in the context of financial applications.
All of these examples can be found in the library examples/ folder as well.
Parsing Pricing Data from File
Example 1. This example demonstrates the numerical differences between parsing of monetary values between using
decimal32_t and float// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// This file briefly demonstrates the difference in results when parsing monetary values between float and decimal32_t
#include "where_file.hpp"
#include <boost/decimal/decimal32_t.hpp> // For type decimal32_t
#include <boost/decimal/charconv.hpp> // For support to <charconv>
#include <boost/decimal/iostream.hpp> // For decimal support to <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <limits>
#include <numeric>
#include <iostream>
#include <iomanip>
using boost::decimal::decimal32_t;
template <typename T>
T parse_opening_price(const std::string& line);
template <>
float parse_opening_price<float>(const std::string& line)
{
const auto result {std::stof(line)};
return result;
}
template <>
decimal32_t parse_opening_price<decimal32_t>(const std::string& line)
{
decimal32_t result;
const auto r = from_chars(line, result);
// If we have a parse failure throw std::invalid_argument if the environment supports it,
// using std::invalid_argument which is the same thing thrown by std::stof in the float case
//
// If we are in a no throw environment returning a qNaN will poison our results as well
if (!r)
{
// LCOV_EXCL_START
result = std::numeric_limits<decimal32_t>::quiet_NaN();
BOOST_DECIMAL_THROW_EXCEPTION(std::invalid_argument("Parsing has failed"));
// LCOV_EXCL_STOP
}
return result;
}
template <typename T>
T parse_csv_line(const std::string& line)
{
std::stringstream ss(line);
std::string token;
std::string date;
std::getline(ss, date, ',');
std::getline(ss, token, ',');
return parse_opening_price<T>(token);
}
int main()
{
// We have a CSV file containing one years worth of daily stock data for AAPL
// Here we will show the differences that arise (however small)
// between parsing with float and decimal32_t
// Open and read the CSV file
std::ifstream file(boost::decimal::where_file("AAPL.csv"));
std::string line;
// Skip header line
std::getline(file, line);
std::vector<decimal32_t> decimal_opening_prices;
std::vector<float> float_opening_prices;
// Parse each line once into decimal32_t and once into a float
while (std::getline(file, line))
{
decimal_opening_prices.emplace_back(parse_csv_line<decimal32_t>(line));
float_opening_prices.emplace_back(parse_csv_line<float>(line));
}
// Use std::accumulate to get the sum of all the pricing information in the array
// This will be used to compare the total value parsed
const auto decimal_sum {std::accumulate(decimal_opening_prices.begin(),
decimal_opening_prices.end(), decimal32_t{0})};
const auto float_sum {std::accumulate(float_opening_prices.begin(),
float_opening_prices.end(), float{0})};
// This is the reference value that was found using the sum command of the CSV
// inside Microsoft Excel
const std::string ms_excel_result {"52151.99"};
std::cout << std::setprecision(std::numeric_limits<float>::digits10 + 1)
<< "Number of data points: " << decimal_opening_prices.size() << '\n'
<< " Sum from MS Excel: " << ms_excel_result << '\n'
<< "Sum using decimal32_t: " << decimal_sum << '\n'
<< " Sum using float: " << float_sum << std::endl;
}
Expected Output
Number of data points: 252
Sum from MS Excel: 52151.99
Sum using decimal32_t: 52151.99
Sum using float: 52151.96
Boost.Math to Calculate Bollinger Bands
Example 2. This example demonstrates how we can use the decimal library with existing Boost.Math facilities to perform statistical analysis
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// This example demonstrates how to perform statistics using boost.math
// Needed for operations with boost math
#define BOOST_DECIMAL_ALLOW_IMPLICIT_INTEGER_CONVERSIONS
#include "where_file.hpp"
#include <boost/decimal/decimal64_t.hpp> // For type decimal64_t
#include <boost/decimal/charconv.hpp> // For from_chars
#include <boost/decimal/iostream.hpp> // Decimal support to <iostream> and <iomanip>
#include <boost/decimal/cmath.hpp> // For sqrt of decimal types
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
// Warning suppression for boost.math
// Boost.decimal is tested with -Werror -Wall -Wextra and a few other additional flags
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
# pragma clang diagnostic ignored "-Wsign-conversion"
# pragma clang diagnostic ignored "-Wundef"
# pragma clang diagnostic ignored "-Wstring-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 boost::decimal::decimal64_t;
// This struct holds all the information that is provided
// for a single trading day
struct daily_data
{
std::string date;
decimal64_t open;
decimal64_t high;
decimal64_t low;
decimal64_t close;
decimal64_t volume;
};
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()
{
// The first few lines of this file are similar to the previous example
// in that we parse a single year of AAPL stock data before we can do anything useful with
std::vector<daily_data> stock_data;
// Open and read the CSV file
std::ifstream file(boost::decimal::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_t> closing_prices;
for (const auto& day : stock_data)
{
closing_prices.emplace_back(day.close);
}
// Here we use Boost.Math's statistics facilities
// As shown at the top of the file you will need to define BOOST_DECIMAL_ALLOW_IMPLICIT_INTEGER_CONVERSIONS,
// and suppress a few warnings to make this build cleanly
const decimal64_t mean_closing_price = boost::math::statistics::mean(closing_prices);
const decimal64_t median_closing_price = boost::math::statistics::median(closing_prices);
const decimal64_t variance_closing_price = boost::math::statistics::variance(closing_prices);
const decimal64_t std_dev_closing_price = boost::decimal::sqrt(variance_closing_price);
// 2-Sigma Bollinger Bands
// These are of a single point in time rather than making a plot over time for simplicity
const decimal64_t upper_band = mean_closing_price + 2 * std_dev_closing_price;
const decimal64_t lower_band = mean_closing_price - 2 * std_dev_closing_price;
std::cout << std::fixed << std::setprecision(2)
<< " Mean Closing Price: $" << mean_closing_price << '\n'
<< "Median Closing Price: $" << median_closing_price << '\n'
<< " Standard Deviation: $" << std_dev_closing_price << '\n'
<< "Upper Bollinger Band: $" << upper_band << '\n'
<< "Lower Bollinger Band: $" << lower_band << std::endl;
}
Expected Output
Mean Closing Price: $207.20 Median Closing Price: $214.26 Standard Deviation: $25.45 Upper Bollinger Band: $258.11 Lower Bollinger Band: $156.30