Java tutorial
/** * This file is part of Pau's Asset Manager Project. * * Pau's Asset Manager Project 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 3 of the License, or * (at your option) any later version. * * Pau's Asset Manager Project 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. * * You should have received a copy of the GNU General Public License * along with Pau's Asset Manager Project. If not, see <http://www.gnu.org/licenses/>. */ package org.pau.assetmanager.viewmodel.stocks; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.Validate; import org.pau.assetmanager.entities.Annotation; import org.pau.assetmanager.entities.Book; import org.pau.assetmanager.entities.StockExpensesAnnotation; import org.pau.assetmanager.entities.StockIncomeAnnotation; /** * @author Pau Carr Cardona */ public class StocksUtils { public static List<StockIncomeDescription> getStockIncomeDescriptionList(List<Annotation> annotations) { StocksHistory stocksHistoryMap = getStocksHistory(annotations); List<StockIncomeDescription> stockIncomeDescriptions = new LinkedList<StockIncomeDescription>(); for (Set<StockIncomeDescription> currentDescriptions : stocksHistoryMap.getStocksHistoryMap().values()) { stockIncomeDescriptions.addAll(currentDescriptions); } return stockIncomeDescriptions; } public static StocksHistory getStocksHistory(List<Annotation> annotations) { Map<String, StocksIncomeError> stocksErrorsHistoryMap = new HashMap<String, StocksIncomeError>(); Map<String, List<Annotation>> conceptToListOfAnnotationsForStocksBooks = getConceptToListOfAnnotationsForStocksBooks( annotations); Map<String, Set<StockIncomeDescription>> conceptToStockHistoryMap = new HashMap<String, Set<StockIncomeDescription>>(); for (String concept : conceptToListOfAnnotationsForStocksBooks.keySet()) { Set<StockIncomeDescription> stockHistory = new HashSet<StockIncomeDescription>(); List<StocksBuySubtransaction> stocksBuySubtransactionList = new LinkedList<StocksBuySubtransaction>(); List<Annotation> currentListOfAnnotations = conceptToListOfAnnotationsForStocksBooks.get(concept); Collections.sort(currentListOfAnnotations, new Comparator<Annotation>() { @Override public int compare(Annotation o1, Annotation o2) { return o1.getDate().compareTo(o2.getDate()); } }); boolean errorInConcept = false; for (Iterator<Annotation> annotationIterator = currentListOfAnnotations.iterator(); annotationIterator .hasNext() && !errorInConcept;) { Annotation annotation = annotationIterator.next(); // if there a purchase stock transaction then we create its subtransaction // For a purchase stock the subtransaction is the same as the transaction initially // but it can then be splited depending on the sold stocks. if (annotation instanceof StockExpensesAnnotation) { StockExpensesAnnotation stockExpensesAnnotation = (StockExpensesAnnotation) annotation; stocksBuySubtransactionList.add(new StocksBuySubtransaction(stockExpensesAnnotation)); } else if (annotation instanceof StockIncomeAnnotation) { // if there a sell stock transaction then we compute the previous purchase // stock transactions related in order to fulfill the amount of // stocks sold StockIncomeAnnotation stockIncomeAnnotation = (StockIncomeAnnotation) annotation; // iterate through all the previously buy subtransactions and divide them // or take their totality of stocks in order to fill the amount of sold stocks Map<StocksBuySubtransaction, Long> stocksBuySubtransactionToStocksNeededMap = splitBuyStockTransaction( stockIncomeAnnotation, stocksBuySubtransactionList); Long numberOfStoksUsed = sum(stocksBuySubtransactionToStocksNeededMap.values()); Long additionalNecessaryStocks = stockIncomeAnnotation.getNumberOfStocks() - numberOfStoksUsed; // if there are no longer stocks necessary to buy in order to // sell the stock then the transaction is consistent and we can create it if (additionalNecessaryStocks == 0L) { Set<StocksBuySubtransaction> stocksBuySubtransactions = getStocksBuySubtransactions( stocksBuySubtransactionToStocksNeededMap, stocksBuySubtransactionList); stockHistory .add(new StockIncomeDescription(stockIncomeAnnotation, stocksBuySubtransactions)); } else { // if there are not enough stocks bought to make the sell operation // there is an inconsistency in the sequences of transactions errorInConcept = true; StocksIncomeError stocksIncomeError = new StocksIncomeError(stockIncomeAnnotation, stocksBuySubtransactionToStocksNeededMap, additionalNecessaryStocks); stocksErrorsHistoryMap.put(concept, stocksIncomeError); } } } if (!errorInConcept) { conceptToStockHistoryMap.put(concept, stockHistory); } } return new StocksHistory(conceptToStockHistoryMap, stocksErrorsHistoryMap); } private static Long sum(Collection<Long> values) { Long sum = 0L; for (Long currentValue : values) { sum += currentValue; } return sum; } private static Map<StocksBuySubtransaction, Long> splitBuyStockTransaction( StockIncomeAnnotation stockIncomeAnnotation, List<StocksBuySubtransaction> stocksBuySubtransactionList) { Long stocksNeeded = stockIncomeAnnotation.getNumberOfStocks(); Map<StocksBuySubtransaction, Long> stocksBuySubtransactionToStocksNeededMap = new HashMap<StocksBuySubtransaction, Long>(); for (Iterator<StocksBuySubtransaction> stocksBuySubtransactionIterator = stocksBuySubtransactionList .iterator(); stocksBuySubtransactionIterator.hasNext() && stocksNeeded > 0;) { StocksBuySubtransaction stocksBuySubtransaction = stocksBuySubtransactionIterator.next(); if (stocksBuySubtransaction.getNumberOfStocks() >= stocksNeeded) { stocksBuySubtransactionToStocksNeededMap.put(stocksBuySubtransaction, stocksNeeded); stocksNeeded = 0L; } else { stocksBuySubtransactionToStocksNeededMap.put(stocksBuySubtransaction, stocksBuySubtransaction.getNumberOfStocks()); stocksNeeded -= stocksBuySubtransaction.getNumberOfStocks(); } } return stocksBuySubtransactionToStocksNeededMap; } private static Set<StocksBuySubtransaction> getStocksBuySubtransactions( Map<StocksBuySubtransaction, Long> stocksBuySubtransactionToStocksNeededMap, List<StocksBuySubtransaction> stocksBuySubtransactionList) { Set<StocksBuySubtransaction> stocksBuySubtransactions = new HashSet<StocksBuySubtransaction>(); for (StocksBuySubtransaction currentBuyTransaction : stocksBuySubtransactionToStocksNeededMap.keySet()) { Long stocksTaken = stocksBuySubtransactionToStocksNeededMap.get(currentBuyTransaction); stocksBuySubtransactions.add( new StocksBuySubtransaction(currentBuyTransaction.getStockExpensesAnnotation(), stocksTaken)); stocksBuySubtransactionList.remove(currentBuyTransaction); stocksBuySubtransactionList .add(new StocksBuySubtransaction(currentBuyTransaction.getStockExpensesAnnotation(), currentBuyTransaction.getNumberOfStocks() - stocksTaken)); } return stocksBuySubtransactions; } public static Map<String, StockConceptPerformance> getStocksConceptToProfitLimitedToNumberOfSoldSlocks( List<Annotation> annotations) { Map<String, StockConceptPerformance> conceptToStockConceptPerformanceMap = new HashMap<String, StockConceptPerformance>(); Map<String, Set<StockIncomeDescription>> stocksHistoryMap = getStocksHistory(annotations) .getStocksHistoryMap(); for (String concept : stocksHistoryMap.keySet()) { Set<StockIncomeDescription> stockIncomeDescriptions = stocksHistoryMap.get(concept); if (stockIncomeDescriptions.size() > 0) { StockConceptPerformance stockConceptPerformance = getStockConceptPerformance( stockIncomeDescriptions); conceptToStockConceptPerformanceMap.put(concept, stockConceptPerformance); } } return conceptToStockConceptPerformanceMap; } public static StockConceptPerformance getStockConceptPerformance( Collection<StockIncomeDescription> stockIncomeDescriptionList) { Double profits = 0.0; Date lastSoldDate = null; Book book = null; Long numberOfStocks = 0L; String concept = null; Validate.notEmpty(stockIncomeDescriptionList, "The StockIncomeDescriptionList cannot be empty"); Set<StockExpensesAnnotation> stocksBoughtSet = new HashSet<StockExpensesAnnotation>(); for (StockIncomeDescription stockIncomeDescription : stockIncomeDescriptionList) { StockIncomeAnnotation stockIncomeAnnotation = stockIncomeDescription.getStockIncomeAnnotation(); numberOfStocks += stockIncomeAnnotation.getNumberOfStocks(); profits += stockIncomeAnnotation.getAmount(); for (StocksBuySubtransaction stocksBuySubtransaction : stockIncomeDescription .getStocksBuySubtransactionSet()) { profits -= (stocksBuySubtransaction.getNumberOfStocks() * stocksBuySubtransaction.getPricePerStock()); stocksBoughtSet.add(stocksBuySubtransaction.getStockExpensesAnnotation()); } if (lastSoldDate == null) { lastSoldDate = stockIncomeAnnotation.getDate(); } else if (lastSoldDate.before(stockIncomeAnnotation.getDate())) { lastSoldDate = stockIncomeAnnotation.getDate(); } book = stockIncomeAnnotation.getBook(); concept = stockIncomeAnnotation.getConcept(); } Long numberOfStocksBought = 0L; for (StockExpensesAnnotation stockExpensesAnnotation : stocksBoughtSet) { numberOfStocksBought += stockExpensesAnnotation.getNumberOfStocks(); } StockConceptPerformance stockConceptPerformance = new StockConceptPerformance(profits, (numberOfStocks.doubleValue() / numberOfStocksBought.doubleValue()) * 100.0, concept, numberOfStocks, lastSoldDate, book); return stockConceptPerformance; } private static Map<String, List<Annotation>> getConceptToListOfAnnotationsForStocksBooks( List<Annotation> annotations) { Map<String, List<Annotation>> conceptToAnnotationMap = new HashMap<String, List<Annotation>>(); for (Annotation annotation : annotations) { List<Annotation> annotationsForMap = conceptToAnnotationMap.get(annotation.getConcept()); if (annotationsForMap == null) { annotationsForMap = new LinkedList<Annotation>(); Boolean hasStocks = false; if (annotation instanceof StockIncomeAnnotation) { StockIncomeAnnotation stockIncomeAnnotation = (StockIncomeAnnotation) annotation; hasStocks = stockIncomeAnnotation.getNumberOfStocks() > 0; } else if (annotation instanceof StockExpensesAnnotation) { StockExpensesAnnotation stockExpensesAnnotation = (StockExpensesAnnotation) annotation; hasStocks = stockExpensesAnnotation.getNumberOfStocks() > 0; } if (hasStocks) { conceptToAnnotationMap.put(annotation.getConcept(), annotationsForMap); } } annotationsForMap.add(annotation); } return conceptToAnnotationMap; } }