diff --git a/pom.xml b/pom.xml index a08510a..384b334 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ com.akathist.maven.plugins.launch4j launch4j-maven-plugin + 1.7.25 l4j-clui @@ -91,6 +92,7 @@ true anything + src/main/resources/icon.ico 1.8.0 diff --git a/src/main/java/stocktracker/CurrencyRateFetcher.java b/src/main/java/stocktracker/CurrencyRateFetcher.java index ae5e538..e5bd8ce 100644 --- a/src/main/java/stocktracker/CurrencyRateFetcher.java +++ b/src/main/java/stocktracker/CurrencyRateFetcher.java @@ -17,16 +17,21 @@ import java.util.Arrays; import java.util.List; -public class CurrencyRateFetcher { +class CurrencyRateFetcher { - private XMLParser xmlParser; - private String currencyCode; + private final XMLParser xmlParser; + private final String currencyCode; + + private CurrencyRateFetcher(String currencyCode) { + this.currencyCode = currencyCode; + this.xmlParser = new XMLParser(); + } public static void main(String[] args) { writeCurrencyInfo("USD", LocalDate.of(2018, 9, 24)); } - public static void writeCurrencyInfo(String currencyCode, LocalDate firstDate) { + static void writeCurrencyInfo(String currencyCode, LocalDate firstDate) { CurrencyRateFetcher fetcher = new CurrencyRateFetcher(currencyCode); String url_str = "https://sdw-wsrest.ecb.europa.eu/service/data/EXR/D." + currencyCode + @@ -41,11 +46,6 @@ public static void writeCurrencyInfo(String currencyCode, LocalDate firstDate) { } } - private CurrencyRateFetcher(String currencyCode) { - this.currencyCode = currencyCode; - this.xmlParser = new XMLParser(); - } - private class XMLParser { private void downloadXMLFile(URL url) { @@ -67,11 +67,9 @@ private void downloadXMLFile(URL url) { } } - /** * If anything ever breaks, use this: * https://www.mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/ - * @param src */ public List parse(String src) { try { @@ -129,7 +127,7 @@ private String readStream(InputStream in) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { String nextLine = ""; while ((nextLine = reader.readLine()) != null) { - sb.append(nextLine + newLine); + sb.append(nextLine).append(newLine); } } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/stocktracker/DataAggregator.java b/src/main/java/stocktracker/DataAggregator.java index 3a0c0ac..75b6015 100644 --- a/src/main/java/stocktracker/DataAggregator.java +++ b/src/main/java/stocktracker/DataAggregator.java @@ -6,8 +6,7 @@ import java.util.Collections; import java.util.List; -public class DataAggregator { - // TODO: Add workingDir field +class DataAggregator { // TODO: Padding money and stock decimal places with zeroes public static void main(String[] args) { test(); @@ -23,7 +22,7 @@ private static void test() { calculateMoney(testList, testAmounts); } - public static void calculateMoney(List ticker_currency, List stockAmounts) { + static void calculateMoney(List ticker_currency, List stockAmounts) { aggregate(ticker_currency); List finalData = FileManager.readLines(StockTracker.PATH + "aggregated_temp.txt"); List dateMoney = new ArrayList<>(); @@ -44,14 +43,40 @@ public static void calculateMoney(List ticker_currency, List sto FileManager.writeList(StockTracker.PATH + "money.txt", dateMoney); } + private static void aggregate(List ticker_currency) { + String workingDir = StockTracker.PATH; + for (String combination: ticker_currency) { + aggregate(combination); + } + List data; + try { + String dest = workingDir + "aggregated_temp.txt"; + data = Files.readAllLines(Paths.get(workingDir + "/" + ticker_currency.get(0) + "_temp.txt")); + for (int i = 0; i < data.size(); i++) { + String line = data.get(i); + data.set(i, line.substring(0,11) + "! " + line.substring(11)); + } + for (int i = 1; i < ticker_currency.size(); i++) { + List fileLines = Files.readAllLines(Paths.get(workingDir + "\\" + ticker_currency.get(i) + "_temp.txt")); + for (int j = 0; j < fileLines.size(); j++) { + String stockPrice = fileLines.get(j).split(" ")[1]; + String currencyRate = fileLines.get(j).split(" ")[2]; + data.set(j, data.get(j) + " ! " + stockPrice + " " + currencyRate); + } + } + FileManager.writeList(dest, data); + }catch (Exception e) { + e.printStackTrace(); + } + } + /** * There are more dates in the currency file than in the ticker one because stock markets * are closed on nation holidays. Therefore we use the the last day's stock market close * on dates with no values. If the first day of the whole file happens to be a market * holiday then we use the next available day's close value instead. - * @param ticker_currency */ - public static void aggregate(String ticker_currency) { + private static void aggregate(String ticker_currency) { String workingDir = StockTracker.PATH; String ticker = ticker_currency.split("_")[0]; String currency = ticker_currency.split("_")[1]; @@ -124,33 +149,4 @@ private static void fillMissingDates(List datesList, List ratesL ratesList.add(index, ratesList.get(index)); } } - - public static void aggregate(List ticker_currency) { - String workingDir = StockTracker.PATH; - for (String combination: ticker_currency) { - aggregate(combination); - } - List data; - try { - String dest = workingDir + "aggregated_temp.txt"; - data = Files.readAllLines(Paths.get(workingDir + "/" + ticker_currency.get(0) + "_temp.txt")); - for (int i = 0; i < data.size(); i++) { - String line = data.get(i); - data.set(i, line.substring(0,11) + "! " + line.substring(11)); - } - for (int i = 1; i < ticker_currency.size(); i++) { - List fileLines = Files.readAllLines(Paths.get(workingDir + "\\" + ticker_currency.get(i) + "_temp.txt")); - for (int j = 0; j < fileLines.size(); j++) { - String stockPrice = fileLines.get(j).split(" ")[1]; - String currencyRate = fileLines.get(j).split(" ")[2]; - data.set(j, data.get(j) + " ! " + stockPrice + " " + currencyRate); - } - } - FileManager.writeList(dest, data); - }catch (Exception e) { - e.printStackTrace(); - } - - } - } diff --git a/src/main/java/stocktracker/FileManager.java b/src/main/java/stocktracker/FileManager.java index 3d28a02..9ab58ee 100644 --- a/src/main/java/stocktracker/FileManager.java +++ b/src/main/java/stocktracker/FileManager.java @@ -26,9 +26,6 @@ private static void test() System.out.println("pom.xml exists: " + FileManager.fileExists("pom.xml")); System.out.println("b.txt exists: " + FileManager.fileExists("b.txt")); - System.out.println("saved_data is empty: " + FileManager.emptyDirectory("src\\main\\resources\\saved_data")); - System.out.println("Empty director /main/java: " + FileManager.emptyDirectory("src\\main\\java")); - System.out.println("Empty director /test/java: " + FileManager.emptyDirectory("src\\test\\java")); System.out.println(readLines(".gitignore")); } @@ -72,7 +69,7 @@ public static List readLines(String dest) { ArrayList lines = new ArrayList<>(); try { Files.lines(Paths.get(dest)) - .forEach(line -> lines.add(line)); + .forEach(lines::add); } catch (Exception e) { e.printStackTrace(); } @@ -93,9 +90,4 @@ public static boolean fileExists(String dest) { File file = new File(dest); return file.isFile(); } - - public static boolean emptyDirectory(String dest) { - File directory = new File(dest); - return directory.list().length == 0; - } } diff --git a/src/main/java/stocktracker/StockInfoFetcher.java b/src/main/java/stocktracker/StockInfoFetcher.java index 3dda31d..ab22369 100644 --- a/src/main/java/stocktracker/StockInfoFetcher.java +++ b/src/main/java/stocktracker/StockInfoFetcher.java @@ -7,11 +7,10 @@ import org.patriques.output.timeseries.DailyAdjusted; import org.patriques.output.timeseries.data.StockData; -import java.io.FileWriter; import java.time.LocalDate; import java.util.*; -public class StockInfoFetcher { +class StockInfoFetcher { private static final String API_KEY = "NZ04YC2MOTE5AN4P"; private static final int TIMEOUT = 3000; @@ -27,27 +26,14 @@ private static void test() { } - public static void getData(String ticker, LocalDate startDate) { + static void getData(String ticker, LocalDate startDate) { Map data = fetchData(ticker, startDate); writeData(data, ticker); System.out.println("Fetcing " + ticker + " done"); } - public static LocalDate getMostRecentDay() { - AlphaVantageConnector apiConnector = new AlphaVantageConnector(API_KEY, TIMEOUT); - TimeSeries stockTimeSeries = new TimeSeries(apiConnector); - try { - List temp = stockTimeSeries.daily("IVV").getStockData(); - LocalDate lastDate = temp.get(0).getDateTime().toLocalDate(); - return lastDate; - } - catch (AlphaVantageException e) { - return LocalDate.now(); - } - } - - public static Map fetchData(String ticker, LocalDate startDate) + private static Map fetchData(String ticker, LocalDate startDate) { AlphaVantageConnector apiConnector = new AlphaVantageConnector(API_KEY, TIMEOUT); TimeSeries stockTimeSeries = new TimeSeries(apiConnector); @@ -83,7 +69,8 @@ public static Map fetchData(String ticker, LocalDate startDate) return null; } } - public static void writeData(Map data, String ticker) { + + private static void writeData(Map data, String ticker) { String filename = StockTracker.PATH + ticker + "_temp.txt"; System.out.println(filename); Map map = new TreeMap<>(data); @@ -104,5 +91,18 @@ public static void writeData(Map data, String ticker) { e.printStackTrace(); } } + + static LocalDate getMostRecentDay() { + AlphaVantageConnector apiConnector = new AlphaVantageConnector(API_KEY, TIMEOUT); + TimeSeries stockTimeSeries = new TimeSeries(apiConnector); + try { + List temp = stockTimeSeries.daily("IVV").getStockData(); + // last date is first in list + return temp.get(0).getDateTime().toLocalDate(); + } + catch (AlphaVantageException e) { + return LocalDate.now(); + } + } } diff --git a/src/main/java/stocktracker/StockTracker.java b/src/main/java/stocktracker/StockTracker.java index 0ac6dff..67ebf7d 100644 --- a/src/main/java/stocktracker/StockTracker.java +++ b/src/main/java/stocktracker/StockTracker.java @@ -9,8 +9,7 @@ import java.util.List; //TODO: Add javadoc comments -//TODO: Keep old save configurations and data in Excel table to use API less and boost speed -//TODO: Add .exe somehow +//TODO: migrate files to .csv format? public class StockTracker { public static final String VERSION = "0.X"; @@ -26,9 +25,7 @@ public class StockTracker { } } - public static void main(String[] args) { - runNewTest(); //runExistingTest(); } @@ -53,18 +50,41 @@ private static void runNewTest() //deleteTempFiles(); System.out.println("Files aggregated, money calculated"); System.out.println("Done"); - } private static void runExistingTest() { updateSave(); } + /** + * Writes data of a specified stock and currency to text files. + * @param ticker Ticker of the stock to be recorded. + * @param currencyCode Currcency code of currency to be recorded. + * @param startDate First date the data is written from. + */ public static void writeData(String ticker, String currencyCode, LocalDate startDate) { StockInfoFetcher.getData(ticker, startDate); CurrencyRateFetcher.writeCurrencyInfo(currencyCode, startDate); } + /** + * Calculates the total value of a stock based on the amount owned. + * @param ticker_currency Ticker and currency of stock. + * @param stockAmounts Amount of stock owned. + */ + public static void calculateMoney(List ticker_currency, List stockAmounts) + { + DataAggregator.calculateMoney(ticker_currency, stockAmounts); + } + + /** + * Creates three text files. The first one saves the stock tickers and currency codes + * and stock amounts specified. The others act as a cache and keep fetched data + * as to not call the APIs too much and improve performance. + * @param nameList List containing stocks' tickers and currency codes in the form + * "TICKER_CURRENCYCODE". + * @param amountList List containing amounts of stocks specified in nameList owned. + */ public static void createSave(ArrayList nameList, ArrayList amountList) { boolean append = false; for (int i = 0; i < nameList.size(); i++) { @@ -82,6 +102,9 @@ public static void createSave(ArrayList nameList, ArrayList amou } } + /** + * Reads the save files and if data in them is outdated, updates them. + */ public static void updateSave() { List saveConfig = FileManager.readLines(PATH + "save_config.txt"); List saveData = FileManager.readLines(PATH + "save_data.txt"); @@ -115,11 +138,6 @@ public static void updateSave() { } } - public static void calculateMoney(List ticker_currency, List stockAmounts) - { - DataAggregator.calculateMoney(ticker_currency, stockAmounts); - } - public static void deleteTempFiles() { FileManager.deleteFiles("src\\main\\resources", "_temp"); } diff --git a/src/main/java/stocktracker/StockViewerGUI.java b/src/main/java/stocktracker/StockViewerGUI.java index 2f62b92..1227427 100644 --- a/src/main/java/stocktracker/StockViewerGUI.java +++ b/src/main/java/stocktracker/StockViewerGUI.java @@ -12,12 +12,10 @@ import javafx.stage.Stage; import jfxtras.styles.jmetro8.JMetro; -import java.io.File; import java.text.NumberFormat; import java.time.LocalDate; import java.util.ArrayList; -//TODO: Add icons //TODO: Add progress bars? public class StockViewerGUI extends Application { @@ -38,62 +36,11 @@ public void start(Stage primaryStage) { primaryStage.show(); } - private void makeGraphScene(boolean newData) { - VBox root = new VBox(); - setupMenuBar(root); - - CategoryAxis xAxis = new CategoryAxis(); - NumberAxis yAxis = new NumberAxis(); - xAxis.setLabel("Date"); - xAxis.setTickMarkVisible(false); - yAxis.setLabel("Ca$h (€)"); - LineChart lineChart = new LineChart<>(xAxis,yAxis); - lineChart.getStylesheets().add("chart-style.css"); - - lineChart.setTitle("€€€"); - XYChart.Series series = new XYChart.Series<>(); - //series.setName("My portfolio"); - double money = 0; - String moneyFile; - if (newData) { - moneyFile = StockTracker.PATH + "money.txt"; - } - else { - moneyFile = StockTracker.PATH + "save_money.txt"; - } - - for (String line: FileManager.readLines(moneyFile)) { - String[] splitLine = line.split(" "); - money = Double.parseDouble(splitLine[1]); - String date = splitLine[0]; - XYChart.Data dataPoint = new XYChart.Data<>(date, money); - dataPoint.setNode(new HoveredThresholdNode(date, money)); - series.getData().add(dataPoint); - } - lineChart.setCreateSymbols(false); - lineChart.setLegendVisible(false); - lineChart.getData().add(series); - - HBox hBox = new HBox(); - Label label = new Label("Total ca$h: "); - TextField field = new TextField("" + money); - field.setDisable(true); - field.setStyle("-fx-background-color: white;" + - "-fx-text-fill: " + "darkgreen"); - field.setAlignment(Pos.CENTER_RIGHT); - hBox.getChildren().addAll(label, field); - hBox.setAlignment(Pos.CENTER); - - Region region = new Region(); - root.getChildren().addAll(lineChart, hBox, region, statusLabel); - VBox.setVgrow(region, Priority.ALWAYS); - - - createScene(root); - } - - private void setStatusLabel(String newProgress) { - statusLabel.setText(newProgress); + private void createScene(Region root) { + Scene scene = new Scene(root, width, height); + new JMetro(JMetro.Style.LIGHT).applyTheme(scene); + root.requestFocus(); + primaryStage.setScene(scene); } private void setupStartScene() @@ -132,15 +79,8 @@ private void setupStartScene() centerNode.getChildren().addAll(newButton, existingButton); centerNode.setAlignment(Pos.CENTER); mainPane.setCenter(centerNode); - String path = StockTracker.PATH; - try { - String asd = new File(path).getParentFile().toString(); - File x = new File(asd + "/axx.txt"); - boolean noSaves = x.createNewFile(); - } catch (Exception e) {} - - setStatusLabel("Ready..." + path); + setStatusLabel("Ready..."); setupMenuBar(root); @@ -151,6 +91,44 @@ private void setupStartScene() createScene(root); } + private void setupMenuBar(Pane parent) { + MenuBar menuBar = new MenuBar(); + parent.getChildren().add(menuBar); + + Menu fileMenu = new Menu("File"); + MenuItem newItem = new MenuItem("New"); + newItem.setOnAction(event -> setupStartScene()); + MenuItem quitItem = new MenuItem("Quit"); + quitItem.setOnAction(event -> System.exit(0)); + fileMenu.getItems().addAll(newItem, quitItem); + + Menu helpMenu = new Menu("Help"); + MenuItem aboutItem = new MenuItem("About"); + aboutItem.setOnAction(event -> { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + // https://stackoverflow.com/questions/27976345/how-do-you-set-the-icon-of-a-dialog-control-java-fx-java-8/27983567 + Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); + stage.getIcons().add(new Image("icon.png")); + + alert.setTitle("About StockTracker"); + alert.setHeaderText(null); // Alerts have an optional header. + alert.setContentText("Author: Sten Laane\nVersion: " + StockTracker.VERSION); + alert.showAndWait();}); + + MenuItem howToUseItem = new MenuItem("Getting started"); + howToUseItem.setOnAction(event -> { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); + stage.getIcons().add(new Image("icon.png")); + alert.setHeaderText(null); // Alerts have an optional header. + alert.setTitle("How to use StockTracker"); + alert.setContentText("***Insert tutorial here***"); + alert.showAndWait();}); + helpMenu.getItems().addAll(aboutItem, howToUseItem); + + menuBar.getMenus().addAll(fileMenu, helpMenu); + } + private void setupNewTrackerScene() { VBox root = new VBox(); setupMenuBar(root); @@ -195,52 +173,6 @@ public void updateItem(LocalDate date, boolean empty) { createScene(root); } - private void createScene(Region root) { - Scene scene = new Scene(root, width, height); - new JMetro(JMetro.Style.LIGHT).applyTheme(scene); - root.requestFocus(); - primaryStage.setScene(scene); - } - - private void setupMenuBar(Pane parent) { - MenuBar menuBar = new MenuBar(); - parent.getChildren().add(menuBar); - - Menu fileMenu = new Menu("File"); - MenuItem newItem = new MenuItem("New"); - newItem.setOnAction(event -> setupStartScene()); - MenuItem quitItem = new MenuItem("Quit"); - quitItem.setOnAction(event -> System.exit(0)); - fileMenu.getItems().addAll(newItem, quitItem); - - Menu helpMenu = new Menu("Help"); - MenuItem aboutItem = new MenuItem("About"); - aboutItem.setOnAction(event -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - // https://stackoverflow.com/questions/27976345/how-do-you-set-the-icon-of-a-dialog-control-java-fx-java-8/27983567 - Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); - stage.getIcons().add(new Image("icon.png")); - - alert.setTitle("About StockTracker"); - alert.setHeaderText(null); // Alerts have an optional header. - alert.setContentText("Author: Sten Laane\nVersion: " + StockTracker.VERSION); - alert.showAndWait();}); - - MenuItem howToUseItem = new MenuItem("Getting started"); - howToUseItem.setOnAction(event -> { - Alert alert = new Alert(Alert.AlertType.INFORMATION); - Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); - stage.getIcons().add(new Image("icon.png")); - alert.setHeaderText(null); // Alerts have an optional header. - alert.setTitle("How to use StockTracker"); - alert.setContentText("***Insert tutorial here***"); - alert.showAndWait();}); - helpMenu.getItems().addAll(aboutItem, howToUseItem); - - menuBar.getMenus().addAll(fileMenu, helpMenu); - - } - private void plotNewData(LocalDate startDate) { ArrayList dataList = new ArrayList<>(); ArrayList amounts = new ArrayList<>(); @@ -263,6 +195,64 @@ private void plotNewData(LocalDate startDate) { makeGraphScene(true); } + private void makeGraphScene(boolean newData) { + VBox root = new VBox(); + setupMenuBar(root); + + CategoryAxis xAxis = new CategoryAxis(); + NumberAxis yAxis = new NumberAxis(); + xAxis.setLabel("Date"); + xAxis.setTickMarkVisible(false); + yAxis.setLabel("Ca$h (€)"); + LineChart lineChart = new LineChart<>(xAxis,yAxis); + lineChart.getStylesheets().add("chart-style.css"); + + lineChart.setTitle("€€€"); + XYChart.Series series = new XYChart.Series<>(); + //series.setName("My portfolio"); + double money = 0; + String moneyFile; + if (newData) { + moneyFile = StockTracker.PATH + "money.txt"; + } + else { + moneyFile = StockTracker.PATH + "save_money.txt"; + } + + for (String line: FileManager.readLines(moneyFile)) { + String[] splitLine = line.split(" "); + money = Double.parseDouble(splitLine[1]); + String date = splitLine[0]; + XYChart.Data dataPoint = new XYChart.Data<>(date, money); + dataPoint.setNode(new HoveredThresholdNode(date, money)); + series.getData().add(dataPoint); + } + lineChart.setCreateSymbols(false); + lineChart.setLegendVisible(false); + lineChart.getData().add(series); + + HBox hBox = new HBox(); + Label label = new Label("Total ca$h: "); + TextField field = new TextField("" + money); + field.setDisable(true); + field.setStyle("-fx-background-color: white;" + + "-fx-text-fill: " + "darkgreen"); + field.setAlignment(Pos.CENTER_RIGHT); + hBox.getChildren().addAll(label, field); + hBox.setAlignment(Pos.CENTER); + + Region region = new Region(); + root.getChildren().addAll(lineChart, hBox, region, statusLabel); + VBox.setVgrow(region, Priority.ALWAYS); + + + createScene(root); + } + + private void setStatusLabel(String newProgress) { + statusLabel.setText(newProgress); + } + private void runTest() { writeData("IVV", "USD", LocalDate.now().minusDays(139)); @@ -290,15 +280,11 @@ private static void createSave(ArrayList dataList, ArrayList amo StockTracker.createSave(dataList, amountList); } - private void calculateMoney(ArrayList ticker_currency, ArrayList stockAmounts) - { + private void calculateMoney(ArrayList ticker_currency, ArrayList stockAmounts) { setStatusLabel("Aggregating data..." ); StockTracker.calculateMoney(ticker_currency, stockAmounts); } - private void deleteTempFiles() { - StockTracker.deleteTempFiles(); - } /** * Extremely simplified version of https://stackoverflow.com/questions/14615590/javafx-linechart-hover-values @@ -332,10 +318,6 @@ private ExtendableTextField(Pane root) { setUp(); } - private void setPreviousField(ExtendableTextField field) { - previousField = field; - } - private void setUp() { focusedProperty().addListener(e -> { if (nextField == null && stocksTracked.size() < StockTracker.MAX_STOCKS) { @@ -361,6 +343,10 @@ else if (previousField.getText().length() > 0) { root.getChildren().addAll(container); } + private void setPreviousField(ExtendableTextField field) { + previousField = field; + } + private void makeNextField() { nextField = new ExtendableTextField(root); nextField.setPromptText(getPromptText()); @@ -370,6 +356,4 @@ private void makeNextField() { nextField.amountField.setDisable(true); } } - - } diff --git a/src/main/resources/saved_data/.gitignore b/src/main/resources/saved_data/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/src/main/resources/saved_data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file