org.eclipsetrader.internal.brokers.paper.PaperBroker.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipsetrader.internal.brokers.paper.PaperBroker.java

Source

/*
 * 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.internal.brokers.paper;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;

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.IFeedIdentifier;
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.markets.IMarket;
import org.eclipsetrader.core.markets.IMarketService;
import org.eclipsetrader.core.markets.MarketPricingEnvironment;
import org.eclipsetrader.core.repositories.IRepositoryService;
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.OrderChangeEvent;
import org.eclipsetrader.core.trading.OrderDelta;
import org.eclipsetrader.internal.brokers.paper.transactions.StockTransaction;

public class PaperBroker implements IBroker {

    private final String id;
    private final String name;
    private final IMarketService marketService;
    private final IRepositoryService repositoryService;

    private MarketPricingEnvironment pricingEnvironment;

    private List<OrderMonitor> pendingOrders = new ArrayList<OrderMonitor>();
    private ListenerList listeners = new ListenerList(ListenerList.IDENTITY);

    private final Log log = LogFactory.getLog(getClass());

    private IPricingListener pricingListener = new IPricingListener() {

        @Override
        public void pricingUpdate(PricingEvent event) {
            for (PricingDelta delta : event.getDelta()) {
                if (delta.getNewValue() instanceof ITrade) {
                    IMarket market = marketService.getMarketForSecurity(event.getSecurity());
                    if (market == null || market.isOpen()) {
                        processTrade(event.getSecurity(), (ITrade) delta.getNewValue());
                    }
                }
            }
        }
    };

    public PaperBroker(String id, String name, IMarketService marketService, IRepositoryService repositoryService) {
        this.id = id;
        this.name = name;
        this.marketService = marketService;
        this.repositoryService = repositoryService;
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getId()
     */
    @Override
    public String getId() {
        return id;
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getName()
     */
    @Override
    public String getName() {
        return name;
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#connect()
     */
    @Override
    public void connect() {
        if (pricingEnvironment != null) {
            pricingEnvironment.removePricingListener(pricingListener);
            pricingEnvironment.dispose();
        }

        pricingEnvironment = new MarketPricingEnvironment(marketService);

        List<OrderDelta> list = new ArrayList<OrderDelta>();
        for (OrderMonitor monitor : pendingOrders) {
            pricingEnvironment.addSecurity(monitor.getOrder().getSecurity());
            list.add(new OrderDelta(OrderDelta.KIND_ADDED, monitor));
        }
        fireUpdateNotifications(list.toArray(new OrderDelta[list.size()]));

        pricingEnvironment.addPricingListener(pricingListener);
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#disconnect()
     */
    @Override
    public void disconnect() {
        if (pricingEnvironment != null) {
            pricingEnvironment.removePricingListener(pricingListener);
            pricingEnvironment.dispose();
            pricingEnvironment = null;
        }
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#canTrade(org.eclipsetrader.core.instruments.ISecurity)
     */
    @Override
    public boolean canTrade(ISecurity security) {
        return true;
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getSecurityFromSymbol(java.lang.String)
     */
    @Override
    public ISecurity getSecurityFromSymbol(String symbol) {
        ISecurity security = null;

        if (repositoryService != null) {
            ISecurity[] securities = repositoryService.getSecurities();
            for (int i = 0; i < securities.length; i++) {
                IFeedIdentifier identifier = securities[i].getIdentifier();
                if (identifier != null && symbol.equals(identifier.getSymbol())) {
                    security = securities[i];
                    break;
                }
            }
        }

        return security;
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBroker#getSymbolFromSecurity(org.eclipsetrader.core.instruments.ISecurity)
     */
    @Override
    public String getSymbolFromSecurity(ISecurity security) {
        return security.getIdentifier() != null ? security.getIdentifier().getSymbol() : null;
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getAllowedSides()
     */
    @Override
    public IOrderSide[] getAllowedSides() {
        return new IOrderSide[] { IOrderSide.Buy, IOrderSide.Sell, };
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getAllowedTypes()
     */
    @Override
    public IOrderType[] getAllowedTypes() {
        return new IOrderType[] { IOrderType.Limit, IOrderType.Market, };
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getAllowedValidity()
     */
    @Override
    public IOrderValidity[] getAllowedValidity() {
        return new IOrderValidity[] { IOrderValidity.Day, };
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getAllowedRoutes()
     */
    @Override
    public IOrderRoute[] getAllowedRoutes() {
        return new IOrderRoute[0];
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#getOrders()
     */
    @Override
    public IOrderMonitor[] getOrders() {
        synchronized (pendingOrders) {
            return pendingOrders.toArray(new IOrderMonitor[pendingOrders.size()]);
        }
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBrokerConnector#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 (pendingOrders) {
                    pendingOrders.add(this);
                }

                SimpleDateFormat idFormatter = new SimpleDateFormat("yyMMddHHmmssSSS");
                setId(idFormatter.format(new Date()));
                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), });
            }
        };

        if (pricingEnvironment != null) {
            pricingEnvironment.addSecurity(order.getSecurity());
        }

        fireUpdateNotifications(new OrderDelta[] { new OrderDelta(OrderDelta.KIND_ADDED, monitor), });

        return monitor;
    }

    protected void processTrade(ISecurity security, ITrade trade) {
        List<OrderDelta> deltas = new ArrayList<OrderDelta>();

        OrderMonitor[] monitors;
        synchronized (pendingOrders) {
            monitors = pendingOrders.toArray(new OrderMonitor[pendingOrders.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], monitors[i].getOrder(), trade.getSize(), 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], monitors[i].getOrder(), trade.getSize(), trade.getPrice());
                        deltas.add(new OrderDelta(OrderDelta.KIND_UPDATED, monitors[i]));
                    } else if (order.getSide() == IOrderSide.Sell && trade.getPrice() >= order.getPrice()) {
                        fillOrder(monitors[i], monitors[i].getOrder(), trade.getSize(), 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, IOrder order, Long size, Double price) {
        double totalPrice = monitor.getFilledQuantity() != null
                ? monitor.getFilledQuantity() * monitor.getAveragePrice()
                : 0.0;
        long filledQuantity = monitor.getFilledQuantity() != null ? monitor.getFilledQuantity() : 0L;
        long remainQuantity = order.getQuantity() - filledQuantity;

        long quantity = size != null && size < remainQuantity ? size : remainQuantity;
        filledQuantity += quantity;
        totalPrice += quantity * price;

        monitor.setFilledQuantity(filledQuantity);
        monitor.setAveragePrice(totalPrice / filledQuantity);

        if (quantity != 0) {
            if (order.getSide() == IOrderSide.Buy || order.getSide() == IOrderSide.BuyCover) {
                monitor.addTransaction(new StockTransaction(monitor.getOrder().getSecurity(), quantity, price));
            }
            if (order.getSide() == IOrderSide.Sell || order.getSide() == IOrderSide.SellShort) {
                monitor.addTransaction(new StockTransaction(monitor.getOrder().getSecurity(), -quantity, price));
            }
        }

        if (log.isInfoEnabled()) {
            StringBuilder sb = new StringBuilder("Order Filled:");
            sb.append(" instrument=" + order.getSecurity().getName());
            sb.append(", type=" + order.getType());
            sb.append(", side=" + order.getSide());
            sb.append(", qty=" + order.getQuantity());
            if (order.getPrice() != null) {
                sb.append(", price=" + order.getPrice());
            }
            sb.append(", fillQty=" + monitor.getFilledQuantity());
            sb.append(", avgPrice=" + monitor.getAveragePrice());
            if (order.getReference() != null) {
                sb.append(", reference=" + order.getReference());
            }
            log.info(sb.toString());
        }

        if (monitor.getFilledQuantity().equals(order.getQuantity())) {
            monitor.setStatus(IOrderStatus.Filled);
            monitor.fireOrderCompletedEvent();

            Account account = (Account) monitor.getOrder().getAccount();
            if (account != null) {
                account.processCompletedOrder(monitor);
            }
        } else {
            monitor.setStatus(IOrderStatus.Partial);
        }
    }

    /* (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);
    }

    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, Activator.PLUGIN_ID, 0, "Error running listener", e); //$NON-NLS-1$
                    Activator.log(status);
                }
            }
        }
    }

    /* (non-Javadoc)
     * @see org.eclipsetrader.core.trading.IBroker#getAccounts()
     */
    @Override
    public IAccount[] getAccounts() {
        return Activator.getDefault().getRepository().getAccounts();
    }

    public void load(File file) throws JAXBException {
        if (!file.exists()) {
            return;
        }

        JAXBContext jaxbContext = JAXBContext.newInstance(OrderMonitor[].class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        unmarshaller.setEventHandler(new ValidationEventHandler() {

            @Override
            public boolean handleEvent(ValidationEvent event) {
                Status status = new Status(IStatus.WARNING, Activator.PLUGIN_ID, 0,
                        "Error validating XML: " + event.getMessage(), null); //$NON-NLS-1$
                Activator.log(status);
                return true;
            }
        });
        JAXBElement<OrderMonitor[]> element = unmarshaller.unmarshal(new StreamSource(file), OrderMonitor[].class);
        if (element != null) {
            Calendar today = Calendar.getInstance();
            Calendar order = Calendar.getInstance();
            for (OrderMonitor monitor : element.getValue()) {
                order.setTime(monitor.getOrder().getDate());
                if (order.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) {
                    pendingOrders.add(monitor);
                }
            }
        }
    }

    public void save(File file) throws JAXBException, IOException {
        if (file.exists()) {
            file.delete();
        }

        JAXBContext jaxbContext = JAXBContext.newInstance(OrderMonitor[].class);
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setEventHandler(new ValidationEventHandler() {

            @Override
            public boolean handleEvent(ValidationEvent event) {
                Status status = new Status(IStatus.WARNING, Activator.PLUGIN_ID, 0,
                        "Error validating XML: " + event.getMessage(), null); //$NON-NLS-1$
                Activator.log(status);
                return true;
            }
        });
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, System.getProperty("file.encoding")); //$NON-NLS-1$

        OrderMonitor[] elements = pendingOrders.toArray(new OrderMonitor[pendingOrders.size()]);
        JAXBElement<OrderMonitor[]> element = new JAXBElement<OrderMonitor[]>(new QName("list"),
                OrderMonitor[].class, elements);
        marshaller.marshal(element, new FileWriter(file));
    }
}