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