aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorMichael Foiani <sotech117@michaels-mbp-3.lan>2021-04-20 00:20:59 -0400
committerMichael Foiani <sotech117@michaels-mbp-3.lan>2021-04-20 00:20:59 -0400
commit56532c3d09b162390602af0f94c78ade0d6181e2 (patch)
treec2af5d8ee92dda73624fc1fed4506c119e953afc /src/main/java
parent2004a6c76be21d37367624a4ed7c00825e969143 (diff)
parent87fe6542c4c39a8b79f8b86a2b965e19d4b674c3 (diff)
Logic of modal in app.js. Watchdogs component created.
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/edu/brown/cs/student/term/Main.java6
-rw-r--r--src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java32
-rw-r--r--src/main/java/edu/brown/cs/student/term/profit/ProfitCalculation.java334
-rw-r--r--src/main/java/edu/brown/cs/student/term/profit/StockHolding.java26
4 files changed, 299 insertions, 99 deletions
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 ee3bec1..dd304c5 100644
--- a/src/main/java/edu/brown/cs/student/term/Main.java
+++ b/src/main/java/edu/brown/cs/student/term/Main.java
@@ -78,12 +78,12 @@ public final class Main {
setConnection.run(new String[] {"data/trades.sqlite3"});
}
- if (!options.has("debug")) {
+ /*if (!options.has("debug")) {
System.setErr(new PrintStream(new OutputStream() {
public void write(int b) {
}
}));
- }
+ }*/
HashMap<String, Command> commandHashMap = new HashMap<>();
@@ -185,7 +185,7 @@ public final class Main {
ProfitCalculation profit =
new ProfitCalculation(DatabaseQuerier.getConn(), person, startPeriod, endPeriod);
List<StockHolding> holdings = profit.getHoldingsList();
- double gains = profit.calculateGains();
+ double gains = profit.calculateGainsSingle();
double sp500PercentGain = profit.compareToSP500();
Map<String, Object> res = new HashMap<>();
diff --git a/src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java b/src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java
index 3283f5c..d37910e 100644
--- a/src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java
+++ b/src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java
@@ -16,8 +16,9 @@ public class SuspicionRanker {
}
private <K, V extends Comparable<V>> V getMaxOfMap(Map<K, V> map) {
- Map.Entry<K, V> maxEntry = Collections.max(map.entrySet(), Map.Entry.comparingByValue());
- return maxEntry.getValue();
+ //Map.Entry<K, V> maxEntry = Collections.max(map.entrySet(), Map.Entry.comparingByValue());
+ Collection<V> values = map.values();
+ return Collections.max(map.values());
}
private <K, V extends Comparable<V>> V getMinOfMap(Map<K, V> map) {
@@ -52,13 +53,12 @@ public class SuspicionRanker {
HubSearch hub = new HubSearch(lm);
Map<Holder, Double> holderToHubScore = hub.runHubSearch(start, end);
- /*
-
ProfitCalculation pc = new ProfitCalculation(DatabaseQuerier.getConn(), "",
new Date(start.toEpochMilli()),
new Date(end.toEpochMilli()));
Map<Integer, Double> profitMap = pc.getProfitMap();
+ System.out.println(profitMap);
//if the maps are empty, we abort because we have entirely incomplete data
if(profitMap.isEmpty() || holderToHubScore.isEmpty()){
@@ -66,30 +66,34 @@ public class SuspicionRanker {
}
double profitMax = getMaxOfMap(profitMap);
- /*if all of our values are negative, we need to flip sides so that the
- * biggest loser doesn't end up being the most suspicious person*/
- /*
+
+ //if all of our values are negative, we need to flip sides so that the
+ //biggest loser doesn't end up being the most suspicious person*/
if(profitMax <= 0) {
profitMax = Math.abs(getMinOfMap(profitMap));
}
- /*if both the min we found and max we found are 0, then we have
- the special case where all the values are 0, in which case we
- need to avoid dividing by 0*/
- /*
+
+ //if both the min we found and max we found are 0, then we have
+ //the special case where all the values are 0, in which case we
+ //need to avoid dividing by 0
+
if(profitMax == 0){
profitMax = 1;
}
- */
double hubMax = getMaxOfMap(holderToHubScore);
for (Holder guy : holderToHubScore.keySet()) {
- //double normalizedProfitScore = profitMap.get(guy.getId()) / profitMax;
+ double normalizedProfitScore = 0;
+ if (profitMap.containsKey(guy.getId())) {
+ normalizedProfitScore = profitMap.get(guy.getId()) / profitMax;
+ }
double normalizedHubScore = holderToHubScore.get(guy) / hubMax;
- double suspicionScore = normalizedHubScore; //* 0.6 + normalizedProfitScore * 0.4;
+ double suspicionScore = normalizedHubScore * 0.6 + normalizedProfitScore * 0.4;
+
guy.setSuspicionScore(suspicionScore);
orderedSuspicion.add(guy);
}
diff --git a/src/main/java/edu/brown/cs/student/term/profit/ProfitCalculation.java b/src/main/java/edu/brown/cs/student/term/profit/ProfitCalculation.java
index 15f31cc..d0df8a8 100644
--- a/src/main/java/edu/brown/cs/student/term/profit/ProfitCalculation.java
+++ b/src/main/java/edu/brown/cs/student/term/profit/ProfitCalculation.java
@@ -19,6 +19,11 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.*;
public class ProfitCalculation {
@@ -78,10 +83,10 @@ public class ProfitCalculation {
private String validateTicker(String ticker) {
//this is cleaning some improperly formatted tickers
ticker = ticker.replaceAll("[^a-zA-Z0-9]", "").toUpperCase();
- if(ticker.contains("[0-9]") ||
- ticker.length() > 5 ||
- ticker.length() < 2 ||
- ticker.contains("NONE")) {
+ if (ticker.contains("[0-9]") ||
+ ticker.length() > 5 ||
+ ticker.length() < 2 ||
+ ticker.contains("NONE")) {
return "";
}
@@ -94,7 +99,7 @@ public class ProfitCalculation {
PreparedStatement prep;
prep =
conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_name= ? "
- + " AND trade_timestamp BETWEEN ? AND ?"
+ + " AND trade_timestamp BETWEEN ? AND ? "
+ "order by trade_timestamp asc;");
prep.setString(1, this.person);
prep.setDate(2, startTime);
@@ -104,7 +109,7 @@ public class ProfitCalculation {
while (rs.next()) {
String ticker = rs.getString("stock_name");
ticker = validateTicker(ticker);
- if(ticker.equals("")){
+ if (ticker.equals("")) {
continue;
}
int shares = rs.getInt("number_of_shares");
@@ -125,13 +130,12 @@ public class ProfitCalculation {
}
} 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);
- }
+ if (sellHistoryMap.containsKey(ticker)) {
+ sellHistoryMap.get(ticker).addLast(order);
+ } else {
+ sellHistoryMap.put(ticker, oneElement);
}
+
}
}
@@ -150,39 +154,41 @@ public class ProfitCalculation {
LinkedList<OrderTuple> sells = sellHistoryMap.get(ticker);
LinkedList<OrderTuple> buys = buyHistoryMap.get(ticker);
double realizedGain = 0;
+ if (sells != null && buys != null) {
+ //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;
+ }
- //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);
+ 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())
+ || sell.getDate().equals(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 {
- sharesToSell -= buyBundle.getShares();
- sharesAtBundlePrice = buyBundle.getShares();
+ break;
}
- realizedGain += sharesAtBundlePrice * (sell.getCost() - buyBundle.getCost());
- } else {
- break;
- }
+ }
}
}
@@ -242,12 +248,16 @@ public class ProfitCalculation {
if (currentStockPrices.containsKey(ticker)) {
return currentStockPrices.get(ticker);
} else {
- String PRICE_URL = BASE_URL + "/last/stocks/" + ticker;
- System.out.println("LOG: Making call to " + PRICE_URL + " in " + getClass());
+ SimpleDateFormat localDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
+ String url = "https://data.alpaca.markets/v1/bars/"
+ + "day?"
+ + "symbols=" + ticker
+ + "&start=" + localDateFormat.format(startTime)
+ + "&end=" + localDateFormat.format(endTime);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(PRICE_URL)).setHeader("APCA-API-KEY-ID", API_KEY)
+ .uri(URI.create(url)).setHeader("APCA-API-KEY-ID", API_KEY)
.setHeader("APCA-API-SECRET-KEY", SECRET_KEY)
.build();
@@ -255,18 +265,15 @@ public class ProfitCalculation {
try {
response = client.send(request,
HttpResponse.BodyHandlers.ofString());
- } catch (IOException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
+ } catch (Exception e) {
+ System.out.println("ERROR: error getting price for profit calculation");
}
-
- JSONObject object = new JSONObject(response.body());
+ JSONArray object = new JSONObject(response.body()).getJSONArray(ticker);
try {
- double price = object.getJSONObject("last").getDouble("price");
- currentStockPrices.put(ticker, price);
- return price;
+ double endPrice = object.getJSONObject(object.length() - 1).getDouble("c");
+ currentStockPrices.put(ticker, endPrice);
+ return endPrice;
} catch (JSONException e) {
currentStockPrices.put(ticker, -1.0);
return -1.0;
@@ -276,27 +283,30 @@ public class ProfitCalculation {
}
- public double calculateGains() {
+ public double calculateGainsSingle() {
+
if (!tablesFilled) {
organizeOrders();
getRealizedGains();
- getUnrealizedGains();
tablesFilled = true;
}
+
+
double realizedGains = 0;
- double unrealizedGains = 0;
for (double value : realizedGainsMap.values()) {
realizedGains += value;
}
- for (double value : unrealizedGainsMap.values()) {
- unrealizedGains += value;
- }
- return unrealizedGains + realizedGains;
+ return realizedGains;
}
public List<StockHolding> getHoldingsList() {
+ if (conn == null) {
+ System.out.println("ERROR: No database connection");
+ return new LinkedList<>();
+ }
+
if (!tablesFilled) {
organizeOrders();
getRealizedGains();
@@ -329,11 +339,12 @@ public class ProfitCalculation {
* return percent change in SPY (SP 500) over the time period.
*/
public double compareToSP500() {
+ SimpleDateFormat localDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String url = "https://data.alpaca.markets/v1/bars/"
+ "day?"
+ "symbols=SPY"
- + "&start=" + startTime
- + "&end=" + endTime;
+ + "&start=" + localDateFormat.format(startTime)
+ + "&end=" + localDateFormat.format(endTime);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
@@ -369,29 +380,199 @@ public class ProfitCalculation {
*/
public Map<Integer, Double> getProfitMap() {
Map<Integer, Double> profitMap = new HashMap<>();
+ if (conn == null) {
+ System.out.println("ERROR: no database connection");
+ return profitMap;
+ }
try {
PreparedStatement prep;
+ long START = System.currentTimeMillis();
prep =
- conn.prepareStatement("SELECT * from trades group by holder_name;");
+ conn.prepareStatement(
+ "SELECT * From trades GROUP BY holder_id having max(is_buy) = 1;");
ResultSet rs = prep.executeQuery();
+
+ long QUERY = System.currentTimeMillis();
+ //System.out.println((QUERY - START) + " query time");
+
+ //set of all people who have made both buy and sell orders
+ Set<Integer> people = new HashSet<>();
+
while (rs.next()) {
- int id = rs.getInt("holder_id");
- this.person = rs.getString("holder_name");
- resetClass();
- double gain = this.calculateGains();
- if (moneyInput == 0) {
- profitMap.put(id, 0.0);
- } else {
- profitMap.put(id, gain / moneyInput);
- }
+// int id = rs.getInt("holder_id");
+// this.person = rs.getString("holder_name");
+// resetClass();
+//
+//
+//
+// double gain = this.calculateGains();
+// if (moneyInput == 0) {
+// profitMap.put(id, 0.0);
+// } else {
+// profitMap.put(id, gain / moneyInput);
+// }
+ people.add(rs.getInt("holder_id"));
}
+
+ profitMap = calculateGains(people);
+
+ long LOOP = System.currentTimeMillis();
+ //System.out.println((LOOP - QUERY) + " loop");
+
} catch (SQLException throwables) {
System.out.println("ERROR: SQl error in profit calculation");
}
return profitMap;
}
+ private Map<Integer, Double> calculateGains(Set<Integer> people) {
+ Map<Integer, Double> gainsMap = new HashMap<>();
+
+ //map of stock to list of buy orders, first element in list is oldest
+ Map<Integer, Map<String, LinkedList<OrderTuple>>> sellMap = new HashMap<>();
+ //map of stock to list of buy orders, first element in list is oldest
+ Map<Integer, Map<String, LinkedList<OrderTuple>>> buyMap = new HashMap<>();
+ //money input
+ Map<Integer, Double> moneyInMap = new HashMap<>();
+
+
+ try {
+ PreparedStatement prep;
+ prep =
+ conn.prepareStatement("SELECT * FROM \'trades\'"
+ + " WHERE NOT number_of_shares = 0 AND trade_timestamp BETWEEN ? AND ? "
+ + "order by trade_timestamp asc;");
+ prep.setDate(1, startTime);
+ prep.setDate(2, endTime);
+ ResultSet rs = prep.executeQuery();
+ while (rs.next()) {
+ if (people.contains(rs.getInt("holder_id"))) {
+ String ticker = rs.getString("stock_name");
+ ticker = validateTicker(ticker);
+ if (ticker.equals("")) {
+ continue;
+ }
+ int shares = rs.getInt("number_of_shares");
+ double price = rs.getDouble("share_price");
+ int holder_id = rs.getInt("holder_id");
+ if (!buyMap.containsKey(holder_id)) {
+ buyMap.put(holder_id, new HashMap<>());
+ }
+ if (!sellMap.containsKey(holder_id)) {
+ sellMap.put(holder_id, new HashMap<>());
+ }
+
+
+ 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) {
+
+ if (moneyInMap.containsKey(holder_id)) {
+ moneyInMap.put(holder_id, moneyInMap.get(holder_id) + shares * price);
+ } else {
+ moneyInMap.put(holder_id, shares * price);
+ }
+
+
+ if (buyMap.get(holder_id).containsKey(ticker)) {
+ buyMap.get(holder_id).get(ticker).addLast(order);
+ } else {
+ buyMap.get(holder_id).put(ticker, oneElement);
+ }
+ } else {
+ //ignore sell orders for which we do not have buys for
+ if (sellMap.get(holder_id).containsKey(ticker)) {
+ sellMap.get(holder_id).get(ticker).addLast(order);
+ } else {
+ sellMap.get(holder_id).put(ticker, oneElement);
+ }
+
+ }
+ }
+ }
+ } catch (SQLException e) {
+ System.out.println("ERROR: sql error getting trades");
+ }
+
+
+ //part 2 doing math...
+ for (Integer person : people) {
+ this.buyHistoryMap = buyMap.get(person);
+ this.sellHistoryMap = sellMap.get(person);
+ if (sellHistoryMap == null) {
+ continue;
+ }
+
+ for (String ticker : sellHistoryMap.keySet()) {
+ //use FIFO selling
+ LinkedList<OrderTuple> sells = sellHistoryMap.get(ticker);
+ LinkedList<OrderTuple> buys = buyHistoryMap.get(ticker);
+ double realizedGain = 0;
+ if (sells != null && buys != null) {
+ //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())
+ || sell.getDate().equals(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;
+ }
+
+
+ }
+ }
+ }
+
+ if (gainsMap.containsKey(person)) {
+ gainsMap.put(person, gainsMap.get(person) + realizedGain);
+ } else {
+ gainsMap.put(person, realizedGain);
+ }
+
+ }
+
+ //percent gain
+ if (gainsMap.containsKey(person) && moneyInMap.containsKey(person)) {
+ gainsMap.put(person, gainsMap.get(person) / moneyInMap.get(person));
+ } else {
+ gainsMap.put(person, 0.0);
+ }
+
+
+ }
+
+ return gainsMap;
+ }
+
public double getMoneyInput() {
return this.moneyInput;
}
@@ -406,15 +587,4 @@ public class ProfitCalculation {
tablesFilled = false;
}
- 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;");
- }
-
}
diff --git a/src/main/java/edu/brown/cs/student/term/profit/StockHolding.java b/src/main/java/edu/brown/cs/student/term/profit/StockHolding.java
index f7924f2..5edb5f7 100644
--- a/src/main/java/edu/brown/cs/student/term/profit/StockHolding.java
+++ b/src/main/java/edu/brown/cs/student/term/profit/StockHolding.java
@@ -1,11 +1,21 @@
package edu.brown.cs.student.term.profit;
+/**
+ * class to map holding info for JSON.
+ */
public class StockHolding {
private String ticker;
private Double realizedGain;
private Double unrealizedGain;
private int shares;
+ /**
+ * constructor.
+ * @param ticker - stock.
+ * @param realizedGain realized gain.
+ * @param unrealizedGain unrealized gain.
+ * @param shares - number of shares
+ */
public StockHolding(String ticker, Double realizedGain, Double unrealizedGain, int shares) {
this.ticker = ticker;
this.realizedGain = realizedGain;
@@ -13,11 +23,27 @@ public class StockHolding {
this.shares = shares;
}
+ /**
+ * getter method.
+ * @return realized gain.
+ */
public Double getRealizedGain() {
return realizedGain;
}
+ /**
+ * getter method.
+ * @return unrealized gain.
+ */
public Double getUnrealizedGain() {
return unrealizedGain;
}
+
+ /**
+ * getter method for testing.
+ * @return shares.
+ */
+ public int getShares() {
+ return shares;
+ }
} \ No newline at end of file