package org.cryptocoinpartners.schema; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import javax.persistence.Basic; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Index; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQuery; import javax.persistence.NoResultException; import javax.persistence.PostPersist; import javax.persistence.QueryHint; import javax.persistence.Table; import javax.persistence.Transient; import org.cryptocoinpartners.enumeration.FeeMethod; import org.cryptocoinpartners.schema.dao.Dao; import org.cryptocoinpartners.schema.dao.MarketDao; import org.cryptocoinpartners.util.EM; import org.cryptocoinpartners.util.RemainderHandler; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; /** * Represents the possibility to trade one Asset for another at a specific Exchange. * * @author Tim Olson */ @Entity @Cacheable @NamedQuery(name = "Market.findByMarket", query = "select m from Market m where exchange=?1 and listing=?2", hints = { @QueryHint(name = "org.hibernate.cacheable", value = "true") }) @Table(indexes = { @Index(columnList = "exchange"), @Index(columnList = "listing"), @Index(columnList = "active") }) public class Market extends EntityBase { @Inject protected MarketDao marketDao; @Inject protected transient static MarketFactory marketFactory; public static List<Market> findAll() { return EM.queryList(Market.class, "select m from Market m"); } /** adds the Market to the database if it does not already exist */ public static Market findOrCreate(Exchange exchange, Listing listing) { return findOrCreate(exchange, listing, listing.getPriceBasis(), listing.getVolumeBasis()); } @PostPersist private void postPersist() { //PersistUtil.detach(this); } public static Market findOrCreate(Exchange exchange, Listing listing, double quoteBasis, double volumeBasis) { // final String queryStr = "select m from Market m where exchange=?1 and listing=?2"; try { return EM.namedQueryOne(Market.class, "Market.findByMarket", exchange, listing); } catch (NoResultException e) { Market ml = marketFactory.create(exchange, listing, quoteBasis, volumeBasis); // Market ml = new Market(exchange, listing, quoteBasis, volumeBasis); ml.persit(); // marketDao.persist(ml); return ml; } } /** @return active Markets for the given exchange */ public static List<Market> find(Exchange exchange) { return EM.queryList(Market.class, "select s from Market s where exchange=?1 and active=?2", exchange, true); } /** @return active Markets for the given listing */ public static List<Market> find(Listing listing) { return EM.queryList(Market.class, "select s from Market s where listing=?1 and active=?2", listing, true); } @ManyToOne(optional = false) public Exchange getExchange() { return exchange; } @ManyToOne(optional = false) //, cascade = CascadeType.ALL) @JoinColumn(name = "listing") public Listing getListing() { return listing; } @Basic(optional = false) public double getPriceBasis() { return listing.getPriceBasis() == 0 ? priceBasis : listing.getPriceBasis(); } @Transient public int getScale() { int length = (int) (Math.log10(getPriceBasis())); return length; } @Basic(optional = false) public double getVolumeBasis() { return listing.getVolumeBasis() == 0 ? volumeBasis : listing.getVolumeBasis(); } /** @return true iff the Listing is currently traded at the Exchange. The Market could have been retired. */ public boolean isActive() { return active; } @Transient public Asset getBase() { return listing.getBase(); } @Transient public Asset getQuote() { return listing.getQuote(); } @Transient public int getMargin() { return listing.getMargin() == 0 ? exchange.getMargin() : listing.getMargin(); } @Transient public double getFeeRate() { return listing.getFeeRate() == 0 ? exchange.getFeeRate() : listing.getFeeRate(); } @Transient public FeeMethod getMarginFeeMethod() { return listing.getMarginFeeMethod() == null ? exchange.getMarginFeeMethod() : listing.getMarginFeeMethod(); } @Transient public FeeMethod getFeeMethod() { return listing.getFeeMethod() == null ? exchange.getFeeMethod() : listing.getFeeMethod(); } @Transient public double getMultiplier() { return listing.getMultiplier(); } @Transient public double getTickValue() { return listing.getTickValue(); } @Transient public double getContractSize() { return listing.getContractSize(); } @Transient public double getTickSize() { return listing.getTickSize(); } @Transient public Asset getTradedCurrency() { return listing.getTradedCurrency(); } @Transient public String getSymbol() { return exchange.toString() + ':' + listing.toString(); } @Override public String toString() { return getSymbol(); } public static Market forSymbol(String marketSymbol) { for (Market market : findAll()) { if (market.getSymbol().equalsIgnoreCase(marketSymbol)) { return market; } } return null; } public static List<String> allSymbols() { List<String> result = new ArrayList<>(); List<Market> markets = EM.queryList(Market.class, "select m from Market m"); for (Market market : markets) result.add((market.getSymbol())); return result; } public static class MarketAmountBuilder { public DiscreteAmount fromPriceCount(long count) { return priceBuilder.fromCount(count); } public DiscreteAmount fromVolumeCount(long count) { return volumeBuilder.fromCount(count); } public DiscreteAmount fromPrice(BigDecimal amount, RemainderHandler remainderHandler) { return priceBuilder.fromValue(amount, remainderHandler); } public DiscreteAmount fromVolume(BigDecimal amount, RemainderHandler remainderHandler) { return volumeBuilder.fromValue(amount, remainderHandler); } public MarketAmountBuilder(double priceBasis, double volumeBasis) { this.priceBuilder = DiscreteAmount.withBasis(priceBasis); this.volumeBuilder = DiscreteAmount.withBasis(volumeBasis); } private final DiscreteAmount.DiscreteAmountBuilder priceBuilder; private final DiscreteAmount.DiscreteAmountBuilder volumeBuilder; } public MarketAmountBuilder buildAmount() { if (marketAmountBuilder == null) marketAmountBuilder = new MarketAmountBuilder(getPriceBasis(), getVolumeBasis()); return marketAmountBuilder; } // JPA protected Market() { } protected void setExchange(Exchange exchange) { this.exchange = exchange; } protected void setListing(Listing listing) { this.listing = listing; } protected void setActive(boolean active) { this.active = active; } protected void setPriceBasis(double quoteBasis) { this.priceBasis = quoteBasis; } protected void setVolumeBasis(double volumeBasis) { this.volumeBasis = volumeBasis; } @AssistedInject private Market(@Assisted Exchange exchange, @Assisted Listing listing, @Assisted("marketPriceBasis") double priceBasis, @Assisted("marketVolumeBasis") double volumeBasis) { this.exchange = exchange; this.listing = listing; this.priceBasis = priceBasis; this.volumeBasis = volumeBasis; this.active = true; } private Exchange exchange; private Listing listing; private double priceBasis; private double volumeBasis; private boolean active; private MarketAmountBuilder marketAmountBuilder; @Override public void persit() { if (listing != null) if (listing.find() == null) listing.persit(); marketDao.persist(this); } @Override public EntityBase refresh() { return marketDao.refresh(this); } @Override public void detach() { marketDao.detach(this); } @Override public void merge() { marketDao.merge(this); } @Override @Transient public Dao getDao() { return marketDao; } @Override public void delete() { // TODO Auto-generated method stub } }