threadMap = new HashSet<>();
+
+ /**
+ * The initial method called when execution begins.
+ *
+ * @param args
+ * An array of command line arguments
+ */
+ public static void main(String[] args) {
+ new Main(args).run();
+ }
+
+ private String[] args;
+
+ private Main(String[] args) {
+ this.args = args;
+ }
+
+ private void run() {
+ // Parse command line arguments
+ OptionParser parser = new OptionParser();
+ parser.accepts("gui");
+ parser.accepts("port").withRequiredArg().ofType(Integer.class)
+ .defaultsTo(DEFAULT_PORT);
+ OptionSet options = parser.parse(args);
+
+ if (options.has("gui")) {
+ runSparkServer((int) options.valueOf("port"));
+ }
+
+ DatabaseConnection.setConnection("data/db.sqlite3");
+ }
+
+ private static FreeMarkerEngine createEngine() {
+ Configuration config = new Configuration();
+ File templates
+ = new File("src/main/resources/spark/template/freemarker");
+ try {
+ config.setDirectoryForTemplateLoading(templates);
+ } catch (IOException ioe) {
+ System.out.printf("ERROR: Unable use %s for template loading.%n",
+ templates);
+ System.exit(1);
+ }
+ return new FreeMarkerEngine(config);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void runSparkServer(int port) {
+ Spark.port(port);
+ Spark.externalStaticFileLocation("src/main/resources/static");
+ Spark.exception(Exception.class, new ExceptionPrinter());
+
+ FreeMarkerEngine freeMarker = createEngine();
+
+ // Setup Spark Routes
+ this.setUpSparkRoutes(freeMarker);
+ }
+
+ private void setUpSparkRoutes(FreeMarkerEngine freeMarker) {
+ Spark.webSocket("/rsi", RsiWebSocket.class);
+ // Spark.webSocket("/walletSocket", WalletSocket.class);
+ Spark.get("/home", new CryptoFrontHandler(), freeMarker);
+ Spark.get("/about", new TermSparkHandlers.AboutHandler(), freeMarker);
+ Spark.get("/contact", new TermSparkHandlers.ContactHandler(),
+ freeMarker);
+ Spark.get("/news", new TermSparkHandlers.NewsHandler(), freeMarker);
+ Spark.get("/profile", new TermSparkHandlers.ProfileHandler(),
+ freeMarker);
+ Spark.get("/trades", new TradesHandler(), freeMarker);
+ Spark.get("/strategy", new TermSparkHandlers.StrategyHandler(),
+ freeMarker);
+ Spark.get("/strategyForm", new TermSparkHandlers.StrategyFormHandler(),
+ freeMarker);
+ Spark.get("/signup", new TermSparkHandlers.SignUpHandler(),
+ freeMarker);
+ Spark.post("/signUpUser", new UserHandler.AddUser());
+ Spark.get("/login", new TermSparkHandlers.LogInHandler(), freeMarker);
+ Spark.post("/checkUser", new UserHandler.CheckUser());
+ Spark.post("/verifyLogin", new UserHandler.VerifyLogin());
+ Spark.post("/getUserTrades", new UserHandler.GetUsersTrades());
+ Spark.post("/getWallet", new UserHandler.GetWalletInformation());
+
+ Spark.post("/updateKeys", new UserHandler.UpdatePublicPrivateKeys());
+ Spark.post("/checkKeyValidity", new UserHandler.CheckKeys());
+ Spark.post("/validKeysInDb", new UserHandler.CheckKeysAndInDB());
+ Spark.post("/validManual", new TradingHandler.ValidOrders());
+ Spark.post("/startFullTradeBuy",
+ new TradingHandler.FullTradeBuyHandler());
+ Spark.post("/startFullTradeSell",
+ new TradingHandler.FullTradeSellHandler());
+ Spark.post("/tradingAlgo", new TradingHandler.AlgorithmicTrader());
+ Spark.post("/populateValue", new UserHandler.startWalletUpdates());
+ Spark.post("/getGraphData", new UserHandler.getGraphInfo());
+
+ }
+
+ /**
+ * Display an error page when an exception occurs in the server.
+ *
+ * @author jj
+ */
+ private static class ExceptionPrinter implements ExceptionHandler {
+ @Override
+ public void handle(Exception e, Request req, Response res) {
+ res.status(500);
+ StringWriter stacktrace = new StringWriter();
+ try (PrintWriter pw = new PrintWriter(stacktrace)) {
+ pw.println("");
+ e.printStackTrace(pw);
+ pw.println("
");
+ }
+ res.body(stacktrace.toString());
+ }
+ }
+
+ private static String testDecimal(String lastPrice, double percentGain) {
+ String[] splits = lastPrice.split("\\.");
+ int decimalLength = splits[1].length();
+ double sellPrice = Double.valueOf(lastPrice);
+ double buyPrice = sellPrice - (sellPrice * percentGain);
+ BigDecimal bp = new BigDecimal(buyPrice, MathContext.DECIMAL64);
+ String[] s = bp.toString().split("\\.");
+ System.out.println(s[0] + ": " + s[1]);
+ int length = s[1].length();
+ String finalBuyPrice = "";
+ if (length > decimalLength) {
+ int difference = length - decimalLength;
+ String decimal = "";
+ decimal = s[1].substring(0, s[1].length() - difference);
+ StringBuilder sb = new StringBuilder();
+ sb.append(s[0]);
+ sb.append(".");
+ sb.append(decimal);
+ finalBuyPrice = sb.toString();
+ } else {
+ finalBuyPrice = bp.toString();
+ }
+ System.out.println("Final buy price is " + finalBuyPrice);
+ return finalBuyPrice;
+ }
+
+ private static String testBigDecimal(String lastPrice,
+ double percentGain) {
+ String[] splits = lastPrice.split("\\.");
+ int decimalLength = splits[1].length();
+ double sellPrice = Double.valueOf(lastPrice);
+ double buyPrice = sellPrice - (sellPrice * percentGain);
+ BigDecimal bd = new BigDecimal(buyPrice).setScale(decimalLength,
+ RoundingMode.HALF_DOWN);
+ System.out.println("BigDecimal price: " + bd.toPlainString());
+ return bd.toPlainString();
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/tests/BinanceAPITest.java b/src/main/java/edu/brown/cs/term/tests/BinanceAPITest.java
new file mode 100644
index 0000000..701f23e
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/tests/BinanceAPITest.java
@@ -0,0 +1,66 @@
+package edu.brown.cs.term.tests;
+
+import com.binance.api.client.BinanceApiAsyncRestClient;
+import com.binance.api.client.BinanceApiCallback;
+import com.binance.api.client.BinanceApiClientFactory;
+import com.binance.api.client.BinanceApiRestClient;
+import com.binance.api.client.BinanceApiWebSocketClient;
+import com.binance.api.client.domain.event.AggTradeEvent;
+import com.binance.api.client.exception.BinanceApiException;
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Tests the Binance API Functionality
+ */
+public class BinanceAPITest {
+ private String APIkey = "qW9xUO8kNpHHnxb0dJjStRrzE7tN0oTlPCbah4IWdxkgyAa5vqJXzSME6IYlDhrp";
+ private String secretKey = "PXEisYYJhKd6T36nC8mH05AoZl4gcyytcz1Gd5Ybin1iv6GeS27rXySsaAn3Z5Mp";
+
+
+ /**
+ * Tests if connection successfully connects to the server and gets the server
+ * time successfully.
+ */
+ @Test
+ public void assertConnectionToServer() {
+ BinanceApiClientFactory fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiRestClient client = fact.newRestClient();
+ client.ping();
+ assertNotNull(client);
+ long serverTime = client.getServerTime();
+ assertNotNull(serverTime);
+ assertTrue(serverTime > 0);
+ }
+
+ /**
+ * Successfully tests the Websockets functionality of binance
+ */
+ @Test
+ public void webSocketsBinance() {
+ BinanceApiClientFactory fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiWebSocketClient client = fact.newWebSocketClient();
+ for(int i = 0; i< 500000; i++) {
+ client.onAggTradeEvent("ethbtc", new BinanceApiCallback() {
+ @Override
+ public void onResponse(AggTradeEvent aggTradeEvent) throws BinanceApiException {
+ System.out.println(aggTradeEvent.getPrice());
+ assertTrue(true);
+ }
+ });
+ }
+ }
+
+ @Test
+ public void getPrice(){
+ BinanceApiClientFactory fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiRestClient client = fact.newRestClient();
+ System.out.println(client.getAllPrices());
+ }
+
+
+}
diff --git a/src/main/java/edu/brown/cs/term/tests/DatabaseTests.java b/src/main/java/edu/brown/cs/term/tests/DatabaseTests.java
new file mode 100644
index 0000000..0abdafa
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/tests/DatabaseTests.java
@@ -0,0 +1,102 @@
+package edu.brown.cs.term.tests;
+
+import edu.brown.cs.term.connection.DatabaseConnection;
+import edu.brown.cs.term.database.Trades;
+import edu.brown.cs.term.database.CrypthubUser;
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.assertEquals;
+
+public class DatabaseTests {
+
+ @Before
+ public void connect(){
+ DatabaseConnection.setConnection("data/db.sqlite3");
+ }
+
+ /**
+ * Checks if we can add a user successfully to the database.
+ */
+ @Test
+ public void checkAndAddUser(){
+ CrypthubUser user = new CrypthubUser();
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ String characterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ while(i<10){
+ sb.append(characterSet.charAt((int)(Math.random()*characterSet.length())));
+ i++;
+ }
+ assertTrue(user.checkUser(sb.toString()).contains("\"exists\":false"));
+ user.addUser(sb.toString(), "pass", "John", "Smith");
+ assertTrue(user.checkUser(sb.toString()).contains("\"exists\":true"));
+ }
+
+ /**
+ * Checks if a user exists in the database.
+ */
+ @Test
+ public void checkUser(){
+ CrypthubUser user = new CrypthubUser();
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ String characterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ while(i<10){
+ sb.append(characterSet.charAt((int)(Math.random()*characterSet.length())));
+ i++;
+ }
+ user.addUser(sb.toString(), "pass", "John", "Smith");
+ assertTrue(user.login(sb.toString(), "pass").contains("\"login_found\":true"));
+ assertTrue(user.login(sb.toString(), "pass1").contains("\"login_found\":false"));
+ }
+
+ /**
+ * Tests adding a public and private key to the DB.
+ */
+ @Test
+ public void addPublicPrivate(){
+ CrypthubUser user = new CrypthubUser();
+ int i = 0;
+ StringBuilder sb = new StringBuilder();
+ String characterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ while(i<10){
+ sb.append(characterSet.charAt((int)(Math.random()*characterSet.length())));
+ i++;
+ }
+ user.addUser(sb.toString(), "pass", "John", "Smith");
+ assertTrue(user.addPublicPrivateKey(sb.toString(), "test1",
+ "test2").contains("\"success\":true"));
+ assertTrue(user.addPublicPrivateKey("22", "test1",
+ "test2").contains("\"success\":false"));
+ }
+
+ /**
+ * tests getting a public and private key from the database.
+ */
+ @Test
+ public void getPublicPrivate(){
+ CrypthubUser user = new CrypthubUser();
+ System.out.println(user.getPublicPrivateKey("22BCWTTWTE").toString());
+ }
+
+ /**
+ * Tests getting the wallet information for all the assets above 0.
+ */
+ @Test
+ public void getWalletInformation(){
+ CrypthubUser user = new CrypthubUser();
+ assertTrue(user.wallet("js").contains("\"assets\":\"ETH\""));
+ }
+
+ @Test
+ public void insertValueIntoValueDatabase(){
+ CrypthubUser user = new CrypthubUser();
+ assertTrue(user.insertAmounts("js",
+ user.totalAmount("js")).contains("\"success\":true"));
+ }
+
+
+}
diff --git a/src/main/java/edu/brown/cs/term/tests/MultithreadingTests.java b/src/main/java/edu/brown/cs/term/tests/MultithreadingTests.java
new file mode 100644
index 0000000..b793477
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/tests/MultithreadingTests.java
@@ -0,0 +1,17 @@
+package edu.brown.cs.term.tests;
+
+import edu.brown.cs.term.connection.DatabaseConnection;
+import edu.brown.cs.term.thread.WalletThread;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MultithreadingTests {
+ @Before
+ public void setup(){
+ DatabaseConnection.setConnection("data/db.sqlite3");
+ }
+
+ @Test
+ public void WalletTest(){
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/BuyThread.java b/src/main/java/edu/brown/cs/term/thread/BuyThread.java
new file mode 100644
index 0000000..24935eb
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/BuyThread.java
@@ -0,0 +1,55 @@
+package edu.brown.cs.term.thread;
+
+import edu.brown.cs.term.trading.FullTradeBuy;
+
+public class BuyThread implements Runnable {
+ String name;
+ Thread t;
+ String symbol;
+ String buyPrice;
+ String sellPrice;
+ String stopPrice;
+ String quantity;
+ String username;
+
+ public BuyThread(String user, String thread, String symb, String bP,
+ String sP, String stopP, String quant) {
+ username = user;
+ name = thread;
+ symbol = symb;
+ sellPrice = sP;
+ buyPrice = bP;
+ stopPrice = stopP;
+ quantity = quant;
+ t = new Thread(this, name);
+ t.start();
+ }
+
+ @Override
+ public void run() {
+ try {
+ FullTradeBuy ftb = new FullTradeBuy(username);
+ ftb.fullTrade(symbol, buyPrice, sellPrice, stopPrice, quantity);
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted.");
+ }
+ }
+
+ public void setSellPrice(String sp) {
+ sellPrice = sp;
+ }
+
+ public void setBuyPrice(String bp) {
+ buyPrice = bp;
+ }
+
+ public void setStopPrice(String stop) {
+ stopPrice = stop;
+ }
+
+ public void setQuantity(String quant) {
+ quantity = quant;
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/MaThread.java b/src/main/java/edu/brown/cs/term/thread/MaThread.java
new file mode 100644
index 0000000..7b79c4e
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/MaThread.java
@@ -0,0 +1,46 @@
+package edu.brown.cs.term.thread;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.binance.api.client.domain.market.CandlestickInterval;
+
+import edu.brown.cs.term.trading.MovingAverage;
+
+public class MaThread implements Runnable{
+ String username;
+ String name;
+ Set symbols;
+ String multiplier;
+ Thread t;
+ MovingAverage ma;
+ Map maMap = new HashMap();
+ Map sdMap = new HashMap();
+ public MaThread(String user, String thread, Set symb, String mult) {
+ username = user;
+ name = thread;
+ symbols = symb;
+ multiplier = mult;
+ t = new Thread(this, name);
+ t.start();
+ }
+ public void run() {
+ try {
+ ma = new MovingAverage(CandlestickInterval.HALF_HOURLY, username, symbols, multiplier);
+ ma.calculate();
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted");
+ }
+ }
+
+ public Map getMAMap() {
+ return ma.getMAMap();
+ }
+
+ public Map getSDMap() {
+ return ma.getSDMap();
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/PopulateValuesDBThread.java b/src/main/java/edu/brown/cs/term/thread/PopulateValuesDBThread.java
new file mode 100644
index 0000000..96cf7e6
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/PopulateValuesDBThread.java
@@ -0,0 +1,31 @@
+package edu.brown.cs.term.thread;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import edu.brown.cs.term.repl.Main;
+
+public class PopulateValuesDBThread implements Runnable {
+ private String username;
+ private CrypthubUser chUser;
+ Thread t;
+
+ public PopulateValuesDBThread(String user) {
+ username = user;
+ chUser = new CrypthubUser();
+ Main.threadMap.add(user + " populator");
+ t = new Thread(this, user + " populator");
+ t.start();
+ }
+
+ @Override
+ public void run() {
+ try {
+ chUser.insertAmounts(username, chUser.totalAmount(username));
+ while (true) {
+ chUser.insertAmounts(username, chUser.totalAmount(username));
+ Thread.sleep(5000);
+ }
+ } catch (InterruptedException e) {
+ System.out.println("ERROR: Interrupted thread");
+ }
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/RSINotifierThread.java b/src/main/java/edu/brown/cs/term/thread/RSINotifierThread.java
new file mode 100644
index 0000000..a9bed8c
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/RSINotifierThread.java
@@ -0,0 +1,44 @@
+package edu.brown.cs.term.thread;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.binance.api.client.domain.market.CandlestickInterval;
+
+import edu.brown.cs.term.trading.RSINotifier;
+
+public class RSINotifierThread implements Runnable {
+ private CandlestickInterval interval = CandlestickInterval.HALF_HOURLY;
+ private String username = "js";
+ private String name;
+ private Set symbols = new HashSet();
+ private String rsiLimitHigh;
+ private String rsiLimitLow;
+ private Thread t;
+ private RSINotifier rsiNotifier;
+
+ public RSINotifierThread(String user, String thread, Set symb,
+ String highLimit, String lowLimit) {
+ username = user;
+ name = thread;
+ symbols = symb;
+ rsiLimitHigh = highLimit;
+ rsiLimitLow = lowLimit;
+ name = thread;
+ t = new Thread(this, name);
+ t.start();
+ }
+
+ @Override
+ public void run() {
+ try {
+ rsiNotifier = new RSINotifier(interval, username, symbols,
+ rsiLimitHigh, rsiLimitLow);
+ rsiNotifier.calculate();
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted.");
+ }
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/RsiThread.java b/src/main/java/edu/brown/cs/term/thread/RsiThread.java
new file mode 100644
index 0000000..fceccd7
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/RsiThread.java
@@ -0,0 +1,42 @@
+package edu.brown.cs.term.thread;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.binance.api.client.domain.market.CandlestickInterval;
+
+import edu.brown.cs.term.trading.RSICalculator;
+import websockets.RsiWebSocket;
+
+public class RsiThread implements Runnable {
+ String name;
+ Thread t;
+ RSICalculator rsi;
+ private String username;
+ private Set symbols;
+ private RsiWebSocket webSocket;
+
+ public RsiThread(String user, String thread, Set symb) {
+ name = thread;
+ username = user;
+ symbols = symb;
+ t = new Thread(this, name);
+ t.start();
+ }
+
+ @Override
+ public void run() {
+ try {
+ rsi = new RSICalculator(CandlestickInterval.HALF_HOURLY, username, symbols);
+ rsi.calculate();
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted");
+ }
+ }
+
+ public Map getRSIMap() {
+ return rsi.getRSIMap();
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/SellThread.java b/src/main/java/edu/brown/cs/term/thread/SellThread.java
new file mode 100644
index 0000000..6ca7aa9
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/SellThread.java
@@ -0,0 +1,52 @@
+package edu.brown.cs.term.thread;
+
+import edu.brown.cs.term.trading.FullTradeSell;
+
+public class SellThread implements Runnable {
+ String name;
+ Thread t;
+ String symbol;
+ String sellPrice;
+ String buyPrice;
+ String stopPrice;
+ String quantity;
+ String username;
+ public SellThread(String user, String thread, String symb, String sP,
+ String bP, String stopP, String quant) {
+ name = thread;
+ symbol = symb;
+ sellPrice = sP;
+ buyPrice = bP;
+ stopPrice = stopP;
+ quantity = quant;
+ username = user;
+ t = new Thread(this, name);
+ t.start();
+ }
+
+ public void run() {
+ try {
+ FullTradeSell fts = new FullTradeSell(username);
+ fts.fullTrade(symbol, sellPrice, buyPrice, stopPrice, quantity);
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted.");
+ }
+ }
+ public void setSellPrice(String sp) {
+ sellPrice = sp;
+ }
+
+ public void setBuyPrice(String bp) {
+ buyPrice = bp;
+ }
+
+ public void setStopPrice(String stop) {
+ stopPrice = stop;
+ }
+
+ public void setQuantity(String quant) {
+ quantity = quant;
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/TradingAlgorithmThread.java b/src/main/java/edu/brown/cs/term/thread/TradingAlgorithmThread.java
new file mode 100644
index 0000000..a6847ae
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/TradingAlgorithmThread.java
@@ -0,0 +1,40 @@
+package edu.brown.cs.term.thread;
+import java.util.List;
+import java.util.Set;
+
+import com.binance.api.client.domain.market.CandlestickInterval;
+
+import edu.brown.cs.term.trading.TradingAlgorithm;
+public class TradingAlgorithmThread implements Runnable {
+ String name;
+ Thread t;
+ Set symbols;
+ CandlestickInterval interval = CandlestickInterval.HALF_HOURLY;
+ String upperRSI;
+ String lowerRSI;
+ String multiplier;
+ String fraction;
+ String username;
+ public TradingAlgorithmThread(String user, String thread, Set symb,
+ String upperLimit, String lowerLimit, String mult, String frac) {
+ username = user;
+ name = thread;
+ symbols = symb;
+ upperRSI = upperLimit;
+ lowerRSI = lowerLimit;
+ multiplier = mult;
+ fraction = frac;
+ t = new Thread(this, name);
+ t.start();
+ }
+
+ public void run() {
+ try {
+ TradingAlgorithm ta = new TradingAlgorithm(interval, username, symbols, upperRSI,
+ lowerRSI, multiplier, fraction);
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted");
+ }
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/TsThread.java b/src/main/java/edu/brown/cs/term/thread/TsThread.java
new file mode 100644
index 0000000..87211a7
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/TsThread.java
@@ -0,0 +1,33 @@
+package edu.brown.cs.term.thread;
+
+import java.util.Set;
+
+import com.binance.api.client.domain.market.CandlestickInterval;
+import edu.brown.cs.term.trading.RSICalculator;
+import edu.brown.cs.term.trading.TrailingStop;
+
+public class TsThread implements Runnable{
+ String name;
+ Thread t;
+ String username;
+ Set symbols;
+ double stopPercent = 0.10;
+ public TsThread(String user, String thread,
+ Set symb, String percent) {
+ username = user;
+ stopPercent = Double.valueOf(percent);
+ name = thread;
+ t = new Thread(this, name);
+ t.start();
+ }
+ public void run() {
+ try {
+ TrailingStop ts = new TrailingStop(username, symbols, stopPercent);
+ ts.calculate();
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println(name + " was interrupted");
+ }
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/thread/WalletThread.java b/src/main/java/edu/brown/cs/term/thread/WalletThread.java
new file mode 100644
index 0000000..b85323f
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/thread/WalletThread.java
@@ -0,0 +1,32 @@
+package edu.brown.cs.term.thread;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import websockets.RsiWebSocket;
+
+public class WalletThread implements Runnable {
+ private String username;
+ Thread t;
+ CrypthubUser chUser;
+
+ public WalletThread(String user) {
+ username = user;
+ chUser = new CrypthubUser();
+ t = new Thread(this, username + "wallet");
+ t.start();
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ double total = chUser.totalAmount(username);
+ RsiWebSocket.sendWalletUpdate(total, username);
+ chUser.insertAmounts(username, total);
+ Thread.sleep(3000);
+ }
+ } catch (InterruptedException e) {
+ System.out.println("Wallet Thread was interrupted");
+ }
+ }
+
+}
diff --git a/src/main/java/edu/brown/cs/term/trading/FullTradeBuy.java b/src/main/java/edu/brown/cs/term/trading/FullTradeBuy.java
new file mode 100644
index 0000000..49f505e
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/trading/FullTradeBuy.java
@@ -0,0 +1,199 @@
+package edu.brown.cs.term.trading;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Set;
+
+import com.binance.api.client.BinanceApiClientFactory;
+import com.binance.api.client.BinanceApiRestClient;
+import com.binance.api.client.domain.TimeInForce;
+import com.binance.api.client.domain.account.NewOrder;
+import com.binance.api.client.domain.account.NewOrderResponse;
+import com.binance.api.client.domain.account.Order;
+import com.binance.api.client.domain.account.request.CancelOrderRequest;
+import com.binance.api.client.domain.account.request.OrderStatusRequest;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import edu.brown.cs.term.database.Trades;
+import edu.brown.cs.term.general.Tuple;
+import websockets.RsiWebSocket;
+
+public class FullTradeBuy {
+ private String APIkey
+ = "aXGg0J5kFv08YOytKZrCav9rWnjgzdFx3BvjIjY5M2gQQCaYSEdznhAGG4kspVr2";
+ private String secretKey
+ = "pgbthhPJBg8FS29FCeyYvlF5ay1CV0we4xxilxL9pytc5djb7RxDCs7k4kChthMI";
+ private String username = "js";
+ private BinanceApiClientFactory fact;
+ private BinanceApiRestClient client;
+ private Trades trades;
+ private Set symbols;
+ private Gson GSON;
+ private String message = "";
+ private int orderId;
+
+ public FullTradeBuy(String user) {
+ GSON = new Gson();
+ username = user;
+ setPublicPrivateKey(username);
+ trades = new Trades();
+ }
+
+ /**
+ * Sets the public and private key for use in trading.
+ * @param username
+ * username of the user
+ * @return String representing JSON object
+ */
+ public String setPublicPrivateKey(String username) {
+ CrypthubUser user = new CrypthubUser();
+ Tuple keys = user.getPublicPrivateKey(username);
+ if (keys.equals(new Tuple<>("", ""))) {
+ return GSON.toJson(ImmutableMap.of("success", false));
+ }
+ APIkey = keys.getFirstEntry();
+ secretKey = keys.getSecondEntry();
+ fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ client = fact.newRestClient();
+ return GSON.toJson(ImmutableMap.of("success", true));
+ }
+
+ public void fullTrade(String symbol, String buyPrice, String sellPrice,
+ String stopPrice, String quantity) {
+ NewOrderResponse buy = placeBuyOrder(symbol, buyPrice, quantity);
+ NewOrderResponse sell = checkAndSell(buy, symbol, sellPrice, false);
+ monitorForStop(sell, symbol, stopPrice, false);
+ }
+
+ public NewOrderResponse placeBuyOrder(String symbol, String buyPrice,
+ String quantity) {
+ double lastPrice = Double
+ .valueOf(client.get24HrPriceStatistics(symbol).getLastPrice());
+ double priceBuy = Double.valueOf(buyPrice);
+ if (!username.equals("")) {
+ NewOrderResponse res = client.newOrder(
+ NewOrder.limitBuy(symbol, TimeInForce.GTC, quantity, buyPrice));
+ trades.addTrade(username, client.getOrderStatus(
+ new OrderStatusRequest(symbol, res.getOrderId())));
+ return res;
+ } else {
+ System.out.println("ERROR: Please login before making a trade.");
+ return null;
+ }
+ }
+
+ public NewOrderResponse checkAndSell(NewOrderResponse buy, String symbol,
+ String sellPrice, boolean buyFilled) {
+ NewOrderResponse res = null;
+ while (!buyFilled) {
+ Order order = client.getOrderStatus(
+ new OrderStatusRequest(symbol, buy.getOrderId()));
+ if (order.getStatus().toString().equals("FILLED")) {
+ buyFilled = true;
+ double assetAmount = Double.valueOf(buy.getOrigQty());
+ res = client.newOrder(NewOrder.limitSell(symbol, TimeInForce.GTC,
+ buy.getOrigQty(), sellPrice));
+ trades.updateTrade(username,
+ client.getOrderStatus(
+ new OrderStatusRequest(symbol, buy.getOrderId())),
+ message);
+ trades.addTrade(username, client.getOrderStatus(
+ new OrderStatusRequest(symbol, res.getOrderId())));
+ } else {
+ message
+ = "Buy not complete. Time is: " + java.time.LocalTime.now();
+ System.out.println(message);
+ RsiWebSocket.sendCustomTradingUpdateMessage(username,
+ buy.getOrderId(), message);
+ try {
+ Thread.sleep(30000);
+ } catch (Exception e) {
+ System.out.println("Thread interrupted.");
+ }
+ }
+ }
+ return res;
+ }
+
+ public void monitorForStop(NewOrderResponse sell, String symbol,
+ String stopPrice, boolean sellFilled) {
+ while (!sellFilled) {
+ Order order = client.getOrderStatus(
+ new OrderStatusRequest(symbol, sell.getOrderId()));
+ if (order.getStatus().toString().equals("FILLED")) {
+ message = "Sell complete. Good trade.";
+ RsiWebSocket.sendCustomTradingUpdateMessage(username,
+ sell.getOrderId(), message);
+ System.out.println(message);
+
+ sellFilled = true;
+ trades.updateTrade(username,
+ client.getOrderStatus(
+ new OrderStatusRequest(symbol, sell.getOrderId())),
+ message);
+ break;
+ } else {
+ double currentPrice = Double
+ .valueOf(client.get24HrPriceStatistics(symbol).getLastPrice());
+ double stop = Double.valueOf(stopPrice);
+ if (currentPrice < stop) {
+ try {
+ Thread.sleep(60000);
+ } catch (Exception e) {
+ System.out.println("Thread interrupted");
+ }
+
+ String newCurrent
+ = client.get24HrPriceStatistics(symbol).getLastPrice();
+ if (Double.valueOf(newCurrent) >= stop) {
+ continue;
+ } else {
+ message = "Current price still below stop price. Cancelling "
+ + "previous sell and generating stop sell. Time is: "
+ + java.time.LocalTime.now();
+ RsiWebSocket.sendCustomTradingUpdateMessage(username,
+ sell.getOrderId(), message);
+ System.out.println(message);
+ client.cancelOrder(
+ new CancelOrderRequest(symbol, sell.getOrderId()));
+ trades.updateTrade(username,
+ client.getOrderStatus(
+ new OrderStatusRequest(symbol, sell.getOrderId())),
+ message);
+
+ double asset = Double.valueOf(sell.getOrigQty());
+ String[] splits = newCurrent.split("\\.");
+ int decimalLength = splits[1].length();
+ double sp = Double.valueOf(newCurrent)
+ - (Double.valueOf(newCurrent) * 0.0025);
+ BigDecimal bd = new BigDecimal(sp).setScale(decimalLength,
+ RoundingMode.HALF_DOWN);
+ NewOrderResponse res = client
+ .newOrder(NewOrder.limitSell(symbol, TimeInForce.GTC,
+ sell.getOrigQty(), bd.toPlainString()));
+ message = "Stop buy posted. Price of " + newCurrent
+ + ". Awaiting completion. Bad trade. Time is "
+ + java.time.LocalTime.now();
+ trades.addTrade(username, client.getOrderStatus(
+ new OrderStatusRequest(symbol, res.getOrderId())));
+ System.out.println(message);
+ }
+ } else {
+ message
+ = "Monitoring for stop loss or sell completion. Time is: "
+ + java.time.LocalTime.now();
+ System.out.println(message);
+ RsiWebSocket.sendCustomTradingUpdateMessage(username,
+ sell.getOrderId(), message);
+ try {
+ Thread.sleep(10000);
+ } catch (Exception e) {
+ System.out.println("Thread interrupted.");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/trading/FullTradeSell.java b/src/main/java/edu/brown/cs/term/trading/FullTradeSell.java
new file mode 100644
index 0000000..65fe484
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/trading/FullTradeSell.java
@@ -0,0 +1,227 @@
+package edu.brown.cs.term.trading;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Set;
+
+import com.binance.api.client.BinanceApiClientFactory;
+import com.binance.api.client.BinanceApiRestClient;
+import com.binance.api.client.domain.TimeInForce;
+import com.binance.api.client.domain.account.NewOrder;
+import com.binance.api.client.domain.account.NewOrderResponse;
+import com.binance.api.client.domain.account.Order;
+import com.binance.api.client.domain.account.request.CancelOrderRequest;
+import com.binance.api.client.domain.account.request.OrderStatusRequest;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import edu.brown.cs.term.database.Trades;
+import edu.brown.cs.term.general.Tuple;
+import websockets.RsiWebSocket;
+
+public class FullTradeSell {
+ // private String APIkey =
+ // "qW9xUO8kNpHHnxb0dJjStRrzE7tN0oTlPCbah4IWdxkgyAa5vqJXzSME6IYlDhrp";
+ // private String secretKey =
+ // "PXEisYYJhKd6T36nC8mH05AoZl4gcyytcz1Gd5Ybin1iv6GeS27rXySsaAn3Z5Mp";
+ private String APIkey = "";
+ private String secretKey = "";
+ private String username = "js";
+ private BinanceApiClientFactory fact;
+ private BinanceApiRestClient client;
+ private Trades trades;
+ private Set symbols;
+ private String message = "";
+ private Gson GSON;
+
+ // TODO: figure out how this works
+ public FullTradeSell(String user) {
+ GSON = new Gson();
+ username = user;
+ setPublicPrivateKey(username);
+ trades = new Trades(); // Intializes Trades DB
+ }
+
+ /**
+ * Sets the public and private key for use in trading.
+ * @param username
+ * username of the user
+ * @return String representing JSON object
+ */
+ public String setPublicPrivateKey(String username) {
+ CrypthubUser user = new CrypthubUser();
+ this.username = username;
+ Tuple keys = user.getPublicPrivateKey(username);
+ if (keys.equals(new Tuple<>("", ""))) {
+ return GSON.toJson(ImmutableMap.of("success", false));
+ }
+ APIkey = keys.getFirstEntry();
+ secretKey = keys.getSecondEntry();
+ fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ client = fact.newRestClient();
+ return GSON.toJson(ImmutableMap.of("success", true));
+ }
+
+ public void fullTrade(String symbol, String sellPrice, String buyPrice,
+ String stopPrice, String quantity) {
+ NewOrderResponse sell = placeSellOrder(symbol, sellPrice, quantity);
+ NewOrderResponse buy = checkAndBuy(sell, symbol, buyPrice, false);
+ monitorForStop(buy, sell, symbol, stopPrice, false);
+ }
+
+ public NewOrderResponse placeSellOrder(String symbol, String sellPrice,
+ String quantity) {
+ double lastPrice = Double
+ .valueOf(client.get24HrPriceStatistics(symbol).getLastPrice());
+ double sell = Double.valueOf(sellPrice);
+ if (!username.equals("")) {
+ // double amountFree =
+ // Double.valueOf(client.getAccount().getAssetBalance(symbol.substring(0,3)).getFree());
+ // double amountToSell = amountFree / frac;
+ NewOrderResponse res = client.newOrder(NewOrder.limitSell(symbol,
+ TimeInForce.GTC, quantity, sellPrice));
+ trades.addTrade(username, client.getOrderStatus(
+ new OrderStatusRequest(symbol, res.getOrderId())));
+ message
+ = "Sell posted for " + quantity + " " + symbol.substring(0, 3)
+ + ". Time is " + java.time.LocalTime.now();
+ RsiWebSocket.sendCustomTradingUpdateMessage(username, res.getOrderId(), message);
+ System.out.println(message);
+
+ return res;
+ // } else if (!username.equals("")) {
+ // System.out.println("ERROR: Current price is higher than sell
+ // price");
+ // return null;
+ } else {
+ System.out.println("ERROR: Please login before making a trade.");
+ return null;
+ }
+ }
+
+ public NewOrderResponse checkAndBuy(NewOrderResponse sell, String symbol,
+ String buyPrice, boolean sellFilled) {
+ NewOrderResponse res = null;
+ while (!sellFilled) {
+ Order order = client.getOrderStatus(
+ new OrderStatusRequest(symbol, sell.getOrderId()));
+ if (order.getStatus().toString().equals("FILLED")) {
+ message = "Sell has been filled. Posting buy. Time is: "
+ + java.time.LocalTime.now();
+ RsiWebSocket.sendCustomTradingUpdateMessage(username,
+ sell.getOrderId(), message);
+ sellFilled = true;
+ double assetAmount = Double.valueOf(sell.getPrice())
+ * Double.valueOf(sell.getOrigQty());
+ double buyQuant = assetAmount / Double.valueOf(buyPrice);
+ BigDecimal bd
+ = new BigDecimal(buyQuant).setScale(2, RoundingMode.HALF_DOWN);
+ trades.updateTrade(username,
+ client.getOrderStatus(
+ new OrderStatusRequest(symbol, sell.getOrderId())),
+ message);
+ res = client.newOrder(NewOrder.limitBuy(symbol, TimeInForce.GTC,
+ bd.toPlainString(), buyPrice));
+ trades.addTrade(username, client.getOrderStatus(
+ new OrderStatusRequest(symbol, res.getOrderId())));
+ } else {
+ message = "Sell not complete. Time is: "
+ + java.time.LocalTime.now();
+ System.out.println(message);
+ RsiWebSocket.sendCustomTradingUpdateMessage(username, sell.getOrderId(),
+ message);
+ try {
+ Thread.sleep(30000);
+ } catch (Exception e) {
+ System.out.println("Thread interrupted");
+ }
+ }
+ }
+ return res;
+ }
+
+ public void monitorForStop(NewOrderResponse buy, NewOrderResponse sell,
+ String symbol, String stopPrice, boolean buyFilled) {
+ while (!buyFilled) {
+ Order order = client.getOrderStatus(
+ new OrderStatusRequest(symbol, buy.getOrderId()));
+ if (order.getStatus().toString().equals("FILLED")) {
+ message = "Buy complete. Good trade. Time is: "
+ + java.time.LocalTime.now();
+ RsiWebSocket.sendCustomTradingUpdateMessage(username, buy.getOrderId(), message);
+ System.out.println(message);
+ trades.updateTrade(username,
+ client.getOrderStatus(
+ new OrderStatusRequest(symbol, buy.getOrderId())),
+ message);
+ buyFilled = true;
+ } else {
+ double currentPrice = Double
+ .valueOf(client.get24HrPriceStatistics(symbol).getLastPrice());
+ double stop = Double.valueOf(stopPrice);
+ if (currentPrice > stop) {
+ try {
+ Thread.sleep(60000);
+ } catch (Exception e) {
+ System.out.println("Thread interrupted");
+ }
+
+ double newCurrent = Double.valueOf(
+ client.get24HrPriceStatistics(symbol).getLastPrice());
+ if (newCurrent <= stop) {
+ continue;
+ } else {
+ message = "Stop price exceeded. Cancelling previous"
+ + " buy and generating stop buy order. Time is: "
+ + java.time.LocalTime.now();
+ RsiWebSocket.sendCustomTradingUpdateMessage(username, buy.getOrderId(), message);
+ System.out.println(message);
+ client.cancelOrder(
+ new CancelOrderRequest(symbol, buy.getOrderId()));
+ trades.updateTrade(username,
+ client.getOrderStatus(
+ new OrderStatusRequest(symbol, buy.getOrderId())),
+ message);
+
+ String price
+ = client.get24HrPriceStatistics(symbol).getLastPrice();
+ String[] splits = price.split("\\.");
+ int decimalLength = splits[1].length();
+ double valPrice = Double.valueOf(price) * 1.0025;
+ BigDecimal bd = new BigDecimal(valPrice)
+ .setScale(decimalLength, RoundingMode.HALF_DOWN);
+ double quant = Double.valueOf(sell.getOrigQty())
+ * Double.valueOf(sell.getPrice());
+ double stopQuant = quant / bd.doubleValue();
+ BigDecimal sd = new BigDecimal(stopQuant).setScale(2,
+ RoundingMode.HALF_DOWN);
+ NewOrderResponse res = client
+ .newOrder(NewOrder.limitBuy(symbol, TimeInForce.GTC,
+ sd.toPlainString(), bd.toPlainString()));
+ message = "Stop buy posted. Price of " + price
+ + ". Awaiting completion. Bad trade. Time is "
+ + java.time.LocalTime.now();
+ RsiWebSocket.sendCustomTradingUpdateMessage(username, buy.getOrderId(), message);
+ trades.addTrade(username, client.getOrderStatus(
+ new OrderStatusRequest(symbol, res.getOrderId())));
+ System.out.println(message);
+ buyFilled = true;
+ }
+ } else {
+ message = "Monitoring for stop loss or buy completion. Time is: "
+ + java.time.LocalTime.now();
+ System.out.println(message);
+ RsiWebSocket.sendCustomTradingUpdateMessage(username, buy.getOrderId(),
+ message);
+
+ try {
+ Thread.sleep(30000);
+ } catch (Exception e) {
+ System.out.println("Thread interrupted");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/trading/MovingAverage.java b/src/main/java/edu/brown/cs/term/trading/MovingAverage.java
new file mode 100644
index 0000000..12fc871
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/trading/MovingAverage.java
@@ -0,0 +1,332 @@
+package edu.brown.cs.term.trading;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import com.binance.api.client.BinanceApiAsyncRestClient;
+import com.binance.api.client.BinanceApiClientFactory;
+import com.binance.api.client.BinanceApiRestClient;
+import com.binance.api.client.domain.market.Candlestick;
+import com.binance.api.client.domain.market.CandlestickInterval;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import edu.brown.cs.term.general.Tuple;
+import websockets.RsiWebSocket;
+
+public class MovingAverage {
+ Map currentMA = new HashMap();
+ Map currentSD = new HashMap();
+ Map> previousMAs
+ = new HashMap>();
+ Map> previousSDs
+ = new HashMap>();
+ Map> previousCandles
+ = new HashMap>();
+ Map firstNewCandle = new HashMap();
+ Map twilioHold = new HashMap();
+ List rsiVals = new ArrayList();
+ boolean finishedInitialCalculations = false;
+ double bandMultiplier = 2;
+ String username = "js";
+ private Gson GSON;
+ CandlestickInterval interval = CandlestickInterval.HALF_HOURLY;
+ private String APIkey
+ = "aXGg0J5kFv08YOytKZrCav9rWnjgzdFx3BvjIjY5M2gQQCaYSEdznhAGG4kspVr2";
+ private String secretKey
+ = "pgbthhPJBg8FS29FCeyYvlF5ay1CV0we4xxilxL9pytc5djb7RxDCs7k4kChthMI";
+ private String twilioKey = "AC740a42cac1fc2a9567eb53594736f458";
+ private String twilioSecret = "9478ee8347691c3410cce9c8f00e0681";
+ private boolean finishedInitialCalculation = false;
+ private BinanceApiClientFactory fact;
+ private BinanceApiRestClient client;
+ private Set symbols = new HashSet();
+ private String message;
+
+ public MovingAverage(CandlestickInterval i, String user,
+ Set symb, String multiplier) {
+ interval = i;
+ symbols = symb;
+ bandMultiplier = Double.valueOf(multiplier);
+ GSON = new Gson();
+ username = user;
+ setPublicPrivateKey(username);
+ }
+
+ public void calculate() {
+ runCalculations(symbols);
+ }
+
+ private void runCalculations(Set symbols) {
+ initiateCalculation(symbols);
+ while (finishedInitialCalculation) {
+ continueCalculation(symbols);
+ }
+ }
+
+ public String setPublicPrivateKey(String username) {
+ CrypthubUser user = new CrypthubUser();
+ this.username = username;
+ Tuple keys = user.getPublicPrivateKey(username);
+ if (keys.equals(new Tuple<>("", ""))) {
+ return GSON.toJson(ImmutableMap.of("success", false));
+ }
+ APIkey = keys.getFirstEntry();
+ secretKey = keys.getSecondEntry();
+ fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ client = fact.newRestClient();
+ return GSON.toJson(ImmutableMap.of("success", true));
+ }
+
+ private void initiateCalculation(Set symbols) {
+ initializeMaps(symbols);
+ BinanceApiClientFactory factory
+ = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiAsyncRestClient client = factory.newAsyncRestClient();
+ for (String symbol : symbols) {
+ CompletableFuture time = new CompletableFuture();
+ client.getServerTime(response -> {
+ time.complete(response.getServerTime());
+ });
+ long endTime = -1;
+ try {
+ endTime = time.get();
+ } catch (Exception e) {
+ System.out.println("Time was not retrieved.");
+ }
+ long startTime = endTime - (128 * 60 * 60 * 1000);
+ CompletableFuture> candles
+ = new CompletableFuture>();
+ List candlesticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 256, startTime, endTime,
+ (List response) -> {
+ candles.complete(response);
+ });
+ try {
+ candlesticks = candles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ int start = 0;
+ int end = 21;
+ while (currentMA.get(symbol) == 0.0) {
+ if (end < 257) {
+ List group21 = candlesticks.subList(start, end);
+ double firstAverage = calculateAverage(group21);
+ double firstSD = calculateStandardDev(group21, firstAverage);
+ previousMAs.get(symbol).add(firstAverage);
+ previousSDs.get(symbol).add(firstSD);
+ start++;
+ end++;
+ }
+ if (end == 257) {
+ currentMA.replace(symbol, previousMAs.get(symbol)
+ .get(previousMAs.get(symbol).size() - 1));
+ currentSD.replace(symbol, previousSDs.get(symbol)
+ .get(previousMAs.get(symbol).size() - 1));
+ message = symbol + " Current Moving Average: "
+ + currentMA.get(symbol);
+// this.updateFrontEnd(message);
+
+ previousCandles.get(symbol)
+ .add(candlesticks.get(candlesticks.size() - 1));
+ finishedInitialCalculation = true;
+ }
+ }
+ }
+ }
+
+ private void continueCalculation(Set symbols) {
+ BinanceApiClientFactory factory
+ = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiAsyncRestClient client = factory.newAsyncRestClient();
+ for (String symbol : symbols) {
+ List previous = previousCandles.get(symbol);
+ Candlestick previousC = previous.get(previous.size() - 1);
+ CompletableFuture time = new CompletableFuture();
+ client.getServerTime(response -> {
+ time.complete(response.getServerTime());
+ });
+ long endTime = -1;
+ try {
+ endTime = time.get();
+ } catch (Exception e) {
+ System.out.println("Time was not retrieved.");
+ }
+ long startTime = endTime - (long) (10.5 * 3600 * 1000);
+ CompletableFuture> candles
+ = new CompletableFuture>();
+ List candlesticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 21, startTime, endTime,
+ (List response) -> {
+ candles.complete(response);
+ });
+ try {
+ candlesticks = candles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ if (firstNewCandle.get(symbol) == false) {
+ double currentAverage = calculateAverage(candlesticks);
+ double currentStandardDev
+ = calculateStandardDev(candlesticks, currentAverage);
+ previousMAs.get(symbol).set(previousMAs.get(symbol).size() - 1,
+ currentAverage);
+ previousSDs.get(symbol).set(previousMAs.get(symbol).size() - 1,
+ currentStandardDev);
+ currentMA.replace(symbol, currentAverage);
+ currentSD.replace(symbol, currentStandardDev);
+
+ message = symbol + " Lower Band: " + Double.toString(
+ (currentAverage - bandMultiplier * currentStandardDev));
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ message = symbol + " Current MA No New Candle Yet: "
+ + Double.toString(currentAverage);
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ message = symbol + " Upper Band: " + Double.toString(
+ (currentAverage + bandMultiplier * currentStandardDev));
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ if (!candlesticks.get(candlesticks.size() - 1).getOpenTime()
+ .equals(previousCandles.get(symbol)
+ .get(previousCandles.get(symbol).size() - 1)
+ .getOpenTime())) {
+ firstNewCandle.replace(symbol, true);
+ }
+ } else {
+ if (!candlesticks.get(candlesticks.size() - 1).getOpenTime()
+ .equals(previousCandles.get(symbol)
+ .get(previousCandles.get(symbol).size() - 1)
+ .getOpenTime())) {
+ previousCandles.get(symbol)
+ .add(candlesticks.get(candlesticks.size() - 1));
+ long tempStart = startTime - (long) (0.5 * 3600 * 1000);
+ CompletableFuture> tempCandles
+ = new CompletableFuture>();
+ List tempSticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 22, tempStart,
+ endTime, (List response) -> {
+ tempCandles.complete(response);
+ });
+ try {
+ tempSticks = tempCandles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ double previousAverage
+ = calculateAverage(tempSticks.subList(0, 21));
+ double previousSD = calculateStandardDev(
+ tempSticks.subList(0, 21), previousAverage);
+ previousMAs.get(symbol).set(previousMAs.get(symbol).size() - 1,
+ previousAverage);
+ previousSDs.get(symbol).set(previousSDs.get(symbol).size() - 1,
+ previousSD);
+
+ message = symbol + " Current MA Old Candle Final: "
+ + previousAverage;
+
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ double newAverage = calculateAverage(candlesticks);
+ double newSD = calculateStandardDev(candlesticks, newAverage);
+ previousMAs.get(symbol).add(newAverage);
+ previousSDs.get(symbol).add(newSD);
+ currentMA.replace(symbol, newAverage);
+ currentSD.replace(symbol, newSD);
+
+ message = symbol + " Current MA New Candle: " + newAverage;
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ } else {
+ double currentAverage = calculateAverage(candlesticks);
+ double currentStandardDev
+ = calculateStandardDev(candlesticks, currentAverage);
+ previousMAs.get(symbol).set(previousMAs.get(symbol).size() - 1,
+ currentAverage);
+ previousSDs.get(symbol).set(previousMAs.get(symbol).size() - 1,
+ currentStandardDev);
+ currentMA.replace(symbol, currentAverage);
+ currentSD.replace(symbol, currentStandardDev);
+
+ message = symbol + " Lower Band: " + Double.toString(
+ (currentAverage - bandMultiplier * currentStandardDev));
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ message = symbol + " Current MA: " + currentAverage;
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ message = symbol + " Upper Band: " + Double.toString(
+ (currentAverage + bandMultiplier * currentStandardDev));
+ System.out.println(message);
+// this.updateFrontEnd(message);
+ }
+ }
+ }
+ try {
+ Thread.sleep(30000);
+ } catch (InterruptedException e) {
+ System.out.println("Thread interrupted");
+ }
+ }
+
+ private void initializeMaps(Set symbols) {
+ for (String symbol : symbols) {
+ currentMA.put(symbol, 0.0);
+ currentSD.put(symbol, 0.0);
+ previousMAs.put(symbol, new ArrayList());
+ previousSDs.put(symbol, new ArrayList());
+ previousCandles.put(symbol, new ArrayList());
+ firstNewCandle.put(symbol, false);
+ }
+ }
+
+ private double calculateAverage(List candles) {
+ double sum = 0.0;
+ for (Candlestick c : candles) {
+ sum += Double.parseDouble(c.getClose());
+ }
+ double average = sum / candles.size();
+ return average;
+ }
+
+ private double calculateStandardDev(List candles,
+ double currentAverage) {
+ double sum = 0.0;
+ for (Candlestick c : candles) {
+ sum += Math.pow((Double.parseDouble(c.getClose()) - currentAverage),
+ 2);
+ }
+ double variance = sum / (candles.size() - 1);
+ double standardDev = Math.sqrt(variance);
+ return standardDev;
+ }
+
+ public Map getMAMap() {
+ return currentMA;
+ }
+
+ public Map getSDMap() {
+ return currentSD;
+ }
+
+ private void updateFrontEnd(String message) {
+ if (username != null) {
+ RsiWebSocket.sendAutomaticradingUpdateMessage(username, message);
+ }
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/trading/RSICalculator.java b/src/main/java/edu/brown/cs/term/trading/RSICalculator.java
new file mode 100644
index 0000000..39f96c2
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/trading/RSICalculator.java
@@ -0,0 +1,414 @@
+package edu.brown.cs.term.trading;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import com.binance.api.client.BinanceApiAsyncRestClient;
+import com.binance.api.client.BinanceApiClientFactory;
+import com.binance.api.client.BinanceApiRestClient;
+import com.binance.api.client.domain.market.Candlestick;
+import com.binance.api.client.domain.market.CandlestickInterval;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.twilio.Twilio;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import edu.brown.cs.term.general.Tuple;
+import websockets.RsiWebSocket;
+
+public class RSICalculator {
+
+ Map currentRSI = new HashMap();
+ private Map> prevGains
+ = new HashMap>();
+ private Map> prevLosses
+ = new HashMap>();
+ private Map> previousCandles
+ = new HashMap>();
+ private Map firstNewCandle
+ = new HashMap();
+ private Map twilioHold = new HashMap();
+ private List rsiVals = new ArrayList();
+ private boolean finishedInitialCalculations = false;
+ private CandlestickInterval interval = CandlestickInterval.HALF_HOURLY;
+ private Map intervalConversion
+ = new HashMap();
+ private String APIkey
+ = "aXGg0J5kFv08YOytKZrCav9rWnjgzdFx3BvjIjY5M2gQQCaYSEdznhAGG4kspVr2";
+ private String secretKey
+ = "pgbthhPJBg8FS29FCeyYvlF5ay1CV0we4xxilxL9pytc5djb7RxDCs7k4kChthMI";
+ private String twilioKey = "AC740a42cac1fc2a9567eb53594736f458";
+ private String twilioSecret = "9478ee8347691c3410cce9c8f00e0681";
+ private Set symbols = new HashSet();
+ private String username = "js";
+ private BinanceApiClientFactory fact;
+ private BinanceApiRestClient client;
+ private Gson GSON;
+
+ public RSICalculator(CandlestickInterval i, String user,
+ Set symb) {
+ GSON = new Gson();
+ Twilio.init(twilioKey, twilioSecret);
+ username = user;
+ interval = i;
+ symbols = symb;
+ setPublicPrivateKey(username);
+ setIntervalConversions();
+ }
+
+ public void calculate() {
+ runCalculationsCurrent(symbols);
+ }
+
+ public String setPublicPrivateKey(String username) {
+ CrypthubUser user = new CrypthubUser();
+ this.username = username;
+ Tuple keys = user.getPublicPrivateKey(username);
+ if (keys.equals(new Tuple<>("", ""))) {
+ return GSON.toJson(ImmutableMap.of("success", false));
+ }
+ APIkey = keys.getFirstEntry();
+ secretKey = keys.getSecondEntry();
+ fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ client = fact.newRestClient();
+ System.out.println("here1");
+ return GSON.toJson(ImmutableMap.of("success", true));
+ }
+
+ private void runCalculationsCurrent(Set symbols) {
+ initiateCalculation(symbols);
+ while (finishedInitialCalculations) {
+ continueCalculation(symbols);
+ }
+ }
+
+ private void initiateCalculation(Set symbols) {
+ initializeMaps(symbols);
+ BinanceApiClientFactory factory
+ = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiAsyncRestClient client = factory.newAsyncRestClient();
+ System.out.println("here2");
+ for (String symbol : symbols) {
+ CompletableFuture time = new CompletableFuture();
+ client.getServerTime(response -> {
+ time.complete(response.getServerTime());
+ });
+ long endTime = -1;
+ try {
+ endTime = time.get();
+ } catch (Exception e) {
+ System.out.println("Time was not retrieved.");
+ }
+ long startTime
+ = endTime - (256 * intervalConversion.get(interval) * 60 * 1000);
+ CompletableFuture> candles
+ = new CompletableFuture>();
+ List candlesticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 256, startTime, endTime,
+ (List response) -> {
+ candles.complete(response);
+ });
+ try {
+ candlesticks = candles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ int count = 15;
+ while (currentRSI.get(symbol) == -1.0) {
+ // if previous gains and losses are populated, use wilder smoothing
+ if (count > 15) {
+ List prev_gains = prevGains.get(symbol);
+ double previousGain = prev_gains.get(prev_gains.size() - 1);
+ List prev_loss = prevLosses.get(symbol);
+ double previousLoss = prev_loss.get(prev_loss.size() - 1);
+
+ double lastDiff = Double
+ .parseDouble(candlesticks.get(count).getClose())
+ - Double.parseDouble(candlesticks.get(count - 1).getClose());
+
+ double rsi = rsiFromDiff(lastDiff, previousGain, previousLoss,
+ symbol, true);
+ count++;
+
+ } else {
+ List firstFifteen = candlesticks.subList(0, 15);
+ List differences = getDifferences(firstFifteen);
+ List gainsClipped = clipGains(differences);
+ List lossesClipped = clipLoss(differences);
+
+ double gain_average = averageGains(gainsClipped);
+ double loss_average = averageLoss(lossesClipped);
+ prevGains.get(symbol).add(gain_average);
+ prevLosses.get(symbol).add(loss_average);
+ double rs = gain_average / loss_average;
+ double rsi = 100 - 100 / (1 + rs);
+ rsiVals.add(rsi);
+ count++;
+ }
+ if (count == 256) {
+ double lastRSI = rsiVals.get(rsiVals.size() - 1);
+ currentRSI.replace(symbol, lastRSI);
+ previousCandles.get(symbol)
+ .add(candlesticks.get(candlesticks.size() - 1));
+ String message
+ = symbol + " Current RSI: " + Double.toString(lastRSI);
+ System.out.println(message);
+// updateFrontEnd(message);
+ finishedInitialCalculations = true;
+ }
+ }
+ }
+
+ }
+
+ private void continueCalculation(Set symbols) {
+ BinanceApiClientFactory factory
+ = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiAsyncRestClient client = factory.newAsyncRestClient();
+ for (String symbol : symbols) {
+ List previous = previousCandles.get(symbol);
+ Candlestick previousC = previous.get(previous.size() - 1);
+ CompletableFuture time = new CompletableFuture();
+ client.getServerTime(response -> {
+ time.complete(response.getServerTime());
+ });
+ long endTime = -1;
+ try {
+ endTime = time.get();
+ } catch (Exception e) {
+ System.out.println("Time was not retrieved.");
+ }
+ long startTime = endTime - (2 * 3600 * 1000);
+ CompletableFuture> candles
+ = new CompletableFuture>();
+ List candlesticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 4, startTime, endTime,
+ (List response) -> {
+ candles.complete(response);
+ });
+ try {
+ candlesticks = candles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ if (firstNewCandle.get(symbol) == false) {
+ List prev_gains = prevGains.get(symbol);
+ double prev_gain = prev_gains.get(prev_gains.size() - 2);
+ List prev_losses = prevLosses.get(symbol);
+ double prev_loss = prev_losses.get(prev_losses.size() - 2);
+ double lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 1).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose());
+
+ double rsi
+ = rsiFromDiff(lastDiff, prev_gain, prev_loss, symbol, false);
+ String message = symbol + " Current RSI No New Candle Yet: "
+ + Double.toString(rsi);
+ System.out.println(message);
+// this.updateFrontEnd(message);
+ currentRSI.replace(symbol, rsi);
+ if (!candlesticks.get(candlesticks.size() - 1).getOpenTime()
+ .equals(previousCandles.get(symbol)
+ .get(previousCandles.get(symbol).size() - 1)
+ .getOpenTime())) {
+ firstNewCandle.replace(symbol, true);
+ }
+ } else {
+ if (!candlesticks.get(candlesticks.size() - 1).getOpenTime()
+ .equals(previousCandles.get(symbol)
+ .get(previousCandles.get(symbol).size() - 1)
+ .getOpenTime())) {
+ previousCandles.get(symbol)
+ .add(candlesticks.get(candlesticks.size() - 1));
+
+ double prev_gain = prevGains.get(symbol)
+ .get(prevGains.get(symbol).size() - 2);
+ double prev_loss = prevLosses.get(symbol)
+ .get(prevGains.get(symbol).size() - 2);
+ double lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 3).getClose());
+
+ double rsi
+ = rsiFromDiff(lastDiff, prev_gain, prev_loss, symbol, false);
+ currentRSI.replace(symbol, rsi);
+ String message = symbol + " Current RSI Old Candle Final: "
+ + Double.toString(rsi);
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ double prev_gain_new = prevGains.get(symbol)
+ .get(prevGains.get(symbol).size() - 1);
+ double prev_loss_new = prevLosses.get(symbol)
+ .get(prevLosses.get(symbol).size() - 1);
+
+ lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 1).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose());
+
+ rsi = rsiFromDiff(lastDiff, prev_gain_new, prev_loss_new, symbol,
+ true);
+ currentRSI.replace(symbol, rsi);
+ message = symbol + " Current RSI New Candle: "
+ + Double.toString(rsi);
+ System.out.println(message);
+// this.updateFrontEnd(message);
+
+ } else {
+ double prev_gain = prevGains.get(symbol)
+ .get(prevGains.get(symbol).size() - 2);
+ double prev_loss = prevLosses.get(symbol)
+ .get(prevLosses.get(symbol).size() - 2);
+ double lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 1).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose());
+ double rsi
+ = rsiFromDiff(lastDiff, prev_gain, prev_loss, symbol, false);
+ currentRSI.replace(symbol, rsi);
+ String message
+ = symbol + " Current RSI: " + Double.toString(rsi);
+ System.out.println(message);
+// updateFrontEnd(message);
+ }
+ }
+ }
+ try {
+ Thread.sleep(30000);
+ } catch (InterruptedException e) {
+ System.out.println("Thread interrupted");
+ continueCalculation(symbols);
+ }
+ }
+
+ private List getDifferences(List candles) {
+ List diffs = new ArrayList();
+ for (int i = 1; i < candles.size(); i++) {
+ double diff = Double.parseDouble(candles.get(i).getClose())
+ - Double.parseDouble(candles.get(i - 1).getClose());
+ diffs.add(diff);
+ }
+ return diffs;
+ }
+
+ private List clipGains(List differences) {
+ List gains = differences;
+ for (double gain : gains) {
+ if (gain < 0) {
+ gain = 0;
+ }
+ }
+ return gains;
+ }
+
+ private List clipLoss(List differences) {
+ List losses = differences;
+ for (double loss : losses) {
+ if (loss > 0) {
+ loss = 0;
+ }
+ }
+ return losses;
+ }
+
+ private double averageGains(List gains) {
+ double sum = 0;
+ for (double gain : gains) {
+ sum += gain;
+ }
+ double average = sum / 14;
+ return average;
+ }
+
+ private double averageLoss(List losses) {
+ double sum = 0;
+ for (double loss : losses) {
+ sum += Math.abs(loss);
+ }
+ double average = sum / 14;
+ return average;
+ }
+
+ private void initializeMaps(Set symbols) {
+ for (String symbol : symbols) {
+ currentRSI.put(symbol, -1.0);
+ prevGains.put(symbol, new ArrayList());
+ prevLosses.put(symbol, new ArrayList());
+ previousCandles.put(symbol, new ArrayList());
+ firstNewCandle.put(symbol, false);
+ twilioHold.put(symbol, false);
+
+ }
+ }
+
+ private double rsiFromDiff(double lastDiff, double previousGain,
+ double previousLoss, String symbol, boolean newCandle) {
+ double rsi = -1.0;
+ if (lastDiff < 0) {
+ double gain_average = previousGain * 13 / 14;
+ double loss_average = (Math.abs(previousLoss * 13 - lastDiff)) / 14;
+ if (newCandle) {
+ prevGains.get(symbol).add(gain_average);
+ prevLosses.get(symbol).add(loss_average);
+ } else {
+ prevGains.get(symbol).set(prevGains.get(symbol).size() - 1,
+ gain_average);
+ prevLosses.get(symbol).set(prevLosses.get(symbol).size() - 1,
+ loss_average);
+ }
+ double rs = gain_average / loss_average;
+ rsi = 100 - 100 / (1 + rs);
+ rsiVals.add(rsi);
+
+ } else {
+ double gain_average = (previousGain * 13 + lastDiff) / 14;
+ double loss_average = (Math.abs(previousLoss * 13)) / 14;
+ if (newCandle) {
+ prevGains.get(symbol).add(gain_average);
+ prevLosses.get(symbol).add(loss_average);
+ } else {
+ prevGains.get(symbol).set(prevGains.get(symbol).size() - 1,
+ gain_average);
+ prevLosses.get(symbol).set(prevLosses.get(symbol).size() - 1,
+ loss_average);
+ }
+ double rs = gain_average / loss_average;
+ rsi = 100 - 100 / (1 + rs);
+ rsiVals.add(rsi);
+ }
+ return rsi;
+ }
+
+ public Map getRSIMap() {
+ return currentRSI;
+ }
+
+ public void setInterval(CandlestickInterval setInterval) {
+ interval = setInterval;
+ }
+
+ public void setIntervalConversions() {
+ intervalConversion.put(CandlestickInterval.HALF_HOURLY, 30);
+ intervalConversion.put(CandlestickInterval.HOURLY, 60);
+ intervalConversion.put(CandlestickInterval.TWO_HOURLY, 120);
+ intervalConversion.put(CandlestickInterval.FOUR_HOURLY, 240);
+ intervalConversion.put(CandlestickInterval.EIGHT_HOURLY, 480);
+ intervalConversion.put(CandlestickInterval.TWELVE_HOURLY, 720);
+ intervalConversion.put(CandlestickInterval.DAILY, 1440);
+
+ }
+
+ private void updateFrontEnd(String message) {
+ if (username != null) {
+ RsiWebSocket.sendAutomaticradingUpdateMessage(username, message);
+ }
+ }
+}
diff --git a/src/main/java/edu/brown/cs/term/trading/RSINotifier.java b/src/main/java/edu/brown/cs/term/trading/RSINotifier.java
new file mode 100644
index 0000000..ccdfda6
--- /dev/null
+++ b/src/main/java/edu/brown/cs/term/trading/RSINotifier.java
@@ -0,0 +1,467 @@
+package edu.brown.cs.term.trading;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import com.binance.api.client.BinanceApiAsyncRestClient;
+import com.binance.api.client.BinanceApiClientFactory;
+import com.binance.api.client.BinanceApiRestClient;
+import com.binance.api.client.domain.market.Candlestick;
+import com.binance.api.client.domain.market.CandlestickInterval;
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.twilio.Twilio;
+import com.twilio.rest.api.v2010.account.Message;
+import com.twilio.type.PhoneNumber;
+
+import edu.brown.cs.term.database.CrypthubUser;
+import edu.brown.cs.term.general.Tuple;
+import websockets.RsiWebSocket;
+
+public class RSINotifier {
+ Map currentRSI = new HashMap();
+ Map> prevGains
+ = new HashMap>();
+ Map> prevLosses
+ = new HashMap>();
+ Map> previousCandles
+ = new HashMap>();
+ Map firstNewCandle = new HashMap();
+ Map twilioHold = new HashMap();
+ Map intervalConversion
+ = new HashMap();
+ List rsiVals = new ArrayList();
+ boolean finishedInitialCalculations = false;
+ String username = "js";
+ CandlestickInterval interval = CandlestickInterval.HALF_HOURLY;
+ Set symbols = new HashSet();
+ double rsiLimitHigh;
+ double rsiLimitLow;
+ List messages;
+ BinanceApiClientFactory fact;
+ BinanceApiRestClient client;
+ private String APIkey
+ = "qW9xUO8kNpHHnxb0dJjStRrzE7tN0oTlPCbah4IWdxkgyAa5vqJXzSME6IYlDhrp";
+ private String secretKey
+ = "PXEisYYJhKd6T36nC8mH05AoZl4gcyytcz1Gd5Ybin1iv6GeS27rXySsaAn3Z5Mp";
+ private String twilioKey = "AC740a42cac1fc2a9567eb53594736f458";
+ private String twilioSecret = "9478ee8347691c3410cce9c8f00e0681";
+ private static final Gson GSON = new Gson();
+
+ public RSINotifier(CandlestickInterval i, String user, Set symb,
+ String highLimit, String lowLimit) {
+ Twilio.init(twilioKey, twilioSecret);
+ username = user;
+ setPublicPrivateKey(username);
+ interval = i;
+ symbols = symb;
+ rsiLimitHigh = Double.valueOf(highLimit);
+ rsiLimitLow = Double.valueOf(lowLimit);
+ messages = new ArrayList();
+ setIntervalConversions();
+ }
+
+ public void calculate() {
+ runCalculationsCurrent(symbols);
+ }
+
+ public String setPublicPrivateKey(String username) {
+ CrypthubUser user = new CrypthubUser();
+ this.username = username;
+ Tuple keys = user.getPublicPrivateKey(username);
+ if (keys.equals(new Tuple<>("", ""))) {
+ return GSON.toJson(ImmutableMap.of("success", false));
+ }
+ APIkey = keys.getFirstEntry();
+ secretKey = keys.getSecondEntry();
+ fact = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ client = fact.newRestClient();
+ return GSON.toJson(ImmutableMap.of("success", true));
+ }
+
+ private void runCalculationsCurrent(Set symbols) {
+ initiateCalculation(symbols);
+ while (finishedInitialCalculations) {
+ continueCalculation(symbols);
+ }
+ }
+
+ private void initiateCalculation(Set symbols) {
+ initializeMaps(symbols);
+ BinanceApiClientFactory factory
+ = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiAsyncRestClient client = factory.newAsyncRestClient();
+ messages = new ArrayList();
+ for (String symbol : symbols) {
+ CompletableFuture time = new CompletableFuture();
+ client.getServerTime(response -> {
+ time.complete(response.getServerTime());
+ });
+ long endTime = -1;
+ try {
+ endTime = time.get();
+ } catch (Exception e) {
+ System.out.println("Time was not retrieved.");
+ }
+ long startTime
+ = endTime - (256 * intervalConversion.get(interval) * 60 * 1000);
+ CompletableFuture> candles
+ = new CompletableFuture>();
+ List candlesticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 256, startTime, endTime,
+ (List response) -> {
+ candles.complete(response);
+ });
+ try {
+ candlesticks = candles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ int count = 15;
+ while (currentRSI.get(symbol) == -1.0) {
+ // if previous gains and losses are populated, use wilder smoothing
+ if (count > 15) {
+ List prev_gains = prevGains.get(symbol);
+ double previousGain = prev_gains.get(prev_gains.size() - 1);
+ List prev_loss = prevLosses.get(symbol);
+ double previousLoss = prev_loss.get(prev_loss.size() - 1);
+
+ double lastDiff = Double
+ .parseDouble(candlesticks.get(count).getClose())
+ - Double.parseDouble(candlesticks.get(count - 1).getClose());
+
+ double rsi = rsiFromDiff(lastDiff, previousGain, previousLoss,
+ symbol, true);
+ count++;
+
+ } else {
+ List firstFifteen = candlesticks.subList(0, 15);
+ List differences = getDifferences(firstFifteen);
+ List gainsClipped = clipGains(differences);
+ List lossesClipped = clipLoss(differences);
+
+ double gain_average = averageGains(gainsClipped);
+ double loss_average = averageLoss(lossesClipped);
+ prevGains.get(symbol).add(gain_average);
+ prevLosses.get(symbol).add(loss_average);
+ double rs = gain_average / loss_average;
+ double rsi = 100 - 100 / (1 + rs);
+ rsiVals.add(rsi);
+ count++;
+ }
+ if (count == 256) {
+ double lastRSI = rsiVals.get(rsiVals.size() - 1);
+ currentRSI.replace(symbol, lastRSI);
+ previousCandles.get(symbol)
+ .add(candlesticks.get(candlesticks.size() - 1));
+ String message
+ = symbol + " Current RSI: " + Double.toString(lastRSI);
+ System.out.println(message);
+ messages.add(message);
+ RsiWebSocket.sendRSIUpdateMessage(messages, username);
+ finishedInitialCalculations = true;
+ }
+ }
+ }
+ }
+
+ private void continueCalculation(Set symbols) {
+ BinanceApiClientFactory factory
+ = BinanceApiClientFactory.newInstance(APIkey, secretKey);
+ BinanceApiAsyncRestClient client = factory.newAsyncRestClient();
+ messages = new ArrayList();
+ for (String symbol : symbols) {
+ List previous = previousCandles.get(symbol);
+ Candlestick previousC = previous.get(previous.size() - 1);
+ CompletableFuture time = new CompletableFuture();
+ client.getServerTime(response -> {
+ time.complete(response.getServerTime());
+ });
+ long endTime = -1;
+ try {
+ endTime = time.get();
+ } catch (Exception e) {
+ System.out.println("Time was not retrieved.");
+ }
+ long startTime
+ = endTime - (4 * intervalConversion.get(interval) * 60 * 1000);
+ CompletableFuture> candles
+ = new CompletableFuture>();
+ List candlesticks = new ArrayList();
+ client.getCandlestickBars(symbol, interval, 4, startTime, endTime,
+ (List response) -> {
+ candles.complete(response);
+ });
+ try {
+ candlesticks = candles.get();
+ } catch (Exception e) {
+ System.out.println("Error getting candlesticks.");
+ }
+ if (firstNewCandle.get(symbol) == false) {
+ List prev_gains = prevGains.get(symbol);
+ double prev_gain = prev_gains.get(prev_gains.size() - 2);
+ List prev_losses = prevLosses.get(symbol);
+ double prev_loss = prev_losses.get(prev_losses.size() - 2);
+ double lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 1).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose());
+
+ double rsi
+ = rsiFromDiff(lastDiff, prev_gain, prev_loss, symbol, false);
+ String rsiMessage = symbol + " Current RSI No New Candle Yet: "
+ + Double.toString(rsi);
+ System.out.println(rsiMessage);
+ messages.add(rsiMessage);
+ currentRSI.replace(symbol, rsi);
+ if (currentRSI.get(symbol) > rsiLimitHigh
+ && twilioHold.get(symbol) == false) {
+ Message message
+ = Message.creator(new PhoneNumber("+13185723217"),
+ new PhoneNumber("+13186159356"),
+ symbol + " RSI is greater than " + rsiLimitHigh + "!").create();
+ twilioHold.replace(symbol, true);
+ } else if (currentRSI.get(symbol) < rsiLimitLow
+ && twilioHold.get(symbol) == false) {
+ Message message
+ = Message
+ .creator(new PhoneNumber("+13185723217"),
+ new PhoneNumber("+13186159356"),
+ symbol + " RSI is less than " + rsiLimitLow)
+ .create();
+ twilioHold.replace(symbol, true);
+ }
+ if (!candlesticks.get(candlesticks.size() - 1).getOpenTime()
+ .equals(previousCandles.get(symbol)
+ .get(previousCandles.get(symbol).size() - 1)
+ .getOpenTime())) {
+ firstNewCandle.replace(symbol, true);
+
+ }
+ } else {
+ if (!candlesticks.get(candlesticks.size() - 1).getOpenTime()
+ .equals(previousCandles.get(symbol)
+ .get(previousCandles.get(symbol).size() - 1)
+ .getOpenTime())) {
+ previousCandles.get(symbol)
+ .add(candlesticks.get(candlesticks.size() - 1));
+
+ double prev_gain = prevGains.get(symbol)
+ .get(prevGains.get(symbol).size() - 2);
+ double prev_loss = prevLosses.get(symbol)
+ .get(prevGains.get(symbol).size() - 2);
+ double lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 3).getClose());
+
+ double rsi
+ = rsiFromDiff(lastDiff, prev_gain, prev_loss, symbol, false);
+ currentRSI.replace(symbol, rsi);
+ String messageRSI = symbol + " Current RSI Old Candle Final: "
+ + Double.toString(rsi);
+ System.out.println(messageRSI);
+ messages.add(messageRSI);
+
+ double prev_gain_new = prevGains.get(symbol)
+ .get(prevGains.get(symbol).size() - 1);
+ double prev_loss_new = prevLosses.get(symbol)
+ .get(prevLosses.get(symbol).size() - 1);
+
+ lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 1).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose());
+
+ rsi = rsiFromDiff(lastDiff, prev_gain_new, prev_loss_new, symbol,
+ true);
+ currentRSI.replace(symbol, rsi);
+ messageRSI = symbol + " Current RSI New Candle: "
+ + Double.toString(rsi);
+ System.out.println(messageRSI);
+ messages.add(messageRSI);
+ } else {
+ double prev_gain = prevGains.get(symbol)
+ .get(prevGains.get(symbol).size() - 2);
+ double prev_loss = prevLosses.get(symbol)
+ .get(prevLosses.get(symbol).size() - 2);
+ double lastDiff = Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 1).getClose())
+ - Double.parseDouble(
+ candlesticks.get(candlesticks.size() - 2).getClose());
+
+ double rsi
+ = rsiFromDiff(lastDiff, prev_gain, prev_loss, symbol, false);
+ currentRSI.replace(symbol, rsi);
+ if (currentRSI.get(symbol) > rsiLimitHigh
+ && twilioHold.get(symbol) == false) {
+ Message message = Message
+ .creator(new PhoneNumber("+13185723217"),
+ new PhoneNumber("+13186159356"),
+ symbol + " RSI is greater than " + rsiLimitHigh)
+ .create();
+ twilioHold.replace(symbol, true);
+ } else if (currentRSI.get(symbol) < rsiLimitLow
+ && twilioHold.get(symbol) == false) {
+ Message message
+ = Message
+ .creator(new PhoneNumber("+13185723217"),
+ new PhoneNumber("+13186159356"),
+ symbol + " RSI is less than " + rsiLimitLow)
+ .create();
+ twilioHold.replace(symbol, true);
+ }
+ String messageRSI
+ = symbol + " Current RSI: " + Double.toString(rsi);
+ System.out.println(messageRSI);
+ messages.add(messageRSI);
+
+ }
+ }
+ }
+
+ RsiWebSocket.sendRSIUpdateMessage(messages, username);
+
+ try {
+ Thread.sleep(30000);
+ } catch (InterruptedException e) {
+ System.out.println("Thread interrupted");
+ }
+ }
+
+ private List getDifferences(List candles) {
+ List diffs = new ArrayList();
+ for (int i = 1; i < candles.size(); i++) {
+ double diff = Double.parseDouble(candles.get(i).getClose())
+ - Double.parseDouble(candles.get(i - 1).getClose());
+ diffs.add(diff);
+ }
+ return diffs;
+ }
+
+ private List clipGains(List differences) {
+ List gains = differences;
+ for (double gain : gains) {
+ if (gain < 0) {
+ gain = 0;
+ }
+ }
+ return gains;
+ }
+
+ private List clipLoss(List differences) {
+ List losses = differences;
+ for (double loss : losses) {
+ if (loss > 0) {
+ loss = 0;
+ }
+ }
+ return losses;
+ }
+
+ private double averageGains(List gains) {
+ double sum = 0;
+ for (double gain : gains) {
+ sum += gain;
+ }
+ double average = sum / 14;
+ return average;
+ }
+
+ private double averageLoss(List losses) {
+ double sum = 0;
+ for (double loss : losses) {
+ sum += Math.abs(loss);
+ }
+ double average = sum / 14;
+ return average;
+ }
+
+ private void initializeMaps(Set symbols) {
+ for (String symbol : symbols) {
+ currentRSI.put(symbol, -1.0);
+ prevGains.put(symbol, new ArrayList