/* * Copyright 2009-2010 Eric Bruno, Greg Bollella. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * Neither the name of the Book, * "Real-Time Java Programming with Java RTS" * nor the names of its authors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * See the GNU General Public License version 2 for more details. * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the: * Free Software Foundation, Inc. * 51 Franklin St, Fifth Floor * Boston, MA 02110-1301 USA. */ package com.sun.oss.trader.tradingengine; import com.sun.oss.trader.data.*; import com.sun.oss.trader.mq.*; import joprt.RtThread; import java.util.*; public class OrderManager implements Runnable { public static final boolean NOTIFY_VIA_UART = false; private static final String TRADES_TAG = "<trades>"; private static final String TRADES_CLOSE = "</trades>"; private static final String TRADE_TAG = "<trade>"; private static final String TRADE_CLOSE = "</trade>"; private static final String TYPE_TAG = "<type>"; private static final String TYPE_CLOSE = "</type>"; private static final String SYMBOL_TAG = "<symbol>"; private static final String SYMBOL_CLOSE = "</symbol>"; private static final String DATETIME_TAG = "<datetime>"; private static final String DATETIME_CLOSE = "</datetime>"; private static final String TRADEPRICE_TAG = "<tradeprice>"; private static final String TRADEPRICE_CLOSE = "</tradeprice>"; private static final String LIMITPRICE_TAG = "<limitprice>"; private static final String LIMITPRICE_CLOSE = "</limitprice>"; private static final String STOPPRICE_TAG = "<stopprice>"; private static final String STOPPRICE_CLOSE = "</stopprice>"; private static final String VOLUME_TAG = "<volume>"; private static final String VOLUME_CLOSE = "</volume>"; private static final String BUY = "Buy"; private static final String SELL = "Sell"; private static final boolean ENABLE_DEBUG = false; private static final int MAX_TRADE_XML_SIZE = 512; private BoundedStringBuffer tradeXML = new BoundedStringBuffer(MAX_TRADE_XML_SIZE); //private String cannedTrade = "<trades><trade><type>Sell</type><symbol>GM</symbol><datetime>2007-02-06T14:49:03.800-05:00</datetime><tradeprice>30.47</tradeprice><limitprice>30.47</limitprice><volume>29500</volume></trade></trades>"; private MarketManager marketMgr = null; private TradeManagement tradeMgr = null; // The order book is a HashMap of linked lists. Each HashMap bucket // is keyed by a stock name (a StringBuffer). The linked list pointed to // by each bucket represents the list of open orders for that // particular stock, in value, low to high for buy orders, and // high to low for sell orders // public static final int INITIAL_CAPACITY = 128; class SubBook { LinkedList<OrderEntry> buy = new LinkedList<OrderEntry>(); LinkedList<OrderEntry> sell = new LinkedList<OrderEntry>(); } HashMap<StringBuffer,SubBook> orderBook = new HashMap<StringBuffer,SubBook>(INITIAL_CAPACITY); StringBuffer[] orderBookKeys = null; // Constructors // public OrderManager(MarketManager marketMgr) throws Exception { this.marketMgr = marketMgr; this.tradeMgr = new TradeManagement("LimitStopTrades"); // Read the local file "orders.csv" and add the orders try { // Read update data from file OrderFile reader = new OrderFile(); String line = reader.readLine(); while ( line != null ) { int seperator = line.indexOf(','); String symbol = line.substring(0, seperator); int begin = seperator; seperator = line.indexOf(',', begin+1); String price = line.substring(begin+1, seperator); begin = seperator; seperator = line.indexOf(',', begin+1); String qty = line.substring(begin+1, seperator); begin = seperator; seperator = line.indexOf(',', begin+1); String type = line.substring(begin+1); int orderType = 0; if ( type.equalsIgnoreCase("limitbuy")) orderType = OrderType.LIMIT_BUY; else if ( type.equalsIgnoreCase("limitsell")) orderType = OrderType.LIMIT_SELL; else if ( type.equalsIgnoreCase("stopbuy")) orderType = OrderType.STOP_BUY; else if ( type.equalsIgnoreCase("stopsell")) orderType = OrderType.STOP_SELL; // Read the next line of fake update data line = reader.readLine(); enterOrder( Double.parseDouble(price), Integer.parseInt(qty), new StringBuffer(symbol), orderType); } } catch ( Exception e ) { e.printStackTrace(); } } // Public methods // public void enterOrder( double price, int quantity, StringBuffer symbol, int type) throws Exception { OrderEntry newEntry = new OrderEntry(symbol, price, quantity, type); // Get the order book for this symbol if it exists SubBook sb = orderBook.get(symbol); if ( sb == null ) { // Create a new order book for this symbol sb = new SubBook(); orderBook.put(symbol, sb); } // Get the right sub book (but or sell orders) LinkedList<OrderEntry> orders = null; if ( newEntry.isBuyOrder() ) orders = sb.buy; else orders = sb.sell; // Walk the sub book and find the correct location for this order for ( int i = 0; i < orders.size(); i++ ) { OrderEntry entry = orders.get(i); if ( newEntry.comesBefore(entry.getPrice()) ) { orders.add( i, newEntry ); return; } } // Just add the order to the end of the list orders.addLast(newEntry); } // Display the entire order book for all symbols public void displayOrderBook() { /* Set<StringBuffer> keys = orderBook.keySet(); Iterator<StringBuffer> iter = keys.iterator(); while ( iter.hasNext() ) { StringBuffer key = iter.next(); displayOrderBook(key); } */ if ( orderBookKeys == null ) { Set<StringBuffer> keys = orderBook.keySet(); orderBookKeys = new StringBuffer[orderBook.size()]; int i = 0; for (StringBuffer k : keys) { orderBookKeys[i++] = k; } } for ( int i = 0; i < orderBook.size(); i++ ) displayOrderBook(orderBookKeys[i]); } // Display the order book for a symbol public void displayOrderBook(StringBuffer symbol) { // Get the order book for this symbol if it exists SubBook sb = orderBook.get(symbol); if ( sb != null ) { System.out.println("******************************************"); System.out.println("Buy orders for " + symbol); displaySubBook(sb.buy); System.out.println("Sell orders for " + symbol); displaySubBook(sb.sell); System.out.println(" "); } } public void run() { try { while ( true ) { long time = System.currentTimeMillis(); // Make sure the global order book index is up-to-date // The call is optimized to ensure the work is only done // once unless the order book is updated // getOrderBookKeys(); // // This loop continually compares the order book to the // market book to see if any trades should be executed // // Loop to compare market to order prices // for ( int i = 0; i < orderBookKeys.length; i++ ) { StringBuffer symbol = orderBookKeys[i]; StringBuffer sPrice = marketMgr.marketBook.get(symbol.toString()); if ( sPrice == null || sPrice.length() == 0 ) continue; double marketPrice = Double.parseDouble( sPrice.toString() ); // Get the sub book for this symbol // SubBook sb = orderBook.get(symbol); // Walk the list of sell orders to see if the market price // has hit or passed a limit or stop order threshold // for ( int x = 0; x < sb.sell.size(); x++ ) { OrderEntry entry = sb.sell.get(x); if ( checkForTrade( entry, marketPrice ) == true ) break; } // Walk the list of buy orders to see if the market price // has hit or passed a limit or stop order threshold // for ( int x = 0; x < sb.buy.size(); x++ ) { OrderEntry entry = sb.buy.get(x); if ( checkForTrade( entry, marketPrice ) == true ) break; } } time = System.currentTimeMillis() - time; System.out.print("O: "); System.out.println(time); boolean madePeriod = RtThread.currentRtThread().waitForNextPeriod(); if (!madePeriod) System.out.print('O'); } } catch ( Exception e ) { e.printStackTrace(); } } public boolean checkForTrade(OrderEntry entry, double marketPrice) { if ( ! entry.isActive() ) return false; switch ( entry.getType() ) { case OrderType.STOP_SELL: if ( marketPrice <= entry.getPrice() ) { /* System.out.println( "Limit sell occured. Limit price: " + entry.getPrice() + ", Market price: " + marketPrice + ", Stock: " + entry.getSymbol() ); */ generateTradeXML(entry, marketPrice); entry.setActive(false); return true; } break; case OrderType.LIMIT_SELL: if ( marketPrice == entry.getPrice() ) { /* System.out.println( "Stop sell occured. Limit price: " + entry.getPrice() + ", Market price: " + marketPrice + ", Stock: " + entry.getSymbol() ); */ generateTradeXML(entry, marketPrice); entry.setActive(false); return true; } else if ( marketPrice < entry.getPrice() ) { if (ENABLE_DEBUG) { System.out.println( "*** MISSED LIMIT SELL: Order price: " + entry.getPrice() + ", Market price=" + marketPrice + ", Stock: " + entry.getSymbol() ); } // entry.setActive(false); } break; case OrderType.STOP_BUY: if ( marketPrice >= entry.getPrice() ) { /* System.out.println( "Stop buy occured. Order price: " + entry.getPrice() + ", Market price: " + marketPrice + ", Stock: " + entry.getSymbol() ); */ generateTradeXML(entry, marketPrice); entry.setActive(false); return true; } break; case OrderType.LIMIT_BUY: if ( marketPrice == entry.getPrice() ) { /* System.out.println( "Limit buy occured. Order price: " + entry.getPrice() + ", Market price: " + marketPrice + ", Stock: " + entry.getSymbol() ); */ generateTradeXML(entry, marketPrice); entry.setActive(false); return true; } else if ( marketPrice > entry.getPrice() ) { if (ENABLE_DEBUG) { System.out.println( "*** MISSED LIMIT BUY: Order price: " + entry.getPrice() + ", Market price=" + marketPrice + ", Stock: " + entry.getSymbol() ); } // entry.setActive(false); } break; } return false; } // Private methods // private BoundedStringBuffer generateTradeXML(OrderEntry entry, double tradePrice) { /* * SAMPLE: <trades> <trade> <type>Buy</type> <symbol>SUNW</Symbol> <datetime>2006-09-20T13:59:25.993-04:00</datetime> <tradeprice>4.9500</tradeprice> <limitprice>4.9000</limitprice> <volume>1000</volume> </trade> </trades> */ try { tradeXML.delete(0,MAX_TRADE_XML_SIZE); // Root tags tradeXML.append(TRADES_TAG); tradeXML.append(TRADE_TAG); // Trade type: Buy or Sell tradeXML.append(TYPE_TAG); if ( entry.isBuyOrder() ) tradeXML.append(BUY); else tradeXML.append(SELL); tradeXML.append(TYPE_CLOSE); // Stock Symbol tradeXML.append(SYMBOL_TAG); tradeXML.append(entry.getSymbol()); tradeXML.append(SYMBOL_CLOSE); // Date/time stamp of trade tradeXML.append(DATETIME_TAG); tradeXML.append(System.currentTimeMillis()); tradeXML.append(DATETIME_CLOSE); // Trade execution price tradeXML.append(TRADEPRICE_TAG); tradeXML.append(entry.getPrice()/*tradePrice*/); tradeXML.append(TRADEPRICE_CLOSE); // Indicate limit or stop order, and the original order price if ( entry.isLimitOrder() ) { tradeXML.append(LIMITPRICE_TAG); tradeXML.append(entry.getPrice()); tradeXML.append(LIMITPRICE_CLOSE); } else { tradeXML.append(LIMITPRICE_TAG); tradeXML.append(entry.getPrice()); tradeXML.append(LIMITPRICE_CLOSE); } // Trade volume tradeXML.append(VOLUME_TAG); tradeXML.append(entry.getQuantity()); tradeXML.append(VOLUME_CLOSE); // Close the root tags tradeXML.append(TRADE_CLOSE); tradeXML.append(TRADES_CLOSE); // Send the XML using an MBean // if(NOTIFY_VIA_UART) { tradeMgr.notifyTrade( tradeXML.toString() ); } } catch ( Exception e ) { e.printStackTrace(); } //System.out.println("Trade Data: " + tradeXML.toString() ); return tradeXML; } private StringBuffer[] getOrderBookKeys() { if ( orderBookKeys == null ) { Set<StringBuffer> keys = orderBook.keySet(); orderBookKeys = new StringBuffer[orderBook.size()]; int i = 0; for (StringBuffer k : keys) { orderBookKeys[i++] = k; } } return orderBookKeys; } // Display the orders within a sub book private void displaySubBook(LinkedList<OrderEntry> orders) { for ( int i = 0; i < orders.size(); i++) { OrderEntry entry = orders.get(i); System.out.println( "Price: " + entry.getPrice() + ", Qty: " + entry.getQuantity() + ", Type: " + OrderType.getAsString( entry.getType() ) ); } } }