aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java92
-rw-r--r--src/main/java/edu/brown/cs/student/term/Main.java23
-rw-r--r--src/main/java/edu/brown/cs/student/term/hub/Holder.java42
-rw-r--r--src/main/java/edu/brown/cs/student/term/hub/HubSearch.java45
-rw-r--r--src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java94
-rw-r--r--src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java4
-rw-r--r--src/main/java/edu/brown/cs/student/term/repl/Command.java6
-rw-r--r--src/main/java/edu/brown/cs/student/term/repl/REPL.java1
-rw-r--r--src/main/java/edu/brown/cs/student/term/repl/commands/SetupCommand.java71
-rw-r--r--src/main/java/edu/brown/cs/student/term/trade/Trade.java58
-rw-r--r--src/test/java/edu/brown/cs/student/DBQuerierTest.java122
-rw-r--r--src/test/java/edu/brown/cs/student/LinkMapperTest.java48
12 files changed, 582 insertions, 24 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
new file mode 100644
index 0000000..1bb49c7
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/DatabaseQuerier.java
@@ -0,0 +1,92 @@
+package edu.brown.cs.student.term;
+import edu.brown.cs.student.term.hub.Holder;
+import edu.brown.cs.student.term.trade.Trade;
+
+import java.sql.*;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DatabaseQuerier{
+ private static Connection conn = null;
+
+ //TODO: Be prepared to overhaul this to account for IDs
+ /**
+ * Makes a database querier for a particular sqlite database
+ * @param filename - String representing filepath of database
+ * @throws SQLException
+ * @throws ClassNotFoundException
+ */
+ public DatabaseQuerier(String filename) throws SQLException,
+ ClassNotFoundException {
+ Class.forName("org.sqlite.JDBC");
+ String urlToDB = "jdbc:sqlite:" + filename;
+ // AutoClosable TRY-WITH-RESOURCES ensures database connection will be closed when it is done
+ conn = DriverManager.getConnection(urlToDB);
+ }
+
+ public static Connection getConn() {
+ return conn;
+ }
+
+ /**
+ * Gets the names of all stocks traded between start and end ddate
+ * @param startDate - the start date
+ * @param endDate - the end date
+ * @return a list of stock names
+ * @throws SQLException
+ */
+ public List<String> getRecentStocks(Instant startDate, Instant endDate) throws SQLException {
+ List<String> stocks = new ArrayList<>();
+
+ PreparedStatement prep = conn.prepareStatement(
+ "SELECT DISTINCT stock_name FROM trades WHERE trade_timestamp <= ? AND trade_timestamp >= ?");
+
+ prep.setLong(1, endDate.toEpochMilli());
+ prep.setLong(2, startDate.toEpochMilli());
+ ResultSet rs = prep.executeQuery();
+
+ while(rs.next()){
+ stocks.add(rs.getString(1));
+ }
+ return stocks;
+ }
+
+ //TODO: Fill these in
+ public List<List<Trade>> getAllTradesByStock(Instant startDate, Instant endDate) throws SQLException {
+ List<List<Trade>> allTrades = new ArrayList<>();
+ List<String> stocks = getRecentStocks(startDate, endDate);
+ //get the buys and sells for each stock
+ for(String stock: stocks){
+ allTrades.add(getTradeByStock(stock, 1, startDate, endDate));
+ allTrades.add(getTradeByStock(stock, 0, startDate, endDate));
+ }
+ return allTrades;
+ }
+
+ public List<Trade> getTradeByStock(String stock, int isBuy, Instant startDate, Instant endDate) throws SQLException{
+ List<Trade> trades = new ArrayList<>();
+
+ PreparedStatement prep = conn.prepareStatement(
+ "SELECT * FROM trades WHERE (stock_name = ? AND is_buy = ?) "
+ + "AND (trade_timestamp <= ? AND trade_timestamp >= ?) ORDER BY trade_timestamp");
+
+ prep.setString(1, stock);
+ prep.setInt(2, isBuy);
+ prep.setLong(3, endDate.toEpochMilli());
+ prep.setLong(4, startDate.toEpochMilli());
+ ResultSet rs = prep.executeQuery();
+
+ while(rs.next()){
+ trades.add(new Trade(rs.getInt(1), rs.getString(2),
+ rs.getLong(4), rs.getInt(5),
+ rs.getInt(6), new Holder(rs.getInt(7), rs.getString(3))));
+ }
+
+ return trades;
+ }
+
+
+
+
+}
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 3124ea4..bd9e096 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 edu.brown.cs.student.term.repl.Command;
import edu.brown.cs.student.term.repl.REPL;
+import edu.brown.cs.student.term.repl.commands.SetupCommand;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
@@ -47,23 +48,11 @@ public final class Main {
//runSparkServer((int) options.valueOf("port"));
}
-
- ProfitCalculation person = new ProfitCalculation(null, "Sophie", new Date(1515629591000L), new Date(1715507898000L));
- try {
- person.setConnection("./data/mock_tradeClarks.sqlite3");
- } catch(Exception e) {
- e.printStackTrace();
- }
-
- person.calculateGains();
- person.compareToNASDAQ();
-
-// HashMap<String, Command> commandHashMap = new HashMap<>();
-// /** add commands to map here! */
-// REPL repl = new REPL(commandHashMap);
-// repl.runREPL();
-
- // TODO: Process commands in a REPL
+ HashMap<String, Command> 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/hub/Holder.java b/src/main/java/edu/brown/cs/student/term/hub/Holder.java
new file mode 100644
index 0000000..21c0aea
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/hub/Holder.java
@@ -0,0 +1,42 @@
+package edu.brown.cs.student.term.hub;
+
+import java.util.Objects;
+
+public class Holder {
+ private int id;
+ private String name;
+
+ public Holder(int id, String name){
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "Holder{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Holder holder = (Holder) o;
+ return id == holder.id && Objects.equals(name, holder.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+}
diff --git a/src/main/java/edu/brown/cs/student/term/hub/HubSearch.java b/src/main/java/edu/brown/cs/student/term/hub/HubSearch.java
new file mode 100644
index 0000000..4c382ec
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/hub/HubSearch.java
@@ -0,0 +1,45 @@
+package edu.brown.cs.student.term.hub;
+
+public class HubSearch {
+
+ /*
+ def pageRank(pList: List[Page]): mutable.HashMap[Int, Double] = {
+ val n = pList.length
+ val weights = new Array[Double](n)
+ val r = new Array[Double](n)
+ val rPrime = Array.fill[Double](n)(1.0 / n)
+
+ while (!distance(r, rPrime)) {
+ Array.copy(rPrime, 0, r, 0, n)
+ for (j <- 0 to n - 1) {
+ rPrime(j) = 0
+ for (k <- 0 to n - 1) {
+ val wjk = getWeight(pList(j), pList(k), n)
+ rPrime(j) = (rPrime(j) + (wjk * r(k)))
+ }
+ }
+ }
+
+ val pageRank = new mutable.HashMap[Int, Double]()
+
+ for (i <- 0 to rPrime.length - 1) {
+ pageRank.put(pList(i).getID, rPrime(i))
+ }
+ pageRank
+ }
+
+ def getWeight(j: Page, k: Page, n: Int): Double = {
+
+ val links = k.getLinks
+ val nk = links.size
+
+ if (links.contains(j.getTitle.toLowerCase)) {
+ ((0.15 / n) + ((1 - 0.15) * (1.0 / nk)))
+ } else if (nk == 0) {
+ ((0.15 / n) + ((1 - 0.15) * (1.0 / n)))
+ } else {
+ (0.15 / n)
+ }
+ }
+ */
+}
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
new file mode 100644
index 0000000..f4ec0a7
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/hub/LinkMapper.java
@@ -0,0 +1,94 @@
+package edu.brown.cs.student.term.hub;
+
+import edu.brown.cs.student.term.DatabaseQuerier;
+import edu.brown.cs.student.term.repl.commands.SetupCommand;
+import edu.brown.cs.student.term.trade.Trade;
+
+import java.sql.SQLException;
+import java.time.Instant;
+import java.util.*;
+
+public class LinkMapper {
+
+ //not strictly necessary but may be nice to maintain
+ private List<List<Trade>> allTrades = new ArrayList<>();
+ private Map<Holder, List<Trade>> personToTradesMap = new HashMap<>();
+ private Map<Holder, List<Holder>> personToFollowers = new HashMap<>();
+ private DatabaseQuerier databaseQuerier;
+
+ public LinkMapper(DatabaseQuerier db){
+ this.databaseQuerier = db;
+ }
+
+ /**
+ * Returns the person => trades map as is, does not update it
+ * @return person to trades map
+ */
+ public Map<Holder, List<Trade>> getPersonToTradesMap() {
+ return personToTradesMap;
+ }
+
+ /**
+ * Returns the person => follower map as is, does not update it
+ * @return person to follower map
+ */
+ public Map<Holder, List<Holder>> getPersonToFollowers() {
+ return personToFollowers;
+ }
+
+ /**
+ * The links between people and their followers who made the same trade
+ * in the same time period
+ * @return the newly updated link map
+ */
+ public Map<Holder, List<Holder>> makeFollowerLinks(Instant start, Instant end){
+ if(databaseQuerier != null){
+ try{
+ List<List<Trade>> allTrades = databaseQuerier.getAllTradesByStock(start, end);
+ //reset the map to be blank
+ personToFollowers = new HashMap<>();
+
+ for(List<Trade> tradeList : allTrades){
+ convertTradeListToFollowerMap(tradeList);
+ }
+
+ //System.out.println(personToFollowers.toString());
+ System.out.println("num people in map: " + personToFollowers.size());
+
+ } catch(SQLException e) {
+ System.out.println("ERROR: SQL Error while retrieving trade list");
+ }
+ } else {
+ System.out.println("ERROR: No database loaded in yet");
+ }
+
+ return personToFollowers;
+ }
+
+ private void convertTradeListToFollowerMap(List<Trade> tradeList){
+ List<Holder> holderList = new ArrayList<>();
+
+ //gets in order list of people
+ for (Trade trade : tradeList) {
+ holderList.add(trade.getHolder());
+ }
+
+ Set<Holder> followers = new HashSet<>(holderList);
+ Set<Holder> encountered = new HashSet<>();
+
+ for(Holder current: holderList){
+ //want to check if we've already gotten followers for this trade for this holder
+ if(!encountered.contains(current)){
+ encountered.add(current);
+ followers.remove(current);
+ if(personToFollowers.containsKey(current)){
+ //TODO: this probably makes it O(n^2), might want to optimize later
+ personToFollowers.get(current).addAll(followers);
+ } else {
+ List<Holder> currentFollowers = new ArrayList<>(followers);
+ personToFollowers.put(current, currentFollowers);
+ }
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000..a1196d8
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/hub/SuspicionRanker.java
@@ -0,0 +1,4 @@
+package edu.brown.cs.student.term.hub;
+
+public class SuspicionRanker {
+}
diff --git a/src/main/java/edu/brown/cs/student/term/repl/Command.java b/src/main/java/edu/brown/cs/student/term/repl/Command.java
index f8b0b04..056c7df 100644
--- a/src/main/java/edu/brown/cs/student/term/repl/Command.java
+++ b/src/main/java/edu/brown/cs/student/term/repl/Command.java
@@ -10,10 +10,4 @@ public interface Command {
* @param args arguments for the command
*/
String run(String[] args);
-
- /**
- * Used to print command output to GUI.
- * @return String representing neighbors found by NeighborsCommand and RadiusCommand commands
- */
- String toString();
}
diff --git a/src/main/java/edu/brown/cs/student/term/repl/REPL.java b/src/main/java/edu/brown/cs/student/term/repl/REPL.java
index 0be7e3f..a1d5c23 100644
--- a/src/main/java/edu/brown/cs/student/term/repl/REPL.java
+++ b/src/main/java/edu/brown/cs/student/term/repl/REPL.java
@@ -1,5 +1,4 @@
package edu.brown.cs.student.term.repl;
-import edu.brown.cs.student.term.repl.Command;
import java.io.BufferedReader;
import java.io.IOException;
diff --git a/src/main/java/edu/brown/cs/student/term/repl/commands/SetupCommand.java b/src/main/java/edu/brown/cs/student/term/repl/commands/SetupCommand.java
new file mode 100644
index 0000000..e19117c
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/repl/commands/SetupCommand.java
@@ -0,0 +1,71 @@
+package edu.brown.cs.student.term.repl.commands;
+
+import edu.brown.cs.student.term.DatabaseQuerier;
+import edu.brown.cs.student.term.repl.Command;
+import edu.brown.cs.student.term.trade.Trade;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.List;
+
+public class SetupCommand implements Command {
+
+ private String error;
+ private static DatabaseQuerier dq;
+
+ @Override
+ /**
+ * Sets up connection to the database
+ * Returns an empty string if no errors, non empty with error message otherwise
+ */
+ public String run(String[] args) {
+ error = "";
+ if(args.length == 1){
+ try{
+ dq = new DatabaseQuerier(args[0]);
+ } catch (Exception e) {
+ error = "ERROR: Could not connect to database. Ensure this is a valid database.";
+ System.out.println(error);
+ return error;
+ }
+ } else {
+ error = "ERROR: Incorrect number of arguments for setup command";
+ System.out.println(error);
+ return error;
+ }
+
+ try{
+ /** Just for testing purposes **/
+ //12 am on 3/12 in UTC
+ Instant start = Instant.parse("2021-03-12T05:00:00.00Z");
+ //12 am on 3/27 in UTC
+ Instant end = Instant.parse("2021-03-27T05:00:00.00Z");
+
+ System.out.println(end.toEpochMilli());
+ System.out.println(start.getEpochSecond());
+
+ ZonedDateTime zdt = ZonedDateTime.ofInstant(start, ZoneId.of("America/New_York"));
+ System.out.println(zdt.toString());
+ List<List<Trade>> trades = dq.getAllTradesByStock(start, end);
+
+ int sum = 0;
+ for(List<Trade> t: trades){
+ System.out.println(t);
+ sum += t.size();
+ }
+
+ System.out.println("num of trades: " + sum);
+
+
+ } catch(Exception e){
+ e.printStackTrace();
+ }
+
+ return error;
+ }
+
+ public static DatabaseQuerier getDq() {
+ return dq;
+ }
+}
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
new file mode 100644
index 0000000..13f7ae1
--- /dev/null
+++ b/src/main/java/edu/brown/cs/student/term/trade/Trade.java
@@ -0,0 +1,58 @@
+package edu.brown.cs.student.term.trade;
+
+import edu.brown.cs.student.term.hub.Holder;
+
+public class Trade {
+
+ private int id;
+ private String stock;
+ private int isBuy;
+ private double timestamp;
+ private Holder holder;
+ private int numShares;
+
+ public Trade(int id, String stockName, double ts, int buy, int shares, Holder holder){
+ this.id = id;
+ this.stock = stockName;
+ this.isBuy = buy;
+ this.timestamp = ts;
+ this.holder = holder;
+ this.numShares = shares;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public boolean isBuy() {
+ if(isBuy == 1){
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public double getTimestamp() {
+ return timestamp;
+ }
+
+ public String getStock() {
+ return stock;
+ }
+
+ public Holder getHolder() {
+ return holder;
+ }
+
+ @Override
+ public String toString() {
+ return "Trade{" +
+ "id=" + id +
+ ", stock='" + stock + '\'' +
+ ", isBuy=" + isBuy +
+ ", timestamp=" + timestamp +
+ ", holder='" + holder + '\'' +
+ ", numShares=" + numShares +
+ '}';
+ }
+}
diff --git a/src/test/java/edu/brown/cs/student/DBQuerierTest.java b/src/test/java/edu/brown/cs/student/DBQuerierTest.java
new file mode 100644
index 0000000..d813969
--- /dev/null
+++ b/src/test/java/edu/brown/cs/student/DBQuerierTest.java
@@ -0,0 +1,122 @@
+package edu.brown.cs.student;
+
+import java.io.PrintStream;
+import java.sql.SQLException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.brown.cs.student.term.DatabaseQuerier;
+import edu.brown.cs.student.term.repl.commands.SetupCommand;
+import edu.brown.cs.student.term.trade.Trade;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+//TODO: Write more tests for methods besides stock by name
+public class DBQuerierTest {
+
+ /** these should span the entire mock dataset */
+ //12 am on 3/11 in UTC
+ private Instant start = Instant.parse("2021-03-11T05:00:00.00Z");
+ //12 am on 3/28 in UTC
+ private Instant end = Instant.parse("2021-03-28T05:00:00.00Z");
+
+ private DatabaseQuerier db;
+
+ @Before
+ public void setUp() {
+ try{
+ db = new DatabaseQuerier("data/mock_trades.sqlite3");
+ } catch(Exception e){
+ System.out.println("DBQuerier Test, couldn't connect to db???");
+ }
+ }
+
+ /*
+ * try{
+
+ } catch(Exception e) {
+ System.out.println("Error in test");
+ }*/
+
+ @After
+ public void tearDown() {
+ db = null;
+ }
+
+ @Test
+ public void testNonExistentStock(){
+ setUp();
+ try{
+ List<Trade> fakeStockList = db.getTradeByStock("NONO", 1, start, end);
+ assertTrue(fakeStockList.isEmpty());
+
+ } catch(Exception e) {
+ System.out.println("Error in test");
+ }
+ tearDown();
+ }
+
+ @Test
+ public void testFlippedDates(){
+ setUp();
+ try{
+ List<Trade> gmeBadDatesList = db.getTradeByStock("GME", 1, end, start);
+ assertTrue(gmeBadDatesList.isEmpty());
+
+ } catch(Exception e) {
+ System.out.println("Error in test");
+ }
+ tearDown();
+ }
+
+ @Test
+ public void testTradeByStockNameBuy(){
+ setUp();
+ try{
+ List<Trade> gmeBuyList = db.getTradeByStock("GME", 1, start, end);
+ System.out.println(gmeBuyList);
+ assertEquals(gmeBuyList.size(), 6);
+ assertEquals(gmeBuyList.get(0).getId(), 482);
+ assertEquals(gmeBuyList.get(3).getId(), 149);
+ assertEquals(gmeBuyList.get(4).getId(), 275);
+ assertEquals(gmeBuyList.get(5).getId(), 30);
+
+
+ List<Trade> teslaBuyList = db.getTradeByStock("TSLA", 1, start, end);
+ assertEquals(teslaBuyList.size(), 16);
+ assertEquals(teslaBuyList.get(0).getId(), 328);
+ assertEquals(teslaBuyList.get(7).getId(), 241);
+ assertEquals(teslaBuyList.get(15).getId(), 774);
+
+ } catch(Exception e) {
+ System.out.println("Error in testTradeByStockName");
+ }
+
+ tearDown();
+ }
+
+ @Test
+ public void testTradeByNameOrdering(){
+ setUp();
+
+ try{
+ List<Trade> gmeSellList = db.getTradeByStock("GME", 0, start, end);
+ for(int i = 1; i < gmeSellList.size(); i++){
+ assertTrue(gmeSellList.get(i-1).getTimestamp() < gmeSellList.get(i).getTimestamp());
+ }
+
+ List<Trade> amznBuyList = db.getTradeByStock("AMZN", 1, start, end);
+ for(int i = 1; i < amznBuyList.size(); i++){
+ assertTrue(amznBuyList.get(i-1).getTimestamp() < amznBuyList.get(i).getTimestamp());
+ }
+
+ } catch(Exception e) {
+ System.out.println("Error in test");
+ }
+ tearDown();
+ }
+}
diff --git a/src/test/java/edu/brown/cs/student/LinkMapperTest.java b/src/test/java/edu/brown/cs/student/LinkMapperTest.java
new file mode 100644
index 0000000..9b46d5e
--- /dev/null
+++ b/src/test/java/edu/brown/cs/student/LinkMapperTest.java
@@ -0,0 +1,48 @@
+package edu.brown.cs.student;
+
+import edu.brown.cs.student.term.DatabaseQuerier;
+import edu.brown.cs.student.term.hub.LinkMapper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Instant;
+
+public class LinkMapperTest {
+
+ /** these should span the entire mock dataset */
+ //12 am on 3/11 in UTC
+ private Instant start = Instant.parse("2021-03-11T05:00:00.00Z");
+ //12 am on 3/28 in UTC
+ private Instant end = Instant.parse("2021-03-28T05:00:00.00Z");
+
+ private DatabaseQuerier db;
+
+ @Before
+ public void setUp() {
+ try{
+ db = new DatabaseQuerier("data/mock_trades.sqlite3");
+ } catch(Exception e){
+ System.out.println("DBQuerier Test, couldn't connect to db???");
+ }
+ }
+
+ /*
+ * try{
+
+ } catch(Exception e) {
+ System.out.println("Error in test");
+ }*/
+
+ @After
+ public void tearDown() {
+ db = null;
+ }
+
+ @Test
+ public void testMapper(){
+ setUp();
+ LinkMapper lm = new LinkMapper(db);
+ lm.makeFollowerLinks(start, end);
+ }
+}