1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
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
101
102
103 return quote;
104
105 }
106
107
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
127
128
129
130 return quote;
131 }
132
133 public Quote getQuote(){
134
135 Quote quote = new Quote();
136
137 quote.setSymbol(symbol);
138
139
140 List<Float> prices = new ArrayList<Float>( limitOrders.keySet());
141
142
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
149 quote.setBidPrice(price.floatValue());
150 quote.setBidSize(entry.getBuyQuantity());
151 break;
152 }
153 }
154
155
156
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
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
252 if( order.isBuy() ){
253
254 if( order.isMarketOrder() ){
255
256
257 for( int i=0; i<prices.size(); i++){
258
259 Float price = prices.get(i);
260 PriceEntry entry = limitOrders.get(price);
261
262
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
280
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
297 for( int i=0; i<prices.size(); i++){
298
299 Float price = prices.get(i);
300 PriceEntry entry = limitOrders.get(price);
301
302
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
320
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
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
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 }