Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding a Java BigDecimal to the nearest interval

I have a BigDecimal calculation result which I need to round to the nearest specified interval (in this case it's the financial market tick size).

e.g. Price [Tick Size] -> Rounded Price

100.1 [0.25] -> 100
100.2 [0.25] -> 100.25
100.1 [0.125] -> 100.125
100.2 [0.125] -> 100.25

Thanks.

Update: schnaader's solution, translated into Java/BigDecimal terms:

price = price.divide(tick).setScale(0, RoundingMode.HALF_UP).multiply(tick)
like image 995
Jon Avatar asked Mar 13 '09 08:03

Jon


People also ask

How do I round off BigDecimal value?

math. BigDecimal. round(MathContext m) is an inbuilt method in Java that returns a BigDecimal value rounded according to the MathContext settings. If the precision setting is 0 then no rounding takes place.

What is rounding mode in BigDecimal Java?

Description. The java. math. BigDecimal. setScale(int newScale, RoundingMode roundingMode) returns a BigDecimal whose scale is the specified value, and whose unscaled value is determined by multiplying or dividing this BigDecimal's unscaled value by the appropriate power of ten to maintain its overall value.

Does Java round 0.5 up or down?

In mathematics, if the fractional part of the argument is greater than 0.5, it is rounded to the next highest integer. If it is less than 0.5, the argument is rounded to the next lowest integer.


2 Answers

You could normalize tick size and then use the usual rounding methods:

100.1 [0.25] -> * (1/0.25) -> 400.4 [1]  -> round -> 400 -> / (1/0.25) -> 100
100.2 [0.25] -> * (1/0.25) -> 400.8 [1] -> round -> 401 -> / (1/0.25) -> 100.25

So it should be:

Price = Round(Price / Tick) * Tick;

Also note that you seem to have to set the correct rounding mode for BigDecimals. See BigDecimal Docs for example. So you should be sure to set this correct and write some tests to check the correctness of your code.

like image 121
schnaader Avatar answered Oct 17 '22 21:10

schnaader


This is more related to "Rounding a stock price to it's nearest tick size".

Answer provided by schnaader is correct, however there are a few things missing.

  • First, you must check whether stock price needs rounding. Unnecessary rounding messes up the fractional part.
  • Then you also need to handle ArithmeticException that might come during BigDecimal divide function

Here's my solution. It would take a lot of time to explain it. I'd recommend to try it with some samples to get a feel. Look for the function roundTick().

import static java.math.RoundingMode.HALF_UP;

import java.math.BigDecimal;

/**
 * Utility class for stock price related operations.
 */
public final class PriceFormatter {

    public static final float DELTA = 0.0001f;

    private PriceFormatter() {
    }

    /**
     * Rounds the price to the nearest tick size.
     *
     * @param price    price
     * @param tickSize tick size
     * @return price rounded to the nearest tick size
     */
    public static final float roundTick(final float price, final float tickSize) {
        if (tickSize < DELTA) {
            return price;
        }

        if (!isRoundingNeeded(price, tickSize)) {
            return price;
        }

        final BigDecimal p = new BigDecimal(price);
        final BigDecimal t = new BigDecimal(tickSize);

        final BigDecimal roundedPrice = p.divide(t, 0, HALF_UP).multiply(t);

        return roundedPrice.floatValue();
    }

    /**
     * Checks whether price needs rounding to the nearest tick size.
     *
     * @param price    price
     * @param tickSize tick size
     * @return true, if rounding is needed; false otherwise
     */
    public static final boolean isRoundingNeeded(final float price, final float tickSize) {
        final int mult = calculateTickMultiplier(tickSize);
        final int mod = (int) (tickSize * mult);
        final float reminder = (((price * mult) % mult) % mod);
        final boolean needsRounding = reminder > DELTA;
        return needsRounding;
    }

    public static final int calculateTickMultiplier(final float tickSize) {
        int result = 1;
        while (((tickSize * result) < 1) || (((tickSize * result) - (int) (tickSize * result)) > DELTA)) {
            result *= 10;
        }

        return result;
    }

}
like image 24
Pritesh Mhatre Avatar answered Oct 17 '22 21:10

Pritesh Mhatre