/* * Copyright (c) 2004-2011 Marco Maccaferri and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marco Maccaferri - initial API and implementation */ package org.eclipsetrader.core.ats.simulation; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Status; import org.eclipsetrader.core.feed.IPricingEnvironment; import org.eclipsetrader.core.feed.IPricingListener; import org.eclipsetrader.core.feed.ITrade; import org.eclipsetrader.core.feed.PricingDelta; import org.eclipsetrader.core.feed.PricingEvent; import org.eclipsetrader.core.instruments.ISecurity; import org.eclipsetrader.core.internal.CoreActivator; import org.eclipsetrader.core.trading.BrokerException; import org.eclipsetrader.core.trading.IAccount; import org.eclipsetrader.core.trading.IBroker; import org.eclipsetrader.core.trading.IOrder; import org.eclipsetrader.core.trading.IOrderChangeListener; import org.eclipsetrader.core.trading.IOrderMonitor; import org.eclipsetrader.core.trading.IOrderRoute; import org.eclipsetrader.core.trading.IOrderSide; import org.eclipsetrader.core.trading.IOrderStatus; import org.eclipsetrader.core.trading.IOrderType; import org.eclipsetrader.core.trading.IOrderValidity; import org.eclipsetrader.core.trading.Order; import org.eclipsetrader.core.trading.OrderChangeEvent; import org.eclipsetrader.core.trading.OrderDelta; public class Broker implements IBroker { private final String id; private final IPricingEnvironment pricingEnvironment; private List<OrderMonitor> orders = new ArrayList<OrderMonitor>(); private final ListenerList listeners = new ListenerList(ListenerList.IDENTITY); private final Log log = LogFactory.getLog(getClass()); private final IPricingListener pricingListener = new IPricingListener() { @Override public void pricingUpdate(PricingEvent event) { for (PricingDelta delta : event.getDelta()) { if (delta.getNewValue() instanceof ITrade) { processTrade(event.getSecurity(), (ITrade) delta.getNewValue()); } } } }; public Broker(IPricingEnvironment pricingEnvironment) { this.id = UUID.randomUUID().toString(); this.pricingEnvironment = pricingEnvironment; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getId() */ @Override public String getId() { return id; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getName() */ @Override public String getName() { return "Simulation"; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#connect() */ @Override public void connect() { pricingEnvironment.addPricingListener(pricingListener); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#disconnect() */ @Override public void disconnect() { pricingEnvironment.removePricingListener(pricingListener); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#prepareOrder(org.eclipsetrader.core.trading.IOrder) */ @Override public IOrderMonitor prepareOrder(IOrder order) throws BrokerException { if (order.getAccount() != null && !(order.getAccount() instanceof Account)) { throw new BrokerException("Invalid account"); } OrderMonitor monitor = new OrderMonitor(this, order) { @Override public void cancel() throws BrokerException { setStatus(IOrderStatus.Canceled); if (log.isInfoEnabled()) { StringBuilder sb = new StringBuilder("Order Cancelled:"); sb.append(" instrument=" + getOrder().getSecurity().getName()); sb.append(", type=" + getOrder().getType()); sb.append(", side=" + getOrder().getSide()); sb.append(", qty=" + getOrder().getQuantity()); if (getOrder().getPrice() != null) { sb.append(", price=" + getOrder().getPrice()); } if (getOrder().getReference() != null) { sb.append(", reference=" + getOrder().getReference()); } log.info(sb.toString()); } fireUpdateNotifications(new OrderDelta[] { new OrderDelta(OrderDelta.KIND_UPDATED, this), }); } @Override public void submit() throws BrokerException { synchronized (orders) { orders.add(this); } setId(UUID.randomUUID().toString()); setStatus(IOrderStatus.PendingNew); if (log.isInfoEnabled()) { StringBuilder sb = new StringBuilder("Order Submitted:"); sb.append(" instrument=" + getOrder().getSecurity().getName()); sb.append(", type=" + getOrder().getType()); sb.append(", side=" + getOrder().getSide()); sb.append(", qty=" + getOrder().getQuantity()); if (getOrder().getPrice() != null) { sb.append(", price=" + getOrder().getPrice()); } if (getOrder().getReference() != null) { sb.append(", reference=" + getOrder().getReference()); } log.info(sb.toString()); } fireUpdateNotifications(new OrderDelta[] { new OrderDelta(OrderDelta.KIND_UPDATED, this), }); } }; return monitor; } protected void fireUpdateNotifications(OrderDelta[] deltas) { if (deltas.length != 0) { OrderChangeEvent event = new OrderChangeEvent(this, deltas); Object[] l = listeners.getListeners(); for (int i = 0; i < l.length; i++) { try { ((IOrderChangeListener) l[i]).orderChanged(event); } catch (Throwable e) { Status status = new Status(IStatus.ERROR, CoreActivator.PLUGIN_ID, 0, "Error running listener", e); //$NON-NLS-1$ CoreActivator.log(status); } } } } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getOrders() */ @Override public IOrderMonitor[] getOrders() { synchronized (orders) { return orders.toArray(new IOrderMonitor[orders.size()]); } } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getAllowedTypes() */ @Override public IOrderType[] getAllowedTypes() { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getAllowedSides() */ @Override public IOrderSide[] getAllowedSides() { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getAllowedValidity() */ @Override public IOrderValidity[] getAllowedValidity() { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getAllowedRoutes() */ @Override public IOrderRoute[] getAllowedRoutes() { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#canTrade(org.eclipsetrader.core.instruments.ISecurity) */ @Override public boolean canTrade(ISecurity security) { return true; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getSecurityFromSymbol(java.lang.String) */ @Override public ISecurity getSecurityFromSymbol(String symbol) { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getSymbolFromSecurity(org.eclipsetrader.core.instruments.ISecurity) */ @Override public String getSymbolFromSecurity(ISecurity security) { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#addOrderChangeListener(org.eclipsetrader.core.trading.IOrderChangeListener) */ @Override public void addOrderChangeListener(IOrderChangeListener listener) { listeners.add(listener); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#removeOrderChangeListener(org.eclipsetrader.core.trading.IOrderChangeListener) */ @Override public void removeOrderChangeListener(IOrderChangeListener listener) { listeners.remove(listener); } /* (non-Javadoc) * @see org.eclipsetrader.core.trading.IBroker#getAccounts() */ @Override public IAccount[] getAccounts() { return null; } protected void processTrade(ISecurity security, ITrade trade) { List<OrderDelta> deltas = new ArrayList<OrderDelta>(); OrderMonitor[] monitors; synchronized (orders) { monitors = orders.toArray(new OrderMonitor[orders.size()]); } for (int i = 0; i < monitors.length; i++) { if (monitors[i].getStatus() != IOrderStatus.PendingNew && monitors[i].getStatus() != IOrderStatus.Partial) { continue; } IOrder order = monitors[i].getOrder(); if (order.getSecurity() == security) { if (order.getType() == IOrderType.Market) { fillOrder(monitors[i], trade.getTime(), trade.getPrice()); deltas.add(new OrderDelta(OrderDelta.KIND_UPDATED, monitors[i])); } else if (order.getType() == IOrderType.Limit) { if (order.getSide() == IOrderSide.Buy && trade.getPrice() <= order.getPrice()) { fillOrder(monitors[i], trade.getTime(), trade.getPrice()); deltas.add(new OrderDelta(OrderDelta.KIND_UPDATED, monitors[i])); } else if (order.getSide() == IOrderSide.Sell && trade.getPrice() >= order.getPrice()) { fillOrder(monitors[i], trade.getTime(), trade.getPrice()); deltas.add(new OrderDelta(OrderDelta.KIND_UPDATED, monitors[i])); } } } } if (deltas.size() != 0) { fireUpdateNotifications(deltas.toArray(new OrderDelta[deltas.size()])); } } protected void fillOrder(OrderMonitor monitor, Date date, Double price) { ((Order) monitor.getOrder()).setDate(date); monitor.setFilledQuantity(monitor.getOrder().getQuantity()); monitor.setAveragePrice(price); monitor.setStatus(IOrderStatus.Filled); if (log.isInfoEnabled()) { StringBuilder sb = new StringBuilder("Order Filled:"); sb.append(" instrument=" + monitor.getOrder().getSecurity().getName()); sb.append(", type=" + monitor.getOrder().getType()); sb.append(", side=" + monitor.getOrder().getSide()); sb.append(", qty=" + monitor.getOrder().getQuantity()); if (monitor.getOrder().getPrice() != null) { sb.append(", price=" + monitor.getOrder().getPrice()); } sb.append(", fillQty=" + monitor.getFilledQuantity()); sb.append(", avgPrice=" + monitor.getAveragePrice()); if (monitor.getOrder().getReference() != null) { sb.append(", reference=" + monitor.getOrder().getReference()); } log.info(sb.toString()); } monitor.fireOrderCompletedEvent(); Account account = (Account) monitor.getOrder().getAccount(); if (account != null) { account.processCompletedOrder(monitor); } } }