Skip to content

Commit

Permalink
issue #7 Create a CurrencyProvider for decimal & rounding
Browse files Browse the repository at this point in the history
  • Loading branch information
benoitx committed Oct 13, 2014
1 parent c47edeb commit 0c36595
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ private CrossRateCalculator() {
* @throws IllegalArgumentException if the 2 fx1 and fx2 do not share a common cross currency or either currencies in the targetPair
*/
public static FxRate calculateCross(final CurrencyPair targetPair, final FxRate fx1, final FxRate fx2, final int precision,
final int precisionForInverseFxRate, final MajorCurrencyRanking ranking, final int bidRounding, final int askRounding) {
final int precisionForInverseFxRate, final MajorCurrencyRanking ranking, final int bidRounding, final int askRounding,
CurrencyProvider currencyProvider) {
final Optional<String> crossCcy = fx1.getCurrencyPair().findCommonCcy(fx2.getCurrencyPair());
final String xCcy = crossCcy.orElseThrow(() -> new IllegalArgumentException("The 2 FXRates do not share a ccy " + fx1.getCurrencyPair() + " "
+ fx2.getCurrencyPair()));
Expand Down Expand Up @@ -72,7 +73,7 @@ public static FxRate calculateCross(final CurrencyPair targetPair, final FxRate
ask = BigDecimalUtil.setScale(BigDecimalUtil.multiply(fx1.getAsk(), fx2.getAsk()), precision);
}
}
final FxRateImpl crossRate = new FxRateImpl(targetPair, xCcy, ranking.isMarketConvention(targetPair), bid, ask);
final FxRateImpl crossRate = new FxRateImpl(targetPair, xCcy, ranking.isMarketConvention(targetPair), bid, ask, currencyProvider);
LOG.debug("X RATE {}", crossRate);
LOG.debug(crossRate.getDescription());
return crossRate;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.objectlab.kit.fxcalc;

public interface CurrencyProvider {
int getFractionDigits(String currencyCode);

int getRounding(String currencyCode);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
.majorCurrencyRanking(StandardMajorCurrencyRanking.getdefault())
.precisionForFxRate(6)
.precisionForInverseFxRate(12)
.currencyProvider(new JdkCurrencyProvider()) // use the JDK currency
.cacheResults(true) // only calculate a cross Fx once, cache for subsequent requests
.cacheBaseRates(true); // if a BaseFxRateRateProvider is used, cache the rates instead of calling again for same currency pair
* </pre>
Expand Down Expand Up @@ -73,6 +74,10 @@ public class FxRateCalculatorBuilder {
* The Rounding to use for ASK, default HALF_UP
*/
private int askRounding = BigDecimal.ROUND_HALF_UP;
/**
* CurrencyProvider the provider for currency details
*/
private CurrencyProvider currencyProvider = new JdkCurrencyProvider();

public FxRateCalculatorBuilder() {
orderedCurrenciesForCross.add("USD");
Expand Down Expand Up @@ -213,6 +218,16 @@ public FxRateCalculatorBuilder majorCurrencyRanking(final MajorCurrencyRanking m
return this;
}

/**
* The interface to determine Currency details.
*/
public FxRateCalculatorBuilder currencyProvider(final CurrencyProvider currencyProvider) {
if (currencyProvider != null) {
this.currencyProvider = currencyProvider;
}
return this;
}

/**
* Snapshot of FxRate, typically they are the Base FX Rates (e.g. vs USD)
*/
Expand Down Expand Up @@ -246,4 +261,7 @@ public int getAskRounding() {
return askRounding;
}

public CurrencyProvider getCurrencyProvider() {
return currencyProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class FxRateCalculatorImpl implements FxRateCalculator {
private final int askRounding;
private final boolean cacheResults;
private final boolean cacheBaseRates;
private final CurrencyProvider currencyProvider;

public FxRateCalculatorImpl(final FxRateCalculatorBuilder builder) {
builder.checkValid();
Expand All @@ -39,6 +40,7 @@ public FxRateCalculatorImpl(final FxRateCalculatorBuilder builder) {
this.cacheResults = builder.isCacheResults();
this.bidRounding = builder.getBidRounding();
this.askRounding = builder.getAskRounding();
this.currencyProvider = builder.getCurrencyProvider();
}

private FxRate getBaseRate(final CurrencyPair ccyPair) {
Expand Down Expand Up @@ -107,7 +109,7 @@ private FxRate findViaCrossCcy(final CurrencyPair ccyPair, final String crossCcy
}
if (xCcy2 != null) {
return CrossRateCalculator.calculateCross(ccyPair, xCcy1, xCcy2, precisionForFxRate, precisionForInverseFxRate, majorCurrencyRanking,
bidRounding, askRounding);
bidRounding, askRounding, currencyProvider);
}
}
return null;
Expand Down
36 changes: 14 additions & 22 deletions fxcalc/src/main/java/net/objectlab/kit/fxcalc/FxRateImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ public class FxRateImpl implements FxRate {
private final boolean marketConvention;
private final BigDecimal bid;
private final BigDecimal ask;
private final CurrencyProvider currencyProvider;

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

/**
Expand Down Expand Up @@ -61,13 +63,13 @@ public String getDescription() {

@Override
public FxRate createInverse() {
return new FxRateImpl(currencyPair.createInverse(), crossCcy, !marketConvention, inverse(ask), inverse(bid));
return new FxRateImpl(currencyPair.createInverse(), crossCcy, !marketConvention, inverse(ask), inverse(bid), currencyProvider);
}

@Override
public FxRate createInverse(final int precision) {
return new FxRateImpl(currencyPair.createInverse(), crossCcy, !marketConvention, setScale(inverse(ask), precision), setScale(inverse(bid),
precision));
precision), currencyProvider);
}

@Override
Expand Down Expand Up @@ -134,17 +136,12 @@ public CurrencyAmount convertAmountUsingMid(final CurrencyAmount originalAmount)
throw new IllegalArgumentException("The original ccy [" + originalAmount.getCurrency() + "] must be one of the pair's " + currencyPair);
}
final boolean ccy1IsOriginal = currencyPair.getCcy1().equals(originalAmount.getCurrency());
int decPlace = DEC_PLACE_FOR_MONEY;

try {
decPlace = Currency.getInstance(ccy1IsOriginal ? currencyPair.getCcy2() : currencyPair.getCcy1()).getDefaultFractionDigits();
} catch (IllegalArgumentException iae) {
// do nothing
}
final int decPlace = currencyProvider.getFractionDigits(ccy1IsOriginal ? currencyPair.getCcy2() : currencyPair.getCcy1());
final int rounding = currencyProvider.getRounding(ccy1IsOriginal ? currencyPair.getCcy2() : currencyPair.getCcy1());

return ccy1IsOriginal ? new Cash(currencyPair.getCcy2(), setScale(multiply(originalAmount.getAmount(), getMid()), decPlace)) : new Cash(
currencyPair.getCcy1(), setScale(
divide(setScale(originalAmount.getAmount(), PRECISION_FOR_INVERSE), getMid(), BigDecimal.ROUND_HALF_UP), decPlace));
return ccy1IsOriginal ? new Cash(currencyPair.getCcy2(), setScale(multiply(originalAmount.getAmount(), getMid()), decPlace, rounding))
: new Cash(currencyPair.getCcy1(), setScale(
divide(setScale(originalAmount.getAmount(), PRECISION_FOR_INVERSE), getMid(), BigDecimal.ROUND_HALF_UP), decPlace, rounding));
}

@Override
Expand All @@ -153,17 +150,12 @@ public CurrencyAmount convertAmountUsingBidOrAsk(final CurrencyAmount originalAm
throw new IllegalArgumentException("The original ccy [" + originalAmount.getCurrency() + "] must be one of the pair's " + currencyPair);
}
final boolean ccy1IsOriginal = currencyPair.getCcy1().equals(originalAmount.getCurrency());
int decPlace = DEC_PLACE_FOR_MONEY;

try {
decPlace = Currency.getInstance(ccy1IsOriginal ? currencyPair.getCcy2() : currencyPair.getCcy1()).getDefaultFractionDigits();
} catch (IllegalArgumentException iae) {
// do nothing
}
final int decPlace = currencyProvider.getFractionDigits(ccy1IsOriginal ? currencyPair.getCcy2() : currencyPair.getCcy1());
final int rounding = currencyProvider.getRounding(ccy1IsOriginal ? currencyPair.getCcy2() : currencyPair.getCcy1());

return ccy1IsOriginal ? new Cash(currencyPair.getCcy2(), setScale(multiply(originalAmount.getAmount(), bid), decPlace)) : new Cash(
return ccy1IsOriginal ? new Cash(currencyPair.getCcy2(), setScale(multiply(originalAmount.getAmount(), bid), decPlace, rounding)) : new Cash(
currencyPair.getCcy1(), setScale(divide(setScale(originalAmount.getAmount(), PRECISION_FOR_INVERSE), ask, BigDecimal.ROUND_HALF_UP),
decPlace));
decPlace, rounding));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.objectlab.kit.fxcalc;

import java.math.BigDecimal;
import java.util.Currency;

public class JdkCurrencyProvider implements CurrencyProvider {

@Override
public int getFractionDigits(String currencyCode) {
try {
return Currency.getInstance(currencyCode).getDefaultFractionDigits();
} catch (IllegalArgumentException iae) {
// do nothing
}
return 2;
}

@Override
public int getRounding(String currencyCode) {
return "JPY".equalsIgnoreCase(currencyCode) ? BigDecimal.ROUND_DOWN : BigDecimal.ROUND_HALF_UP;
}
}

0 comments on commit 0c36595

Please sign in to comment.