package org.cryptocoinpartners.module.xchange; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import javax.persistence.Transient; import org.apache.commons.configuration.Configuration; import org.cryptocoinpartners.enumeration.OrderState; import org.cryptocoinpartners.enumeration.PositionEffect; import org.cryptocoinpartners.module.BaseOrderService; import org.cryptocoinpartners.module.Context; import org.cryptocoinpartners.schema.Amount; import org.cryptocoinpartners.schema.DecimalAmount; import org.cryptocoinpartners.schema.DiscreteAmount; import org.cryptocoinpartners.schema.Exchange; import org.cryptocoinpartners.schema.Fill; import org.cryptocoinpartners.schema.FillFactory; import org.cryptocoinpartners.schema.Listing; import org.cryptocoinpartners.schema.Market; import org.cryptocoinpartners.schema.Portfolio; import org.cryptocoinpartners.schema.Prompt; import org.cryptocoinpartners.schema.SpecificOrder; import org.cryptocoinpartners.util.CompareUtils; import org.cryptocoinpartners.util.EM; import org.cryptocoinpartners.util.RateLimiter; import org.cryptocoinpartners.util.Remainder; import org.cryptocoinpartners.util.XchangeUtil; import org.joda.time.Duration; import org.joda.time.Instant; import com.google.common.collect.HashBiMap; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.xeiam.xchange.currency.CurrencyPair; import com.xeiam.xchange.dto.Order; import com.xeiam.xchange.dto.Order.OrderStatus; import com.xeiam.xchange.dto.Order.OrderType; import com.xeiam.xchange.dto.marketdata.OrderBook; import com.xeiam.xchange.dto.marketdata.Trade; import com.xeiam.xchange.dto.marketdata.Trades; import com.xeiam.xchange.dto.trade.LimitOrder; import com.xeiam.xchange.dto.trade.MarketOrder; import com.xeiam.xchange.dto.trade.OpenOrders; import com.xeiam.xchange.exceptions.ExchangeException; import com.xeiam.xchange.exceptions.NotAvailableFromExchangeException; import com.xeiam.xchange.exceptions.NotYetImplementedForExchangeException; import com.xeiam.xchange.okcoin.FuturesContract; import com.xeiam.xchange.okcoin.service.polling.OkCoinFuturesTradeService.OkCoinFuturesTradeHistoryParams; import com.xeiam.xchange.service.polling.trade.PollingTradeService; import com.xeiam.xchange.service.polling.trade.params.TradeHistoryParams; import com.xeiam.xchange.service.streaming.ExchangeEvent; import com.xeiam.xchange.service.streaming.ExchangeEventType; import com.xeiam.xchange.service.streaming.ExchangeStreamingConfiguration; import com.xeiam.xchange.service.streaming.StreamingExchangeService; /** * This module routes SpecificOrders through Xchange * * @author Tim Olson */ @Singleton @SuppressWarnings("UnusedDeclaration") public class XchangeOrderService extends BaseOrderService { private final FillFactory fillFactory; // @Inject //protected EntityManager entityManager; private final Context context; @Inject public XchangeOrderService(Context context, Configuration config, FillFactory fillFactory) { this.context = context; this.fillFactory = fillFactory; final String configPrefix = "xchange"; Set<String> exchangeTags = XchangeUtil.getExchangeTags(); // now we have all the exchange tags. process each config group for (String tag : exchangeTags) { // three configs required: // .class the full classname of the Xchange implementation // .rate.queries rate limit the number of queries to this many (default: 1) // .rate.period rate limit the number of queries during this period of time (default: 1 second) // .listings identifies which Listings should be fetched from this exchange org.cryptocoinpartners.schema.Exchange exchange = XchangeUtil.getExchangeForTag(tag); String prefix = configPrefix + "." + tag + '.'; if (exchange != null && config.getString(prefix + "apikey", null) != null && config.getString(prefix + "apisecret", null) != null) { final String helperClassName = config.getString(prefix + "helper.class", null); final String streamingConfigClassName = config.getString(prefix + "streaming.config.class", null); int queries = config.getInt(prefix + "rate.queries", 1); int retryCount = config.getInt(prefix + "retry", 10); Duration period = Duration.millis((long) (2000 * config.getDouble(prefix + "rate.period", 1))); // rate.period in seconds final List listings = config.getList(prefix + "listings"); initExchange(helperClassName, streamingConfigClassName, retryCount, queries, period, exchange, listings); } else { log.warn("Could not find Exchange for property \"xchange." + tag + ".*\""); } } } public interface Helper { Object[] getTradesParameters(CurrencyPair pair, long lastTradeTime, long lastTradeId); Object[] getOrderBookParameters(CurrencyPair pair); void handleTrades(Trades tradeSpec); void handleOrderBook(OrderBook orderBook); } private void initExchange(@Nullable String helperClassName, @Nullable String streamingConfigClassName, int retryCount, int queries, Duration per, Exchange coinTraderExchange, List listings) { com.xeiam.xchange.Exchange xchangeExchange = XchangeUtil.getExchangeForMarket(coinTraderExchange); StreamingExchangeService streamingDataService; Helper helper = null; if (helperClassName != null && !helperClassName.isEmpty()) { if (helperClassName.indexOf('.') == -1) helperClassName = XchangeData.class.getPackage().getName() + '.' + helperClassName; try { final Class<?> helperClass = getClass().getClassLoader().loadClass(helperClassName); try { helper = (Helper) helperClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { log.error("Could not initialize XchangeData because helper class " + helperClassName + " could not be instantiated ", e); return; } catch (ClassCastException e) { log.error("Could not initialize XchangeData because helper class " + helperClassName + " does not implement " + Helper.class); return; } } catch (ClassNotFoundException e) { log.error("Could not initialize XchangeData because helper class " + helperClassName + " was not found"); return; } } ExchangeStreamingConfiguration streamingConfiguration = null; if (streamingConfigClassName != null && !streamingConfigClassName.isEmpty()) { } List<Market> markets = new ArrayList<>(listings.size()); Market market; // ExchangeStreamingConfiguration streamingConfiguration = new OkCoinExchangeStreamingConfiguration(); for (Iterator<List> il = listings.iterator(); il.hasNext();) { Object listingSymbol = il.next(); Listing listing = Listing.forSymbol(listingSymbol.toString().toUpperCase()); market = context.getInjector().getInstance(Market.class).findOrCreate(coinTraderExchange, listing); markets.add(market); } if (streamingConfigClassName != null) { RateLimiter rateLimiter = new RateLimiter(queries, per); // streamingDataService = xchangeExchange.getStreamingExchangeService(streamingConfiguration); for (Iterator<Market> im = markets.iterator(); im.hasNext();) { market = im.next(); ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); ListenableFuture<StreamOrdersRunnable> streamingTradesFuture = service.submit(new StreamOrdersRunnable(context, xchangeExchange, market, rateLimiter, streamingConfigClassName, helper)); Futures.addCallback(streamingTradesFuture, new FutureCallback<StreamOrdersRunnable>() { // we want this handler to run immediately after we push the big red button! @Override public void onSuccess(StreamOrdersRunnable streamingTradesFuture) { System.out.println("complete"); //walkAwayFrom(explosion); } @Override public void onFailure(Throwable thrown) { System.out.println("failed"); //battleArchNemesis(); // escaped the explosion! } }); } return; } else { // PollingTradeService dataService = xchangeExchange.getPollingTradeService(); RateLimiter rateLimiter = new RateLimiter(queries, per); for (Iterator<Market> im = markets.iterator(); im.hasNext(); rateLimiter.execute(new FetchOrdersRunnable(context, market, rateLimiter, coinTraderExchange, retryCount, helper))) market = im.next(); return; } } private class StreamOrdersRunnable implements Callable { private final Helper helper; DateFormat dateFormat = new SimpleDateFormat("ddMMyy"); private ExchangeStreamingConfiguration streamingConfiguration; private CurrencyPair[] pairs; public StreamOrdersRunnable(Context context, com.xeiam.xchange.Exchange xchangeExchange, Market market, RateLimiter rateLimiter, String streamingConfigClassName, @Nullable Helper helper) { this.context = context; this.rateLimiter = rateLimiter; this.market = market; this.helper = helper; this.prompt = market.getListing().getPrompt(); pairs = new CurrencyPair[] { XchangeUtil.getCurrencyPairForListing(market.getListing()) }; contract = prompt == null ? null : XchangeUtil.getContractForListing(market.getListing()); // Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { // @Override // public void uncaughtException(Thread t, Throwable e) { // e.printStackTrace(); // } // }); if (streamingConfigClassName.indexOf('.') == -1) streamingConfigClassName = XchangeData.class.getPackage().getName() + '.' + streamingConfigClassName; try { final Class<?> streamingConfigClass = getClass().getClassLoader().loadClass(streamingConfigClassName); // CurrencyPair[] ccy = new CurrencyPair[] { CurrencyPair.BTC_USD }; try { streamingConfiguration = (ExchangeStreamingConfiguration) CompareUtils.tryToCreateBestMatch(streamingConfigClass, new Object[] { pairs }); dataService = xchangeExchange.getStreamingExchangeService(streamingConfiguration); dataService.connect(); String str = streamingConfigClass.getCanonicalName(); } catch (InstantiationException e1) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e1); e1.printStackTrace(); } catch (IllegalAccessException e1) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e1); e1.printStackTrace(); } catch (InvocationTargetException e1) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e1); e1.printStackTrace(); } catch (Exception | Error e2) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e2); e2.printStackTrace(); } try { streamingConfiguration = (ExchangeStreamingConfiguration) streamingConfigClass.newInstance(); // StreamingExchangeService service = xchangeExchange.getStreamingExchangeService(new (ExchangeStreamingConfiguration) streamingConfigClass(new CurrencyPair[]{ CurrencyPair.BTC_USD })); } catch (InstantiationException | IllegalAccessException e) { log.error("Could not initialize XchangeData because stremaing configuration class " + streamingConfigClassName + " could not be instantiated ", e); return; } catch (ClassCastException e) { log.error("Could not initialize XchangeData because stremaing configuration class " + streamingConfigClassName + " does not implement " + ExchangeStreamingConfiguration.class); return; } catch (Exception | Error e2) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e2); e2.printStackTrace(); return; } } catch (ClassNotFoundException e) { log.error("Could not initialize XchangeData because stremaing configuration class " + streamingConfigClassName + " was not found"); return; } catch (Exception | Error e2) { log.error("Threw a Execption, full stack trace follows:", e2); // TODO Auto-generated catch block e2.printStackTrace(); } Class<? extends ExchangeStreamingConfiguration> myclass = streamingConfiguration.getClass(); Class<?> streamingConfigClass = null; try { streamingConfigClass = getClass().getClassLoader().loadClass(streamingConfigClassName); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e); e.printStackTrace(); } catch (Exception | Error e2) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e2); e2.printStackTrace(); } try { streamingConfiguration = (ExchangeStreamingConfiguration) streamingConfigClass.newInstance(); } catch (InstantiationException e) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e); e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e); e.printStackTrace(); } catch (Exception | Error e2) { log.error("Threw a Execption, full stack trace follows:", e2); // TODO Auto-generated catch block e2.printStackTrace(); } // EntityManager entityManager = //.createEntityManager(); try { List<org.cryptocoinpartners.schema.Trade> results = EM.queryList(org.cryptocoinpartners.schema.Trade.class, "select t from Trade t where market=?1 and time=(select max(time) from Trade where market=?1)", market); for (org.cryptocoinpartners.schema.Trade trade : results) { // org.cryptocoinpartners.schema.Trade trade = query.getSingleResult(); long millis = trade.getTime().getMillis(); if (millis > lastTradeTime) lastTradeTime = millis; // todo this is broken and assumes an increasing integer remote key // Long remoteId = Long.valueOf(trade.getRemoteKey().concat(String.valueOf(trade.getTimestamp()))); Long remoteId = Long.valueOf(trade.getRemoteKey()); if (remoteId > lastTradeId) lastTradeId = remoteId; } } finally { // EM.em().close(); } //StreamingExchangeService streamingDataService = xchangeExchange.getStreamingExchangeService(streamingConfiguration,new CurrencyPair[]{ CurrencyPair.BTC_USD })); lastTradeTime = 0; lastTradeId = 0; } @Override public Object call() { try { while (true) { ExchangeEvent event = dataService.getNextEvent(); if (event != null) { //System.out.println("---> " + event.getPayload() + " " + event.getEventType()); if (event.getEventType().equals(ExchangeEventType.TRADE)) { com.xeiam.xchange.dto.marketdata.Trade trade = (com.xeiam.xchange.dto.marketdata.Trade) event.getPayload(); long remoteId = Long.valueOf(String.valueOf(dateFormat.format(trade.getTimestamp()).concat(trade.getId()))).longValue(); if (remoteId > lastTradeId) { Instant tradeInstant = new Instant(trade.getTimestamp()); BigDecimal volume = (trade.getType() == OrderType.ASK) ? trade.getTradableAmount().negate() : trade.getTradableAmount(); // Fill fill = fillFactory.create(order, context.getTime(), context.getTime(), bid.getMarket(), bid.getPriceCount(), fillVolume, // Long.toString(bid.getTime().getMillis())); // context.publish(fill); lastTradeTime = tradeInstant.getMillis(); lastTradeId = remoteId; } } if (event.getEventType().equals(ExchangeEventType.DISCONNECT)) { log.error(this.getClass().getSimpleName() + " Disconnected"); //Thread.currentThread().interrupt(); //dataService. // dataService.disconnect(); // dataService.connect(); //READYSTATE status = dataService.getWebSocketStatus(); // dataService.connect(); // let's resubmit and connect } } } } catch (InterruptedException e) { log.error("Threw a Execption, full stack trace follows disconnecting:", e); // Thread.currentThread().interrupt(); dataService.disconnect(); // Thread.currentThread().interrupt(); } catch (RejectedExecutionException rej) { log.error("Threw a Execption, full stack trace follows:", rej); rej.printStackTrace(); } catch (Exception | Error e2) { // TODO Auto-generated catch block log.error("Threw a Execption, full stack trace follows:", e2); e2.printStackTrace(); } return Thread.currentThread(); } // private final Book.Builder bookBuilder = new Book.Builder(); private final boolean getTradesNext = true; private StreamingExchangeService dataService = null; private final Context context; private final Market market; private final RateLimiter rateLimiter; private final FuturesContract contract; private long lastTradeTime; private final Prompt prompt; private long lastTradeId; } @Override protected void handleSpecificOrder(SpecificOrder specificOrder) throws Throwable { Order.OrderType orderType = null; com.xeiam.xchange.Exchange exchange = XchangeUtil.getExchangeForMarket(specificOrder.getMarket().getExchange()); PollingTradeService tradeService = exchange.getPollingTradeService(); if (specificOrder.getLimitPrice() != null && specificOrder.getStopPrice() != null) reject(specificOrder, "Stop-limit orders are not supported"); if (specificOrder.getPositionEffect() == null || specificOrder.getPositionEffect() == PositionEffect.OPEN) orderType = specificOrder.isBid() ? Order.OrderType.BID : Order.OrderType.ASK; else if (specificOrder.getPositionEffect() == PositionEffect.CLOSE) // if order volume is < 0 && it is closing, then I am exiting Bid, else I am exit bid // orderType = specificOrder.isBid() ? Order.OrderType.BID : Order.OrderType.ASK; orderType = specificOrder.isAsk() ? Order.OrderType.EXIT_BID : Order.OrderType.EXIT_ASK; BigDecimal tradeableVolume = specificOrder.getVolume().abs().asBigDecimal(); CurrencyPair currencyPair = XchangeUtil.getCurrencyPairForListing(specificOrder.getMarket().getListing()); String id = specificOrder.getId().toString(); Date timestamp = specificOrder.getTime().toDate(); if (specificOrder.getLimitPrice() != null) { LimitOrder limitOrder = new LimitOrder(orderType, tradeableVolume, currencyPair, "", null, specificOrder.getLimitPrice().asBigDecimal()); // todo put on a queue try { specificOrder.setRemoteKey(tradeService.placeLimitOrder(limitOrder)); specificOrder.persit(); updateOrderState(specificOrder, OrderState.PLACED, false); } catch (Exception | Error e) { log.error(this.getClass().getSimpleName() + ":handleSpecificOrder Unable to place order " + specificOrder + ". Threw a Execption, full stack trace follows:", e); throw e; // todo retry until expiration or reject as invalid } } else { MarketOrder marketOrder = new MarketOrder(orderType, tradeableVolume, currencyPair, id, timestamp); // todo put on a queue try { specificOrder.setRemoteKey(tradeService.placeMarketOrder(marketOrder)); specificOrder.persit(); updateOrderState(specificOrder, OrderState.PLACED, false); } catch (NotYetImplementedForExchangeException e) { log.warn("XChange adapter " + exchange + " does not support this order: " + specificOrder, e); reject(specificOrder, "XChange adapter " + exchange + " does not support this order"); throw e; } catch (Exception | Error e) { log.error(this.getClass().getSimpleName() + ":handleSpecificOrder Unable to place order " + specificOrder + ". Threw a Execption, full stack trace follows:", e); throw e; // todo retry until expiration or reject as invalid } } } @Transient protected Collection<SpecificOrder> getPendingXchangeOrders(Market market, Portfolio portfolio) { Collection<SpecificOrder> pendingOrders = new ArrayList<SpecificOrder>(); com.xeiam.xchange.Exchange exchange; try { exchange = XchangeUtil.getExchangeForMarket(market.getExchange()); } catch (Error err) { log.info("market:" + market + " not found"); return pendingOrders; } PollingTradeService tradeService = exchange.getPollingTradeService(); SpecificOrder specificOrder; boolean exists = false; //TODO: need to check prompts to ensure they have the full OKCOIN_THISWEEK:BTC.USD.THISWEEK not just OKCOIN_THISWEEK:BTC.USD try { OpenOrders openOrders = tradeService.getOpenOrders(); for (LimitOrder xchangeOrder : openOrders.getOpenOrders()) { for (org.cryptocoinpartners.schema.Order cointraderOrder : orderStateMap.keySet()) { if (cointraderOrder instanceof SpecificOrder) { specificOrder = (SpecificOrder) cointraderOrder; if (xchangeOrder.getId().equals(specificOrder.getRemoteKey()) && specificOrder.getMarket().equals(market)) { if (!adaptOrderState(xchangeOrder.getStatus()).equals(orderStateMap.get(specificOrder))) if (adaptOrderState(xchangeOrder.getStatus()) != OrderState.FILLED || adaptOrderState(xchangeOrder.getStatus()) != OrderState.PARTFILLED) updateOrderState(specificOrder, adaptOrderState(xchangeOrder.getStatus()), true); Fill fill = createFill(xchangeOrder, specificOrder); if (fill != null) handleFillProcessing(fill); pendingOrders.add(specificOrder); exists = true; break; } } } /* if (!exists) { Date time = (xchangeOrder.getTimestamp() != null) ? xchangeOrder.getTimestamp() : new Date(); specificOrder = new SpecificOrder(xchangeOrder, exchange, portfolio, time); specificOrder.persit(); updateOrderState(specificOrder, adaptOrderState(xchangeOrder.getStatus()), false); Fill fill = createFill(xchangeOrder, specificOrder); if (fill != null) handleFillProcessing(fill); // need to create fills if these are not the same pendingOrders.add(specificOrder); }*/ } } catch (IOException e) { log.error("Threw a Execption, full stack trace follows:", e); e.printStackTrace(); } return pendingOrders; } @Transient private Collection<Trade> getFills(Portfolio portfolio) { Collection<Trade> fills = new ArrayList<Trade>(); for (Market market : portfolio.getContext().getInjector().getInstance(Market.class).findAll()) fills.addAll(getXchangeFills(market, portfolio)); return fills; } private class FetchOrdersRunnable implements Runnable { private final Helper helper; DateFormat dateFormat = new SimpleDateFormat("ddMMyy"); public FetchOrdersRunnable(Context context, Market market, RateLimiter rateLimiter, Exchange coinTraderExchange, int restartCount, @Nullable Helper helper) { this.context = context; this.market = market; this.rateLimiter = rateLimiter; this.coinTraderExchange = coinTraderExchange; // this.tradeService = tradeService; this.helper = helper; this.prompt = market.getListing().getPrompt(); pair = XchangeUtil.getCurrencyPairForListing(market.getListing()); contract = prompt == null ? null : XchangeUtil.getContractForListing(market.getListing()); this.restartCount = restartCount; lastTradeTime = 0; lastTradeId = 0; // EntityManager entityManager = PersistUtil.createEntityManager(); } @Override public void run() { try { getOrders(); } catch (Throwable e) { log.error(this.getClass().getSimpleName() + ":run. Unable to retrive order statuses for market:" + market); //Thread.currentThread(). // throw e; //Thread.currentThread().interrupt(); // throw e; } finally { // getTradesNext = !getTradesNext; rateLimiter.execute(this); // run again. requeue } } /* protected Fill createFill(com.xeiam.xchange.dto.Order exchangeOrder, SpecificOrder order) { Fill fill = null; // new DiscreteAmount(DiscreteAmount.roundedCountForBasis(stopPrice.asBigDecimal(), fill.getMarket().getPriceBasis()), fill // .getMarket().getPriceBasis()); DiscreteAmount exchangeVolume = new DiscreteAmount(DiscreteAmount.roundedCountForBasis(exchangeOrder.getTradableAmount(), order.getMarket() .getVolumeBasis()), order.getMarket().getVolumeBasis()); // DiscreteAmount exchnageVolume = DecimalAmount.of(exchangeOrder.getTradableAmount()).toBasis(order.getMarket().getVolumeBasis(), Remainder.DISCARD); DiscreteAmount exchangeFilledVolume = new DiscreteAmount(DiscreteAmount.roundedCountForBasis(exchangeOrder.getCumulativeAmount(), order.getMarket() .getVolumeBasis()), order.getMarket().getVolumeBasis()); DiscreteAmount exchangeUnfilledVolume = (DiscreteAmount) exchangeVolume.minus(exchangeFilledVolume); DecimalAmount averagePrice = new DecimalAmount(exchangeOrder.getAveragePrice()); Amount fillVolume = (order.getUnfilledVolume().minus(exchangeUnfilledVolume)); // new DiscreteAmount(DiscreteAmount.roundedCountForBasis(exchangeOrder.getAveragePrice(), order.getMarket().getPriceBasis()); DecimalAmount fillPrice = DecimalAmount.ZERO; //exchangeOrder.getAveragePrice(); //order.getAverageFillPrice() if (!fillVolume.isZero()) { fillPrice = ((exchangeFilledVolume.times(averagePrice, Remainder.ROUND_EVEN)).minus((order.getVolume().minus(order.getUnfilledVolume()).times( order.getAverageFillPrice(), Remainder.ROUND_EVEN)))).divide(fillVolume.asBigDecimal(), Remainder.ROUND_EVEN); fill = fillFactory.create(order, context.getTime(), context.getTime(), order.getMarket(), fillPrice.toBasis(order.getMarket().getPriceBasis(), Remainder.ROUND_EVEN).getCount(), fillVolume.toBasis(order.getMarket().getVolumeBasis(), Remainder.ROUND_EVEN).getCount(), Long.toString(exchangeOrder.getTimestamp().getTime())); // this.priceCount = DiscreteAmount.roundedCountForBasis(fillPrice, market.getPriceBasis()); } return fill; // -(order.getAverageFillPrice().times(order.getVolume().minus(order.getUnfilledVolume()), Remainder.ROUND_EVEN)); // DiscreteAmount volume; // If the unfilled volume of specifcOrder > tradedAmount-Cumalitve quanity, create fill // fill volume= (specficOrder.unfiledVolume) - (xchangeOrder.tradableAmount - xchangeOrder.cumlativeQuanity) // price = (exchangeFilledVolumeCount*) //Case 2 //Specfic Order is created after the xchange order is filled or part filled // // specificOpenOrder.getVolume().compareTo(o)(exchnageVolume)!=0) // if (order.getUnfilledVolume().compareTo(exchnageUnfilledVolume) == 0 || order.getVolume().compareTo(exchnageVolume) != 0) { // Amount fillVolume = exchnageUnfilledVolume.minus(order.getUnfilledVolume()); // Fill fill = fillFactory.create(order, context.getTime(), context.getTime(), order.getMarket(), order.getPriceCount(), fillVolume, // exchangeOrder.getTimestamp().getTime()); //} // volume = exchnageUnfilledVolume; // we need to create a fill //return null; }*/ protected void getOrders() throws Throwable { try { Object params[]; if (helper != null) params = helper.getTradesParameters(pair, lastTradeTime, lastTradeId); else { if (contract == null) params = new Object[] {}; else params = new Object[] { contract }; } log.trace("Attempting to get trades from data service"); List<String> openOrders = new ArrayList<String>(); Map<Market, Set<String>> cointraderOrders = new ConcurrentHashMap<Market, Set<String>>(); Set<org.cryptocoinpartners.schema.Order> cointraderOpenOrders = new HashSet<org.cryptocoinpartners.schema.Order>(); if (stateOrderMap.get(OrderState.NEW) != null) cointraderOpenOrders.addAll(stateOrderMap.get(OrderState.NEW)); if (stateOrderMap.get(OrderState.PLACED) != null) cointraderOpenOrders.addAll(stateOrderMap.get(OrderState.PLACED)); if (stateOrderMap.get(OrderState.PARTFILLED) != null) cointraderOpenOrders.addAll(stateOrderMap.get(OrderState.PARTFILLED)); if (stateOrderMap.get(OrderState.PARTFILLED) != null) cointraderOpenOrders.addAll(stateOrderMap.get(OrderState.ROUTED)); if (stateOrderMap.get(OrderState.CANCELLING) != null) cointraderOpenOrders.addAll(stateOrderMap.get(OrderState.CANCELLING)); for (org.cryptocoinpartners.schema.Order openOrder : cointraderOpenOrders) { SpecificOrder openSpecificOrder; if (openOrder instanceof SpecificOrder) { openSpecificOrder = (SpecificOrder) openOrder; openOrders.add(openSpecificOrder.getRemoteKey()); // Set<String> openMarketOrders; // if(cointraderOrders.get(openSpecificOrder.getMarket())==null){ // openMarketOrders=new HashSet<String>(); // openMarketOrders.add(openSpecificOrder.getRemoteKey()); // cointraderOrders.put(openSpecificOrder.getMarket(), openMarketOrders); // } else // { // cointraderOrders.get(openSpecificOrder.getMarket()).add(openSpecificOrder.getRemoteKey()); // } //openOrders.add(openSpecificOrder.getRemoteKey()); } } // getPendingOrders(); Collection<Order> exchangeOrders = null; if (!openOrders.isEmpty()) { // Trades tradeSpec = XchangeUtil.getExchangeForMarket(coinTraderExchange). exchangeOrders = XchangeUtil.getExchangeForMarket(coinTraderExchange).getPollingTradeService() .getOrder(openOrders.toArray(new String[openOrders.size()])); // OpenOrders tradeSpec = tradeService.getOpenOrders(); // List<LimitOrder> openExchangeOrders = tradeSpec.getOpenOrders(); Boolean orderFound = false; for (Order exchangeOrder : exchangeOrders) { // so let's check these are in the ordermap for (org.cryptocoinpartners.schema.Order openOrder : cointraderOpenOrders) { SpecificOrder specificOpenOrder = null; if (openOrder instanceof SpecificOrder) { specificOpenOrder = (SpecificOrder) openOrder; if (specificOpenOrder.getRemoteKey().equals(exchangeOrder.getId())) { orderFound = true; if (!adaptOrderState(exchangeOrder.getStatus()).equals(orderStateMap.get(openOrder))) if (adaptOrderState(exchangeOrder.getStatus()) != OrderState.FILLED || adaptOrderState(exchangeOrder.getStatus()) != OrderState.PARTFILLED) updateOrderState(openOrder, adaptOrderState(exchangeOrder.getStatus()), true); Fill fill = createFill(exchangeOrder, specificOpenOrder); if (fill != null) handleFillProcessing(fill); //lets see if we need to create some fills // let's create any fills } } } /* if (!orderFound) { String comment; if (exchangeOrder.getTradableAmount().compareTo(BigDecimal.ZERO) > 0) comment = "Long Order Entry"; else comment = "Long Order Entry"; DiscreteAmount volume = DecimalAmount.of(exchangeOrder.getTradableAmount()).toBasis(market.getVolumeBasis(), Remainder.DISCARD); SpecificOrder specificOrder = specificOrderFactory.create(context.getTime(), context.getInjector().getInstance(Portfolio.class), market, volume, comment); Fill fill = createFill(exchangeOrder, specificOrder); if (fill != null) handleFillProcessing(fill); // Market markettest = specificOrder.getMarket(); // specificOrder.withParentFill(generalOrder.getParentFill()); specificOrder.withPositionEffect(PositionEffect.OPEN); specificOrder.withExecutionInstruction(ExecutionInstruction.TAKER); orderStateMap.put(specificOrder, adaptOrderState(exchangeOrder.getStatus())); // work out if we need any fills }*/ } } /* for (org.cryptocoinpartners.schema.Order openOrder : orderStateMap.keySet()) { SpecificOrder specificOpenOrder = null; if (openOrder instanceof SpecificOrder) { specificOpenOrder = (SpecificOrder) openOrder; if (specificOpenOrder.getRemoteKey().equals(exchangeOrder.getId())) { orderFound = true; if (!adaptOrderState(exchangeOrder.getStatus()).equals(orderStateMap.get(openOrder))) updateOrderState(openOrder, adaptOrderState(exchangeOrder.getStatus()), true); Fill fill = createFill(exchangeOrder, specificOpenOrder); if (fill != null) handleFillProcessing(fill); //lets see if we need to create some fills // let's create any fills } } }*/ // externalOrderMap.get(order) //// orderStateMap.get //if (state.isOpen()) // tradeSpec. // List<LimitOrder> openOrders = tradeSpec.getOpenOrders(); // List<LimitOrder> orderlist = exchangeOrder.getOpenOrders(); // List trades = tradeHistSpec.getTrades(); // [LimitOrder [limitPrice=100, Order [type=BID, tradableAmount=1, currencyPair=BTC/USD, id=1469893924, timestamp=null]]] /* log.trace("sorting trades by time and id: " + openExchangeOrders); Collections.sort(openExchangeOrders, timeOrderIdComparator); Iterator<LimitOrder> ilt = openExchangeOrders.iterator(); // Iterator<LimitOrder> ilt = null; openOrders.iterator(); log.trace("itterating over sorted trades: " + openExchangeOrders.size()); */// do { // if (!ilt.hasNext()) // break; // com.xeiam.xchange.dto.trade.LimitOrder order = ilt.next(); // long remoteId = Long.valueOf(String.valueOf(dateFormat.format(order.getTimestamp()).concat(order.getId()))).longValue(); // if (remoteId > lastTradeId) { // Instant tradeInstant = new Instant(order.getTimestamp()); // BigDecimal volume = (order.getType() == OrderType.ASK) ? order.getTradableAmount().negate() : order.getTradableAmount(); // for (org.cryptocoinpartners.schema.SpecificOrder workingOrder : getWorkingOrdersOrderFromStateMap()) { // long priceCount = DiscreteAmount.roundedCountForBasis(order.getLimitPrice(), market.getPriceBasis()); // long volumeCount = DiscreteAmount.roundedCountForBasis(volume, market.getVolumeBasis()); // org.cryptocoinpartners.schema.Fill ourFill = fillFactory.create(workingOrder, tradeInstant, context.getTime(), market, priceCount, // volumeCount, order.getId()); // // // .create(market, tradeInstant, trade.getId(), trade.getPrice(), volume); // context.publish(ourFill); // if (ourFill.getDao() == null) // lastTradeTime = tradeInstant.getMillis(); // lastTradeId = remoteId; // } // } // // } while (true); tradeFailureCount = 0; return; } catch (Exception | Error e) { tradeFailureCount++; log.error(this.getClass().getSimpleName() + ":getOrders unable to get orders for market " + market + " pair " + pair + ". Failure " + tradeFailureCount + " of " + restartCount + ". Full Stack Trace: " + e); if (tradeFailureCount >= restartCount) { //try { // if (rateLimiter.getRunnables() == null || rateLimiter.getRunnables().isEmpty() || rateLimiter.remove(this)) { log.error(this.getClass().getSimpleName() + ":getOrders unable to get orders for " + market + " pair " + pair + " for " + tradeFailureCount + " of " + restartCount + " time. Resetting Trade Service Connection."); com.xeiam.xchange.Exchange xchangeExchange = XchangeUtil.resetExchange(coinTraderExchange); // dataService = xchangeExchange.getPollingMarketDataService(); tradeFailureCount = 0; throw e; //} } } return; } // private final Book.Builder bookBuilder = new Book.Builder(); private final boolean getTradesNext = true; private final RateLimiter rateLimiter; private final Exchange coinTraderExchange; private final int restartCount; private int tradeFailureCount = 0; private final Context context; private final Market market; private final CurrencyPair pair; private final FuturesContract contract; private final long lastTradeTime; private final Prompt prompt; private final long lastTradeId; } protected OrderState adaptOrderState(OrderStatus state) { switch (state) { case PENDING_NEW: return OrderState.NEW; case NEW: return OrderState.PLACED; case PARTIALLY_FILLED: return OrderState.PARTFILLED; case FILLED: return OrderState.FILLED; case CANCELED: return OrderState.CANCELLED; case PENDING_REPLACE: return OrderState.NEW; case REPLACED: return OrderState.PLACED; case STOPPED: return OrderState.TRIGGER; case REJECTED: return OrderState.REJECTED; case EXPIRED: return OrderState.EXPIRED; default: return OrderState.PLACED; } } protected Fill createFill(com.xeiam.xchange.dto.Order exchangeOrder, SpecificOrder order) { Fill fill = null; // new DiscreteAmount(DiscreteAmount.roundedCountForBasis(stopPrice.asBigDecimal(), fill.getMarket().getPriceBasis()), fill // .getMarket().getPriceBasis()); DiscreteAmount exchangeVolume = new DiscreteAmount(DiscreteAmount.roundedCountForBasis(exchangeOrder.getTradableAmount(), order.getMarket() .getVolumeBasis()), order.getMarket().getVolumeBasis()); // DiscreteAmount exchnageVolume = DecimalAmount.of(exchangeOrder.getTradableAmount()).toBasis(order.getMarket().getVolumeBasis(), Remainder.DISCARD); DiscreteAmount exchangeFilledVolume = new DiscreteAmount(DiscreteAmount.roundedCountForBasis(exchangeOrder.getCumulativeAmount(), order.getMarket() .getVolumeBasis()), order.getMarket().getVolumeBasis()); DiscreteAmount exchangeUnfilledVolume = (DiscreteAmount) exchangeVolume.minus(exchangeFilledVolume); DecimalAmount averagePrice = new DecimalAmount(exchangeOrder.getAveragePrice()); Amount fillVolume = (order.isAsk()) ? (order.getUnfilledVolume().abs().minus(exchangeUnfilledVolume)).negate() : (order.getUnfilledVolume().abs() .minus(exchangeUnfilledVolume)); // if (order.isAsk()) // new DiscreteAmount(DiscreteAmount.roundedCountForBasis(exchangeOrder.getAveragePrice(), order.getMarket().getPriceBasis()); DecimalAmount fillPrice = DecimalAmount.ZERO; //exchangeOrder.getAveragePrice(); //order.getAverageFillPrice() if (!fillVolume.isZero()) { fillPrice = ((exchangeFilledVolume.times(averagePrice, Remainder.ROUND_EVEN)).minus((order.getVolume().abs().minus(order.getUnfilledVolume().abs()) .times(order.getAverageFillPrice(), Remainder.ROUND_EVEN)))).divide(fillVolume.abs().asBigDecimal(), Remainder.ROUND_EVEN); fill = fillFactory.create(order, context.getTime(), context.getTime(), order.getMarket(), fillPrice.toBasis(order.getMarket().getPriceBasis(), Remainder.ROUND_EVEN).getCount(), fillVolume.toBasis(order.getMarket().getVolumeBasis(), Remainder.ROUND_EVEN).getCount(), Long.toString(exchangeOrder.getTimestamp().getTime())); // this.priceCount = DiscreteAmount.roundedCountForBasis(fillPrice, market.getPriceBasis()); } return fill; // -(order.getAverageFillPrice().times(order.getVolume().minus(order.getUnfilledVolume()), Remainder.ROUND_EVEN)); // DiscreteAmount volume; // If the unfilled volume of specifcOrder > tradedAmount-Cumalitve quanity, create fill // fill volume= (specficOrder.unfiledVolume) - (xchangeOrder.tradableAmount - xchangeOrder.cumlativeQuanity) // price = (exchangeFilledVolumeCount*) //Case 2 //Specfic Order is created after the xchange order is filled or part filled // // specificOpenOrder.getVolume().compareTo(o)(exchnageVolume)!=0) // if (order.getUnfilledVolume().compareTo(exchnageUnfilledVolume) == 0 || order.getVolume().compareTo(exchnageVolume) != 0) { // Amount fillVolume = exchnageUnfilledVolume.minus(order.getUnfilledVolume()); // Fill fill = fillFactory.create(order, context.getTime(), context.getTime(), order.getMarket(), order.getPriceCount(), fillVolume, // exchangeOrder.getTimestamp().getTime()); //} // volume = exchnageUnfilledVolume; // we need to create a fill //return null; } protected OrderStatus adaptOrderState(OrderState state) { switch (state) { case NEW: return OrderStatus.PENDING_NEW; case PLACED: return OrderStatus.NEW; case FILLED: return OrderStatus.FILLED; case CANCELLING: return OrderStatus.PENDING_CANCEL; case CANCELLED: return OrderStatus.CANCELED; case REJECTED: return OrderStatus.REJECTED; case EXPIRED: return OrderStatus.EXPIRED; default: return OrderStatus.NEW; } } @Transient protected Collection<Trade> getXchangeFills(Market market, Portfolio portfolio) { com.xeiam.xchange.Exchange exchange; Collection<Trade> fills = Collections.synchronizedList(new ArrayList<Trade>()); try { exchange = XchangeUtil.getExchangeForMarket(market.getExchange()); } catch (Error err) { log.info("market:" + market + " not found"); return fills; } PollingTradeService tradeService = exchange.getPollingTradeService(); try { // TradeHistoryParamsAll params = new TradeHistoryParamsAll(); // for (order workingOrder : orderStateMap. TradeHistoryParams params = new OkCoinFuturesTradeHistoryParams(50, 0, CurrencyPair.BTC_USD, FuturesContract.ThisWeek, "86751191"); //tradeService.createTradeHistoryParams(); //new TradeHistoryParamFuturesContract(); //params. //params.setCurrencyPair(CurrencyPair.BTC_MXN); //params.setStartId("86751191"); // params. Trades trades = tradeService.getTradeHistory(params); //for (Trade trade : trades.getTrades()) fills.addAll(trades.getTrades()); return fills; } catch (ExchangeException e) { log.error("Unable to find orders of portfolio :" + portfolio); } catch (NotAvailableFromExchangeException e) { log.error("Unable to cancel order :" + portfolio); } catch (NotYetImplementedForExchangeException e) { log.error("Unable to cancel order :" + portfolio); } catch (IOException e) { log.error("failed to cancel order " + portfolio + " with execption:" + e); e.printStackTrace(); } return fills; } protected static final Collection<SpecificOrder> pendingOrders = new ArrayList<SpecificOrder>(); protected static final HashBiMap<com.xeiam.xchange.dto.Order, com.xeiam.xchange.dto.Order> externalOrderMap = HashBiMap.create(); @Override public void init() { super.init(); // do we need to get teh status of orders from the xchange? // TODO Auto-generated method stub } private static final Comparator<LimitOrder> timeOrderIdComparator = new Comparator<LimitOrder>() { @Override public int compare(LimitOrder event, LimitOrder event2) { int sComp = event.getTimestamp().compareTo(event2.getTimestamp()); if (sComp != 0) { return sComp; } else { return (event.getId().compareTo(event2.getId())); } } }; @SuppressWarnings("finally") @Override protected synchronized boolean cancelSpecificOrder(SpecificOrder order) { com.xeiam.xchange.Exchange exchange; boolean deleted = false; try { exchange = XchangeUtil.getExchangeForMarket(order.getMarket().getExchange()); PollingTradeService tradeService = exchange.getPollingTradeService(); if (tradeService.cancelOrder(order.getRemoteKey())) deleted = true; else { /// if (!pendingOrders.contains(order)) { //TODO check if order is on exchange log.error("Unable to cancel order as not present in exchange order book. Order:" + order); // deleted = false; // } } } catch (Error | Exception e) { //TODO we need to handel this better, do we add them to a qeueue and then keep retrying in the event of an execption and reset the exchange after x attempts log.error("Unable to cancel order :" + order + ". full stack trace" + e); } finally { return deleted; } } }