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