package org.marketcetera.marketdata.marketcetera; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import org.apache.commons.lang.ObjectUtils; import org.marketcetera.core.ClassVersion; import org.marketcetera.core.CoreException; import org.marketcetera.event.AskEvent; import org.marketcetera.event.BidEvent; import org.marketcetera.event.Event; import org.marketcetera.event.EventTranslator; import org.marketcetera.event.TradeEvent; import org.marketcetera.event.UnsupportedEventException; import org.marketcetera.event.impl.QuoteEventBuilder; import org.marketcetera.event.impl.TradeEventBuilder; import org.marketcetera.marketdata.Content; import org.marketcetera.marketdata.DateUtils; import org.marketcetera.marketdata.marketcetera.MarketceteraFeed.Request; import org.marketcetera.trade.Equity; import org.marketcetera.util.log.I18NBoundMessage1P; import quickfix.FieldNotFound; import quickfix.Group; import quickfix.field.MDEntryPx; import quickfix.field.MDEntrySize; import quickfix.field.MDEntryType; import quickfix.field.MDMkt; import quickfix.field.NoMDEntries; import quickfix.field.Symbol; import quickfix.fix44.MarketDataSnapshotFullRefresh; /* $License$ */ /** * Market data feed implementation that connects to Marketcetera's * exchange simulator. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: MarketceteraFeedEventTranslator.java 16154 2012-07-14 16:34:05Z colin $ * @since 0.5.0 */ @ClassVersion("$Id: MarketceteraFeedEventTranslator.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$ public class MarketceteraFeedEventTranslator implements EventTranslator, Messages { private static final String UNKNOWN = "?"; //$NON-NLS-1$ private static final MarketceteraFeedEventTranslator sInstance = new MarketceteraFeedEventTranslator(); public static MarketceteraFeedEventTranslator getInstance() { return sInstance; } private MarketceteraFeedEventTranslator() { } /* (non-Javadoc) * @see org.marketcetera.event.IEventTranslator#translate(java.lang.Object) */ public List<Event> toEvent(Object inData, String inHandle) throws CoreException { if(!(inData instanceof MarketDataSnapshotFullRefresh)) { throw new UnsupportedEventException(new I18NBoundMessage1P(UNKNOWN_EVENT_TYPE, ObjectUtils.toString(inData, null))); } MarketDataSnapshotFullRefresh refresh = (MarketDataSnapshotFullRefresh)inData; List<Event> events = new ArrayList<Event>(); try { int entries = refresh.getInt(NoMDEntries.FIELD); // marketcetera feed returns bid/ask/trade for every query (each entry corresponds to one of these). // we have to decide which data to convert to events and pass along. we know the symbol and the handle. // the handle is sufficient to determine what content was requested with the original request. Request request = MarketceteraFeed.getRequestByHandle(inHandle); if(request == null) { // this could happen if the request were canceled (and removed from the collection) but the feed // is still sending updates. just bail out, no worries, the feed will stop soon. return events; } Set<Content> requestedContent = request.getRequest().getContent(); for(int i=1;i<=entries;i++) { Group group = new MarketDataSnapshotFullRefresh.NoMDEntries(); refresh.getGroup(i, group); String symbol = refresh.getString(Symbol.FIELD); // exchange is *somewhat* optional String exchange; try { exchange = group.getString(MDMkt.FIELD); } catch (FieldNotFound e) { exchange = UNKNOWN; } String price = group.getString(MDEntryPx.FIELD); String size = group.getString(MDEntrySize.FIELD); char type = group.getChar(MDEntryType.FIELD); switch(type){ case MDEntryType.BID : if(requestedContent.contains(Content.TOP_OF_BOOK)) { BidEvent bid = QuoteEventBuilder.equityBidEvent().withMessageId(System.nanoTime()) .withTimestamp(new Date()) .withQuoteDate(DateUtils.dateToString(new Date())) .withInstrument(new Equity(symbol)) .withExchange(exchange) .withPrice(new BigDecimal(price)) .withSize(new BigDecimal(size)).create(); events.add(bid); } break; case MDEntryType.OFFER : if(requestedContent.contains(Content.TOP_OF_BOOK)) { AskEvent ask = QuoteEventBuilder.equityAskEvent().withMessageId(System.nanoTime()) .withTimestamp(new Date()) .withQuoteDate(DateUtils.dateToString(new Date())) .withInstrument(new Equity(symbol)) .withExchange(exchange) .withPrice(new BigDecimal(price)) .withSize(new BigDecimal(size)).create(); events.add(ask); } break; case MDEntryType.TRADE: if(requestedContent.contains(Content.LATEST_TICK)) { TradeEvent trade = TradeEventBuilder.equityTradeEvent().withMessageId(System.nanoTime()) .withTimestamp(new Date()) .withTradeDate(DateUtils.dateToString(new Date())) .withInstrument(new Equity(symbol)) .withExchange(exchange) .withPrice(new BigDecimal(price)) .withSize(new BigDecimal(size)).create(); events.add(trade); } break; default: throw new UnsupportedEventException(new I18NBoundMessage1P(UNKNOWN_MESSAGE_ENTRY_TYPE, type)); }; } } catch (FieldNotFound e) { e.printStackTrace(); } return events; } /* (non-Javadoc) * @see org.marketcetera.event.IEventTranslator#translate(org.marketcetera.event.EventBase) */ public Object fromEvent(Event inEvent) throws CoreException { throw new UnsupportedOperationException(); } }