package org.cryptocoinpartners.schema; import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; import javax.persistence.Basic; import javax.persistence.Cacheable; import javax.persistence.Embedded; import javax.persistence.Entity; import javax.persistence.ManyToOne; import javax.persistence.Transient; import jline.internal.Log; import org.apache.commons.lang.NotImplementedException; import org.cryptocoinpartners.enumeration.PositionEffect; import org.cryptocoinpartners.enumeration.TransactionType; import org.cryptocoinpartners.util.Remainder; import org.cryptocoinpartners.util.XchangeUtil; import org.hibernate.annotations.Type; import org.joda.time.Instant; import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; import com.xeiam.xchange.dto.trade.LimitOrder; /** * SpecificOrders are bound to a Market and express their prices and volumes in DiscreteAmounts with the correct * basis for the Market. A SpecificOrder may be immediately passed to a Exchange for execution without any further * reduction or processing. * * @author Tim Olson */ @SuppressWarnings("UnusedDeclaration") @Entity @Cacheable public class SpecificOrder extends Order { @AssistedInject public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted long volumeCount) { super(time); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.remoteKey = getId().toString(); this.market = market; this.volumeCount = volumeCount; super.setPortfolio(portfolio); this.placementCount = 1; //this.positionEffect = PositionEffect.OPEN; } @AssistedInject public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted long volumeCount, @Assisted @Nullable String comment) { super(time); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.remoteKey = getId().toString(); this.market = market; this.volumeCount = volumeCount; super.setComment(comment); super.setPortfolio(portfolio); this.placementCount = 1; // this.positionEffect = PositionEffect.OPEN; } @AssistedInject public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted long volumeCount, @Assisted Order parentOrder, @Assisted @Nullable String comment) { super(time); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.remoteKey = getId().toString(); this.market = market; this.volumeCount = volumeCount; if (comment != null) super.setComment(comment); parentOrder.addChildOrder(this); this.setParentOrder(parentOrder); super.setPortfolio(portfolio); this.placementCount = 1; // this.positionEffect = PositionEffect.OPEN; } @AssistedInject public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted long volumeCount, @Assisted Order parentOrder) { super(time); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.remoteKey = getId().toString(); this.market = market; this.volumeCount = volumeCount; parentOrder.addChildOrder(this); this.setParentOrder(parentOrder); super.setPortfolio(portfolio); this.placementCount = 1; // this.positionEffect = PositionEffect.OPEN; } @AssistedInject public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted Amount volume, @Assisted @Nullable String comment) { super(time); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.remoteKey = getId().toString(); this.market = market; this.volumeCount = volume.toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount(); super.setComment(comment); super.setPortfolio(portfolio); this.placementCount = 1; this.positionEffect = PositionEffect.OPEN; } @AssistedInject public SpecificOrder(@Assisted LimitOrder limitOrder, @Assisted com.xeiam.xchange.Exchange xchangeExchange, @Assisted Portfolio portfolio, @Assisted Date date) { super(new Instant(date.getTime())); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); // currencyPair // Asset baseCCY = Asset.forSymbol(limitOrder.getBaseSymbol().toUpperCase()); // Asset quoteCCY = Asset.forSymbol(limitOrder.getCounterSymbol().toUpperCase()); Listing listing = Listing.forPair(Asset.forSymbol(limitOrder.getCurrencyPair().base.getCurrencyCode().toUpperCase()), Asset.forSymbol(limitOrder.getCurrencyPair().counter.getCurrencyCode().toUpperCase())); Exchange exchange = XchangeUtil.getExchangeForMarket(xchangeExchange); this.market = market.findOrCreate(exchange, listing); this.setRemoteKey(limitOrder.getId()); long vol = limitOrder.getTradableAmount().divide(BigDecimal.valueOf(market.getPriceBasis())).longValue(); this.volume = new DiscreteAmount(vol, market.getPriceBasis()); this.volumeCount = volume.toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount(); this.positionEffect = PositionEffect.OPEN; super.setComment(comment); this.placementCount = 1; // parentOrder.addChild(this); // this.setParentOrder(parentOrder); super.setPortfolio(portfolio); // this.placementCount = 1; } @AssistedInject public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted Amount volume, @Assisted Order parentOrder, @Assisted @Nullable String comment) { super(time); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.market = market; // setMarket(market); // this.market = market; this.remoteKey = getId().toString(); this.volumeCount = volume.toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount(); super.setComment(comment); parentOrder.addChildOrder(this); this.setParentOrder(parentOrder); super.setPortfolio(portfolio); this.placementCount = 1; this.positionEffect = (parentOrder.getPositionEffect() == null) ? PositionEffect.OPEN : parentOrder.getPositionEffect(); } public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted BigDecimal volume, @Assisted @Nullable String comment) { this(time, portfolio, market, new DecimalAmount(volume), comment); } @AssistedInject public SpecificOrder(@Assisted SpecificOrder specficOrder) { super(specficOrder.getTime()); this.orderUpdates = new CopyOnWriteArrayList<OrderUpdate>(); this.children = new CopyOnWriteArrayList<Order>(); this.fills = new CopyOnWriteArrayList<Fill>(); this.externalFills = new CopyOnWriteArrayList<Fill>(); this.transactions = new CopyOnWriteArrayList<Transaction>(); this.market = specficOrder.getMarket(); this.remoteKey = getId().toString(); if (!specficOrder.getFills().isEmpty()) this.volumeCount = specficOrder.getUnfilledVolumeCount(); this.volumeCount = specficOrder.getUnfilledVolumeCount(); // this.volumeCount = specficOrder.getOpenVolumeCount(); if (specficOrder.getComment() != null) super.setComment(specficOrder.getComment()); if (specficOrder.getParentOrder() != null) { specficOrder.getParentOrder().addChildOrder(this); this.setParentOrder(specficOrder.getParentOrder()); } super.setPortfolio(specficOrder.getPortfolio()); this.placementCount = 1; this.positionEffect = specficOrder.getPositionEffect(); this.limitPriceCount = specficOrder.getLimitPriceCount(); this.fillType = specficOrder.getFillType(); this.executionInstruction = specficOrder.getExecutionInstruction(); } public SpecificOrder(@Assisted Instant time, @Assisted Portfolio portfolio, @Assisted Market market, @Assisted double volume, @Assisted @Nullable String comment) { this(time, portfolio, market, new DecimalAmount(new BigDecimal(volume)), comment); } @Override @ManyToOne(optional = false) public Market getMarket() { if (market == null) { Log.debug("null market"); return null; } if (market.getListing() == null) { Log.debug("null listing"); return null; } return market; } @Transient public boolean update(LimitOrder limitOrder) { try { this.setRemoteKey(limitOrder.getId()); this.setTimeReceived(new Instant(limitOrder.getTimestamp())); long vol = limitOrder.getTradableAmount().divide(BigDecimal.valueOf(market.getPriceBasis())).longValue(); this.volume = new DiscreteAmount(vol, market.getPriceBasis()); this.volumeCount = volume.toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount(); return true; } catch (Error e) { e.printStackTrace(); return false; } // parentOrder.addChild(this); // this.setParentOrder(parentOrder); } @Override @Embedded public DiscreteAmount getVolume() { if (volume == null) volume = amount().fromVolumeCount(volumeCount); if (volume == null) System.out.println("volume is null"); return volume; } protected void setVolume(DiscreteAmount volume) { this.volume = volume; } @Override @Nullable @Embedded public DiscreteAmount getLimitPrice() { if (limitPriceCount == 0) return null; if (limitPrice == null) limitPrice = amount().fromPriceCount(limitPriceCount); return limitPrice; } @Override @Nullable @Embedded public DiscreteAmount getMarketPrice() { if (marketPriceCount == 0) return null; if (marketPrice == null) marketPrice = amount().fromPriceCount(marketPriceCount); return marketPrice; } protected void setLimitPrice(DiscreteAmount limitPrice) { this.limitPrice = limitPrice; } protected void setMarketPrice(DiscreteAmount marketPrice) { this.marketPrice = marketPrice; } @Override @Transient public DiscreteAmount getStopAmount() { return null; } @Override @Transient public DiscreteAmount getTargetAmount() { return null; } @Override @Transient public TransactionType getTransactionType() { return null; } @Override @Transient public DiscreteAmount getStopPrice() { return null; } @Override @Transient public DiscreteAmount getTargetPrice() { return null; } @Override @Transient public DiscreteAmount getTrailingStopPrice() { return null; } @Override @Transient public DiscreteAmount getUnfilledVolume() { return new DiscreteAmount(getUnfilledVolumeCount(), market.getVolumeBasis()); } @Transient public long getUnfilledVolumeCount() { long filled = 0; List<Fill> fills = getFills(); if (fills == null || fills.isEmpty()) return volumeCount; for (Fill fill : fills) filled += fill.getVolumeCount(); long unfilled = (volumeCount < 0) ? (Math.abs(volumeCount) - Math.abs(filled)) * -1 : Math.abs(volumeCount) - Math.abs(filled); return unfilled; } @Transient public DiscreteAmount getExternalUnfilledVolume() { return new DiscreteAmount(getExternalUnfilledVolumeCount(), market.getVolumeBasis()); } @Transient public long getExternalUnfilledVolumeCount() { long filled = 0; List<Fill> fills = getExternalFills(); if (fills == null || fills.isEmpty()) return volumeCount; for (Fill fill : fills) filled += fill.getVolumeCount(); long unfilled = (volumeCount < 0) ? (Math.abs(volumeCount) - Math.abs(filled)) * -1 : Math.abs(volumeCount) - Math.abs(filled); return unfilled; } @Transient public long getOpenVolumeCount() { long filled = 0; List<Fill> fills = getFills(); if (fills == null || fills.isEmpty()) return volumeCount; for (Fill fill : fills) filled += fill.getOpenVolumeCount(); return filled; } @Override @Transient public boolean isFilled() { return getUnfilledVolumeCount() == 0; } @Override @Transient public boolean isBid() { return volumeCount > 0; } public void copyCommonOrderProperties(GeneralOrder generalOrder) { //setTime(generalOrder.getTime()); setTimeToLive(generalOrder.getTimeToLive()); setEmulation(generalOrder.isEmulation()); setExpiration(generalOrder.getExpiration()); setPortfolio(generalOrder.getPortfolio()); setMarginType(generalOrder.getMarginType()); setPanicForce(generalOrder.getPanicForce()); } @Override public String toString() { return "SpecificOrder{ id=" + getId() + SEPARATOR + "time=" + (getTime() != null ? (FORMAT.print(getTime())) : "") + SEPARATOR + "remote key=" + getRemoteKey() + SEPARATOR + "parentOrder=" + (getParentOrder() == null ? "null" : getParentOrder().getId()) + SEPARATOR + "parentFill={" + (getParentFill() == null ? "null}" : getParentFill() + "}") + SEPARATOR + "portfolio=" + getPortfolio() + SEPARATOR + "market=" + market + SEPARATOR + "unfilled volume=" + getUnfilledVolume() + SEPARATOR + "volumeCount=" + getVolume() + (limitPriceCount != 0 ? (SEPARATOR + "limitPriceCount=" + getLimitPrice()) : "") + (SEPARATOR + "PlacementCount=" + getPlacementCount()) + (getComment() == null ? "" : (SEPARATOR + "Comment=" + getComment())) + (getFillType() == null ? "" : (SEPARATOR + "Order Type=" + getFillType())) + (getPositionEffect() == null ? "" : (SEPARATOR + "Position Effect=" + getPositionEffect())) + (getExecutionInstruction() == null ? "" : (SEPARATOR + "Execution Instruction=" + getExecutionInstruction())) + (hasFills() ? (SEPARATOR + "averageFillPrice=" + getAverageFillPrice()) : "") + "}"; } // JPA protected long getVolumeCount() { return volumeCount; } /** 0 if no limit is set */ protected long getLimitPriceCount() { return limitPriceCount; } protected long getMarketPriceCount() { return marketPriceCount; } public int getPlacementCount() { if (placementCount == 0) return 1; return placementCount; } protected SpecificOrder() { } protected SpecificOrder(Instant time) { super(time); } @Override public void setMarket(Market market) { this.market = market; } public void setVolumeCount(long volumeCount) { this.volumeCount = volumeCount; volume = null; } public void setLimitPriceCount(long limitPriceCount) { this.limitPriceCount = limitPriceCount; limitPrice = null; } public void setMarketPriceCount(long marketPriceCount) { this.marketPriceCount = marketPriceCount; marketPrice = null; } @Override public Order withLimitPrice(String price) { this.setLimitPriceCount(DecimalAmount.of(price).toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount()); return this; } @Override public Order withLimitPrice(DiscreteAmount price) { this.setLimitPriceCount(price.getCount()); return this; } @Override public Order withLimitPrice(BigDecimal price) { this.setLimitPriceCount(DecimalAmount.of(price).toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount()); return this; } @Override public Order withMarketPrice(String price) { this.setMarketPriceCount(DecimalAmount.of(price).toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount()); return this; } @Override public Order withMarketPrice(DiscreteAmount price) { this.setMarketPriceCount(price.getCount()); return this; } @Override public Order withMarketPrice(BigDecimal price) { this.setMarketPriceCount(DecimalAmount.of(price).toBasis(market.getVolumeBasis(), Remainder.DISCARD).getCount()); return this; } public void setPlacementCount(int placementCount) { this.placementCount = placementCount; } @Override public void setStopAmount(DecimalAmount stopAmount) { throw new NotImplementedException(); } @Override public void setTargetAmount(DecimalAmount targetAmount) { throw new NotImplementedException(); } @Override public void setStopPrice(DecimalAmount stopPrice) { throw new NotImplementedException(); } @Override public void setTargetPrice(DecimalAmount targetPrice) { throw new NotImplementedException(); } @Override public void setTrailingStopPrice(DecimalAmount stopPrice) { throw new NotImplementedException(); } @Basic(optional = true) public String getRemoteKey() { return remoteKey; } public void setRemoteKey(@Nullable String remoteKey) { this.remoteKey = remoteKey; } @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentInstantAsMillisLong") @Basic(optional = true) public Instant getTimeReceived() { return timeReceived; } public void addExternalFill(Fill fill) { this.externalFills.add(fill); } @Transient public List<Fill> getExternalFills() { return externalFills; // } } @Transient public long getTimestampReceived() { return timestampReceived; } protected void setTimeReceived(@Nullable Instant timeReceived) { this.timeReceived = timeReceived; if (timeReceived != null) this.timestampReceived = timeReceived.getMillis(); } private Market.MarketAmountBuilder amount() { if (getMarket() == null) Log.debug("test"); if (amountBuilder == null) amountBuilder = getMarket().buildAmount(); if (amountBuilder == null) Log.debug("test"); return amountBuilder; } private Market market; private DiscreteAmount volume; private DiscreteAmount limitPrice; private DiscreteAmount marketPrice; private int placementCount; private long volumeCount; private long limitPriceCount; private long marketPriceCount; private String remoteKey; private Instant timeReceived; private long timestampReceived; protected List<Fill> externalFills; private static Object lock = new Object(); private Market.MarketAmountBuilder amountBuilder; @Override public void delete() { // TODO Auto-generated method stub } }