View Javadoc

1   /*
2    *  
3    *  author nambi sankaran
4    *  copyright (C) 2009 nambi sankaran.
5    *
6    *  This program is free software: you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation, either version 3 of the License, or
9    *  (at your option) any later version.
10   *
11   *  This program is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   *  GNU General Public License for more details.
15   *
16   *  You should have received a copy of the GNU General Public License
17   *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   *
19   */
20  package net.sf.emarket.trade.domain;
21  
22  import java.util.ArrayList;
23  import java.util.Comparator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.TreeMap;
27  
28  import net.sf.emarket.order.domain.Order;
29  import net.sf.emarket.quote.domain.Quote;
30  import net.sf.emarket.trade.service.WrongSymbolException;
31  
32  /***
33   * <code>OrderBook</code> arranges the orders according to their price, in their precedence.
34   *
35   * <pre>
36   * buy size     price     sell size
37   * ---------------------------------------
38   *               31.8      20
39   *               31.7      3
40   *               31.6      8
41   *               31.5      
42   *               31.4      10
43   *  5            31.3
44   *  20           31.2
45   *  40           31.1
46   * </pre>
47   *
48   * @author nambi sankaran
49   */
50  public class OrderBook {
51  	
52      /*** instrument for the book */
53      private String symbol;
54  
55      /*** limit orders */
56      private Map<Float,PriceEntry> limitOrders = new TreeMap<Float,PriceEntry>();
57  
58      /*** market orders */
59      private PriceEntry marketOrders = new PriceEntry(0f);
60      
61      public OrderBook( String symbol){
62      	this.symbol =symbol;
63      }
64      
65      public String getSymbol(){
66      	return symbol;
67      }
68      
69      public void loadOrders( List<ReceivedOrder> orders ){
70      	
71      	for( ReceivedOrder order : orders){
72      		
73      		// limit orders form the basis of order book
74              if( order.getPriceType().equals(Order.PRICE_TYPE_LIMIT) ){
75              	
76                  Float price = new Float( order.getLimitPrice() );
77  
78                  if( limitOrders.containsKey( price )){
79                      PriceEntry entry = limitOrders.get(price);
80                      entry.add( order );
81                  } else{
82                      PriceEntry entry = new PriceEntry( order.getLimitPrice() );
83                      entry.add( order );
84                      limitOrders.put( price, entry );
85                  }
86                  
87              }else{
88              	// add the market orders
89              	marketOrders.add(order);
90              }
91      	}
92      	
93      }
94      
95      public Quote getQuote( Quote quote ){
96      	
97      	Quote q = getQuote();
98      	
99      	if( !quote.getSymbol().equalsIgnoreCase(getSymbol())){
100     		// POSSIBLE: this method will be always called by the "matching engine", 
101     		// i.e., this is internal to eMarket
102     		// so, silently return the same quote object.
103     		return quote;
104     		//throw new WrongSymbolException("OrderBook does not belong to symbol : "+ quote.getSymbol() );
105     	}
106     	
107     	// some values may be different, from the quote generated from OrderBook
108     	if( q.getAskPrice() != 0 && 
109     			( q.getAskPrice() != quote.getAskPrice() ) ){
110     		quote.setAskPrice(q.getAskPrice());
111     	}
112     	if( q.getAskSize() != 0 && 
113     			( q.getAskSize() != quote.getAskSize() ) ){
114     		quote.setAskSize(q.getAskSize());
115     	}
116     	
117     	if( q.getBidPrice() != 0 && 
118     			( q.getBidPrice() != quote.getBidPrice() ) ){
119     		quote.setBidPrice(q.getBidPrice());
120     	}
121     	if( q.getBidSize() != 0 && 
122     			( q.getBidSize() != quote.getBidSize() ) ){
123     		quote.setBidSize(q.getBidSize());
124     	}
125 
126         // get volume
127 
128         // set change
129     	
130     	return quote;
131     }
132     
133     public Quote getQuote(){
134     	
135     	Quote quote = new Quote();
136     	
137     	quote.setSymbol(symbol);
138     	
139     	// this set has all prices, and it is sorted in ascending order
140     	List<Float> prices = new ArrayList<Float>( limitOrders.keySet());
141     	
142     	// find the highest buy price.
143     	for( int i= (prices.size()-1)  ; i>=0; i--){
144     		Float price = prices.get(i);
145     		PriceEntry entry = limitOrders.get(price);
146     		
147     		if( entry.hasBuyOrders() ){
148     			// set the buy price for quote
149     			quote.setBidPrice(price.floatValue());
150     			quote.setBidSize(entry.getBuyQuantity());
151     			break;
152     		}
153     	}
154     	
155     	
156     	// find lowest selling price
157     	for( int i= 0 ; i< prices.size() ; i++ ){
158     		Float price = prices.get(i);
159     		PriceEntry entry = limitOrders.get(price);
160     		
161     		if( entry.hasSellOrders() ){
162     			// set the buy price for quote
163     			quote.setAskPrice( price.floatValue() );
164     			quote.setAskSize( entry.getSellQuantity() );
165     			break;
166     		}
167     	}    	
168     	return quote;
169     }
170     
171     public List<PriceDepth> getBuyPriceDepths(int level ){
172     	
173     		List<PriceDepth> depths = new ArrayList<PriceDepth>(level);
174     		
175     		List<Float> prices = new ArrayList<Float>( limitOrders.keySet());
176     		
177     		int levelcount=1;
178     		for( Float price : prices ){
179     			
180     			PriceEntry entry = limitOrders.get(price);
181     			
182     			if( entry.hasBuyOrders()){
183     			
184 		    			PriceDepth depth = new PriceDepth();
185 		    			depth.setPrice(price);
186 		    			depth.setNoOfOrders(entry.getTotalBuyOrders());
187 		    			depth.setQuantity(entry.getTotalBuyQuantity());
188 		    			
189 		    			depths.add(depth);
190 		    			
191 		    			if( levelcount== level){
192 		    				break;
193 		    			}
194 		    			
195 		    			levelcount++;
196     			}
197     		}
198     		
199     		return depths;
200     }
201     
202     public List<PriceDepth> getSellPriceDepths(int level ){
203     	
204 		List<PriceDepth> depths = new ArrayList<PriceDepth>();
205 		
206 		List<Float> prices = new ArrayList<Float>( limitOrders.keySet());
207 		
208 		int levelcount=1;
209 		for( int i=(prices.size()-1); i>=0 ; i-- ){
210 			
211 			Float price = prices.get(i);
212 			
213 			PriceEntry entry = limitOrders.get(price);
214 			
215 			if( entry.hasSellOrders() ){
216 			
217 					PriceDepth depth = new PriceDepth();
218 					depth.setPrice(price);
219 					depth.setNoOfOrders(entry.getTotalSellOrders());
220 					depth.setQuantity(entry.getTotalSellQuantity());
221 					
222 					depths.add(depth);
223 					
224 					if( levelcount== level){
225 						break;
226 					}
227 					
228 					levelcount++;
229 			}
230 		}
231 		
232 		return depths;
233 }    
234     
235     /***
236      * <code>findMatchingOrders</code> creates a list of matching orders from order book.
237      * If there are no matches, the list is empty.
238      * The list simply contains a list of orders, that contains the list of matching orders.
239      * This method doesn't take care of partial matches.
240      * 
241      * @param order
242      * @return
243      */
244     public int findMatchingOrders( ReceivedOrder order, List<ReceivedOrder> matchedOrders){
245     	
246     	int remainingQuantity = order.getQuantity();
247     	int matchedQuantity = 0;
248     	
249     	List<Float> prices = new ArrayList<Float>( limitOrders.keySet());
250     	
251     	// if the order is "buy", get the matching "sell" orders
252     	if( order.isBuy() ){
253     		
254     		if( order.isMarketOrder() ){
255     					
256     			// since this is a "buy" order, find the orders with  lowest selling prices
257     			for( int i=0; i<prices.size(); i++){
258     				
259     				Float price = prices.get(i);
260     				PriceEntry entry = limitOrders.get(price);
261     				
262     				// check whether this price has any sell orders
263     				if( entry.hasSellOrders() ){
264     					if( entry.getTotalSellQuantity() >= 0  ){
265     						
266     						int q = entry.getSellOrdersForQuantity(remainingQuantity, matchedOrders );
267     						remainingQuantity -= q;
268     						matchedQuantity += q;
269     						
270     						if( remainingQuantity <= 0 ){
271     							break;
272     						}
273     					}
274     				}	
275     			}
276     			
277     		}else if( order.isLimitOrder() ){
278     			
279     			// in an order driver market, limit order must satisfy the limit price
280     			// TODO: in a Quote driven market, buy limit price can be matched against a quote from market maker
281     			if( prices.contains( order.getLimitPrice() )){
282     				PriceEntry entry = limitOrders.get(order.getLimitPrice());
283     				
284     				if( entry.getTotalSellQuantity() > 0 ){
285     					int q = entry.getSellOrdersForQuantity(remainingQuantity, matchedOrders);
286     					remainingQuantity -= q;
287     					matchedQuantity += q;
288     				}
289     			}
290     		}
291     		
292     	}else if( order.isSell() ){
293     		
294     		if( order.isMarketOrder() ){
295 				
296     			// FIXME: since this is a "buy" order, find the orders with  lowest selling prices
297     			for( int i=0; i<prices.size(); i++){
298     				
299     				Float price = prices.get(i);
300     				PriceEntry entry = limitOrders.get(price);
301     				
302     				// check whether this price has any sell orders
303     				if( entry.hasBuyOrders() ){
304     					if( entry.getTotalBuyQuantity() >= 0  ){
305     						
306     						int q = entry.getSellOrdersForQuantity(remainingQuantity, matchedOrders );
307     						remainingQuantity -= q;
308     						matchedQuantity += q;
309     						
310     						if( remainingQuantity <= 0 ){
311     							break;
312     						}
313     					}
314     				}	
315     			}
316     			
317     		}else if( order.isLimitOrder() ){
318 
319     			// in an order driver market, limit order must satisfy the limit price
320     			// TODO: in a Quote driven market, buy limit price can be matched against a quote from market maker
321     			if( prices.contains( order.getLimitPrice() )){
322     				PriceEntry entry = limitOrders.get(order.getLimitPrice());
323     				
324     				if( entry.getTotalBuyQuantity() > 0 ){
325     					int q = entry.getBuyOrdersForQuantity(remainingQuantity, matchedOrders);
326     					remainingQuantity -= q;
327     					matchedQuantity += q;
328     				}
329     			}    			
330     		}
331     		
332     	}
333     	
334     	
335     	return matchedQuantity;
336     }
337     
338     //PriceComparator
339     public static Comparator<? super Order> limitPriceComparator = new Comparator<Order>(){
340         public int compare( Order o1, Order o2 ){
341             return (int) (o1.getLimitPrice() - o2.getLimitPrice());
342         }
343     };
344 
345     //TimeComparator
346     public static Comparator<Order> timeComparator = new Comparator<Order>() {
347         public int compare( Order o1, Order o2 ){
348             return (int) o1.getCreationTime().compareTo(o2.getCreationTime() );
349         }
350     };
351 
352 }