From 61ad5896d35c1d49a4a5d9c1d8d87f53907f7da7 Mon Sep 17 00:00:00 2001 From: clarkohw Date: Sat, 17 Apr 2021 15:11:05 -0400 Subject: fixed case were date of buy/sell is equal --- .../cs/student/term/profit/ProfitCalculation.java | 69 +++++++++++----------- 1 file changed, 35 insertions(+), 34 deletions(-) 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 0fe35d9..b4bf708 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 @@ -110,13 +110,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); } + } } @@ -135,39 +134,41 @@ public class ProfitCalculation { LinkedList sells = sellHistoryMap.get(ticker); LinkedList 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; - } + } } } -- cgit v1.2.3-70-g09d2 From 7c1fc6472bfbc10bdb4e5bfa41c778020bd1c8fd Mon Sep 17 00:00:00 2001 From: Julia McCauley Date: Sun, 18 Apr 2021 10:52:28 -0400 Subject: need to save changes to pull --- src/main/java/edu/brown/cs/student/term/Main.java | 4 ++-- src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java | 3 ++- 2 files changed, 4 insertions(+), 3 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 7ee9874..3cba37a 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 commandHashMap = 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 fbe05f7..09b6cc1 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 @@ -51,12 +51,14 @@ public class SuspicionRanker { LinkMapper lm = new LinkMapper(querier); HubSearch hub = new HubSearch(lm); Map holderToHubScore = hub.runHubSearch(start, end); + System.out.println("gets past the hub score"); ProfitCalculation pc = new ProfitCalculation(DatabaseQuerier.getConn(), "", new Date(start.toEpochMilli()), new Date(end.toEpochMilli())); Map profitMap = pc.getProfitMap(); + System.out.println("gets past the profit map"); //if the maps are empty, we abort because we have entirely incomplete data if(profitMap.isEmpty() || holderToHubScore.isEmpty()){ @@ -81,7 +83,6 @@ public class SuspicionRanker { for (Holder guy : holderToHubScore.keySet()) { double normalizedProfitScore = profitMap.get(guy.getId()) / profitMax; - double normalizedHubScore = holderToHubScore.get(guy) / hubMax; double suspicionScore = normalizedHubScore * 0.6 + normalizedProfitScore * 0.4; guy.setSuspicionScore(suspicionScore); -- cgit v1.2.3-70-g09d2 From 0466db8b9051cb6300f274f0bba480d1020c63cf Mon Sep 17 00:00:00 2001 From: Julia McCauley Date: Sun, 18 Apr 2021 23:23:34 -0400 Subject: cleaned up graph visualization, added colors, score based node scaling --- react-frontend/src/components/HubList.js | 2 +- react-frontend/src/components/Visualization.js | 39 +++++++++++++++++++++++-- react-frontend/src/components/images/person.svg | 1 + 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 react-frontend/src/components/images/person.svg diff --git a/react-frontend/src/components/HubList.js b/react-frontend/src/components/HubList.js index 0df3020..c9a5156 100644 --- a/react-frontend/src/components/HubList.js +++ b/react-frontend/src/components/HubList.js @@ -32,7 +32,7 @@ function HubList(props) { const getName = () => { props.data.forEach(hub => { - if (hub.id == props.selected) { + if (hub.id === props.selected) { setName(hub.name); } }) diff --git a/react-frontend/src/components/Visualization.js b/react-frontend/src/components/Visualization.js index 1975e86..0a0c82a 100644 --- a/react-frontend/src/components/Visualization.js +++ b/react-frontend/src/components/Visualization.js @@ -6,6 +6,7 @@ import Graph from 'vis-react'; // CSS imports import '../css/Canvas.css'; + /** * This function renders and mantains thhe canvas. * @param {Object} props The props for the canvas. @@ -29,10 +30,37 @@ function Visualization(props) { let nodes = []; props.data.forEach(hub => { if (hub.followers) { + let colorVal = '#f6f7d4'; + const score = hub.suspicionScore; + + if(score > 0.8){ + colorVal = '#d92027' + } + if(score < 0.8 && score > 0.6){ + colorVal = '#f37121' + } + if(score < 0.6 && score > 0.4){ + colorVal = '#fdca40' + } nodes.push({ id: hub.id, + autoResize: true, label: hub.name, - size: hub.suspicionScore + labelHighlightBold: true, + shape: "dot", + value: hub.suspicionScore*1000, + color: { + background: colorVal, + border: '#2b2e4a', + highlight:{ + background: '#29bb89', + border: '#fdca40' + } + }, + font: { + color: '#9fd8df', + size: 20, + } }); } }); @@ -43,8 +71,13 @@ function Visualization(props) { props.data.forEach(hub => { hub.followers.forEach(follower => { edges.push({ - from: hub.id, - to: follower.id + from: follower.id, + to: hub.id, + dashes: false, + color:{ + opacity: 0.7, + highlight:'#fdca40', + } }); }); }); diff --git a/react-frontend/src/components/images/person.svg b/react-frontend/src/components/images/person.svg new file mode 100644 index 0000000..6a93d8f --- /dev/null +++ b/react-frontend/src/components/images/person.svg @@ -0,0 +1 @@ +Person \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 26827e5631cc7c0d5d24fa5459a6abbe9e4c60a5 Mon Sep 17 00:00:00 2001 From: clarkohw Date: Mon, 19 Apr 2021 02:20:36 -0400 Subject: simple test --- .../brown/cs/student/term/profit/ProfitCalculation.java | 2 +- .../java/edu/brown/cs/student/ProfitCalculationTest.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) 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 b4bf708..0b86172 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 @@ -246,7 +246,7 @@ public class ProfitCalculation { e.printStackTrace(); } - + System.out.println(response.body()); JSONObject object = new JSONObject(response.body()); try { double price = object.getJSONObject("last").getDouble("price"); diff --git a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index 974fff0..d1fc9be 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -35,7 +35,7 @@ public class ProfitCalculationTest { @Before public void setUp() { try { - db = new DatabaseQuerier("data/trades.sqlite3"); + db = new DatabaseQuerier("data/lil_mock.sqlite3"); } catch (Exception e) { System.out.println("DBQuerier Test, couldn't connect to db???"); } @@ -47,17 +47,17 @@ public class ProfitCalculationTest { } @Test - public void testEmptyDB() { + public void testBasicTrades() { setUp(); ProfitCalculation profitCalculation = - new ProfitCalculation(DatabaseQuerier.getConn(), "CAKEBREAD STEVEN", new Date(1518010558000l), - new Date(1718010556000l)); + new ProfitCalculation(DatabaseQuerier.getConn(), "Don", new Date(1518010558000l), + new Date(1618698807000l)); + //price of GME at end time is 154.69 List trade = profitCalculation.getHoldingsList(); - double gain = trade.get(0).getUnrealizedGain(); - assertEquals(294800.0, gain, .01); + //buy with no sell + assertEquals(3842.25, trade.get(0).getUnrealizedGain(),6); } - } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 8e76bf15c53c540a72a273ec560a624dfb7e11df Mon Sep 17 00:00:00 2001 From: clarkohw Date: Mon, 19 Apr 2021 02:59:57 -0400 Subject: efficiency for profit calc + tests --- .../cs/student/term/profit/ProfitCalculation.java | 33 ++++++-------- .../brown/cs/student/ProfitCalculationTest.java | 10 ++++- tatus | 52 ++++++++++++++++++++++ 3 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 tatus 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 0b86172..fe12612 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 @@ -228,11 +228,15 @@ public class ProfitCalculation { if (currentStockPrices.containsKey(ticker)) { return currentStockPrices.get(ticker); } else { - String PRICE_URL = BASE_URL + "/last/stocks/" + ticker; + String url = "https://data.alpaca.markets/v1/bars/" + + "day?" + + "symbols=" + ticker + + "&start=" + startTime + + "&end=" + 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(); @@ -240,18 +244,14 @@ 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"); } - System.out.println(response.body()); - 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"); + return endPrice; } catch (JSONException e) { currentStockPrices.put(ticker, -1.0); return -1.0; @@ -265,20 +265,15 @@ public class ProfitCalculation { 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 getHoldingsList() { @@ -317,8 +312,8 @@ public class ProfitCalculation { String url = "https://data.alpaca.markets/v1/bars/" + "day?" + "symbols=SPY" - + "&start=" + startTime - + "&end=" + endTime; + + "&start=" + startTime.toString() + "T09:30:00-04:00" + + "&end=" + endTime.toString() + "T09:30:00-04:00"; HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() diff --git a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index d1fc9be..f435c7c 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -12,6 +12,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.time.LocalDate; import java.util.List; import java.sql.Date; import java.time.Instant; @@ -55,7 +56,14 @@ public class ProfitCalculationTest { //price of GME at end time is 154.69 List trade = profitCalculation.getHoldingsList(); //buy with no sell - assertEquals(3842.25, trade.get(0).getUnrealizedGain(),6); + assertEquals(trade.get(0).getUnrealizedGain(), 3842.25,.25); + assertEquals(trade.get(0).getRealizedGain(), 0, .01); + + + profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "Don", new Date(1618234200000l), + new Date(1618814264000l)); + + assertEquals(profitCalculation.compareToSP500(), .01464, .001); } diff --git a/tatus b/tatus new file mode 100644 index 0000000..29e3798 --- /dev/null +++ b/tatus @@ -0,0 +1,52 @@ +diff --git a/data/mock_tradeClarks.sqlite3 b/data/mock_tradeClarks.sqlite3 +index 980c539..e816ee8 100644 +Binary files a/data/mock_tradeClarks.sqlite3 and b/data/mock_tradeClarks.sqlite3 differ +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 31bf7a3..c4a7814 100644 +--- a/src/main/java/edu/brown/cs/student/term/Main.java ++++ b/src/main/java/edu/brown/cs/student/term/Main.java +@@ -47,12 +47,21 @@ public final class Main { + //do a gui type thing + //runSparkServer((int) options.valueOf("port")); + } +-  +- HashMap commandHashMap = new HashMap<>(); +- commandHashMap.put("setup", new SetupCommand()); +- /** add commands to map here! */ +- REPL repl = new REPL(commandHashMap); +- repl.runREPL(); ++ ++ ProfitCalculation person = new ProfitCalculation(null, "Vincent", new Date(1515629591000L), new Date(1715507898000L)); ++ try { ++ person.setConnection("./data/mock_tradeClarks.sqlite3"); ++ } catch(Exception e) { ++ e.printStackTrace(); ++ } ++ ++ person.calculateGains(); ++ ++// HashMap commandHashMap = new HashMap<>(); ++// commandHashMap.put("setup", new SetupCommand()); ++// /** add commands to map here! */ ++// REPL repl = new REPL(commandHashMap); ++// repl.runREPL(); + } +  +  +diff --git a/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java b/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java +index aa1bc09..94f87f7 100644 +--- a/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java ++++ b/src/main/java/edu/brown/cs/student/term/ProfitCalculation.java +@@ -265,10 +265,10 @@ public class ProfitCalculation { + double totalGains = unrealizedGains + realizedGains; +  + System.out.println("Money In: " + moneyInput); ++ System.out.println("SP500 on money In: " + (moneyInput * compareToSP500())); + System.out.println("Money Out: " + (moneyInput + totalGains)); +- System.out.println("NASDAQ on money In: " + (moneyInput * compareToSP500())); + System.out.println( +- "Total: " + totalGains + "| unrealized: " + unrealizedGains + " | realized: " + ++ "Total gain: " + totalGains + "| unrealized: " + unrealizedGains + " | realized: " + + realizedGains); + } +  -- cgit v1.2.3-70-g09d2 From 0508b076ac948a11bde14cfa9f5261796d890ef2 Mon Sep 17 00:00:00 2001 From: clarkohw Date: Mon, 19 Apr 2021 03:01:48 -0400 Subject: caching of prices profit --- .../edu/brown/cs/student/term/profit/ProfitCalculation.java | 1 + .../java/edu/brown/cs/student/ProfitCalculationTest.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) 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 fe12612..77c1c3a 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 @@ -251,6 +251,7 @@ public class ProfitCalculation { JSONArray object = new JSONObject(response.body()).getJSONArray(ticker); try { double endPrice = object.getJSONObject(object.length() - 1).getDouble("c"); + currentStockPrices.put(ticker, endPrice); return endPrice; } catch (JSONException e) { currentStockPrices.put(ticker, -1.0); diff --git a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index f435c7c..a4baa53 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -56,13 +56,17 @@ public class ProfitCalculationTest { //price of GME at end time is 154.69 List trade = profitCalculation.getHoldingsList(); //buy with no sell - assertEquals(trade.get(0).getUnrealizedGain(), 3842.25,.25); + assertEquals(trade.get(0).getUnrealizedGain(), 3842.25, .25); assertEquals(trade.get(0).getRealizedGain(), 0, .01); + } + + public void checkAPICalls() { + ProfitCalculation profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "Don", new Date(1618234200000l), + new Date(1618814264000l)); - profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "Don", new Date(1618234200000l), - new Date(1618814264000l)); - + //check sp500 calculation. 411.28 to 417.30 assertEquals(profitCalculation.compareToSP500(), .01464, .001); } -- cgit v1.2.3-70-g09d2 From f00c1c6e89db16b19267202c6982b980e736d5a0 Mon Sep 17 00:00:00 2001 From: clarkohw Date: Mon, 19 Apr 2021 16:15:58 -0400 Subject: more profit testing --- data/mock_tradeClarks.sqlite3 | Bin 53248 -> 0 bytes data/profit_testing.sqlite3 | Bin 0 -> 8192 bytes .../cs/student/term/profit/ProfitCalculation.java | 31 ++++----- .../brown/cs/student/term/profit/StockHolding.java | 26 +++++++ .../brown/cs/student/ProfitCalculationTest.java | 75 ++++++++++++++++++++- 5 files changed, 114 insertions(+), 18 deletions(-) delete mode 100644 data/mock_tradeClarks.sqlite3 create mode 100644 data/profit_testing.sqlite3 diff --git a/data/mock_tradeClarks.sqlite3 b/data/mock_tradeClarks.sqlite3 deleted file mode 100644 index 980c539..0000000 Binary files a/data/mock_tradeClarks.sqlite3 and /dev/null differ diff --git a/data/profit_testing.sqlite3 b/data/profit_testing.sqlite3 new file mode 100644 index 0000000..c3b32ee Binary files /dev/null and b/data/profit_testing.sqlite3 differ 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 77c1c3a..18b79be 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,7 @@ 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; @@ -228,11 +229,12 @@ public class ProfitCalculation { if (currentStockPrices.containsKey(ticker)) { return currentStockPrices.get(ticker); } else { + SimpleDateFormat localDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); String url = "https://data.alpaca.markets/v1/bars/" + "day?" + "symbols=" + ticker - + "&start=" + startTime - + "&end=" + endTime; + + "&start=" + localDateFormat.format(startTime) + + "&end=" + localDateFormat.format(endTime); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() @@ -278,6 +280,11 @@ public class ProfitCalculation { } public List getHoldingsList() { + if (conn == null) { + System.out.println("ERROR: No database connection"); + return new LinkedList<>(); + } + if (!tablesFilled) { organizeOrders(); getRealizedGains(); @@ -310,11 +317,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.toString() + "T09:30:00-04:00" - + "&end=" + endTime.toString() + "T09:30:00-04:00"; + + "&start=" + localDateFormat.format(startTime) + + "&end=" + localDateFormat.format(endTime); HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() @@ -350,6 +358,10 @@ public class ProfitCalculation { */ public Map getProfitMap() { Map profitMap = new HashMap<>(); + if (conn == null) { + System.out.println("ERROR: no database connection"); + return profitMap; + } try { PreparedStatement prep; prep = @@ -387,15 +399,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 diff --git a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index a4baa53..1291245 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -13,6 +13,8 @@ import org.junit.Before; import org.junit.Test; import java.time.LocalDate; +import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.sql.Date; import java.time.Instant; @@ -36,7 +38,7 @@ public class ProfitCalculationTest { @Before public void setUp() { try { - db = new DatabaseQuerier("data/lil_mock.sqlite3"); + db = new DatabaseQuerier("data/profit_testing.sqlite3"); } catch (Exception e) { System.out.println("DBQuerier Test, couldn't connect to db???"); } @@ -59,16 +61,83 @@ public class ProfitCalculationTest { assertEquals(trade.get(0).getUnrealizedGain(), 3842.25, .25); assertEquals(trade.get(0).getRealizedGain(), 0, .01); + //just sell + profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "SELL", new Date(1518010558000l), + new Date(1618698807000l)); + trade = profitCalculation.getHoldingsList(); + assertTrue(trade.isEmpty()); + assertEquals(profitCalculation.calculateGains(), 0, 0.001); + + tearDown(); } - public void checkAPICalls() { + @Test + public void otherBuySellCases() { + setUp(); + //buy and sell at same timestamp + ProfitCalculation profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "concurrentBS", new Date(1518010558000l), + new Date(1715629591000l)); + + assertEquals(profitCalculation.getProfitMap().get(100), 1, .01); + + //buys at multiple prices + profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "mulitpleBuyPrices", + new Date(1518010558000l), + new Date(1715629591000l)); + assertEquals(profitCalculation.getProfitMap().get(101), 1, .01); + assertEquals(profitCalculation.getMoneyInput(), 3750, .01); + assertEquals(profitCalculation.getHoldingsList().get(0).getRealizedGain(), 3750, 0.01); + + //left over holdings + profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "dontSellAll", + new Date(1518010558000l), + new Date(1715629591000l)); + + assertEquals(profitCalculation.getHoldingsList().get(0).getShares(), 25, .01); + tearDown(); + } + + @Test + public void testAPICalls() { ProfitCalculation profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "Don", new Date(1618234200000l), - new Date(1618814264000l)); + new Date(1618703800000l)); //check sp500 calculation. 411.28 to 417.30 assertEquals(profitCalculation.compareToSP500(), .01464, .001); + tearDown(); + + } + + @Test + public void databaseAndConnectionIssues() { + //no database connection + ProfitCalculation profitCalculation = + new ProfitCalculation(null, "Don", new Date(1518010558000l), + new Date(1618698807000l)); + assertEquals(profitCalculation.getProfitMap(), new HashMap<>()); + + assertEquals(profitCalculation.getHoldingsList(), new LinkedList<>()); + + setUp(); + //invalid person + profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "1234", new Date(1518010558000l), + new Date(1618698807000l)); + assertEquals(profitCalculation.getHoldingsList(), new LinkedList<>()); + + + //invalid stock ticker + profitCalculation = + new ProfitCalculation(DatabaseQuerier.getConn(), "invalidTicker", new Date(1518010558000l), + new Date(1618698807000l)); + assertEquals(profitCalculation.getHoldingsList().get(0).getRealizedGain(), 0, .01); + assertEquals(profitCalculation.getHoldingsList().get(0).getUnrealizedGain(), 0, .01); } -- cgit v1.2.3-70-g09d2 From 43d42b0b88927a552c09bc32145f7e7ef2222652 Mon Sep 17 00:00:00 2001 From: clarkohw Date: Mon, 19 Apr 2021 18:49:44 -0400 Subject: exclude only sell people --- src/main/java/edu/brown/cs/student/term/Main.java | 4 ++++ .../brown/cs/student/term/hub/SuspicionRanker.java | 4 ---- .../cs/student/term/profit/ProfitCalculation.java | 26 +++++++++++++++++----- .../brown/cs/student/ProfitCalculationTest.java | 16 +++++++++++++ 4 files changed, 40 insertions(+), 10 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 df95ec0..6a20de3 100644 --- a/src/main/java/edu/brown/cs/student/term/Main.java +++ b/src/main/java/edu/brown/cs/student/term/Main.java @@ -151,6 +151,7 @@ public final class Main { public Object handle(Request request, Response response) throws Exception { //String str = request.body(); //xmlLinks = new JSONObject(str); //this is all the filedAt times and xml files + long startTime = System.currentTimeMillis(); try { System.err.println("LOG: Call to /data from frontend"); DatabaseQuerier db = SetupCommand.getDq(); @@ -166,6 +167,9 @@ public final class Main { List suspiciousHolders = ranker.getSuspicionScoreList(start, end); System.err.println("LOG: Making map " + getClass()); Map variables = ImmutableMap.of("holders", suspiciousHolders); + + System.out.println("DATA ENDPOINT " + ((System.currentTimeMillis() - startTime) / 1000) + "seconds"); + return GSON.toJson(variables); } catch (Exception e) { System.out.println("Error retrieving the suspicion ranks for GUI"); 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..c77eb9f 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 @@ -52,7 +52,6 @@ public class SuspicionRanker { HubSearch hub = new HubSearch(lm); Map holderToHubScore = hub.runHubSearch(start, end); - /* ProfitCalculation pc = new ProfitCalculation(DatabaseQuerier.getConn(), "", new Date(start.toEpochMilli()), @@ -68,7 +67,6 @@ 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(profitMax <= 0) { profitMax = Math.abs(getMinOfMap(profitMap)); } @@ -76,11 +74,9 @@ public class SuspicionRanker { /*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); 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 0ef87c3..ab76003 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 @@ -83,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 ""; } @@ -109,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"); @@ -284,11 +284,14 @@ public class ProfitCalculation { } public double calculateGains() { + if (!tablesFilled) { organizeOrders(); getRealizedGains(); tablesFilled = true; } + + double realizedGains = 0; for (double value : realizedGainsMap.values()) { @@ -383,13 +386,22 @@ public class ProfitCalculation { } 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_name having max(is_buy) = 1;"); ResultSet rs = prep.executeQuery(); + + long QUERY = System.currentTimeMillis(); + System.out.println((QUERY - START) + " query time"); + 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); @@ -398,6 +410,8 @@ public class ProfitCalculation { } } + + } catch (SQLException throwables) { System.out.println("ERROR: SQl error in profit calculation"); } diff --git a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index 1291245..4ca1780 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -140,5 +140,21 @@ public class ProfitCalculationTest { assertEquals(profitCalculation.getHoldingsList().get(0).getUnrealizedGain(), 0, .01); } + @Test + public void optimizationTest() { + try { + db = new DatabaseQuerier("data/trades.sqlite3"); + } catch (Exception e) { + System.out.println("DBQuerier Test, couldn't connect to db???"); + } + + ProfitCalculation profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "invalidTicker", new Date(1618223864000l), + new Date(1618483064000l)); + + long startTime = System.currentTimeMillis(); + profitCalculation.getProfitMap(); + System.out.println("DATA ENDPOINT " + ((System.currentTimeMillis() - startTime) / 1000) + "seconds"); + } + } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 01e50d7a0bbab78ffbbf858b03c6b365d2886397 Mon Sep 17 00:00:00 2001 From: Julia McCauley Date: Mon, 19 Apr 2021 19:18:48 -0400 Subject: added profit calcs back in, made visualization more dynamic --- react-frontend/src/components/Visualization.js | 8 ++++--- .../brown/cs/student/term/hub/SuspicionRanker.java | 26 ++++++++++++---------- .../brown/cs/student/ProfitCalculationTest.java | 10 ++++----- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/react-frontend/src/components/Visualization.js b/react-frontend/src/components/Visualization.js index 0a0c82a..9a837a1 100644 --- a/react-frontend/src/components/Visualization.js +++ b/react-frontend/src/components/Visualization.js @@ -28,18 +28,20 @@ function Visualization(props) { }); const getNodes = () => { let nodes = []; + const maxScore = props.data[0].suspicionScore; + const interval = maxScore / 4; props.data.forEach(hub => { if (hub.followers) { let colorVal = '#f6f7d4'; const score = hub.suspicionScore; - if(score > 0.8){ + if(score > (maxScore - interval)){ colorVal = '#d92027' } - if(score < 0.8 && score > 0.6){ + if(score <= (maxScore - interval) && score > (maxScore - interval*2)){ colorVal = '#f37121' } - if(score < 0.6 && score > 0.4){ + if(score <= (maxScore - interval*2) && score > (maxScore - interval*3)){ colorVal = '#fdca40' } nodes.push({ 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..0ca7258 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 @@ -52,13 +52,12 @@ public class SuspicionRanker { HubSearch hub = new HubSearch(lm); Map holderToHubScore = hub.runHubSearch(start, end); - /* - ProfitCalculation pc = new ProfitCalculation(DatabaseQuerier.getConn(), "", new Date(start.toEpochMilli()), new Date(end.toEpochMilli())); Map 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 +65,33 @@ 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; + if(!profitMap.containsKey(guy.getId())){ + continue; + } + double 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/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index 1291245..0721052 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -133,11 +133,11 @@ public class ProfitCalculationTest { //invalid stock ticker - profitCalculation = - new ProfitCalculation(DatabaseQuerier.getConn(), "invalidTicker", new Date(1518010558000l), - new Date(1618698807000l)); - assertEquals(profitCalculation.getHoldingsList().get(0).getRealizedGain(), 0, .01); - assertEquals(profitCalculation.getHoldingsList().get(0).getUnrealizedGain(), 0, .01); + //profitCalculation = + //new ProfitCalculation(DatabaseQuerier.getConn(), "invalidTicker", new Date(1518010558000l), + ///new Date(1618698807000l)); + //assertEquals(profitCalculation.getHoldingsList().get(0).getRealizedGain(), 0, .01); + //assertEquals(profitCalculation.getHoldingsList().get(0).getUnrealizedGain(), 0, .01); } -- cgit v1.2.3-70-g09d2 From 905460902c06e9df370c91109554ecbbeb730ac7 Mon Sep 17 00:00:00 2001 From: clarkohw Date: Mon, 19 Apr 2021 20:36:33 -0400 Subject: heavily optimized profit calculations --- data/profit_testing.sqlite3 | Bin 8192 -> 8192 bytes src/main/java/edu/brown/cs/student/term/Main.java | 6 +- .../brown/cs/student/term/hub/SuspicionRanker.java | 13 +- .../cs/student/term/profit/ProfitCalculation.java | 185 +++++++++++++++++++-- .../brown/cs/student/ProfitCalculationTest.java | 29 +--- 5 files changed, 187 insertions(+), 46 deletions(-) diff --git a/data/profit_testing.sqlite3 b/data/profit_testing.sqlite3 index c3b32ee..33dd2b8 100644 Binary files a/data/profit_testing.sqlite3 and b/data/profit_testing.sqlite3 differ 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 6a20de3..dd304c5 100644 --- a/src/main/java/edu/brown/cs/student/term/Main.java +++ b/src/main/java/edu/brown/cs/student/term/Main.java @@ -151,7 +151,6 @@ public final class Main { public Object handle(Request request, Response response) throws Exception { //String str = request.body(); //xmlLinks = new JSONObject(str); //this is all the filedAt times and xml files - long startTime = System.currentTimeMillis(); try { System.err.println("LOG: Call to /data from frontend"); DatabaseQuerier db = SetupCommand.getDq(); @@ -167,9 +166,6 @@ public final class Main { List suspiciousHolders = ranker.getSuspicionScoreList(start, end); System.err.println("LOG: Making map " + getClass()); Map variables = ImmutableMap.of("holders", suspiciousHolders); - - System.out.println("DATA ENDPOINT " + ((System.currentTimeMillis() - startTime) / 1000) + "seconds"); - return GSON.toJson(variables); } catch (Exception e) { System.out.println("Error retrieving the suspicion ranks for GUI"); @@ -189,7 +185,7 @@ public final class Main { ProfitCalculation profit = new ProfitCalculation(DatabaseQuerier.getConn(), person, startPeriod, endPeriod); List holdings = profit.getHoldingsList(); - double gains = profit.calculateGains(); + double gains = profit.calculateGainsSingle(); double sp500PercentGain = profit.compareToSP500(); Map 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 c77eb9f..9f17569 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 > V getMaxOfMap(Map map) { - Map.Entry maxEntry = Collections.max(map.entrySet(), Map.Entry.comparingByValue()); - return maxEntry.getValue(); + //Map.Entry maxEntry = Collections.max(map.entrySet(), Map.Entry.comparingByValue()); + Collection values = map.values(); + return Collections.max(map.values()); } private > V getMinOfMap(Map map) { @@ -82,10 +83,14 @@ public class SuspicionRanker { 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 ab76003..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 @@ -99,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); @@ -283,7 +283,7 @@ public class ProfitCalculation { } - public double calculateGains() { + public double calculateGainsSingle() { if (!tablesFilled) { organizeOrders(); @@ -389,33 +389,188 @@ public class ProfitCalculation { long START = System.currentTimeMillis(); prep = conn.prepareStatement( - "SELECT * From trades GROUP BY holder_name having max(is_buy) = 1;"); + "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"); + //System.out.println((QUERY - START) + " query time"); + //set of all people who have made both buy and sell orders + Set 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); +// } + 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 calculateGains(Set people) { + Map gainsMap = new HashMap<>(); + + //map of stock to list of buy orders, first element in list is oldest + Map>> sellMap = new HashMap<>(); + //map of stock to list of buy orders, first element in list is oldest + Map>> buyMap = new HashMap<>(); + //money input + Map 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()) { - int id = rs.getInt("holder_id"); - this.person = rs.getString("holder_name"); - resetClass(); + 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 oneElement = new LinkedList(); + 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 sells = sellHistoryMap.get(ticker); + LinkedList 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; + } + } + } + } - double gain = this.calculateGains(); - if (moneyInput == 0) { - profitMap.put(id, 0.0); + if (gainsMap.containsKey(person)) { + gainsMap.put(person, gainsMap.get(person) + realizedGain); } else { - profitMap.put(id, gain / moneyInput); + gainsMap.put(person, realizedGain); } } - - } catch (SQLException throwables) { - System.out.println("ERROR: SQl error in profit calculation"); + //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 profitMap; + + return gainsMap; } public double getMoneyInput() { diff --git a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java index 4ca1780..68f53fc 100644 --- a/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java +++ b/src/test/java/edu/brown/cs/student/ProfitCalculationTest.java @@ -67,7 +67,7 @@ public class ProfitCalculationTest { new Date(1618698807000l)); trade = profitCalculation.getHoldingsList(); assertTrue(trade.isEmpty()); - assertEquals(profitCalculation.calculateGains(), 0, 0.001); + assertEquals(profitCalculation.calculateGainsSingle(), 0, 0.001); tearDown(); } @@ -80,16 +80,18 @@ public class ProfitCalculationTest { new ProfitCalculation(DatabaseQuerier.getConn(), "concurrentBS", new Date(1518010558000l), new Date(1715629591000l)); - assertEquals(profitCalculation.getProfitMap().get(100), 1, .01); + Map map = profitCalculation.getProfitMap(); + + assertEquals(map.get(100), 1, .01); //buys at multiple prices profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "mulitpleBuyPrices", new Date(1518010558000l), new Date(1715629591000l)); - assertEquals(profitCalculation.getProfitMap().get(101), 1, .01); - assertEquals(profitCalculation.getMoneyInput(), 3750, .01); + assertEquals(profitCalculation.getHoldingsList().get(0).getRealizedGain(), 3750, 0.01); + assertEquals(profitCalculation.getMoneyInput(), 3750, .01); //left over holdings profitCalculation = @@ -136,25 +138,8 @@ public class ProfitCalculationTest { profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "invalidTicker", new Date(1518010558000l), new Date(1618698807000l)); - assertEquals(profitCalculation.getHoldingsList().get(0).getRealizedGain(), 0, .01); - assertEquals(profitCalculation.getHoldingsList().get(0).getUnrealizedGain(), 0, .01); - } + assertTrue(profitCalculation.getHoldingsList().isEmpty()); - @Test - public void optimizationTest() { - try { - db = new DatabaseQuerier("data/trades.sqlite3"); - } catch (Exception e) { - System.out.println("DBQuerier Test, couldn't connect to db???"); - } - - ProfitCalculation profitCalculation = new ProfitCalculation(DatabaseQuerier.getConn(), "invalidTicker", new Date(1618223864000l), - new Date(1618483064000l)); - - long startTime = System.currentTimeMillis(); - profitCalculation.getProfitMap(); - System.out.println("DATA ENDPOINT " + ((System.currentTimeMillis() - startTime) / 1000) + "seconds"); } - } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 2004a6c76be21d37367624a4ed7c00825e969143 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Tue, 20 Apr 2021 00:18:19 -0400 Subject: Random merge --- react-frontend/src/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/react-frontend/src/App.js b/react-frontend/src/App.js index 0a6e6c1..e242115 100644 --- a/react-frontend/src/App.js +++ b/react-frontend/src/App.js @@ -49,6 +49,7 @@ function App() { .then(res => res.json()) .then(data => { //TODO: optimize this + console.log(data.holders.length); const sliced = data.holders.slice(0, 500); console.log(sliced); setData(sliced); -- cgit v1.2.3-70-g09d2 From bd050926124a6eceaf17ab8426ee734c6148352f Mon Sep 17 00:00:00 2001 From: clarkohw Date: Tue, 20 Apr 2021 00:38:18 -0400 Subject: can search trades by holder id --- react-frontend/src/components/HubList.js | 53 ++++++++++++++--------- react-frontend/src/components/InvestorInfo.js | 60 +++++++++++++-------------- 2 files changed, 63 insertions(+), 50 deletions(-) diff --git a/react-frontend/src/components/HubList.js b/react-frontend/src/components/HubList.js index c9a5156..8aeb013 100644 --- a/react-frontend/src/components/HubList.js +++ b/react-frontend/src/components/HubList.js @@ -4,18 +4,18 @@ import Hub from "./Hub.js"; import InvestorInfo from "./InvestorInfo.js"; // CSS import -import '../css/UserCheckin.css'; +import "../css/UserCheckin.css"; /** * Component that build the checkin list and displays checkin info. - * @returns {import('react').HtmlHTMLAttributes} A div with the hubs + * @returns {import('react').HtmlHTMLAttributes} A div with the hubs * in a vertical layout. */ function HubList(props) { const [hubItems, setHubItems] = useState([]); const [isSelected, setIsSelected] = useState(false); - const [name, setName] = useState(''); - + const [name, setName] = useState(""); + /** * Loads new the checkins into the current cache/map of hubs. */ @@ -23,41 +23,54 @@ function HubList(props) { // sort and create the elemnts let hubs = []; //const sorted = props.data.sort((a, b) => b.suspicionScore - a.suspicionScore); - props.data.forEach(hub => hubs.push( - - )); + props.data.forEach((hub) => + hubs.push( + + ) + ); setHubItems(hubs); - } + }; const getName = () => { - props.data.forEach(hub => { + props.data.forEach((hub) => { if (hub.id === props.selected) { setName(hub.name); } - }) - setName(''); - } + }); + setName(""); + }; - // React hook that updates when the hubs are recalculated useEffect(() => updateHubItems(), [props.data]); //React hook to show data for an investor useEffect(() => { - setIsSelected(true) + setIsSelected(true); getName(); + console.log("DEVLOG"); }, [props.selected]); return (
-
-

Suspicion Ranks

-
    {hubItems}
-
- +
+

Suspicion Ranks

+
    {hubItems}
+
+
); } -export default HubList; \ No newline at end of file +export default HubList; diff --git a/react-frontend/src/components/InvestorInfo.js b/react-frontend/src/components/InvestorInfo.js index d368984..703c4c6 100644 --- a/react-frontend/src/components/InvestorInfo.js +++ b/react-frontend/src/components/InvestorInfo.js @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; // CSS import -import '../css/UserCheckin.css'; +import "../css/UserCheckin.css"; /** * Componenet for checkins. Has a toggle to show more info. @@ -12,40 +12,40 @@ import '../css/UserCheckin.css'; function InvestorInfo(props) { const [info, setInfo] = useState({}); - const toEpochMilli = date => Date.parse(date); + const toEpochMilli = (date) => Date.parse(date); const getInfo = () => { - console.log({ + console.log({ person: props.name, - start: toEpochMilli(props.dates.start), - end: toEpochMilli(props.dates.end) + start: toEpochMilli(props.dates.start), + end: toEpochMilli(props.dates.end), }); - if (props.name === "") { + if (props.name === "Mathews Krista Jean") { return; } - + fetch("http://localhost:4567/profit", { - method: "POST", - body: JSON.stringify({ - person: props.name, - start: toEpochMilli(props.dates.start), - end: toEpochMilli(props.dates.end) - }), - headers: { - "Content-Type": "application/json", - }, - credentials: "same-origin" - }) - .then(res => { - console.log(res); - res.json(); - }) - .then(data => { - console.log(data); - setInfo(data); + method: "POST", + body: JSON.stringify({ + person: props.name, + start: toEpochMilli(props.dates.start), + end: toEpochMilli(props.dates.end), + }), + headers: { + "Content-Type": "application/json", + }, + credentials: "same-origin", }) - .catch(err => console.log(err)); - } + .then((res) => { + console.log(res); + res.json(); + }) + .then((data) => { + console.log(data); + setInfo(data); + }) + .catch((err) => console.log(err)); + }; /* const coords = userCoords.map((coord, index) => @@ -54,13 +54,13 @@ function InvestorInfo(props) { );*/ - useEffect(() => getInfo(), [props.name, props.isSelected, props.personId]) + useEffect(() => getInfo(), [props.name, props.isSelected, props.personId]); return ( -