aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java6
-rw-r--r--src/main/java/edu/brown/cs/student/term/Main.java40
-rw-r--r--src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java32
-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.java366
-rw-r--r--src/main/java/edu/brown/cs/student/term/profit/StockHolding.java61
-rw-r--r--src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java2
-rw-r--r--src/main/java/edu/brown/cs/student/term/trade/Trade.java22
8 files changed, 441 insertions, 120 deletions
diff --git a/src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java b/src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java
index 53c8cdc..2a9af65 100644
--- a/src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java
+++ b/src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java
@@ -125,16 +125,16 @@ public class DatabaseQuerier {
return trades;
}
- public List<Trade> getAllTradesByHolder(String person, Date startDate, Date endDate) {
+ public List<Trade> getAllTradesByHolder(Integer holder_id, Date startDate, Date endDate) {
LinkedList<Trade> trades = new LinkedList<>();
try {
PreparedStatement prep;
prep =
- conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_name= ? "
+ conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_id = ?"
+ " AND trade_timestamp BETWEEN ? AND ?"
+ "order by trade_timestamp asc;");
- prep.setString(1, person);
+ prep.setInt(1, holder_id);
prep.setDate(2, startDate);
prep.setDate(3, endDate);
ResultSet rs = prep.executeQuery();
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..37317c6 100644
--- a/src/main/java/edu/brown/cs/student/term/Main.java
+++ b/src/main/java/edu/brown/cs/student/term/Main.java
@@ -2,6 +2,7 @@ package edu.brown.cs.student.term;
import com.google.common.collect.ImmutableMap;
import edu.brown.cs.student.term.hub.Holder;
+import edu.brown.cs.student.term.hub.LinkMapper;
import edu.brown.cs.student.term.profit.ProfitCalculation;
import edu.brown.cs.student.term.profit.StockHolding;
import edu.brown.cs.student.term.hub.SuspicionRanker;
@@ -78,12 +79,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<>();
@@ -137,6 +138,7 @@ public final class Main {
Spark.post("/data", new SuspicionRankHandler());
Spark.post("/profit", new ProfitQueryHandler());
Spark.post("/trade-lookup", new TradeQueryHandler());
+ Spark.post("/edge-data", new EdgeDataQueryHandler());
}
/**
@@ -178,24 +180,31 @@ public final class Main {
@Override
public Object handle(Request request, Response response) throws Exception {
JSONObject req = new JSONObject(request.body());
- String person = req.getString("person");
+ System.err.println("LOG: Call to /profit with " + req.toMap());
+ Integer holder_id = req.getInt("selectedId");
Date startPeriod = new Date(req.getLong("startTime"));
Date endPeriod = new Date(req.getLong("endTime"));
ProfitCalculation profit =
- new ProfitCalculation(DatabaseQuerier.getConn(), person, startPeriod, endPeriod);
- List<StockHolding> holdings = profit.getHoldingsList();
- double gains = profit.calculateGains();
+ new ProfitCalculation(DatabaseQuerier.getConn(), "", startPeriod, endPeriod);
+ List<StockHolding> holdings = profit.getHoldingsList(holder_id);
+ double gains = profit.calculateGainsSingle(holder_id);
double sp500PercentGain = profit.compareToSP500();
+ double percentGain = 100 * (gains / profit.getMoneyInput());
+ if (profit.getMoneyInput() == 0) {
+ percentGain = 0;
+ }
+
Map<String, Object> res = new HashMap<>();
- res.put("person", person);
+ res.put("holder_id", holder_id);
res.put("moneyIn", profit.getMoneyInput());
res.put("moneyOut", profit.getMoneyInput() + gains);
res.put("holdings", holdings);
- res.put("percentGain", 100 * gains / profit.getMoneyInput());
+ res.put("percentGain", percentGain);
res.put("SP500", (1 + sp500PercentGain) * profit.getMoneyInput());
res.put("percentSP500", 100 * sp500PercentGain);
+ System.err.println("LOG: Returning to GUI " + res);
return GSON.toJson(res);
}
@@ -205,15 +214,26 @@ public final class Main {
@Override
public Object handle(Request request, Response response) throws Exception {
JSONObject req = new JSONObject(request.body());
- String person = req.getString("person");
+ Integer holder_id = req.getInt("selectedId");
Date startPeriod = new Date(req.getLong("startTime"));
Date endPeriod = new Date(req.getLong("endTime"));
DatabaseQuerier db = SetupCommand.getDq();
- List<Trade> trades = db.getAllTradesByHolder(person, startPeriod, endPeriod);
+ List<Trade> trades = db.getAllTradesByHolder(holder_id, startPeriod, endPeriod);
return GSON.toJson(trades);
+ }
+ }
+ private static class EdgeDataQueryHandler implements Route {
+ @Override
+ public Object handle(Request request, Response response) throws Exception {
+ JSONObject req = new JSONObject(request.body());
+ int leaderID = req.getInt("leaderID");
+ int followerID = req.getInt("followerID");
+ List<String> commonStocks = LinkMapper.getCommonTrades(leaderID, followerID);
+ System.out.println(commonStocks);
+ return GSON.toJson(commonStocks);
}
}
diff --git a/src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java b/src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java
index 31e2625..e749aff 100644
--- a/src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java
+++ b/src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java
@@ -11,8 +11,9 @@ public class LinkMapper {
//TODO: Review what we actually need in here
//not strictly necessary but may be nice to maintain
- private List<List<Trade>> allTrades = new ArrayList<>();
+ //private List<List<Trade>> allTrades = new ArrayList<>();
private Map<Holder, Set<Holder>> followerToLeaders = new HashMap<>();
+ private static Map<Integer, Set<Trade>> holderIDToTrades = new HashMap<>();
private DatabaseQuerier databaseQuerier;
public LinkMapper(DatabaseQuerier db){
@@ -55,6 +56,25 @@ public class LinkMapper {
return followerToLeaders;
}
+ public static List<String> getCommonTrades(int leaderID, int followerID){
+ Set<Trade> leaderTrades = new HashSet<>(holderIDToTrades.get(leaderID));
+ Set<Trade> followerTrades = new HashSet<>(holderIDToTrades.get(followerID));
+
+ leaderTrades.retainAll(followerTrades);
+ //TODO: Could retain WAY more info in here!
+ List<String> commonTrades = new ArrayList<>();
+ for(Trade leaderTrade: leaderTrades){
+ String buyType = "";
+ if(leaderTrade.isBuy()){
+ buyType = "Buy";
+ } else{
+ buyType = "Sell";
+ }
+ commonTrades.add(buyType + ": " + leaderTrade.getStock());
+ }
+ return commonTrades;
+ }
+
/**
* Converts a single trade list into entries in the follower to leader map
* @param tradeList - a list of trades for a single stock (either buy or sell)
@@ -64,7 +84,15 @@ public class LinkMapper {
//gets in order list of people
for (Trade trade : tradeList) {
- holderList.add(trade.getHolder());
+ Holder currentHolder = trade.getHolder();
+ holderList.add(currentHolder);
+ if(!holderIDToTrades.containsKey(currentHolder.getId())){
+ Set<Trade> tradeSet = new HashSet<>();
+ tradeSet.add(trade);
+ holderIDToTrades.put(currentHolder.getId(), tradeSet);
+ } else {
+ holderIDToTrades.get(currentHolder.getId()).add(trade);
+ }
}
//Set<Holder> followers = new HashSet<>(holderList);
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..4b19899 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 {
@@ -71,6 +76,7 @@ public class ProfitCalculation {
tablesFilled = false;
}
+
/**
* This method fills the maps of sell and buy orders with lists of oldest - new trades.
*/
@@ -78,25 +84,35 @@ 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 "";
}
return ticker;
}
- private void organizeOrders() {
+ private void organizeOrders(Integer id) {
//get a list of trades for a person to consider
try {
PreparedStatement prep;
- prep =
- conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_name= ? "
- + " AND trade_timestamp BETWEEN ? AND ?"
- + "order by trade_timestamp asc;");
- prep.setString(1, this.person);
+ if (id == -1) {
+ //search by name
+ prep =
+ conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_name= ? "
+ + " AND trade_timestamp BETWEEN ? AND ? "
+ + "order by trade_timestamp asc;");
+ prep.setString(1, this.person);
+ } else {
+ //search by id
+ prep =
+ conn.prepareStatement("SELECT * FROM \'trades\' WHERE holder_id = ? "
+ + " AND trade_timestamp BETWEEN ? AND ? "
+ + "order by trade_timestamp asc;");
+ prep.setInt(1, id);
+ }
prep.setDate(2, startTime);
prep.setDate(3, endTime);
ResultSet rs = prep.executeQuery();
@@ -104,7 +120,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 +141,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 +165,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 +259,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 +276,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,29 +294,37 @@ public class ProfitCalculation {
}
- public double calculateGains() {
+ public double calculateGainsSingle(Integer id) {
+
if (!tablesFilled) {
- organizeOrders();
+ organizeOrders(id);
getRealizedGains();
getUnrealizedGains();
tablesFilled = true;
}
- double realizedGains = 0;
- double unrealizedGains = 0;
+
+
+ double gains = 0;
for (double value : realizedGainsMap.values()) {
- realizedGains += value;
+ gains += value;
}
- for (double value : unrealizedGainsMap.values()) {
- unrealizedGains += value;
+ for (double value: unrealizedGainsMap.values()) {
+ gains += value;
}
- return unrealizedGains + realizedGains;
+
+ return gains;
}
- public List<StockHolding> getHoldingsList() {
+ public List<StockHolding> getHoldingsList(Integer id) {
+ if (conn == null) {
+ System.out.println("ERROR: No database connection");
+ return new LinkedList<>();
+ }
+
if (!tablesFilled) {
- organizeOrders();
+ organizeOrders(id);
getRealizedGains();
getUnrealizedGains();
tablesFilled = true;
@@ -329,11 +355,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 +396,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 +603,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..dd57ce1 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,23 @@
package edu.brown.cs.student.term.profit;
+import java.util.Objects;
+
+/**
+ * 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 +25,60 @@ 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;
+ }
+
+ public String getTicker() {
+ return ticker;
+ }
+
+ @Override
+ public String toString() {
+ return "StockHolding{" +
+ "ticker='" + ticker + '\'' +
+ ", realizedGain=" + realizedGain +
+ ", unrealizedGain=" + unrealizedGain +
+ ", shares=" + shares +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ StockHolding that = (StockHolding) o;
+ return shares == that.shares && Objects.equals(ticker, that.ticker) &&
+ Objects.equals(realizedGain, that.realizedGain) &&
+ Objects.equals(unrealizedGain, that.unrealizedGain);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ticker, realizedGain, unrealizedGain, shares);
+ }
} \ No newline at end of file
diff --git a/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java b/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java
index 00ba3ad..6f8f610 100644
--- a/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java
+++ b/src/main/java/edu/brown/cs/student/term/repl/commands/LoadCommand.java
@@ -125,7 +125,7 @@ public class LoadCommand implements Command {
"text=form-type%3D4+and+(filing-date%3D" + filingDate + ")" +
"&start=" + (100*counter++ + shift) +
"&count=" + 100 +
- "&first=2020" +
+ "&first=2021" +
"&last=2021" +
"&output=atom"
:
diff --git a/src/main/java/edu/brown/cs/student/term/trade/Trade.java b/src/main/java/edu/brown/cs/student/term/trade/Trade.java
index 353de8d..df52a4f 100644
--- a/src/main/java/edu/brown/cs/student/term/trade/Trade.java
+++ b/src/main/java/edu/brown/cs/student/term/trade/Trade.java
@@ -2,6 +2,8 @@ package edu.brown.cs.student.term.trade;
import edu.brown.cs.student.term.hub.Holder;
+import java.util.Objects;
+
public class Trade {
private int id;
@@ -54,6 +56,26 @@ public class Trade {
return price;
}
+ /**
+ * This equals method differs from what may be expected,
+ * it considers trades "equal" if they have the same buy value and stock name
+ * because they are the same type of trade of the same stock in that case
+ * @param o - object to compare to
+ * @return true if equal by the considerations above
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Trade trade = (Trade) o;
+ return isBuy == trade.isBuy && stock.equals(trade.stock);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(stock, isBuy);
+ }
+
@Override
public String toString() {
return "Trade{" +