Java tutorial
/* * JASA Java Auction Simulator API * Copyright (C) 2013 Steve Phelps * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. */ package net.sourceforge.jasa.report; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Observable; import java.util.Observer; import java.util.TreeSet; import net.sourceforge.jabm.event.RoundFinishedEvent; import net.sourceforge.jabm.event.SimEvent; import net.sourceforge.jabm.util.Resetable; import net.sourceforge.jasa.event.OrderPlacedEvent; import net.sourceforge.jasa.event.TransactionExecutedEvent; import net.sourceforge.jasa.market.AuctionRuntimeException; import net.sourceforge.jasa.market.Order; import net.sourceforge.jasa.market.ShoutsNotVisibleException; import org.apache.commons.collections.list.TreeList; import org.apache.log4j.Logger; /** * <p> * A historicalDataReport that keeps a historical record of the shouts in the market that lead * to the last N transactions. This report is used to keep historical data that * is used by various different trading strategies. * </p> * <p> * Since GDStrategy uses this historicalDataReport to compute the number of shouts above or * below a certain price, which leads to slow simulation, SortedView and * IncreasingQueryAccelerator are introduced to speed up GDStrategy's queries * based on the pattern of prices of concern. * </p> * * @author Steve Phelps * @version $Revision$ */ @Deprecated public class HistoricalDataReport extends AbstractAuctionReport implements Serializable, Resetable { protected LinkedList<Order> asks = new LinkedList<Order>(); protected LinkedList<Order> bids = new LinkedList<Order>(); protected TreeSet<Order> sortedShouts = new TreeSet<Order>(); protected HashSet<Order> acceptedShouts = new HashSet<Order>(); // // protected Map shoutMap = Collections.synchronizedMap(new HashMap()); protected int memorySize = 10; protected int currentMemoryCell = 0; protected int[] memoryBids = new int[memorySize]; protected int[] memoryAsks = new int[memorySize]; protected double lowestAskPrice; protected double highestBidPrice; protected Order highestUnacceptedBid; protected Order lowestUnacceptedAsk; protected IncreasingQueryAccelerator accelerator; protected SortedView view; protected Observable observableProxy; static Logger logger = Logger.getLogger(HistoricalDataReport.class); public HistoricalDataReport() { observableProxy = new Observable() { public void notifyObservers() { setChanged(); super.notifyObservers(); } }; } public void addObserver(Observer o) { observableProxy.addObserver(o); } public void deleteObserver(Observer o) { observableProxy.deleteObserver(o); } public void checkConsistency() { assert asks.size() + bids.size() == sortedShouts.size(); } protected void removeNShouts(int n, LinkedList<Order> shouts) { for (int i = 0; i < n; i++) { Order shout = shouts.removeFirst(); if (!sortedShouts.remove(shout)) { assert !sortedShouts.contains(shout); throw new AuctionRuntimeException("Could not process " + shout); } acceptedShouts.remove(shout); } } public void updateTransPriceLog(TransactionExecutedEvent event) { // Object o; currentMemoryCell = (currentMemoryCell + 1) % memorySize; if (memoryAsks[currentMemoryCell] > 0 || memoryBids[currentMemoryCell] > 0) { removeNShouts(memoryAsks[currentMemoryCell], asks); removeNShouts(memoryBids[currentMemoryCell], bids); memoryBids[currentMemoryCell] = 0; memoryAsks[currentMemoryCell] = 0; markMatched(asks); markMatched(bids); checkConsistency(); } if (event.getAsk() == lowestUnacceptedAsk) { lowestUnacceptedAsk = null; } if (event.getBid() == highestUnacceptedBid) { highestUnacceptedBid = null; } observableProxy.notifyObservers(); } public void initialise() { acceptedShouts.clear(); bids.clear(); asks.clear(); sortedShouts.clear(); for (int i = 0; i < memorySize; i++) { memoryBids[i] = 0; memoryAsks[i] = 0; } initialisePriceRanges(); observableProxy.notifyObservers(); } public void reset() { initialise(); } public void updateShoutLog(OrderPlacedEvent event) { Order shout = event.getOrder(); // if (sortedShouts.getCount(shout) > 0) { // report.info(sortedShouts.getCount(shout) + "\n" + shout); // report.info(shoutMap.get(shout.getId())); // } // // shoutMap.put(shout.getId(), shout); addToSortedShouts(shout); if (shout.isAsk()) { asks.add(shout); memoryAsks[currentMemoryCell]++; if (shout.getPriceAsDouble() < lowestAskPrice) { lowestAskPrice = shout.getPriceAsDouble(); } if (lowestUnacceptedAsk == null || lowestUnacceptedAsk.getPriceAsDouble() > shout.getPriceAsDouble()) { lowestUnacceptedAsk = shout; } } else { bids.add(shout); memoryBids[currentMemoryCell]++; if (shout.getPriceAsDouble() > highestBidPrice) { highestBidPrice = shout.getPriceAsDouble(); } if (highestUnacceptedBid == null || highestUnacceptedBid.getPriceAsDouble() < shout.getPriceAsDouble()) { highestUnacceptedBid = shout; } } markMatched(asks); markMatched(bids); checkConsistency(); observableProxy.notifyObservers(); } public void roundClosed(RoundFinishedEvent event) { markMatched(asks); markMatched(bids); // if ( getNumberOfTrades() > memorySize ) { // deleteOldShouts(); // } initialisePriceRanges(); observableProxy.notifyObservers(); } public void eventOccurred(SimEvent event) { super.eventOccurred(event); if (event instanceof RoundFinishedEvent) { roundClosed((RoundFinishedEvent) event); } else if (event instanceof OrderPlacedEvent) { updateShoutLog((OrderPlacedEvent) event); } else if (event instanceof TransactionExecutedEvent) { updateTransPriceLog((TransactionExecutedEvent) event); } } public int getNumberOfTrades() { return acceptedShouts.size() / 2; } public double getHighestBidPrice() { return highestBidPrice; } public double getLowestAskPrice() { return lowestAskPrice; } public double getHighestUnacceptedBidPrice() { if (highestUnacceptedBid != null) { return highestUnacceptedBid.getPriceAsDouble(); } Iterator<Order> i = bids.iterator(); double highestUnacceptedBidPrice = Double.NEGATIVE_INFINITY; while (i.hasNext()) { Order s = i.next(); if (!accepted(s)) { if (s.getPriceAsDouble() > highestUnacceptedBidPrice) { highestUnacceptedBidPrice = s.getPriceAsDouble(); highestUnacceptedBid = s; } } } return highestUnacceptedBidPrice; } public double getLowestAcceptedBidPrice() { Iterator<Order> i = bids.iterator(); double lowestAcceptedBidPrice = Double.POSITIVE_INFINITY; while (i.hasNext()) { Order s = i.next(); if (accepted(s)) { if (s.getPriceAsDouble() < lowestAcceptedBidPrice) { lowestAcceptedBidPrice = s.getPriceAsDouble(); } } } return lowestAcceptedBidPrice; } public double getLowestUnacceptedAskPrice() { if (lowestUnacceptedAsk != null) { return lowestUnacceptedAsk.getPriceAsDouble(); } Iterator<Order> i = asks.iterator(); double lowestUnacceptedBidPrice = Double.POSITIVE_INFINITY; while (i.hasNext()) { Order s = i.next(); if (!accepted(s)) { if (s.getPriceAsDouble() < lowestUnacceptedBidPrice) { lowestUnacceptedBidPrice = s.getPriceAsDouble(); lowestUnacceptedAsk = s; } } } return lowestUnacceptedBidPrice; } public double getHighestAcceptedAskPrice() { Iterator<Order> i = asks.iterator(); double highestAcceptedAskPrice = Double.NEGATIVE_INFINITY; while (i.hasNext()) { Order s = i.next(); if (accepted(s)) { if (s.getPriceAsDouble() > highestAcceptedAskPrice) { highestAcceptedAskPrice = s.getPriceAsDouble(); } } } return highestAcceptedAskPrice; } public List<Order> getBids() { return bids; } public List<Order> getAsks() { return asks; } public boolean accepted(Order shout) { return acceptedShouts.contains(shout); } public int getNumberOfAsks(double price, boolean accepted) { return getNumberOfShouts(asks, price, accepted); } public int getNumberOfBids(double price, boolean accepted) { return getNumberOfShouts(bids, price, accepted); } @SuppressWarnings("rawtypes") public Iterator sortedShoutIterator() { return sortedShouts.iterator(); } public void addToSortedShouts(Order shout) { sortedShouts.add(shout); } /** * * @param shouts * @param price * the sign of price controls whether higher shouts or lower shouts * are needed * @param accepted * @return the number of shouts that meet the specified condition */ public int getNumberOfShouts(List<Order> shouts, double price, boolean accepted) { int numShouts = 0; Iterator<Order> i = shouts.iterator(); while (i.hasNext()) { Order shout = i.next(); if ((price >= 0 && shout.getPriceAsDouble() >= price) || (price < 0 && shout.getPriceAsDouble() <= -price)) { if (accepted) { if (acceptedShouts.contains(shout)) { numShouts++; } } else { numShouts++; } } } return numShouts; } public void produceUserOutput() { } protected void initialisePriceRanges() { highestBidPrice = Double.NEGATIVE_INFINITY; lowestAskPrice = Double.POSITIVE_INFINITY; highestUnacceptedBid = null; lowestUnacceptedAsk = null; } protected void markMatched(List<Order> shouts) { try { Iterator<Order> i = shouts.iterator(); while (i.hasNext()) { Order s = i.next(); if (auction.orderAccepted(s)) { acceptedShouts.add(s); } } } catch (ShoutsNotVisibleException e) { throw new AuctionRuntimeException(e); } } public void finalReport() { } public String toString() { return "(" + getClass() + " market:" + auction + " memorySize:" + memorySize + " bids:" + bids + " asks:" + asks + ")"; } public SortedView getSortedView() { if (view == null) { view = new SortedView(); } return view; } public void disableSortedView() { disableIncreasingQueryAccelerator(); view.destroy(); view = null; } public IncreasingQueryAccelerator getIncreasingQueryAccelerator() { if (accelerator == null) { accelerator = new IncreasingQueryAccelerator(getSortedView()); } return accelerator; } public void disableIncreasingQueryAccelerator() { accelerator.destroy(); accelerator = null; } /** * a class providing sorted lists of shouts. * */ public class SortedView extends Observable implements Observer { private TreeList sortedAsks; private TreeList sortedBids; private TreeList sortedAcceptedAsks; private TreeList sortedAcceptedBids; private TreeList sortedRejectedAsks; private TreeList sortedRejectedBids; private boolean toBeReset; public SortedView() { HistoricalDataReport.this.addObserver(this); toBeReset = true; } public void destroy() { HistoricalDataReport.this.deleteObserver(this); } @SuppressWarnings("unchecked") public void reset() { sortedAsks = new SortedTreeList("sortedAsks", asks); sortedBids = new SortedTreeList("sortedBids", bids); sortedAcceptedAsks = new SortedTreeList("sortedAcceptedAsks"); sortedAcceptedBids = new SortedTreeList("sortedAcceptedBids"); sortedRejectedAsks = new SortedTreeList("sortedRejectedAsks"); sortedRejectedBids = new SortedTreeList("sortedRejectedBids"); Order s; Iterator<Order> i = asks.iterator(); while (i.hasNext()) { s = i.next(); if (acceptedShouts.contains(s)) { sortedAcceptedAsks.add(s); } else { sortedRejectedAsks.add(s); } } i = bids.iterator(); while (i.hasNext()) { s = i.next(); if (acceptedShouts.contains(s)) { sortedAcceptedBids.add(s); } else { sortedRejectedBids.add(s); } } setChanged(); notifyObservers(); } public void update(Observable o, Object arg) { toBeReset = true; setChanged(); notifyObservers(); } public void resetIfNeeded() { if (toBeReset) { reset(); toBeReset = false; } } public TreeList getSortedAsks() { resetIfNeeded(); return sortedAsks; } public TreeList getSortedBids() { resetIfNeeded(); return sortedBids; } public TreeList getSortedAcceptedAsks() { resetIfNeeded(); return sortedAcceptedAsks; } public TreeList getSortedAcceptedBids() { resetIfNeeded(); return sortedAcceptedBids; } public TreeList getSortedRejectedAsks() { resetIfNeeded(); return sortedRejectedAsks; } public TreeList getSortedRejectedBids() { resetIfNeeded(); return sortedRejectedBids; } } /** * a class to speed up queries from GDStrategy regarding the number of shouts * above or below a certain price. It is designed based on the pattern of * increasing prices queried about. * */ @SuppressWarnings("rawtypes") public class IncreasingQueryAccelerator implements Observer { protected ListIterator asksI; protected ListIterator bidsI; protected ListIterator acceptedAsksI; protected ListIterator acceptedBidsI; protected ListIterator rejectedAsksI; protected ListIterator rejectedBidsI; protected int numOfAsksBelow; protected int numOfBidsAbove; protected int numOfAcceptedAsksAbove; protected int numOfAcceptedBidsBelow; protected int numOfRejectedAsksBelow; protected int numOfRejectedBidsAbove; protected double priceForAsksBelow; protected double priceForBidsAbove; protected double priceForAcceptedAsksAbove; protected double priceForAcceptedBidsBelow; protected double priceForRejectedAsksBelow; protected double priceForRejectedBidsAbove; protected SortedView view; private boolean toBeReset; public IncreasingQueryAccelerator(SortedView view) { this.view = view; view.addObserver(this); toBeReset = true; } public IncreasingQueryAccelerator() { this(new SortedView()); } public void destroy() { if (view != null) { view.deleteObserver(this); } } /* * resets all the iterations and counting variables when the underlying * sorted view changes. */ public void update(Observable o, Object arg) { toBeReset = true; } protected void resetIfNeeded() { if (toBeReset) { reset(); toBeReset = false; } } public void reset() { resetForAsksBelow(); resetForBidsAbove(); resetForAcceptedAsksAbove(); resetForAcceptedBidsBelow(); resetForRejectedAsksBelow(); resetForRejectedBidsAbove(); } protected void resetForAsksBelow() { asksI = view.getSortedAsks().listIterator(); numOfAsksBelow = 0; priceForAsksBelow = -1; } protected void resetForBidsAbove() { bidsI = view.getSortedBids().listIterator(); numOfBidsAbove = view.getSortedBids().size(); priceForBidsAbove = -1; } protected void resetForAcceptedAsksAbove() { acceptedAsksI = view.getSortedAcceptedAsks().listIterator(); numOfAcceptedAsksAbove = view.getSortedAcceptedAsks().size(); priceForAcceptedAsksAbove = -1; } protected void resetForAcceptedBidsBelow() { acceptedBidsI = view.getSortedAcceptedBids().listIterator(); numOfAcceptedBidsBelow = 0; priceForAcceptedBidsBelow = -1; } protected void resetForRejectedAsksBelow() { rejectedAsksI = view.getSortedRejectedAsks().listIterator(); numOfRejectedAsksBelow = 0; priceForRejectedAsksBelow = -1; } protected void resetForRejectedBidsAbove() { rejectedBidsI = view.getSortedRejectedBids().listIterator(); numOfRejectedBidsAbove = view.getSortedRejectedBids().size(); priceForRejectedBidsAbove = -1; } public int getNumOfAsksBelow(double price) { resetIfNeeded(); if (priceForAsksBelow > price) resetForAsksBelow(); priceForAsksBelow = price; while (asksI.hasNext()) if (((Order) asksI.next()).getPriceAsDouble() <= price) { numOfAsksBelow++; } else { try { asksI.previous(); } catch (Exception e) { logger.info(e); asksI.previous(); } break; } return numOfAsksBelow; } public int getNumOfBidsAbove(double price) { resetIfNeeded(); if (priceForBidsAbove > price) resetForBidsAbove(); priceForBidsAbove = price; while (bidsI.hasNext()) { if (((Order) bidsI.next()).getPriceAsDouble() < price) { numOfBidsAbove--; } else { try { bidsI.previous(); } catch (Exception e) { logger.info(e); bidsI.previous(); } break; } } return numOfBidsAbove; } public int getNumOfAcceptedAsksAbove(double price) { resetIfNeeded(); if (priceForAcceptedAsksAbove > price) resetForAcceptedAsksAbove(); priceForAcceptedAsksAbove = price; while (acceptedAsksI.hasNext()) if (((Order) acceptedAsksI.next()).getPriceAsDouble() < price) { numOfAcceptedAsksAbove--; } else { try { acceptedAsksI.previous(); } catch (Exception e) { logger.info(e); acceptedAsksI.previous(); } break; } return numOfAcceptedAsksAbove; } public int getNumOfAcceptedBidsBelow(double price) { resetIfNeeded(); if (priceForAcceptedBidsBelow > price) resetForAcceptedBidsBelow(); priceForAcceptedBidsBelow = price; while (acceptedBidsI.hasNext()) if (((Order) acceptedBidsI.next()).getPriceAsDouble() <= price) { numOfAcceptedBidsBelow++; } else { // NOTE: due to a possible bug in TreeList, // NullPointerException may be // thrown. Simply doing it again seems working fine. try { acceptedBidsI.previous(); } catch (Exception e) { logger.info(e); acceptedBidsI.previous(); } break; } return numOfAcceptedBidsBelow; } public int getNumOfRejectedAsksBelow(double price) { resetIfNeeded(); if (priceForRejectedAsksBelow > price) { resetForRejectedAsksBelow(); } priceForRejectedAsksBelow = price; while (rejectedAsksI.hasNext()) if (((Order) rejectedAsksI.next()).getPriceAsDouble() <= price) { numOfRejectedAsksBelow++; } else { try { rejectedAsksI.previous(); } catch (Exception e) { logger.info(e); rejectedAsksI.previous(); } break; } return numOfRejectedAsksBelow; } public int getNumOfRejectedBidsAbove(double price) { resetIfNeeded(); if (priceForRejectedBidsAbove > price) { resetForRejectedBidsAbove(); } priceForRejectedBidsAbove = price; while (rejectedBidsI.hasNext()) if (((Order) rejectedBidsI.next()).getPriceAsDouble() < price) { numOfRejectedBidsAbove--; } else { try { rejectedBidsI.previous(); } catch (Exception e) { logger.info(e); rejectedBidsI.previous(); } break; } return numOfRejectedBidsAbove; } } /** * a tree-based sorted list, which can enable increasing queries about shout * counting. */ static class SortedTreeList extends TreeList { private String name; public SortedTreeList(String name) { this.name = name; } @SuppressWarnings("rawtypes") public SortedTreeList(String name, Collection c) { super(c); this.name = name; } /** * adds <code>o</code> into the list maintaining its sorted nature. * * @param o * @return always returns true */ public boolean add(Object o) { insert(0, size() - 1, o); return true; } /** * inserts <code>o</code> into the segment from <code>b</code> to * <code>e</code> inclusively at both ends. * * @param b * @param e * @param o */ @SuppressWarnings("rawtypes") private void insert(int b, int e, Object o) { if (b > e) add(b, o); else { @SuppressWarnings("unchecked") int c = ((Comparable) o).compareTo(get((b + e) / 2)); if (c == 1) { // o has higher price insert(1 + ((b + e) / 2), e, o); } else if (c == -1) { // o has lower price insert(b, ((b + e) / 2) - 1, o); } else { add((b + e) / 2, o); } } } @SuppressWarnings("rawtypes") public String toString() { String s = "["; ListIterator iterator = listIterator(); while (iterator.hasNext()) { s += ((Order) iterator.next()).getPriceAsDouble() + " "; } s += "] "; return "(" + getClass() + s + "name: " + name + " size: " + size() + ")"; } } }