diff options
-rw-r--r-- | .idea/term-project-cohwille-jmccaul3-mfoiani-rhunt2.iml | 1 | ||||
-rw-r--r-- | data/mock_tradeClarks.sqlite3 | bin | 0 -> 45056 bytes | |||
-rw-r--r-- | data/mock_tradeTesting.sqlite3 | bin | 0 -> 45056 bytes | |||
-rw-r--r-- | pom.xml | 5 | ||||
-rw-r--r-- | src/main/java/edu/brown/cs/student/term/Main.java | 13 | ||||
-rw-r--r-- | src/main/java/edu/brown/cs/student/term/ProfitCalculation.java | 323 |
6 files changed, 338 insertions, 4 deletions
diff --git a/.idea/term-project-cohwille-jmccaul3-mfoiani-rhunt2.iml b/.idea/term-project-cohwille-jmccaul3-mfoiani-rhunt2.iml index d624609..d9f2955 100644 --- a/.idea/term-project-cohwille-jmccaul3-mfoiani-rhunt2.iml +++ b/.idea/term-project-cohwille-jmccaul3-mfoiani-rhunt2.iml @@ -49,5 +49,6 @@ <orderEntry type="library" name="Maven: com.sparkjava:spark-template-freemarker:2.7.1" level="project" /> <orderEntry type="library" name="Maven: org.freemarker:freemarker:2.3.26-incubating" level="project" /> <orderEntry type="library" name="Maven: org.xerial:sqlite-jdbc:3.30.1" level="project" /> + <orderEntry type="library" name="Maven: org.json:json:20201115" level="project" /> </component> </module>
\ No newline at end of file diff --git a/data/mock_tradeClarks.sqlite3 b/data/mock_tradeClarks.sqlite3 Binary files differnew file mode 100644 index 0000000..7b5f3d1 --- /dev/null +++ b/data/mock_tradeClarks.sqlite3 diff --git a/data/mock_tradeTesting.sqlite3 b/data/mock_tradeTesting.sqlite3 Binary files differnew file mode 100644 index 0000000..d8515fe --- /dev/null +++ b/data/mock_tradeTesting.sqlite3 @@ -116,6 +116,11 @@ <artifactId>sqlite-jdbc</artifactId> <version>3.30.1</version> </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20201115</version> + </dependency> </dependencies> <!-- Build section --> diff --git a/src/main/java/edu/brown/cs/student/term/Main.java b/src/main/java/edu/brown/cs/student/term/Main.java index 268a725..bd9e096 100644 --- a/src/main/java/edu/brown/cs/student/term/Main.java +++ b/src/main/java/edu/brown/cs/student/term/Main.java @@ -6,11 +6,15 @@ import edu.brown.cs.student.term.repl.commands.SetupCommand; import joptsimple.OptionParser; import joptsimple.OptionSet; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; import java.util.HashMap; /** * The Main class of our project. This is where execution begins. - * */ public final class Main { @@ -19,8 +23,7 @@ public final class Main { /** * The initial method called when execution begins. * - * @param args - * An array of command line arguments + * @param args An array of command line arguments */ public static void main(String[] args) { new Main(args).run(); @@ -37,7 +40,7 @@ public final class Main { OptionParser parser = new OptionParser(); parser.accepts("gui"); parser.accepts("port").withRequiredArg().ofType(Integer.class) - .defaultsTo(DEFAULT_PORT); + .defaultsTo(DEFAULT_PORT); OptionSet options = parser.parse(args); if (options.has("gui")) { @@ -51,4 +54,6 @@ public final class Main { REPL repl = new REPL(commandHashMap); repl.runREPL(); } + + }
\ No newline at end of file diff --git a/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java b/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java new file mode 100644 index 0000000..6510194 --- /dev/null +++ b/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java @@ -0,0 +1,323 @@ +package edu.brown.cs.student.term; + + +import org.json.JSONArray; +import org.json.JSONObject; + + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URI; +import java.net.URL; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.LinkedList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; + +public class ProfitCalculation { + private Connection conn; + private String person; + private Date startTime; + private Date endTime; + + private String BASE_URL = "https://data.alpaca.markets/v1"; + + private String API_KEY = "PKT53Z9QW0TMSSC9XNPQ"; + private String SECRET_KEY = "udvetWCvwyVmZgrjgCPfX3W0nprKBrbunh5wNnCv"; + + //map of stock to list of buy orders, first element in list is oldest + private Map<String, LinkedList<OrderTuple>> buyHistoryMap; + + //map of stock to list of buy orders, first element in list is oldest + private Map<String, LinkedList<OrderTuple>> sellHistoryMap; + + //map of stock to gains from sell orders + private Map<String, Double> realizedGainsMap; + + //map of stock to gains from increases in value of holdings + private Map<String, Double> unrealizedGainsMap; + + private double moneyInput; + + /** + * constructor for ProfitCalculation. + * + * @param conn - database connection, with trade data. + * @param person - person of interest to calculate profit for. + * @param startTime - start of period to look at. + * @param endTime - end of period to look at. + */ + public ProfitCalculation(Connection conn, String person, Date startTime, Date endTime) { + this.conn = conn; + this.person = person; + this.startTime = startTime; + this.endTime = endTime; + buyHistoryMap = new HashMap<>(); + sellHistoryMap = new HashMap<>(); + realizedGainsMap = new HashMap<>(); + unrealizedGainsMap = new HashMap<>(); + } + + /** + * This method fills the maps of sell and buy orders with lists of oldest - new trades + */ + private void organizeOrders() { + //get a list of trades for a person to consider + try { + PreparedStatement prep; + // TODO: add start and end time + prep = + conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_name= ? " + + " AND trade_timestamp BETWEEN ? AND ?" + + "order by trade_timestamp asc;"); + prep.setString(1, this.person); + prep.setDate(2, startTime); + prep.setDate(3, endTime); + ResultSet rs = prep.executeQuery(); + + while (rs.next()) { + String ticker = rs.getString("stock_name"); + int shares = rs.getInt("number_of_shares"); + double price = rs.getDouble("trade_amount") / shares; + OrderTuple order = new OrderTuple(shares, price, rs.getDate("trade_timestamp")); + + //one element list for first time ticker is seen. + LinkedList<OrderTuple> oneElement = new LinkedList<OrderTuple>(); + oneElement.addLast(order); + + //for buy orders, build up buy history + if (rs.getInt("is_buy") != 0) { + moneyInput += shares * price; + if (buyHistoryMap.containsKey(ticker)) { + buyHistoryMap.get(ticker).addLast(order); + } else { + buyHistoryMap.put(ticker, oneElement); + } + } + //for sell orders build up sell history + else { + //ignore sell orders for which we do not have buys for + if (buyHistoryMap.containsKey(ticker)) { + if (sellHistoryMap.containsKey(ticker)) { + sellHistoryMap.get(ticker).addLast(order); + } else { + sellHistoryMap.put(ticker, oneElement); + } + } + } + + } + prep.close(); + } catch (SQLException e) { + System.out.println("ERROR: sql error getting trades"); + } + } + + /** + * This method processes the sell orders in the sellHistoryMap to get realized gains + */ + private void getRealizedGains() { + for (String ticker : sellHistoryMap.keySet()) { + //use FIFO selling + LinkedList<OrderTuple> sells = sellHistoryMap.get(ticker); + LinkedList<OrderTuple> buys = buyHistoryMap.get(ticker); + double realizedGain = 0; + + //process each sell order (unless all buy orders are "drained" + for (OrderTuple sell : sells) { + //stop if buys are empty, stop if buy happened after sell + if (buys.isEmpty()) { + break; + } + + int sharesToSell = sell.getShares(); + + //sell off through list of buys + while (sharesToSell > 0 && !buys.isEmpty()) { + //dont sell from buys which didn't exist at the time. + if (sell.getDate().after(buys.getFirst().getDate())) { + OrderTuple buyBundle = buys.removeFirst(); + int sharesAtBundlePrice; + //the buy has more shares than we want to sell + if (buyBundle.getShares() > sharesToSell) { + sharesAtBundlePrice = sharesToSell; + sharesToSell = 0; + //add back the holdings that were not sold + buyBundle.setShares(buyBundle.getShares() - sharesAtBundlePrice); + buys.addFirst(buyBundle); + } else { + sharesToSell -= buyBundle.getShares(); + sharesAtBundlePrice = buyBundle.getShares(); + } + realizedGain += sharesAtBundlePrice * (sell.getCost() - buyBundle.getCost()); + } else { + break; + } + + + } + } + + realizedGainsMap.put(ticker, realizedGain); + } + } + + /** + * get the change in value of stocks which are still held + */ + private void getUnrealizedGains() { + + //calculate change in value of holdings + for (String ticker : buyHistoryMap.keySet()) { + double unrealizedGains = 0; + + double currentPrice = getCurrentPrice(ticker); + LinkedList<OrderTuple> stockHistory = buyHistoryMap.get(ticker); + for (OrderTuple order : stockHistory) { + unrealizedGains += order.getShares() * (currentPrice - order.getCost()); + } + + unrealizedGainsMap.put(ticker, unrealizedGains); + } + } + + private class OrderTuple { + private int shares; + private double cost; + private Date date; + + private OrderTuple(int shares, double cost, Date date) { + this.shares = shares; + this.cost = cost; + this.date = date; + } + + public double getCost() { + return cost; + } + + public int getShares() { + return shares; + } + + public Date getDate() { + return date; + } + + public void setShares(int shares) { + this.shares = shares; + } + } + + public double getCurrentPrice(String ticker) { + String PRICE_URL = BASE_URL + "/last/stocks/" + ticker; + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(PRICE_URL)).setHeader("APCA-API-KEY-ID", API_KEY) + .setHeader("APCA-API-SECRET-KEY", SECRET_KEY) + .build(); + + HttpResponse<String> response = null; + try { + response = client.send(request, + HttpResponse.BodyHandlers.ofString()); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + + JSONObject object = new JSONObject(response.body()); + return object.getJSONObject("last").getDouble("price"); + } + + public void calculateGains() { + organizeOrders(); + getRealizedGains(); + getUnrealizedGains(); + double realizedGains = 0; + double unrealizedGains = 0; + + for (double value : realizedGainsMap.values()) { + realizedGains += value; + } + + for (double value : unrealizedGainsMap.values()) { + unrealizedGains += value; + } + + double totalGains = unrealizedGains + realizedGains; + + System.out.println("Money In: " + moneyInput); + System.out.println("Money Out: " + (moneyInput + totalGains)); + System.out.println("NASDAQ on money In: " + (moneyInput * compareToSP500())); + System.out.println( + "Total: " + totalGains + "| unrealized: " + unrealizedGains + " | realized: " + + realizedGains); + } + + /** + * return percent change in SPY (SP 500) over the time period. + */ + public double compareToSP500() { + String url = "https://data.alpaca.markets/v1/bars/" + + "day?" + + "symbols=SPY" + + "&start=" + startTime + + "&end=" + endTime; + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)).setHeader("APCA-API-KEY-ID", API_KEY) + .setHeader("APCA-API-SECRET-KEY", SECRET_KEY) + .build(); + + HttpResponse<String> response = null; + try { + response = client.send(request, + HttpResponse.BodyHandlers.ofString()); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + JSONArray object = new JSONObject(response.body()).getJSONArray("SPY"); + + //get close price of start SPY at start time + double startPrice = object.getJSONObject(0).getDouble("c"); + double endPrice = object.getJSONObject(object.length() - 1).getDouble("c"); + //get percent change + //end - start /start + return 1 + ((endPrice - startPrice) /startPrice); + + } + + public void setConnection(String filename) throws SQLException, ClassNotFoundException { + + // Initialize the database connection, turn foreign keys on + Class.forName("org.sqlite.JDBC"); + String urlToDB = "jdbc:sqlite:" + filename; + conn = DriverManager.getConnection(urlToDB); + + Statement stat = conn.createStatement(); + stat.executeUpdate("PRAGMA foreign_keys=ON;"); + } + +} |