C++ price type to properly deal with rounding error?

Hey All,

What type is considered is "best practice" for price representation in C++?

I'm building an order book, and have run into the rounding error gremlin.

The problem is ubiquitous, but in this example, I'm using a double to both represent a price point, and also as a key in the bid/ask queues. Rounding error is causing the key lookup to be empty. Example: here I am simply trying to print L2 book data +/- 10 price points above/below market price. Using a loop and an increment of $0.01 (penny pricing), I try to index bid[12.78]. Due to rounding error, I get bid[12.78000000000000001], which maps to nothing. :mad:

Any recommendations on how to represent price type? I'm not sure what direction to head here. I've heard:

1. Internally represent prices as integers, and convert to doubles only when necessary.
2. Represent the whole number and decimal part of numbers as integers. This seems like a lot of work.
3. Use the Intel scientific library.

This is pretty much driving me crazy, so any help is appreciated.

What is the best practice for price representation in C++?

Thank you.

roundingError.png

Solucion numero uno.
 
Why do you think that I'd be better off building this in C#? As I see it, C++ has some clear advantages:

1. Implement in Linux, making available an open-source, open-kernel platform for perf tweaking, and all other Linux beauty. C# can't be compiled on Linux (at least not a C# compiler that I would trust with financial data).
2. Avoiding Windows fees, crashes, and "The Microsoft Way"
3. Portable code that can be compiled under Linux, Windows, Mac, UNIX (Sun, AIX, HPUX), even OS/2 :D
4. Terminal and shell scripts available for console-based applications. (I suppose you could argue for PowerShell here; it's come a long way, but the terminal is the terminal).
5. Perf. C++ beats C# everywhere from a performance perspective:
https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=csharpcore&lang2=gpp
6. C++ is the standard in the financial industry

Also, what do you mean by "Stick to decimals." You mean floating point base (2 ^ -n) internal representation?


I meant get a library that has classes that represent decimal. Sorry I didn't answer that. BTW, java/c# have that built in :D
 
An other possible solution could be to store for each instrument its specific price step size. And represent the price as a multiple of this step size. The multiple is an integer, without rounding issues.
 
In my opinion there is nothing better than my utterly beloved int64_t with fixed decimal points on a per "instrument" basis. You can get most of "instruments" with 6 decimals.
I recommend to read http://www.nanex.net/downloads/APIDocs/NxCoreAPIDocs_V21_20100212.zip "concept prices"
For your purposes you could simply define a "price type" available of each "instrument"
Funny you should mention this! That's the type that I used to build the class; great minds think alike ;)

Note that I'm not sold on this exact implementation of price. Noting your comment regarding fixing decimal places per instrument, I've tried storing the number of decimal places in the private place_ property.

Code:
// price.h

#ifndef _RITCHIE_PRICE_
#define _RITCHIE_PRICE_

#include <iostream>
#include <iomanip>
#include <cstdint>
#include <string>
#include <cmath>

class Price {
    public:
        Price():price_(100), place_(2) {};
        Price(int64_t price, int place):price_(price), place_(place) {};
        Price(const Price & rhs) {
            price_ = rhs.price_;
            place_ = rhs.place_;
        }
        long double price()const {
            return (static_cast<long double> (price_)/pow(10, place_));
        } //getter
        void setPrice(int64_t price, int place) {
            price_ = price;
            place_ = place;
        }
    private:
        int64_t price_;
        int place_;
};
#endif

int main() {

    Price aaa(9835, 2);
    Price bbb(11129835, 5);
    Price ccc(1200078125, 7);
    Price ddd;
    std::cout << "$ " << aaa.price() << "\t\t" << "equity price" << std::endl;
    std::cout << std::setprecision(5);
    std::cout << "$ " << bbb.price() << "\t" << "forex price" << std::endl;
    std::cout << std::setprecision(7);
    std::cout << "$ " << ccc.price() << "\t" << "bond price" << std::endl;
    std::cout << std::setprecision(2);
    std::cout << "$ " << ddd.price() << "\t\t" << "default price" << std::endl;
    std::cout << std::endl;

    std::cout << "Press enter to quit.";
    std::cin.get();
    std::cout << std::endl;
    return 0;
}

/* OUTPUT

$ 98.35         equity price
$ 111.29835     forex price
$ 120.0078125   bond price
$ 1.00          default price

*/
 
Last edited:
Note that I'm not sold on this exact implementation of price. Noting your comment regarding fixing decimal places per instrument, I've tried storing the number of decimal places in the private place_ property.
What your code does not check for is whether the given price is a valid price. For example your forex example would allow a price step of 0.00001, whereas in reality maybe only a multiple of 0.00005 is allowed. This is why I decided in my trading program not to store the position of the decimal point, but use the minimum step size (increment size) as a parameter of each instrument.
 
I think in the class/data structure is necessary to add Instrument Tick Size variable/property and then dance from it.
From tick size you could derive rounding function to fix precision for comparison.
It's allow you to keep price data in double/decimal whatever you want.

Funny you should mention this! That's the type that I used to build the class; great minds think alike ;)

Note that I'm not sold on this exact implementation of price. Noting your comment regarding fixing decimal places per instrument, I've tried storing the number of decimal places in the private place_ property.

Code:
// price.h

#ifndef _RITCHIE_PRICE_
#define _RITCHIE_PRICE_

#include <iostream>
#include <iomanip>
#include <cstdint>
#include <string>
#include <cmath>

class Price {
    public:
        Price():price_(100), place_(2) {};
        Price(int64_t price, int place):price_(price), place_(place) {};
        Price(const Price & rhs) {
            price_ = rhs.price_;
            place_ = rhs.place_;
        }
        long double price()const {
            return (static_cast<long double> (price_)/pow(10, place_));
        } //getter
        void setPrice(int64_t price, int place) {
            price_ = price;
            place_ = place;
        }
    private:
        int64_t price_;
        int place_;
};
#endif

int main() {

    Price aaa(9835, 2);
    Price bbb(11129835, 5);
    Price ccc(1200078125, 7);
    Price ddd;
    std::cout << "$ " << aaa.price() << "\t\t" << "equity price" << std::endl;
    std::cout << std::setprecision(5);
    std::cout << "$ " << bbb.price() << "\t" << "forex price" << std::endl;
    std::cout << std::setprecision(7);
    std::cout << "$ " << ccc.price() << "\t" << "bond price" << std::endl;
    std::cout << std::setprecision(2);
    std::cout << "$ " << ddd.price() << "\t\t" << "default price" << std::endl;
    std::cout << std::endl;

    std::cout << "Press enter to quit.";
    std::cin.get();
    std::cout << std::endl;
    return 0;
}

/* OUTPUT

$ 98.35         equity price
$ 111.29835     forex price
$ 120.0078125   bond price
$ 1.00          default price

*/

What your code does not check for is whether the given price is a valid price. For example your forex example would allow a price step of 0.00001, whereas in reality maybe only a multiple of 0.00005 is allowed. This is why I decided in my trading program not to store the position of the decimal point, but use the minimum step size (increment size) as a parameter of each instrument.
 
Last edited:
Back
Top