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)
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.
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.
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.
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.
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.
ArithmeticException
that might come during BigDecimal
divide functionHere'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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With