All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.objectlab.kit.fxcalc.FxRateImpl Maven / Gradle / Ivy

package net.objectlab.kit.fxcalc;

import static net.objectlab.kit.util.BigDecimalUtil.divide;
import static net.objectlab.kit.util.BigDecimalUtil.inverse;
import static net.objectlab.kit.util.BigDecimalUtil.isNullOrZero;
import static net.objectlab.kit.util.BigDecimalUtil.multiply;
import static net.objectlab.kit.util.BigDecimalUtil.setScale;

import java.math.BigDecimal;
import java.util.Optional;

import net.objectlab.kit.util.BigDecimalUtil;

/**
 * Represents an immutable FxRate.
 * @author Benoit Xhenseval
 */
public class FxRateImpl implements FxRate {
    private static final int DEC_PLACE_FOR_MONEY = 2;
    private static final int PRECISION_FOR_INVERSE = 10;
    private static final BigDecimal TWO = BigDecimal.valueOf(2L);
    private final CurrencyPair currencyPair;
    private final String crossCcy;
    private final boolean marketConvention;
    private final BigDecimal bid;
    private final BigDecimal ask;

    public FxRateImpl(final CurrencyPair currencyPair, final String crossCcy, final boolean marketConvention, final BigDecimal bid,
            final BigDecimal ask) {
        this.currencyPair = currencyPair;
        this.crossCcy = crossCcy;
        this.marketConvention = marketConvention;
        this.bid = bid;
        this.ask = ask;
    }

    /**
     * Nice human readable description of the FX Rate, useful reminder.
     * 
     * CAD.SGD Mkt Convention:true
     * Quoter buys  CAD and sells SGD at 1.1279
     * Quoter sells CAD and buys  SGD at 1.1289
     * 
*/ @Override public String getDescription() { final StringBuilder b = new StringBuilder(); b.append(currencyPair).append(" Mkt Convention:").append(marketConvention); if (crossCcy != null) { b.append(" Cross Ccy:").append(crossCcy); } final String cr = System.getProperty("line.separator"); b.append(cr); b.append("Quoter buys ").append(currencyPair.getCcy1()).append(" and sells ").append(currencyPair.getCcy2()).append(" at ").append(bid) .append(cr); b.append("Quoter sells ").append(currencyPair.getCcy1()).append(" and buys ").append(currencyPair.getCcy2()).append(" at ").append(ask); return b.toString(); } @Override public FxRate createInverse() { return new FxRateImpl(currencyPair.createInverse(), crossCcy, !marketConvention, inverse(ask), inverse(bid)); } @Override public FxRate createInverse(final int precision) { return new FxRateImpl(currencyPair.createInverse(), crossCcy, !marketConvention, setScale(inverse(ask), precision), setScale(inverse(bid), precision)); } @Override public BigDecimal getBid() { return bid; } @Override public BigDecimal getAsk() { return ask; } @Override public CurrencyPair getCurrencyPair() { return currencyPair; } @Override public Optional getCrossCcy() { return Optional.ofNullable(crossCcy); } @Override public boolean isMarketConvention() { return marketConvention; } @Override public BigDecimal getMid() { BigDecimal mid; if (isNullOrZero(ask)) { mid = bid; } else if (isNullOrZero(bid)) { mid = ask; } else { mid = divide(PRECISION_FOR_INVERSE, bid.add(ask), TWO, BigDecimal.ROUND_HALF_UP); } return mid; } @Override public BigDecimal getBidInMarketConvention() { return marketConvention ? bid : inverse(ask); } @Override public BigDecimal getMidInMarketConvention() { return marketConvention ? getMid() : inverse(getMid()); } @Override public BigDecimal getAskInMarketConvention() { return marketConvention ? ask : inverse(bid); } @Override public String toString() { return getCurrencyPair() + " B:" + getBid() + " A:" + getAsk(); } @Override public CurrencyAmount convertAmountUsingMid(final CurrencyAmount originalAmount) { if (!currencyPair.containsCcy(originalAmount.getCurrency())) { throw new IllegalArgumentException("The original ccy [" + originalAmount.getCurrency() + "] must be one of the pair's " + currencyPair); } return currencyPair.getCcy1().equals(originalAmount.getCurrency()) ? new Cash(currencyPair.getCcy2(), setScale( multiply(originalAmount.getAmount(), getMid()), DEC_PLACE_FOR_MONEY)) : new Cash(currencyPair.getCcy1(), setScale( divide(setScale(originalAmount.getAmount(), PRECISION_FOR_INVERSE), getMid(), BigDecimal.ROUND_HALF_UP), DEC_PLACE_FOR_MONEY)); } @Override public CurrencyAmount convertAmountUsingBidOrAsk(final CurrencyAmount originalAmount) { if (!currencyPair.containsCcy(originalAmount.getCurrency())) { throw new IllegalArgumentException("The original ccy [" + originalAmount.getCurrency() + "] must be one of the pair's " + currencyPair); } return currencyPair.getCcy1().equals(originalAmount.getCurrency()) ? new Cash(currencyPair.getCcy2(), setScale( multiply(originalAmount.getAmount(), bid), DEC_PLACE_FOR_MONEY)) : new Cash(currencyPair.getCcy1(), setScale( divide(setScale(originalAmount.getAmount(), PRECISION_FOR_INVERSE), ask, BigDecimal.ROUND_HALF_UP), DEC_PLACE_FOR_MONEY)); } /** * Calculates the amount to pay given an amount to buy, e.g. how much should you pay in Y if you are buying n X? * More explicitly, how many EURos should you pay to buy 1,000 USD? * Given EUR/USD rate, this would be 1,000 / bid. * @param amountToBuy amount you want to buy. * @return amount you have to pay in the other ccy. */ @Override public CurrencyAmount getPaymentAmountForBuying(final CurrencyAmount amountToBuy) { boolean inverse = amountToBuy.getCurrency().equals(currencyPair.getCcy2()); final String targetCcy = inverse ? currencyPair.getCcy1() : currencyPair.getCcy2(); return inverse ? new Cash(targetCcy, setScale( BigDecimalUtil.divide(PRECISION_FOR_INVERSE, amountToBuy.getAmount(), bid, BigDecimal.ROUND_HALF_UP), DEC_PLACE_FOR_MONEY)) : new Cash(targetCcy, setScale(amountToBuy.getAmount().multiply(ask), DEC_PLACE_FOR_MONEY)); } /** * Calculates the amount the seller would receive given an amount to sell, e.g. how many Y will you get if you are selling n X? * More explicitly, how many EURos should you get to sell 1,000 USD? * Given EUR/USD rate, this would be 1,000 / ask. * @param amountToSell amount you want to sell. * @return amount you will receive in the other ccy. */ @Override public CurrencyAmount getReceiptAmountForSelling(final CurrencyAmount amountToSell) { boolean inverse = amountToSell.getCurrency().equals(currencyPair.getCcy2()); final String targetCcy = inverse ? currencyPair.getCcy1() : currencyPair.getCcy2(); return inverse ? // new Cash(targetCcy, setScale(BigDecimalUtil.divide(PRECISION_FOR_INVERSE, amountToSell.getAmount(), ask, BigDecimal.ROUND_HALF_UP), DEC_PLACE_FOR_MONEY)) // : // new Cash(targetCcy, setScale(amountToSell.getAmount().multiply(bid), DEC_PLACE_FOR_MONEY)) // ; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy