package org.marketcetera.photon.views; import java.math.BigDecimal; import java.util.Iterator; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.commons.lang.StringUtils; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.IHandler; import org.eclipse.core.databinding.beans.BeansObservables; import org.eclipse.core.databinding.observable.list.WritableList; import org.eclipse.core.databinding.observable.map.CompositeMap; import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.runtime.AssertionFailedException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.databinding.EMFObservables; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.viewers.*; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.*; import org.eclipse.ui.*; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.ui.part.ViewPart; import org.marketcetera.marketdata.FeedStatus; import org.marketcetera.photon.FIXFieldLocalizer; import org.marketcetera.photon.Messages; import org.marketcetera.photon.PhotonPlugin; import org.marketcetera.photon.commons.ui.workbench.ChooseColumnsMenu.IColumnProvider; import org.marketcetera.photon.commons.ui.workbench.ColumnState; import org.marketcetera.photon.marketdata.IFeedStatusChangedListener; import org.marketcetera.photon.marketdata.IMarketDataManager; import org.marketcetera.photon.model.marketdata.MDPackage; import org.marketcetera.photon.ui.TextContributionItem; import org.marketcetera.trade.*; import org.marketcetera.trade.Side; import org.marketcetera.trade.TimeInForce; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; import quickfix.field.*; import com.google.common.collect.Lists; import com.google.common.collect.Maps; /* $License$ */ /** * Market data view. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $") public final class MarketDataView extends ViewPart implements IMSymbolListener,IColumnProvider { @Override public void init(IViewSite site, IMemento memento) throws PartInitException { super.init(site); mViewState = memento; } /** * Returns the clipboard for this view. * * @return the clipboard for this view */ public Clipboard getClipboard() { if (mClipboard == null) { mClipboard = new Clipboard(mViewer.getControl().getDisplay()); } return mClipboard; } @Override public Table getColumnWidget() { return mViewer != null ? mViewer.getTable() : null; } @Override public void createPartControl(Composite parent) { final IActionBars actionBars = getViewSite().getActionBars(); IToolBarManager toolbar = actionBars.getToolBarManager(); mSymbolEntryText = new TextContributionItem(""); //$NON-NLS-1$ toolbar.add(mSymbolEntryText); toolbar.add(new AddSymbolAction(mSymbolEntryText, this)); PhotonPlugin.getDefault().getMarketDataManager().addActiveFeedStatusChangedListener(new IFeedStatusChangedListener() { @Override public void feedStatusChanged(final IFeedStatusEvent inEvent) { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { mSymbolEntryText.setEnabled(inEvent.getNewStatus() == FeedStatus.AVAILABLE); } }); } }); final Table table = new Table(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.BORDER); table.setHeaderVisible(true); mViewer = new TableViewer(table); GridDataFactory.defaultsFor(table).applyTo(table); final MarketDataItemComparator comparator = new MarketDataItemComparator(); mViewer.setComparator(comparator); SelectionListener listener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // determine new sort column and direction TableColumn sortColumn = table.getSortColumn(); TableColumn currentColumn = (TableColumn) e.widget; final int index = table.indexOf(currentColumn); int dir = table.getSortDirection(); if (sortColumn == currentColumn) { dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; } else { table.setSortColumn(currentColumn); dir = SWT.UP; } table.setSortDirection(dir); comparator.setSort(dir == SWT.UP ? 1 : -1); comparator.setIndex(index); mViewer.refresh(); } }; // create columns, using FIXFieldLocalizer to preserve backwards compatibility TableViewerColumn symbolColumn = new TableViewerColumn(mViewer, createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(Symbol.class.getSimpleName()), SWT.LEFT, listener)); symbolColumn.setEditingSupport(new SymbolEditingSupport()); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(LastPx.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(LastQty.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(BidSize.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(BidPx.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(OfferPx.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(OfferSize.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(PrevClosePx.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(OpenClose.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(HighPx.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(LowPx.class.getSimpleName()), SWT.RIGHT, listener); createColumn(table, FIXFieldLocalizer.getLocalizedFIXFieldName(TradeVolume.class.getSimpleName()), SWT.RIGHT, listener); // restore table state if it exists if(mViewState != null) { ColumnState.restore(table, mViewState); for(TableColumn column : table.getColumns()) { if(column.getWidth() == 0) { column.setResizable(false); } } } registerContextMenu(); getSite().setSelectionProvider(mViewer); ObservableListContentProvider content = new ObservableListContentProvider(); mViewer.setContentProvider(content); IObservableSet domain = content.getKnownElements(); IObservableMap[] maps = new IObservableMap[] { BeansObservables.observeMap(domain, MarketDataViewItem.class, "symbol"), //$NON-NLS-1$ createCompositeMap(domain, "latestTick", //$NON-NLS-1$ MDPackage.Literals.MD_LATEST_TICK__PRICE), createCompositeMap(domain, "latestTick", //$NON-NLS-1$ MDPackage.Literals.MD_LATEST_TICK__SIZE), createCompositeMap(domain, "topOfBook", //$NON-NLS-1$ MDPackage.Literals.MD_TOP_OF_BOOK__BID_SIZE), createCompositeMap(domain, "topOfBook", //$NON-NLS-1$ MDPackage.Literals.MD_TOP_OF_BOOK__BID_PRICE), createCompositeMap( domain, "topOfBook", MDPackage.Literals.MD_TOP_OF_BOOK__ASK_PRICE), //$NON-NLS-1$ createCompositeMap( domain, "topOfBook", MDPackage.Literals.MD_TOP_OF_BOOK__ASK_SIZE), //$NON-NLS-1$ createCompositeMap(domain, "marketStat", //$NON-NLS-1$ MDPackage.Literals.MD_MARKETSTAT__PREVIOUS_CLOSE_PRICE), createCompositeMap(domain, "marketStat", //$NON-NLS-1$ MDPackage.Literals.MD_MARKETSTAT__OPEN_PRICE), createCompositeMap(domain, "marketStat", //$NON-NLS-1$ MDPackage.Literals.MD_MARKETSTAT__HIGH_PRICE), createCompositeMap(domain, "marketStat", //$NON-NLS-1$ MDPackage.Literals.MD_MARKETSTAT__LOW_PRICE), createCompositeMap(domain, "marketStat", //$NON-NLS-1$ MDPackage.Literals.MD_MARKETSTAT__VOLUME) }; mViewer.setLabelProvider(new ObservableMapLabelProvider(maps)); mViewer.setUseHashlookup(true); mItems = WritableList.withElementType(MarketDataViewItem.class); mViewer.setInput(mItems); if(mViewState != null ) { IMemento[] instrumentList = mViewState.getChildren(INSTRUMENT_LIST); if(instrumentList != null) { for(IMemento symbolMemo : instrumentList) { instrumentsToAdd.add(InstrumentFromMemento.restore(symbolMemo)); } addInstrumentJobToken = addInstrumentService.scheduleAtFixedRate(new AddInstrumentAgent(), 1000, 1000, TimeUnit.MILLISECONDS); } } table.addListener(SWT.MouseDoubleClick, new Listener() { @Override public void handleEvent(Event event) { Point pt = new Point(event.x, event.y); TableItem item = table.getItem(pt); if (item == null) return; int col_count = table.getColumnCount(); for (int i = 0; i < col_count; i++) { Rectangle rect = item.getBounds(i); if (rect.contains(pt)) { MD_TABLE_COLUMNS enum_col = MD_TABLE_COLUMNS.values()[i]; MarketDataViewItem mdi = (MarketDataViewItem) item .getData(); if (mdi != null && mdi.getTopOfBook() != null) switch (enum_col) { case BID_PX: // hit the bid newOrder(mdi, Side.Sell, mdi.getTopOfBook() .getBidPrice(), mdi.getTopOfBook() .getBidSize()); break; case BID_SZ: // join the bid newOrder(mdi, Side.Buy, mdi.getTopOfBook() .getBidPrice(), null); break; case OFFER_PX: // lift the offer newOrder(mdi, Side.Buy, mdi.getTopOfBook() .getAskPrice(), mdi.getTopOfBook() .getAskSize()); break; case OFFER_SZ: // join the ask newOrder(mdi, Side.Sell, mdi.getTopOfBook() .getAskPrice(), null); break; default: break; } } } } }); } private void newOrder(final MarketDataViewItem mdi, final Side side, final BigDecimal Px, final BigDecimal ordSz) { busyRun(new Runnable() { public void run() { BigDecimal defaultOrderSize = getDefaultOrderSize(mdi.getInstrument(), Px); Instrument instrument = mdi.getInstrument(); OrderSingle newOrder = Factory.getInstance().createOrderSingle(); newOrder.setInstrument(instrument); newOrder.setOrderType(OrderType.Limit); newOrder.setSide(side); newOrder.setQuantity(defaultOrderSize); if (ordSz != null && ordSz.compareTo(defaultOrderSize) == -1) { newOrder.setQuantity(ordSz); } newOrder.setPrice(Px); newOrder.setTimeInForce(TimeInForce.Day); try { PhotonPlugin.getDefault().showOrderInTicket(newOrder); } catch (WorkbenchException e) { SLF4JLoggerProxy.error(this, e); ErrorDialog.openError(null, null, null, new Status(IStatus.ERROR,PhotonPlugin.ID,e.getLocalizedMessage())); } } }); } private IObservableMap createCompositeMap(IObservableSet domain, String property, EStructuralFeature feature) { return new CompositeMap(BeansObservables.observeMap(domain, MarketDataViewItem.class, property), EMFObservables.mapFactory(feature)); } private TableColumn createColumn(final Table table, String text, int alignment, SelectionListener listener) { final TableColumn column = new TableColumn(table, SWT.NONE); column.setWidth(70); column.setText(text); column.setMoveable(true); column.setAlignment(alignment); column.addSelectionListener(listener); return column; } /** * Register the context menu for the viewer so that commands may be added to * it. */ private void registerContextMenu() { MenuManager contextMenu = new MenuManager(); contextMenu.setRemoveAllWhenShown(true); getSite().registerContextMenu(contextMenu, mViewer); Control control = mViewer.getControl(); Menu menu = contextMenu.createContextMenu(control); control.setMenu(menu); } /* (non-Javadoc) * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento) */ @Override public void saveState(IMemento inMemento) { ColumnState.save(getColumnWidget(), inMemento); if(inMemento != null) { for(Instrument instrument : mItemMap.keySet()) { IMemento instrumentMemento = inMemento.createChild(INSTRUMENT_LIST); InstrumentToMemento.save(instrument, instrumentMemento); } } } @Override public void setFocus() { if (mSymbolEntryText.isEnabled()) mSymbolEntryText.setFocus(); } @Override public boolean isListeningSymbol(Instrument instrument) { return false; } @Override public void onAssertSymbol(Instrument instrument) { addSymbol(instrument); } /** * Adds a new row in the view for the given instrument (if one does not already exist). * * @param inInstrument an <code>Instrument</code> value */ public void addSymbol(final Instrument inInstrument) { if (mItemMap.containsKey(inInstrument)) { PhotonPlugin.getMainConsoleLogger().warn(Messages.DUPLICATE_SYMBOL.getText(inInstrument)); } else { busyRun(new Runnable() { @Override public void run() { // the item was generated by the passed instrument and will generate multiple market data requests to fulfill it // the symbol resolver should be deterministic, so we can use the instrument as the key for the items collection MarketDataViewItem item = new MarketDataViewItem(mMarketDataManager.getMarketData(), inInstrument); mItemMap.put(inInstrument, item); mItems.add(item); } }); } } private void busyRun(Runnable runnable) { BusyIndicator.showWhile(getViewSite().getShell().getDisplay(), runnable); } private void remove(final MarketDataViewItem item) { mItemMap.remove(item.getInstrument()); mItems.remove(item); item.dispose(); } @Override public void dispose() { for (MarketDataViewItem item : mItemMap.values()) { item.dispose(); } mItemMap = null; mItems = null; if (mClipboard != null) { mClipboard.dispose(); } super.dispose(); } /** * Handles column sorting. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $")//$NON-NLS-1$ private static class MarketDataItemComparator extends ViewerComparator { private int mIndex = -1; private int mDirection = 0; /** * @param index * index of sort column */ public void setIndex(int index) { mIndex = index; } /** * @param direction * sort direction, 1 for ascending and -1 for descending */ public void setSort(int direction) { mDirection = direction; } @Override public int compare(Viewer viewer, Object e1, Object e2) { if (mIndex == -1) return 0; MarketDataViewItem item1 = (MarketDataViewItem) e1; MarketDataViewItem item2 = (MarketDataViewItem) e2; int compare; MD_TABLE_COLUMNS enum_col = MD_TABLE_COLUMNS.values()[mIndex]; switch (enum_col) { case MD_SYMBOL: String symbol1 = item1.getInstrument().getSymbol(); String symbol2 = item2.getInstrument().getSymbol(); compare = compareNulls(symbol1, symbol2); if (compare == 0) { compare = symbol1.compareTo(symbol2); } break; case LAST_PX: BigDecimal tradePrice1 = item1.getLatestTick().getPrice(); BigDecimal tradePrice2 = item2.getLatestTick().getPrice(); compare = compareNulls(tradePrice1, tradePrice2); if (compare == 0) { compare = tradePrice1.compareTo(tradePrice2); } break; case LAST_SZ: BigDecimal tradeSize1 = item1.getLatestTick().getSize(); BigDecimal tradeSize2 = item2.getLatestTick().getSize(); compare = compareNulls(tradeSize1, tradeSize2); if (compare == 0) { compare = tradeSize1.compareTo(tradeSize2); } break; case BID_SZ: BigDecimal bidSize1 = item1.getTopOfBook().getBidSize(); BigDecimal bidSize2 = item2.getTopOfBook().getBidSize(); compare = compareNulls(bidSize1, bidSize2); if (compare == 0) { compare = bidSize1.compareTo(bidSize2); } break; case BID_PX: BigDecimal bidPrice1 = item1.getTopOfBook().getBidPrice(); BigDecimal bidPrice2 = item2.getTopOfBook().getBidPrice(); compare = compareNulls(bidPrice1, bidPrice2); if (compare == 0) { compare = bidPrice1.compareTo(bidPrice2); } break; case OFFER_PX: BigDecimal askPrice1 = item1.getTopOfBook().getAskPrice(); BigDecimal askPrice2 = item2.getTopOfBook().getAskPrice(); compare = compareNulls(askPrice1, askPrice2); if (compare == 0) { compare = askPrice1.compareTo(askPrice2); } break; case OFFER_SZ: BigDecimal askSize1 = item1.getTopOfBook().getAskSize(); BigDecimal askSize2 = item2.getTopOfBook().getAskSize(); compare = compareNulls(askSize1, askSize2); if (compare == 0) { compare = askSize1.compareTo(askSize2); } break; case PREV_CLOSE: BigDecimal closeSize1 = item1.getMarketStat() .getPreviousClosePrice(); BigDecimal closeSize2 = item2.getMarketStat() .getPreviousClosePrice(); compare = compareNulls(closeSize1, closeSize2); if (compare == 0) { compare = closeSize1.compareTo(closeSize2); } break; case OPEN_PX: BigDecimal openSize1 = item1.getMarketStat().getOpenPrice(); BigDecimal openSize2 = item2.getMarketStat().getOpenPrice(); compare = compareNulls(openSize1, openSize2); if (compare == 0) { compare = openSize1.compareTo(openSize2); } break; case HIGH_PX: BigDecimal highSize1 = item1.getMarketStat().getHighPrice(); BigDecimal highSize2 = item2.getMarketStat().getHighPrice(); compare = compareNulls(highSize1, highSize2); if (compare == 0) { compare = highSize1.compareTo(highSize2); } break; case LOW_PX: BigDecimal lowSize1 = item1.getMarketStat().getLowPrice(); BigDecimal lowSize2 = item2.getMarketStat().getLowPrice(); compare = compareNulls(lowSize1, lowSize2); if (compare == 0) { compare = lowSize1.compareTo(lowSize2); } break; case TRD_VOLUME: BigDecimal volumeSize1 = item1.getMarketStat() .getVolumeTraded(); BigDecimal volumeSize2 = item2.getMarketStat() .getVolumeTraded(); compare = compareNulls(volumeSize1, volumeSize2); if (compare == 0) { compare = volumeSize1.compareTo(volumeSize2); } break; default: throw new AssertionFailedException("Invalid column index"); //$NON-NLS-1$ } return mDirection * compare; } private int compareNulls(Object o1, Object o2) { if (o1 == null) { return (o2 == null) ? 0 : -1; } if (o2 == null) { return (o1 == null) ? 0 : 1; } return 0; } } /** * Provides support for editing symbols in-line * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $") private final class SymbolEditingSupport extends EditingSupport { private final TextCellEditor mTextCellEditor; private SymbolEditingSupport() { super(mViewer); this.mTextCellEditor = new TextCellEditor(mViewer.getTable()); } @Override protected void setValue(Object inElement, Object inValue) { if(StringUtils.isBlank(inValue.toString())) { return; } final MarketDataViewItem item = (MarketDataViewItem) inElement; final Instrument instrument = item.getInstrument(); if(instrument.getSymbol().equals(inValue)) { return; } final Instrument newInstrument = PhotonPlugin.getDefault().getSymbolResolver().resolveSymbol(inValue.toString()); if(mItemMap.containsKey(newInstrument)) { PhotonPlugin.getMainConsoleLogger().warn(Messages.DUPLICATE_SYMBOL.getText(newInstrument.getSymbol())); return; } busyRun(new Runnable() { @Override public void run() { mItemMap.remove(instrument); item.setInstrument(newInstrument); mItemMap.put(newInstrument, item); } }); } @Override protected Object getValue(Object element) { return ((MarketDataViewItem) element).getInstrument().getSymbol(); } @Override protected CellEditor getCellEditor(Object element) { return mTextCellEditor; } @Override protected boolean canEdit(Object element) { return true; } } /** * Handles the delete command for this view. * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $") public static final class DeleteCommandHandler extends AbstractHandler implements IHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchPart part = HandlerUtil.getActivePartChecked(event); ISelection selection = HandlerUtil .getCurrentSelectionChecked(event); if (part instanceof MarketDataView && selection instanceof IStructuredSelection) { final MarketDataView view = (MarketDataView) part; final IStructuredSelection sselection = (IStructuredSelection) selection; // this can take some time view.busyRun(new Runnable() { public void run() { for (Object obj : sselection.toArray()) { if (obj instanceof MarketDataViewItem) { view.remove((MarketDataViewItem) obj); } } } }); } return null; } } @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $") public static final class BuyCommandHandler extends OrderCommandHandler implements IHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { super.setSide(Side.Buy); return super.execute(event); } } @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $") public static final class SellCommandHandler extends OrderCommandHandler implements IHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { super.setSide(Side.Sell); return super.execute(event); } } public static BigDecimal getDefaultOrderSize(Instrument instr, BigDecimal Px) { BigDecimal defaultOrderSize = null; if (Px == null) return defaultOrderSize; if (instr instanceof Equity) { defaultOrderSize = new BigDecimal(100); } else if (instr instanceof Future) { defaultOrderSize = new BigDecimal(1); } else if (instr instanceof Option) { defaultOrderSize = new BigDecimal(1); } else if (instr instanceof org.marketcetera.trade.Currency) { defaultOrderSize = new BigDecimal(1); } return defaultOrderSize; } /** * Handles the send order command for this view. * */ @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $") public static class OrderCommandHandler extends AbstractHandler implements IHandler { private Side side; public void setSide(Side side) { this.side = side; } @Override public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchPart part = HandlerUtil.getActivePartChecked(event); ISelection selection = HandlerUtil.getCurrentSelectionChecked(event); if(part instanceof MarketDataView && selection instanceof IStructuredSelection) { final MarketDataView view = (MarketDataView) part; final IStructuredSelection sselection = (IStructuredSelection) selection; view.busyRun(new Runnable() { public void run() { for(Object obj : sselection.toArray()) { if(obj instanceof MarketDataViewItem) { MarketDataViewItem mdi = (MarketDataViewItem) obj; Instrument instrument = mdi.getInstrument(); OrderSingle newOrder = Factory.getInstance().createOrderSingle(); newOrder.setInstrument(instrument); newOrder.setOrderType(OrderType.Limit); newOrder.setSide(side); BigDecimal order_px = side == Side.Buy ? mdi.getTopOfBook().getAskPrice() : mdi.getTopOfBook().getBidPrice(); BigDecimal defaultOrderSize = getDefaultOrderSize(instrument, order_px); if(side == Side.Buy && mdi.getTopOfBook().getAskSize() != null) { if(defaultOrderSize != null && mdi.getTopOfBook().getAskSize().compareTo(defaultOrderSize) == 1) { newOrder.setQuantity(defaultOrderSize); } else { newOrder.setQuantity(mdi.getTopOfBook().getAskSize()); } if(order_px != null) { newOrder.setPrice(mdi.getTopOfBook().getAskPrice()); } } else if(mdi.getTopOfBook().getBidSize() != null) { if(defaultOrderSize != null && mdi.getTopOfBook().getBidSize().compareTo(defaultOrderSize) == 1) { newOrder.setQuantity(defaultOrderSize); } else { newOrder.setQuantity(mdi.getTopOfBook().getBidSize()); } if(order_px != null) { newOrder.setPrice(mdi.getTopOfBook().getBidPrice()); } } newOrder.setTimeInForce(TimeInForce.Day); try { PhotonPlugin.getDefault().showOrderInTicket(newOrder); } catch (WorkbenchException e) { SLF4JLoggerProxy.error(this, e); ErrorDialog.openError(null, null, null, new Status(IStatus.ERROR, PhotonPlugin.ID, e.getLocalizedMessage())); } } } } }); } return null; } } /** * Handles the copy command for this view * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $")//$NON-NLS-1$ public static final class CopyCommandHandler extends AbstractHandler implements IHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchPart part = HandlerUtil.getActivePartChecked(event); ISelection selection = HandlerUtil .getCurrentSelectionChecked(event); if (part instanceof MarketDataView && selection instanceof IStructuredSelection) { MarketDataView view = (MarketDataView) part; IStructuredSelection sselection = (IStructuredSelection) selection; StringBuilder builder = new StringBuilder(); for (Object obj : sselection.toArray()) { if (obj instanceof MarketDataViewItem) { MarketDataViewItem item = (MarketDataViewItem) obj; builder.append(item); builder.append(System.getProperty("line.separator")); //$NON-NLS-1$ } } view.getClipboard().setContents( new Object[] { builder.toString() }, new Transfer[] { TextTransfer.getInstance() }); } return null; } } /** * * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: MarketDataView.java 16899 2014-05-11 16:03:04Z colin $ * @since 2.4.0 */ private class AddInstrumentAgent implements Runnable { /* (non-Javadoc) * @see java.lang.Runnable#run() */ @Override public void run() { if(!mMarketDataManager.isRunning()) { return; } try { if(instrumentsToAdd.isEmpty()) { return; } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Iterator<Instrument> instrumentToAddIterator = instrumentsToAdd.iterator(); while(instrumentToAddIterator.hasNext()) { Instrument instrumentToAdd = instrumentToAddIterator.next(); try { addSymbol(instrumentToAdd); } catch (Exception e) { SLF4JLoggerProxy.error(org.marketcetera.core.Messages.USER_MSG_CATEGORY, "A problem occurred restoring market data for {}", instrumentToAdd); SLF4JLoggerProxy.error(MarketDataView.this, e); } instrumentToAddIterator.remove(); } } }); } finally { addInstrumentJobToken.cancel(true); addInstrumentJobToken = null; } } } /** * */ private ScheduledExecutorService addInstrumentService = Executors.newScheduledThreadPool(1); /** * The view ID. */ public static final String ID = "org.marketcetera.photon.views.MarketDataView"; //$NON-NLS-1$ private Map<Instrument,MarketDataViewItem> mItemMap = Maps.newLinkedHashMap(); private final java.util.List<Instrument> instrumentsToAdd = Lists.newArrayList(); private java.util.concurrent.Future<?> addInstrumentJobToken; private TextContributionItem mSymbolEntryText; private final IMarketDataManager mMarketDataManager = PhotonPlugin.getDefault().getMarketDataManager(); private TableViewer mViewer; private WritableList mItems; private IMemento mViewState; private Clipboard mClipboard; private final static String INSTRUMENT_LIST = "Instrument_List"; //$NON-NLS-1$ public static enum MD_TABLE_COLUMNS { MD_SYMBOL, LAST_PX, LAST_SZ, BID_SZ, BID_PX, OFFER_PX, OFFER_SZ, PREV_CLOSE, OPEN_PX, HIGH_PX, LOW_PX, TRD_VOLUME, } }