diff --git a/common/src/main/java/haveno/common/util/MathUtils.java b/common/src/main/java/haveno/common/util/MathUtils.java index 25c91ed254..a89e4bc01e 100644 --- a/common/src/main/java/haveno/common/util/MathUtils.java +++ b/common/src/main/java/haveno/common/util/MathUtils.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; +import java.math.BigInteger; import java.math.RoundingMode; import java.util.ArrayDeque; import java.util.Deque; @@ -85,6 +86,11 @@ public class MathUtils { return ((double) value) * factor; } + public static BigInteger scaleUpByPowerOf10(BigInteger value, int exponent) { + BigInteger factor = BigInteger.TEN.pow(exponent); + return value.multiply(factor); + } + public static double scaleDownByPowerOf10(double value, int exponent) { double factor = Math.pow(10, exponent); return value / factor; @@ -95,6 +101,11 @@ public class MathUtils { return ((double) value) / factor; } + public static BigInteger scaleDownByPowerOf10(BigInteger value, int exponent) { + BigInteger factor = BigInteger.TEN.pow(exponent); + return value.divide(factor); + } + public static double exactMultiply(double value1, double value2) { return BigDecimal.valueOf(value1).multiply(BigDecimal.valueOf(value2)).doubleValue(); } diff --git a/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java b/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java index 39b67d1942..0863ab28be 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java +++ b/desktop/src/main/java/haveno/desktop/main/market/trades/ChartCalculations.java @@ -22,13 +22,16 @@ import haveno.common.util.MathUtils; import haveno.core.locale.CurrencyUtil; import haveno.core.monetary.CryptoMoney; import haveno.core.monetary.TraditionalMoney; +import haveno.core.trade.HavenoUtils; import haveno.core.trade.statistics.TradeStatistics3; import haveno.desktop.main.market.trades.charts.CandleData; import haveno.desktop.util.DisplayUtils; import javafx.scene.chart.XYChart; import javafx.util.Pair; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; @@ -47,6 +50,7 @@ import java.util.stream.Collectors; import static haveno.desktop.main.market.trades.TradesChartsViewModel.MAX_TICKS; +@Slf4j public class ChartCalculations { static final ZoneId ZONE_ID = ZoneId.systemDefault(); @@ -211,15 +215,15 @@ public class ChartCalculations { } private static long getAverageTraditionalPrice(List tradeStatisticsList) { - long accumulatedAmount = 0; // TODO: use BigInteger - long accumulatedVolume = 0; + BigInteger accumulatedAmount = BigInteger.ZERO; + BigInteger accumulatedVolume = BigInteger.ZERO; for (TradeStatistics3 tradeStatistics : tradeStatisticsList) { - accumulatedAmount += tradeStatistics.getAmount(); - accumulatedVolume += tradeStatistics.getTradeVolume().getValue(); + accumulatedAmount = accumulatedAmount.add(BigInteger.valueOf(tradeStatistics.getAmount())); + accumulatedVolume = accumulatedVolume.add(BigInteger.valueOf(tradeStatistics.getTradeVolume().getValue())); } - double accumulatedVolumeAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedVolume, 4 + TraditionalMoney.SMALLEST_UNIT_EXPONENT); - return MathUtils.roundDoubleToLong(accumulatedVolumeAsDouble / accumulatedAmount); + BigInteger accumulatedVolumeAsBI = MathUtils.scaleUpByPowerOf10(accumulatedVolume, TraditionalMoney.SMALLEST_UNIT_EXPONENT + 4); + return MathUtils.roundDoubleToLong(HavenoUtils.divide(accumulatedVolumeAsBI, accumulatedAmount)); } @VisibleForTesting @@ -232,8 +236,8 @@ public class ChartCalculations { long close = 0; long high = 0; long low = 0; - long accumulatedVolume = 0; // TODO: use BigInteger - long accumulatedAmount = 0; + BigInteger accumulatedVolume = BigInteger.ZERO; + BigInteger accumulatedAmount = BigInteger.ZERO; long numTrades = set.size(); List tradePrices = new ArrayList<>(); for (TradeStatistics3 item : set) { @@ -242,8 +246,8 @@ public class ChartCalculations { low = (low != 0) ? Math.min(low, tradePriceAsLong) : tradePriceAsLong; high = (high != 0) ? Math.max(high, tradePriceAsLong) : tradePriceAsLong; - accumulatedVolume += item.getTradeVolume().getValue(); - accumulatedAmount += item.getTradeAmount().longValueExact(); + accumulatedVolume = accumulatedVolume.add(BigInteger.valueOf(item.getTradeVolume().getValue())); + accumulatedAmount = accumulatedAmount.add(item.getTradeAmount()); tradePrices.add(tradePriceAsLong); } Collections.sort(tradePrices); @@ -262,12 +266,12 @@ public class ChartCalculations { boolean isBullish; if (CurrencyUtil.isCryptoCurrency(currencyCode)) { isBullish = close < open; - double accumulatedAmountAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedAmount, CryptoMoney.SMALLEST_UNIT_EXPONENT - 4); - averagePrice = MathUtils.roundDoubleToLong(accumulatedAmountAsDouble / accumulatedVolume); + BigInteger accumulatedAmountAsBI = MathUtils.scaleUpByPowerOf10(accumulatedAmount, CryptoMoney.SMALLEST_UNIT_EXPONENT - 4); + averagePrice = MathUtils.roundDoubleToLong(HavenoUtils.divide(accumulatedAmountAsBI, accumulatedVolume)); } else { isBullish = close > open; - double accumulatedVolumeAsDouble = MathUtils.scaleUpByPowerOf10((double) accumulatedVolume, 4 + TraditionalMoney.SMALLEST_UNIT_EXPONENT); - averagePrice = MathUtils.roundDoubleToLong(accumulatedVolumeAsDouble / accumulatedAmount); + BigInteger accumulatedVolumeAsBI = MathUtils.scaleUpByPowerOf10(accumulatedVolume, TraditionalMoney.SMALLEST_UNIT_EXPONENT + 4); + averagePrice = MathUtils.roundDoubleToLong(HavenoUtils.divide(accumulatedVolumeAsBI, accumulatedAmount)); } Date dateFrom = new Date(getTimeFromTickIndex(tick, itemsPerInterval)); @@ -278,10 +282,10 @@ public class ChartCalculations { // We do not need precision, so we scale down before multiplication otherwise we could get an overflow. averageUsdPrice = (long) MathUtils.scaleDownByPowerOf10((double) averageUsdPrice, TraditionalMoney.SMALLEST_UNIT_EXPONENT); - long volumeInUsd = averageUsdPrice * (long) MathUtils.scaleDownByPowerOf10((double) accumulatedAmount, 4); + long volumeInUsd = averageUsdPrice * MathUtils.scaleDownByPowerOf10(accumulatedAmount, 4).longValue(); // We store USD value without decimals as its only total volume, no precision is needed. volumeInUsd = (long) MathUtils.scaleDownByPowerOf10((double) volumeInUsd, TraditionalMoney.SMALLEST_UNIT_EXPONENT); - return new CandleData(tick, open, close, high, low, averagePrice, medianPrice, accumulatedAmount, accumulatedVolume, + return new CandleData(tick, open, close, high, low, averagePrice, medianPrice, accumulatedAmount.longValueExact(), accumulatedVolume.longValueExact(), numTrades, isBullish, dateString, volumeInUsd); }