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.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.pau.assetmanager.business.DaoFunction; import org.pau.assetmanager.business.HistoricalStockValuesBusiness; import org.pau.assetmanager.entities.HistoricalStockValue; import org.pau.assetmanager.utils.AssetManagerRuntimeException; import com.google.common.base.Optional; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.collect.Table; public class HistoricalStocksValuesDownloader { private static final String DATE = "Date"; private static final String OPEN = "Open"; private static final String HIGH = "High"; private static final String LOW = "Low"; private static final String CLOSE = "Close"; private static final String VOLUME = "Volume"; private static final String ADJ_CLOSE = "Adj Close"; private static final List<String> HEADERS = Lists.newArrayList(DATE, OPEN, HIGH, LOW, CLOSE, VOLUME, ADJ_CLOSE); public static final String EURO = "EUR=X"; private static final String POUND = "GBP=X"; public static final Map<String, String> LABEL_TO_CURRENCY_MAP = MyMaps.<String, String>newMap( Sets.<Entry<String, String>>newHashSet(MyEntries.<String, String>newEntry("L", POUND), MyEntries.<String, String>newEntry("*", EURO))); // MyEntries.<String, String>newEntry("L", "JPY=X"), private static class MyMaps { private static <K, V> Map<K, V> newMap(Collection<Entry<K, V>> entries) { Map<K, V> newHashMap = Maps.newHashMap(); for (Entry<K, V> currentEntry : entries) { newHashMap.put(currentEntry.getKey(), currentEntry.getValue()); } return newHashMap; } } private synchronized static void updateCurrencyHistoricalValues() { // get the minimun date for stocks Calendar minumDateCalendar = GregorianCalendar.getInstance(); String query = "select min(date) as min_date from Annotation " + "left join StockExpensesAnnotation sea using (id) " + "left join StockIncomeAnnotation sia using(id) " + "where sia.id is not null or sea.id is not null"; Optional<Object> stocksTimeInformation = DaoFunction.<Object>nativeQuerySimpleUniqueFunction().apply(query); if (stocksTimeInformation.isPresent()) { Date minimumDate = (Date) stocksTimeInformation.get(); minumDateCalendar.setTime(minimumDate); } Integer currentStartYear = minumDateCalendar.get(Calendar.YEAR); Calendar calendar = GregorianCalendar.getInstance(); Date currentDate = new Date(); calendar.setTime(currentDate); Integer currentEndYear = calendar.get(Calendar.YEAR); for (String symbol : LABEL_TO_CURRENCY_MAP.values()) { for (int currentYear = currentStartYear; currentYear <= currentEndYear; currentYear++) { HistoricalStockValuesBusiness.saveOrUpdate(symbol, currentYear); } } } private static class MyEntries { private static <K, V> SimpleEntry<K, V> newEntry(K key, V value) { return new SimpleEntry<K, V>(key, value); } } public synchronized static void updateStocksHistoricalValuesAsync() { Runnable runnableStocksUpdater = new Runnable() { @Override public void run() { HistoricalStocksValuesDownloader.updateStocksHistoricalValues(); } }; Thread threadStocksUpdater = new Thread(runnableStocksUpdater); threadStocksUpdater.start(); } public synchronized static void updateStocksHistoricalValues() { Calendar calendar = GregorianCalendar.getInstance(); Date endDate = new Date(); // get all values and time ranges String query = "select min(date) as min_date, concept from Annotation " + "left join StockExpensesAnnotation sea using (id) " + "left join StockIncomeAnnotation sia using(id) " + "where sia.id is not null or sea.id is not null group by concept"; // remove all the values that were collected the same month as // the month they are related to // HistoricalStockValuesBusiness // .removeHistoricalValuesCollectedInTheirMonth(); // insert the lacking values List<Object[]> stocksTimeInformation = DaoFunction.<Object[]>nativeQuerySimpleListFunction().apply(query); for (Object[] currentStockTimeInformation : stocksTimeInformation) { calendar.setTime(endDate); Integer currentEndYear = calendar.get(Calendar.YEAR); Date currentStartYearDate = (Date) currentStockTimeInformation[0]; calendar.setTime(currentStartYearDate); Integer currentStartYear = calendar.get(Calendar.YEAR); String symbol = (String) currentStockTimeInformation[1]; // get the historical values we have in the database for that symbol for (int currentYear = currentStartYear; currentYear <= currentEndYear; currentYear++) { HistoricalStockValuesBusiness.saveOrUpdate(symbol, currentYear); } } updateCurrencyHistoricalValues(); } public static List<HistoricalStockValue> getHistoricalStockValuesForYear(String symbol, Integer year) { try { return getHistoricalStockValues(symbol, year, year); } catch (Throwable throwable) { // TODO: log this throwable.printStackTrace(); } return new LinkedList<HistoricalStockValue>(); } private static List<HistoricalStockValue> getHistoricalStockValues(String symbol, Integer startYear, Integer finalYear) { List<HistoricalStockValue> historicalStockValues = new LinkedList<HistoricalStockValue>(); Table<Integer, Integer, Double> yearToMonthToValueTable = getYearToMonthToValueTable(symbol, startYear, finalYear); for (Integer currentYear : yearToMonthToValueTable.rowKeySet()) { Map<Integer, Double> monthToValue = yearToMonthToValueTable.row(currentYear); for (Integer currentMonth : monthToValue.keySet()) { Double value = monthToValue.get(currentMonth); HistoricalStockValue historicalStockValue = new HistoricalStockValue(); historicalStockValue.setCollectionDate(new Date()); historicalStockValue.setMonth(currentMonth); historicalStockValue.setYear(currentYear); historicalStockValue.setSymbol(symbol); historicalStockValue.setNumericalValue(Math.round(value * 100.0)); historicalStockValues.add(historicalStockValue); } } return historicalStockValues; } private static Table<Integer, Integer, Double> getYearToMonthToValueTable(String symbol, Integer startYear, Integer finalYear) { try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); Calendar calendar = GregorianCalendar.getInstance(); Table<Integer, Integer, Double> yearMonthValueTable = HashBasedTable.<Integer, Integer, Double>create(); Table<Integer, Integer, Integer> yearMonthTimesTable = HashBasedTable .<Integer, Integer, Integer>create(); List<Map<String, String>> csvContents = getContents(symbol, startYear, finalYear); for (Map<String, String> fieldToValueMap : csvContents) { Double open = Double.parseDouble(fieldToValueMap.get(OPEN)); Double close = Double.parseDouble(fieldToValueMap.get(OPEN)); Double averagePrice = (open + close) / 2.0; Date date = simpleDateFormat.parse(fieldToValueMap.get(DATE)); calendar.setTime(date); Integer year = calendar.get(Calendar.YEAR); Integer month = calendar.get(Calendar.MONTH); Integer day = calendar.get(Calendar.DAY_OF_MONTH); Double currentPrice = yearMonthValueTable.get(year, month); if (currentPrice == null) { currentPrice = averagePrice; } else { currentPrice += averagePrice; } yearMonthValueTable.put(year, month, currentPrice); Integer times = yearMonthTimesTable.get(year, month); if (times == null) { times = 1; } else { times++; } yearMonthTimesTable.put(year, month, times); } for (Integer year : yearMonthTimesTable.rowKeySet()) { for (Integer month : yearMonthTimesTable.row(year).keySet()) { yearMonthValueTable.put(year, month, yearMonthValueTable.get(year, month) / yearMonthTimesTable.get(year, month).doubleValue()); } } return yearMonthValueTable; } catch (ParseException e) { throw new AssetManagerRuntimeException("Error parsing date from CSV file for '" + symbol + "' between '" + startYear + "' and '" + finalYear + "'.", e); } } private static List<Map<String, String>> getContents(String symbol, Integer startYear, Integer finalYear) { String url = "http://ichart.finance.yahoo.com/table.csv?s=" + symbol + "&a=0&b=1&c=" + startYear + "&d=11&e=31&f=" + finalYear; try { URL urlObject = new URL(url); HttpURLConnection httpURLConnection = (HttpURLConnection) urlObject.openConnection(); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405"); int responseCode = httpURLConnection.getResponseCode(); if (responseCode != 200) { throw new AssetManagerRuntimeException( "Error code '" + responseCode + "' when requesting to '" + url + "'"); } try (BufferedReader bufferReader = new BufferedReader( new InputStreamReader(httpURLConnection.getInputStream()));) { String currentLine = null; List<Map<String, String>> csvContents = new LinkedList<Map<String, String>>(); if ((currentLine = bufferReader.readLine()) != null) { String[] headers = currentLine.split(","); List<String> actualHeadersInCVS = new ArrayList<String>(Arrays.asList(headers)); if (!actualHeadersInCVS.equals(HEADERS)) { throw new RuntimeException(String.format( "CVS header %s is not consistent with the expected list of headers %s", currentLine, HEADERS.toString())); } } while ((currentLine = bufferReader.readLine()) != null) { Map<String, String> fieldToValueMap = new HashMap<String, String>(); String[] valuesInLine = currentLine.split(","); for (int valueIndex = 0; valueIndex < valuesInLine.length; valueIndex++) { fieldToValueMap.put(HEADERS.get(valueIndex), valuesInLine[valueIndex]); } csvContents.add(fieldToValueMap); } return csvContents; } } catch (IOException ioException) { throw new AssetManagerRuntimeException("Error downloading stocks for '" + symbol + "' between '" + startYear + "' and '" + finalYear + "'.", ioException); } } }