package org.cryptocoinpartners.util;
import javax.inject.Inject;
import org.cryptocoinpartners.enumeration.FeeMethod;
import org.cryptocoinpartners.enumeration.PositionEffect;
import org.cryptocoinpartners.schema.Amount;
import org.cryptocoinpartners.schema.DecimalAmount;
import org.cryptocoinpartners.schema.Fill;
import org.cryptocoinpartners.schema.Market;
import org.cryptocoinpartners.schema.Order;
import org.cryptocoinpartners.schema.Position;
import org.cryptocoinpartners.service.QuoteService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Tim Olson
*/
public class FeesUtil {
@Inject
protected transient QuoteService quotes;
public static Amount getCommission(Fill fill) {
double rate = fill.getMarket().getFeeRate();
//* fill.getMarket().getContractSize();
//* fill.getMarket().getMargin();
FeeMethod method = fill.getMarket().getFeeMethod();
Amount price = fill.getPrice();
Amount ammount = fill.getVolume();
Amount commission;
switch (method) {
case PercentagePerUnit:
return calculatePercentagePerUnit(price, ammount, rate, fill.getMarket());
case PerUnit:
return calculatePerUnit(ammount, rate, fill.getMarket());
case PercentagePerUnitOpening:
commission = (fill.getOrder().getPositionEffect() == (PositionEffect.OPEN)) ? calculatePercentagePerUnit(price, ammount, rate, fill.getMarket())
: DecimalAmount.ZERO;
return commission;
case FlatRatePerUnitOpening:
commission = (fill.getOrder().getPositionEffect() == (PositionEffect.OPEN)) ? calculateFlatRatePerUnit(price, ammount, rate, fill.getMarket())
: DecimalAmount.ZERO;
return commission;
case PerUnitOpening:
commission = (fill.getOrder().getPositionEffect() == (PositionEffect.OPEN)) ? calculatePerUnit(ammount, rate, fill.getMarket())
: DecimalAmount.ZERO;
return commission;
default:
log.error("No commission fee method calcation for : " + method);
return DecimalAmount.ZERO;
}
}
public static Amount getMargin(Amount price, Amount ammount, double rate, FeeMethod method, Market market, PositionEffect positionEffect) {
Amount margin;
rate = 1 / rate;
switch (method) {
case PercentagePerUnit:
return calculatePercentagePerUnit(price, ammount, rate, market);
case PerUnit:
return calculatePerUnit(ammount, rate, market);
case PercentagePerUnitOpening:
margin = (positionEffect == (PositionEffect.OPEN)) ? calculatePercentagePerUnit(price, ammount, rate, market) : DecimalAmount.ZERO;
return margin;
case PerUnitOpening:
margin = (positionEffect == (PositionEffect.OPEN)) ? calculatePerUnit(ammount, rate, market) : DecimalAmount.ZERO;
return margin;
default:
log.error("No margin fee method calcation for : " + method);
return DecimalAmount.ZERO;
}
}
public static Amount getMargin(Fill fill) {
double rate = (fill.getMarket().getMargin() == 0) ? 1 : fill.getMarket().getMargin();
FeeMethod method = (fill.getMarket().getMarginFeeMethod() == null) ? FeeMethod.PercentagePerUnit : fill.getMarket().getMarginFeeMethod();
Amount price = fill.getPrice();
Amount ammount = fill.getVolume();
Market market = fill.getMarket();
PositionEffect positionEffect = fill.getOrder().getPositionEffect();
return getMargin(price, ammount, rate, method, market, positionEffect);
}
public static Amount getCommission(Order order) {
if (order.getMarket() != null) {
double rate = order.getMarket().getFeeRate();
FeeMethod method = order.getMarket().getFeeMethod();
Amount price = (order.getLimitPrice() != null) ? order.getLimitPrice() : order.getMarketPrice();
Amount ammount = order.getVolume().abs();
Amount commission;
switch (method) {
case PercentagePerUnit:
return calculatePercentagePerUnit(price, ammount, rate, order.getMarket());
case PerUnit:
return calculatePerUnit(ammount, rate, order.getMarket());
case FlatRatePerUnitOpening:
commission = (order.getPositionEffect() == (PositionEffect.OPEN)) ? calculateFlatRatePerUnit(price, ammount, rate, order.getMarket())
: DecimalAmount.ZERO;
case PercentagePerUnitOpening:
commission = (order.getPositionEffect() == (PositionEffect.OPEN)) ? calculatePercentagePerUnit(price, ammount, rate, order.getMarket())
: DecimalAmount.ZERO;
return commission;
case PerUnitOpening:
commission = (order.getPositionEffect().equals(PositionEffect.OPEN)) ? calculatePerUnit(ammount, rate, order.getMarket())
: DecimalAmount.ZERO;
return commission;
default:
log.error("No commision fee method calcation for : " + method);
return DecimalAmount.ZERO;
}
}
return DecimalAmount.ZERO;
}
public static Amount getMargin(Order order) {
if (order.getMarket() != null) {
double rate = (order.getMarket().getMargin() == 0) ? 1 : order.getMarket().getMargin();
// need a check in here to see if margin fee moethos is null they assume full margin
//quotes.
FeeMethod method = (order.getMarket().getMarginFeeMethod() == null) ? FeeMethod.PercentagePerUnit : order.getMarket().getMarginFeeMethod();
Amount price = (order.getLimitPrice() != null) ? order.getLimitPrice() : order.getMarketPrice();
Amount ammount = order.getVolume().abs();
Market market = order.getMarket();
PositionEffect positionEffect = order.getPositionEffect();
return getMargin(price, ammount, rate, method, market, positionEffect);
}
return DecimalAmount.ZERO;
}
public static Amount getMargin(Position position) {
if (position.isOpen()) {
double rate = (position.getMarket().getMargin() == 0) ? 1 : position.getMarket().getMargin();
FeeMethod method = (position.getMarket().getMarginFeeMethod() == null) ? FeeMethod.PercentagePerUnit : position.getMarket().getMarginFeeMethod();
Amount price = (position.isLong()) ? position.getLongAvgPrice() : position.getShortAvgPrice();
Amount ammount = (position.isLong()) ? position.getLongVolume() : position.getShortVolume();
Market market = position.getMarket();
PositionEffect positionEffect = PositionEffect.OPEN;
return getMargin(price, ammount, rate, method, market, positionEffect);
}
return DecimalAmount.ZERO;
}
public static Amount getCommission(Amount price, Amount ammount, Market market, PositionEffect postionEffect) {
double rate = market.getFeeRate();
FeeMethod method = market.getFeeMethod();
Amount commission;
switch (method) {
case PercentagePerUnit:
return calculatePercentagePerUnit(price, ammount, rate, market);
case PerUnit:
return calculatePerUnit(ammount, rate, market);
case PercentagePerUnitOpening:
commission = (postionEffect == (PositionEffect.OPEN)) ? calculatePercentagePerUnit(price, ammount, rate, market) : DecimalAmount.ZERO;
return commission;
case PerUnitOpening:
commission = (postionEffect == (PositionEffect.OPEN)) ? calculatePerUnit(ammount, rate, market) : DecimalAmount.ZERO;
return commission;
default:
log.error("No commision fee method calcation for : " + method);
return DecimalAmount.ZERO;
}
}
protected static Amount getMargin(Amount price, Amount amount, Market market, PositionEffect postionEffect) {
double rate = market.getMargin();
FeeMethod method = market.getMarginFeeMethod();
Amount margin;
switch (method) {
case PercentagePerUnit:
return calculatePercentagePerUnit(price, amount, rate, market);
case PerUnit:
return calculatePerUnit(amount, rate, market);
case PercentagePerUnitOpening:
margin = (postionEffect == (PositionEffect.OPEN)) ? calculatePercentagePerUnitOpening(price, amount, rate, market) : DecimalAmount.ZERO;
return margin;
case PerUnitOpening:
margin = (postionEffect == (PositionEffect.OPEN)) ? calculatePerUnitOpening(amount, rate, market) : DecimalAmount.ZERO;
return margin;
default:
log.error("No margin fee method calcation for : " + method);
return DecimalAmount.ZERO;
}
}
private static Amount calculatePercentagePerUnit(Amount price, Amount amount, double rate, Market market) {
//BigDecimal precision = BigDecimal.valueOf(market.getPriceBasis());
if (price == null)
log.debug("null price");
if (market.getTradedCurrency().equals(market.getBase())) {
price = price.invert();
//precision = BigDecimal.valueOf(market.getTradedCurrency().getBasis());
}
Amount notional = ((price.times(amount, Remainder.ROUND_EVEN)).times(rate, Remainder.ROUND_EVEN).abs()).times(market.getContractSize(),
Remainder.ROUND_EVEN);
return notional.toBasis(market.getTradedCurrency().getBasis(), Remainder.ROUND_CEILING).negate();
//
// BigDecimal fees = notional.asBigDecimal().setScale(price.getScale(), BigDecimal.ROUND_UP);
// fees = fees.divide(precision, BigDecimal.ROUND_UP);
// long feeCount = fees.longValue();
// DiscreteAmount newAmount = new DiscreteAmount(feeCount, precision.doubleValue());
// return newAmount.negate();
}
private static Amount calculateFlatRatePerUnit(Amount price, Amount amount, double rate, Market market) {
//BigDecimal precision = BigDecimal.valueOf(market.getPriceBasis());
if (market.getTradedCurrency().equals(market.getBase())) {
price = price.invert();
//precision = BigDecimal.valueOf(market.getTradedCurrency().getBasis());
}
Amount notional = (amount.times(rate, Remainder.ROUND_EVEN).abs());
return notional.toBasis(market.getTradedCurrency().getBasis(), Remainder.ROUND_CEILING).negate();
//
// BigDecimal fees = notional.asBigDecimal().setScale(price.getScale(), BigDecimal.ROUND_UP);
// fees = fees.divide(precision, BigDecimal.ROUND_UP);
// long feeCount = fees.longValue();
// DiscreteAmount newAmount = new DiscreteAmount(feeCount, precision.doubleValue());
// return newAmount.negate();
}
private static Amount calculatePerUnit(Amount amount, double rate, Market market) {
Amount notional = ((amount.times(rate, Remainder.ROUND_EVEN)).abs());
return notional.toBasis(market.getTradedCurrency().getBasis(), Remainder.ROUND_CEILING).negate();
//
// BigDecimal fees = notional.asBigDecimal().setScale(amount.getScale(), BigDecimal.ROUND_UP);
// BigDecimal precision = BigDecimal.valueOf(market.getPriceBasis());
// fees = fees.divide(precision, BigDecimal.ROUND_UP);
// long feeCount = fees.longValue();
// DiscreteAmount newAmount = new DiscreteAmount(feeCount, market.getPriceBasis());
// return newAmount.negate();
}
private static Amount calculatePerUnitOpening(Amount amount, double rate, Market market) {
return calculatePerUnit(amount, rate, market);
}
private static Amount calculatePercentagePerUnitOpening(Amount price, Amount amount, double rate, Market market) {
return calculatePercentagePerUnit(price, amount, rate, market);
}
public static Logger log = LoggerFactory.getLogger(FeesUtil.class);
}