From baeb5799f14dff70ed8e2adade6fec7b7d40a67d Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Mon, 2 Aug 2021 17:18:03 -0600
Subject: [PATCH 01/85] Fix parseAPIErrorResponse so response code and message
 are included

---
 .../alpaca/rest/AlpacaClientException.java    | 34 +++++++++----------
 1 file changed, 16 insertions(+), 18 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
index 40ef1d6c..bcf89c2b 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
@@ -66,11 +66,7 @@ public String getMessage() {
         if (responseBody == null) {
             return super.getMessage();
         } else {
-            try {
-                return parseAPIErrorResponse();
-            } catch (Exception ignored) {
-                return super.getMessage();
-            }
+            return parseAPIErrorResponse();
         }
     }
 
@@ -87,24 +83,26 @@ public String getMessage() {
      * @return a formatted message of the error response
      */
     private String parseAPIErrorResponse() {
-        JsonElement responseJsonElement = JsonParser.parseString(responseBody);
+        try {
+            JsonElement responseJsonElement = JsonParser.parseString(responseBody);
 
-        if (responseJsonElement instanceof JsonObject) {
-            JsonObject responseJsonObject = responseJsonElement.getAsJsonObject();
+            if (responseJsonElement instanceof JsonObject) {
+                JsonObject responseJsonObject = responseJsonElement.getAsJsonObject();
 
-            if (responseJsonObject.has(CODE_KEY)) {
-                apiResponseCode = responseJsonObject.get(CODE_KEY).getAsInt();
-            }
+                if (responseJsonObject.has(CODE_KEY)) {
+                    apiResponseCode = responseJsonObject.get(CODE_KEY).getAsInt();
+                }
 
-            if (responseJsonObject.has(MESSAGE_KEY)) {
-                apiResponseMessage = responseJsonObject.get(MESSAGE_KEY).getAsString();
+                if (responseJsonObject.has(MESSAGE_KEY)) {
+                    apiResponseMessage = responseJsonObject.get(MESSAGE_KEY).getAsString();
+                }
             }
-        }
 
-        // Just use the response JSON if the message could not be parsed.
-        if (apiResponseMessage == null) {
-            apiResponseMessage = responseJsonElement.toString();
-        }
+            // Just use the response JSON if the message could not be parsed.
+            if (apiResponseMessage == null) {
+                apiResponseMessage = responseJsonElement.toString();
+            }
+        } catch (Exception ignored) {}
 
         // Build message
         StringBuilder messageBuilder = new StringBuilder();

From 997ec27ba85a9cf8b6dd4c7740ffc274bda17b8f Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Tue, 3 Aug 2021 16:44:58 -0600
Subject: [PATCH 02/85] Fix NPE if no listener is set in AlpacaWebsocket

---
 .../alpaca/websocket/AlpacaWebsocket.java              | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index 19dee225..331d89da 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -259,10 +259,12 @@ public Future<Boolean> getAuthorizationFuture() {
      * @param message     the message
      */
     protected void callListener(T messageType, M message) {
-        try {
-            listener.onMessage(messageType, message);
-        } catch (Exception exception) {
-            LOGGER.error("{} listener threw exception!", websocketName, exception);
+        if (listener != null) {
+            try {
+                listener.onMessage(messageType, message);
+            } catch (Exception exception) {
+                LOGGER.error("{} listener threw exception!", websocketName, exception);
+            }
         }
     }
 

From fe54b204c965aa5372f6bf1e52687a79c823a4eb Mon Sep 17 00:00:00 2001
From: cloudygeek <adrian.asher@acta.co.uk>
Date: Wed, 4 Aug 2021 23:13:46 +0100
Subject: [PATCH 03/85] Update FormatUtil.java

TimeZone Bug
---
 .../net/jacobpeterson/alpaca/util/format/FormatUtil.java | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
index 0b76f3b5..d2bf5b82 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
@@ -22,9 +22,6 @@ public class FormatUtil {
     // the 3rd and 4th least significant decimal digits.
     private static final NumberFormat CURRENCY_FORMATTER = new DecimalFormat("#0.00##",
             DecimalFormatSymbols.getInstance(Locale.US));
-    private static final DateTimeFormatter RFC_3339_FORMATTER =
-            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").withZone(NEW_YORK_ZONED_ID);
-
     /**
      * Formats an arbitrary number to a currency format. e.g. $123.45
      *
@@ -37,13 +34,15 @@ public static String toCurrencyFormat(Number numberToFormat) {
     }
 
     /**
-     * Formats a {@link TemporalAccessor} using {@link #RFC_3339_FORMATTER} {@link DateTimeFormatter}.
+     * Formats a {@link TemporalAccessor} using {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}.
      *
      * @param zonedDateTime the {@link ZonedDateTime}
      *
      * @return the formatted string
      */
     public static String toRFC3339Format(ZonedDateTime zonedDateTime) {
-        return RFC_3339_FORMATTER.format(zonedDateTime);
+        //Alpaca requires RFC 3339 (https://www.ietf.org/rfc/rfc3339.txt) date time to be provided which includes time zone.
+        // ISO 8601 is compatible with RFC 3339.
+        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime);
     }
 }

From 206774fe8b3878081801e455b108b187f4722411 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 11:04:58 -0600
Subject: [PATCH 04/85] Add 'n' and 'vw' fields to Bar

---
 schema_json/endpoint/market_data/historical/bar/bar.json | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/schema_json/endpoint/market_data/historical/bar/bar.json b/schema_json/endpoint/market_data/historical/bar/bar.json
index b39bff53..6be1fe66 100644
--- a/schema_json/endpoint/market_data/historical/bar/bar.json
+++ b/schema_json/endpoint/market_data/historical/bar/bar.json
@@ -25,6 +25,14 @@
     "v": {
       "existingJavaType": "java.lang.Long",
       "title": "Volume."
+    },
+    "n": {
+      "existingJavaType": "java.lang.Long",
+      "title": "Trade count."
+    },
+    "vw": {
+      "existingJavaType": "java.lang.Double",
+      "title": "VWAP (Volume Weighted Average Price)."
     }
   }
 }

From 23f126f257daff669f82ff9076d52e277c4d97b6 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 11:17:30 -0600
Subject: [PATCH 05/85] Fix Javadoc and formatting in FormatUtil

---
 .../net/jacobpeterson/alpaca/util/format/FormatUtil.java   | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
index d2bf5b82..b5a53a27 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
@@ -22,6 +22,7 @@ public class FormatUtil {
     // the 3rd and 4th least significant decimal digits.
     private static final NumberFormat CURRENCY_FORMATTER = new DecimalFormat("#0.00##",
             DecimalFormatSymbols.getInstance(Locale.US));
+
     /**
      * Formats an arbitrary number to a currency format. e.g. $123.45
      *
@@ -35,14 +36,16 @@ public static String toCurrencyFormat(Number numberToFormat) {
 
     /**
      * Formats a {@link TemporalAccessor} using {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}.
+     * <br>
+     * Alpaca requires <code>RFC 3339</code> formatted timestamps to be provided which includes the timezone. <code>ISO
+     * 8601</code> is compatible with <code>RFC 3339</code> which is what {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}
+     * uses.
      *
      * @param zonedDateTime the {@link ZonedDateTime}
      *
      * @return the formatted string
      */
     public static String toRFC3339Format(ZonedDateTime zonedDateTime) {
-        //Alpaca requires RFC 3339 (https://www.ietf.org/rfc/rfc3339.txt) date time to be provided which includes time zone.
-        // ISO 8601 is compatible with RFC 3339.
         return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime);
     }
 }

From 945bed160093150dfce957905f5e573188fdb689 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 11:56:38 -0600
Subject: [PATCH 06/85] Modify websocket Javadocs

---
 .../websocket/AlpacaWebsocketInterface.java    |  7 +++++--
 .../MarketDataWebsocketInterface.java          | 18 +++++++++---------
 .../streaming/StreamingWebsocketInterface.java |  4 ++--
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
index c508e799..272774f6 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
@@ -39,14 +39,17 @@ public interface AlpacaWebsocketInterface<L extends AlpacaWebsocketMessageListen
     /**
      * Gets a {@link Boolean} {@link Future} that completes when an authentication message that is received after a new
      * websocket connection indicates successful authentication.
+     * <br>
+     * Note that if this {@link AlpacaWebsocketInterface} is already authorized, the returned {@link Future} will likely
+     * never complete.
      *
      * @return a {@link Boolean} {@link Future}
      */
     Future<Boolean> getAuthorizationFuture();
 
     /**
-     * Waits for {@link #getAuthorizationFuture()} to complete and returns its value. This will timeout after 10 seconds
-     * and then return <code>false</code>.
+     * Waits for {@link #getAuthorizationFuture()} to complete and returns its value. After 10 seconds of waiting, this
+     * will timeout then return <code>false</code>.
      *
      * @return a boolean
      */
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
index 7c7d4d90..2170e898 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
@@ -15,11 +15,11 @@
 public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<MarketDataListener> {
 
     /**
-     * Subscribe to a specific control {@link MarketDataMessage}. That is, {@link ErrorMessage}, {@link
-     * SubscriptionsMessage}, or {@link SuccessMessage}.
+     * Subscribe to a specific control {@link MarketDataMessage} which contain information about the stream's current
+     * state. That is, an {@link ErrorMessage}, {@link SubscriptionsMessage}, or {@link SuccessMessage}.
      *
-     * @param marketDataMessageTypes array of any of: {@link MarketDataMessageType#SUCCESS}, {@link
-     *                               MarketDataMessageType#ERROR}, or {@link MarketDataMessageType#SUBSCRIPTION}
+     * @param marketDataMessageTypes array containing any of the following: {@link MarketDataMessageType#SUCCESS},
+     *                               {@link MarketDataMessageType#ERROR}, or {@link MarketDataMessageType#SUBSCRIPTION}
      */
     void subscribeToControl(MarketDataMessageType... marketDataMessageTypes);
 
@@ -44,7 +44,7 @@ public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<M
     /**
      * Unsubscribes from trades, quotes, or bars according to the given {@link Collection} of symbols.
      * <br>
-     * Note that any one of the given {@link Collection}s can contain the wildcard character e.g. "*" to unsubscribe
+     * Note that any one of the given {@link Collection}s can contain the wildcard character (e.g. "*") to unsubscribe
      * from a previously subscribed wildcard.
      *
      * @param tradeSymbols a {@link Collection} of symbols to unsubscribe from trades or <code>null</code> for no
@@ -58,28 +58,28 @@ public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<M
     void unsubscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols);
 
     /**
-     * Gets all of the currently subscribed control {@link MarketDataMessageType}s.
+     * Gets all the currently subscribed control {@link MarketDataMessageType}s.
      *
      * @return a {@link Collection} of {@link MarketDataMessageType}s
      */
     Collection<MarketDataMessageType> subscribedControls();
 
     /**
-     * Gets all of the currently subscribed symbols for trade updates.
+     * Gets all the currently subscribed symbols for trade updates.
      *
      * @return a {@link Collection} of {@link String}s
      */
     Collection<String> subscribedTrades();
 
     /**
-     * Gets all of the currently subscribed symbols for quote updates.
+     * Gets all the currently subscribed symbols for quote updates.
      *
      * @return a {@link Collection} of {@link String}s
      */
     Collection<String> subscribedQuotes();
 
     /**
-     * Gets all of the currently subscribed symbols for bar updates.
+     * Gets all the currently subscribed symbols for bar updates.
      *
      * @return a {@link Collection} of {@link String}s
      */
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
index 9b2dc5ca..07112a68 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
@@ -11,7 +11,7 @@
 public interface StreamingWebsocketInterface extends AlpacaWebsocketInterface<StreamingListener> {
 
     /**
-     * Sets the {@link StreamingMessageType}s to <code>streamingMessageTypes</code>.
+     * Sets the {@link StreamingMessageType}s for this stream to the given <code>streamingMessageTypes</code>.
      * <br>
      * Note that this will call {@link StreamingWebsocketInterface#connect()} and {@link
      * StreamingWebsocketInterface#waitForAuthorization()} if {@link StreamingWebsocketInterface#isConnected()} returns
@@ -22,7 +22,7 @@ public interface StreamingWebsocketInterface extends AlpacaWebsocketInterface<St
     void streams(StreamingMessageType... streamingMessageTypes);
 
     /**
-     * Gets the {@link StreamingMessageType}s.
+     * Gets all the currently subscribed {@link StreamingMessageType}s.
      *
      * @return a {@link Collection} of {@link StreamingMessageType}
      */

From 940937cd9a0b84127bc9babd8ee6483a603c6962 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 12:04:42 -0600
Subject: [PATCH 07/85] Add websocket clarifications to README

---
 README.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/README.md b/README.md
index dad90b54..f144c7d6 100644
--- a/README.md
+++ b/README.md
@@ -384,9 +384,13 @@ alpacaAPI.streaming().streams(
 
 The following methods show how you can control the state of the [`StreamingWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java) directly.
 ```java
+// Note that you usually won't need to call connect() and waitForAuthorization() 
+// directly, as they are automatically called when subscribing to streams.
 alpacaAPI.streaming().connect();
 alpacaAPI.streaming().waitForAuthorization();
 System.out.println(alpacaAPI.streaming().isValid());
+
+// You can manually disconnect the websocket as needed
 alpacaAPI.streaming().disconnect();
 ```
 
@@ -417,9 +421,13 @@ alpacaAPI.marketDataStreaming().subscribe(
 
 The following methods show how you can control the state of the [`MarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java) directly.
 ```java
+// Note that you usually won't need to call connect() and waitForAuthorization() 
+// directly, as they are automatically called when subscribing to streams.
 alpacaAPI.marketDataStreaming().connect();
 alpacaAPI.marketDataStreaming().waitForAuthorization();
 System.out.println(alpacaAPI.marketDataStreaming().isValid());
+
+// You can manually disconnect the websocket as needed
 alpacaAPI.marketDataStreaming().disconnect();
 ```
 

From 7986444a31b5f3f6cb2664e679392140457b9fe0 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 13:35:06 -0600
Subject: [PATCH 08/85] Remove auto-connect feature from websockets

---
 README.md                                     | 56 ++++++++++---------
 .../websocket/AlpacaWebsocketInterface.java   | 11 ++--
 .../marketdata/MarketDataWebsocket.java       | 10 +---
 .../MarketDataWebsocketInterface.java         |  4 --
 .../streaming/StreamingWebsocket.java         | 14 ++---
 .../StreamingWebsocketInterface.java          |  4 --
 6 files changed, 44 insertions(+), 55 deletions(-)

diff --git a/README.md b/README.md
index f144c7d6..b0c47a8d 100644
--- a/README.md
+++ b/README.md
@@ -374,28 +374,31 @@ StreamingListener streamingListener = (messageType, message) ->
         System.out.printf("%s: %s\n", messageType.name(), message);
 alpacaAPI.streaming().setListener(streamingListener);
 
-// Listen to the 'authorization' and 'trade update' streams.
-// Note this will connect the websocket and wait for authorization
-// if it isn't already connected.
-alpacaAPI.streaming().streams(
-        StreamingMessageType.AUTHORIZATION,
-        StreamingMessageType.TRADE_UPDATES);
-```
+// Listen 'AuthorizationMessage' and 'ListeningMessage' messages that contain 
+// information about the stream's current state.
+alpacaAPI.streaming().streams(StreamingMessageType.AUTHORIZATION,
+        StreamingMessageType.LISTENING);
 
-The following methods show how you can control the state of the [`StreamingWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java) directly.
-```java
-// Note that you usually won't need to call connect() and waitForAuthorization() 
-// directly, as they are automatically called when subscribing to streams.
+// Connect the websocket and confirm authentication
 alpacaAPI.streaming().connect();
-alpacaAPI.streaming().waitForAuthorization();
-System.out.println(alpacaAPI.streaming().isValid());
+alpacaAPI.streaming().waitForAuthorization(5, TimeUnit.SECONDS);
+if (!alpacaAPI.streaming().isValid()) {
+    System.out.println("Websocket not valid!");
+    return;
+}
 
-// You can manually disconnect the websocket as needed
+// Listen to the 'trade update' streams.
+alpacaAPI.streaming().streams(StreamingMessageType.TRADE_UPDATES);
+
+// Wait a few seconds
+Thread.sleep(5000);
+
+// Manually disconnect the websocket
 alpacaAPI.streaming().disconnect();
 ```
 
 ## [`MarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java)
-Alpaca's Data API v2 provides websocket streaming for trades, quotes, and minute bars. This helps receive the most up to date market information that could help your trading strategy to act upon certain market movement.
+Alpaca's Data API v2 provides websocket streaming for trades, quotes, and minute bars. This helps receive the most up-to-date market information that could help your trading strategy to act upon certain market movement.
 
 Example usage:
 ```java
@@ -410,24 +413,25 @@ alpacaAPI.marketDataStreaming().subscribeToControl(
         MarketDataMessageType.SUCCESS,
         MarketDataMessageType.SUBSCRIPTION,
         MarketDataMessageType.ERROR);
+
+// Connect the websocket and confirm authentication
+alpacaAPI.marketDataStreaming().connect();
+alpacaAPI.marketDataStreaming().waitForAuthorization(5, TimeUnit.SECONDS);
+if (!alpacaAPI.marketDataStreaming().isValid()) {
+    System.out.println("Websocket not valid!");
+    return;
+}
+
 // Listen to the AAPL and TSLA trades and all ('*') bars.
-// Note this will connect the websocket and wait for authorization
-// if it isn't already connected.
 alpacaAPI.marketDataStreaming().subscribe(
         Arrays.asList("AAPL", "TSLA"),
         null,
         Arrays.asList("*"));
-```
 
-The following methods show how you can control the state of the [`MarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java) directly.
-```java
-// Note that you usually won't need to call connect() and waitForAuthorization() 
-// directly, as they are automatically called when subscribing to streams.
-alpacaAPI.marketDataStreaming().connect();
-alpacaAPI.marketDataStreaming().waitForAuthorization();
-System.out.println(alpacaAPI.marketDataStreaming().isValid());
+// Wait a few seconds
+Thread.sleep(5000);
 
-// You can manually disconnect the websocket as needed
+// Manually disconnect the websocket
 alpacaAPI.marketDataStreaming().disconnect();
 ```
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
index 272774f6..e7d8b60c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
@@ -48,14 +48,17 @@ public interface AlpacaWebsocketInterface<L extends AlpacaWebsocketMessageListen
     Future<Boolean> getAuthorizationFuture();
 
     /**
-     * Waits for {@link #getAuthorizationFuture()} to complete and returns its value. After 10 seconds of waiting, this
-     * will timeout then return <code>false</code>.
+     * Waits for {@link #getAuthorizationFuture()} to complete and returns its value, except when <code>timeout</code>
+     * time has elapsed, then this will return <code>false</code>.
+     *
+     * @param timeout the timeout time
+     * @param unit    the timeout {@link TimeUnit}
      *
      * @return a boolean
      */
-    default boolean waitForAuthorization() {
+    default boolean waitForAuthorization(long timeout, TimeUnit unit) {
         try {
-            return getAuthorizationFuture().get(10, TimeUnit.SECONDS);
+            return getAuthorizationFuture().get(timeout, unit);
         } catch (InterruptedException | ExecutionException | TimeoutException ignored) {}
         return false;
     }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
index ad189169..48953560 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -27,6 +27,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
@@ -107,7 +108,7 @@ protected void onConnection() {
     @Override
     protected void onReconnection() {
         sendAuthenticationMessage();
-        if (waitForAuthorization()) {
+        if (waitForAuthorization(5, TimeUnit.SECONDS)) {
             subscribeToControl(Iterables.toArray(listenedMarketDataMessageTypes, MarketDataMessageType.class));
             subscribe(subscribedTrades, subscribedQuotes, subscribedBars);
         }
@@ -292,12 +293,7 @@ public void unsubscribe(Collection<String> tradeSymbols, Collection<String> quot
     private void sendSubscriptionUpdate(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
             Collection<String> barSymbols, boolean subscribe) {
         if (!isConnected()) {
-            connect();
-
-            if (!waitForAuthorization()) {
-                LOGGER.error("Not subscribing to streams due to unauthorized {} websocket.", websocketName);
-                return;
-            }
+            throw new IllegalStateException("This websocket must be connected before sending subscription updates!");
         }
 
         /* Format of message is:
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
index 2170e898..9ac90112 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
@@ -28,10 +28,6 @@ public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<M
      * <br>
      * Note that any one of the given {@link Collection}s can contain the wildcard character e.g. "*" to subscribe to
      * ALL available symbols.
-     * <br>
-     * Note that this will call {@link MarketDataWebsocketInterface#connect()} and {@link
-     * MarketDataWebsocketInterface#waitForAuthorization()} if {@link MarketDataWebsocketInterface#isConnected()}
-     * returns false.
      *
      * @param tradeSymbols a {@link Collection} of symbols to subscribe to trades or <code>null</code> for no change
      * @param quoteSymbols a {@link Collection} of symbols to subscribe to quotes or <code>null</code> for no change
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
index 8c52b078..57ede8a4 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
@@ -27,6 +27,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
@@ -94,7 +95,7 @@ protected void onConnection() {
     @Override
     protected void onReconnection() {
         sendAuthenticationMessage();
-        if (waitForAuthorization()) {
+        if (waitForAuthorization(5, TimeUnit.SECONDS)) {
             streams(Iterables.toArray(listenedStreamMessageTypes, StreamingMessageType.class));
         }
     }
@@ -214,15 +215,6 @@ public void streams(StreamingMessageType... streamingMessageTypes) {
                 .filter(not(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains))
                 .forEach(listenedStreamMessageTypes::add);
 
-        if (!isConnected()) {
-            connect();
-
-            if (!waitForAuthorization()) {
-                LOGGER.error("Not subscribing to streams due to unauthorized {} websocket.", websocketName);
-                return;
-            }
-        }
-
         // Stream request format:
         // {
         //     "action": "listen",
@@ -241,6 +233,8 @@ public void streams(StreamingMessageType... streamingMessageTypes) {
 
         if (streamsArray.isEmpty()) {
             return;
+        } else if (!isConnected()) {
+            throw new IllegalStateException("This websocket must be connected before subscribing to streams!");
         }
 
         JsonObject dataObject = new JsonObject();
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
index 07112a68..44f0042e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
@@ -12,10 +12,6 @@ public interface StreamingWebsocketInterface extends AlpacaWebsocketInterface<St
 
     /**
      * Sets the {@link StreamingMessageType}s for this stream to the given <code>streamingMessageTypes</code>.
-     * <br>
-     * Note that this will call {@link StreamingWebsocketInterface#connect()} and {@link
-     * StreamingWebsocketInterface#waitForAuthorization()} if {@link StreamingWebsocketInterface#isConnected()} returns
-     * false.
      *
      * @param streamingMessageTypes the {@link StreamingMessageType}s
      */

From 2a95a4911f3a0eedd3a8c9b67a07d79f9b059b3f Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 14:17:24 -0600
Subject: [PATCH 09/85] Add websocket clarification to README

---
 README.md                                      |  8 ++++++--
 .../websocket/AlpacaWebsocketInterface.java    | 18 +++++++++---------
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/README.md b/README.md
index b0c47a8d..cca3aa50 100644
--- a/README.md
+++ b/README.md
@@ -375,7 +375,9 @@ StreamingListener streamingListener = (messageType, message) ->
 alpacaAPI.streaming().setListener(streamingListener);
 
 // Listen 'AuthorizationMessage' and 'ListeningMessage' messages that contain 
-// information about the stream's current state.
+// information about the stream's current state. Note that these are subscribed
+// to before the websocket is connected since these messages usually are sent
+// upon websocket connection. 
 alpacaAPI.streaming().streams(StreamingMessageType.AUTHORIZATION,
         StreamingMessageType.LISTENING);
 
@@ -408,7 +410,9 @@ MarketDataListener marketDataListener = (messageType, message) ->
 alpacaAPI.marketDataStreaming().setListener(marketDataListener);
 
 // Listen to 'SubscriptionsMessage', 'SuccessMessage', and 'ErrorMessage' control messages
-// that contain information about the stream's current state.
+// that contain information about the stream's current state. Note that these are subscribed
+// to before the websocket is connected since these messages usually are sent
+// upon websocket connection. 
 alpacaAPI.marketDataStreaming().subscribeToControl(
         MarketDataMessageType.SUCCESS,
         MarketDataMessageType.SUBSCRIPTION,
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
index e7d8b60c..f524e1df 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
@@ -36,6 +36,15 @@ public interface AlpacaWebsocketInterface<L extends AlpacaWebsocketMessageListen
      */
     boolean isAuthenticated();
 
+    /**
+     * True if {@link #isConnected()} and {@link #isAuthenticated()}.
+     *
+     * @return a boolean
+     */
+    default boolean isValid() {
+        return isConnected() && isAuthenticated();
+    }
+
     /**
      * Gets a {@link Boolean} {@link Future} that completes when an authentication message that is received after a new
      * websocket connection indicates successful authentication.
@@ -63,15 +72,6 @@ default boolean waitForAuthorization(long timeout, TimeUnit unit) {
         return false;
     }
 
-    /**
-     * True if {@link #isConnected()} and {@link #isAuthenticated()}.
-     *
-     * @return a boolean
-     */
-    default boolean isValid() {
-        return isConnected() && isAuthenticated();
-    }
-
     /**
      * Sets the {@link AlpacaWebsocketMessageListener} for this {@link AlpacaWebsocketInterface}.
      *

From d70ec87e06964ddada16ec0cdf32566e4ae9ec07 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Thu, 5 Aug 2021 14:31:58 -0600
Subject: [PATCH 10/85] Update to 8.2

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index cca3aa50..e56eab73 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.1'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.2'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>8.1</version>
+    <version>8.2</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index 6e25af5e..cbace30e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '8.1-SNAPSHOT'
+version = '8.2-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 9f6a5493a27381fd76512bec5b48bc7ad9ec3ab4 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Mon, 13 Sep 2021 15:24:52 -0600
Subject: [PATCH 11/85] Add BarAdjustment and BarTimePeriod enums to getBars()

---
 README.md                                     |  6 ++-
 .../historical/bar/enums/bar_adjustment.json  | 24 ++++++++++++
 ...s_time_frame.json => bar_time_period.json} | 12 +++---
 .../rest/endpoint/MarketDataEndpoint.java     | 39 ++++++++++++-------
 .../rest/endpoint/PositionsEndpoint.java      |  7 +++-
 5 files changed, 65 insertions(+), 23 deletions(-)
 create mode 100644 schema_json/endpoint/market_data/historical/bar/enums/bar_adjustment.json
 rename schema_json/endpoint/market_data/historical/bar/enums/{bars_time_frame.json => bar_time_period.json} (68%)

diff --git a/README.md b/README.md
index e56eab73..7886cd50 100644
--- a/README.md
+++ b/README.md
@@ -101,7 +101,7 @@ The Data API v2 provides market data through an easy-to-use API for historical d
 Example usage:
 ```java
 try {
-    // Get AAPL one hour bars from 7/6/2021 market open
+    // Get AAPL one hour, split-adjusted bars from 7/6/2021 market open
     // to 7/8/2021 market close and print them out
     BarsResponse aaplBarsResponse = alpacaAPI.marketData().getBars(
             "AAPL",
@@ -109,7 +109,9 @@ try {
             ZonedDateTime.of(2021, 7, 8, 12 + 4, 0, 0, 0, ZoneId.of("America/New_York")),
             null,
             null,
-            BarsTimeFrame.ONE_HOUR);
+            1,
+            BarTimePeriod.HOUR,
+            BarAdjustment.SPLIT);
     aaplBarsResponse.getBars().forEach(System.out::println);
 
     // Get AAPL first 10 trades on 7/8/2021 at market open and print them out
diff --git a/schema_json/endpoint/market_data/historical/bar/enums/bar_adjustment.json b/schema_json/endpoint/market_data/historical/bar/enums/bar_adjustment.json
new file mode 100644
index 00000000..4f28b6ed
--- /dev/null
+++ b/schema_json/endpoint/market_data/historical/bar/enums/bar_adjustment.json
@@ -0,0 +1,24 @@
+{
+  "type": "string",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "enum": [
+    "raw",
+    "split",
+    "dividend",
+    "all"
+  ],
+  "javaEnums": [
+    {
+      "name": "RAW"
+    },
+    {
+      "name": "SPLIT"
+    },
+    {
+      "name": "DIVIDEND"
+    },
+    {
+      "name": "ALL"
+    }
+  ]
+}
diff --git a/schema_json/endpoint/market_data/historical/bar/enums/bars_time_frame.json b/schema_json/endpoint/market_data/historical/bar/enums/bar_time_period.json
similarity index 68%
rename from schema_json/endpoint/market_data/historical/bar/enums/bars_time_frame.json
rename to schema_json/endpoint/market_data/historical/bar/enums/bar_time_period.json
index 89eb394f..627d9413 100644
--- a/schema_json/endpoint/market_data/historical/bar/enums/bars_time_frame.json
+++ b/schema_json/endpoint/market_data/historical/bar/enums/bar_time_period.json
@@ -2,19 +2,19 @@
   "type": "string",
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
   "enum": [
-    "1Min",
-    "1Hour",
-    "1Day"
+    "Min",
+    "Hour",
+    "Day"
   ],
   "javaEnums": [
     {
-      "name": "ONE_MINUTE"
+      "name": "MINUTE"
     },
     {
-      "name": "ONE_HOUR"
+      "name": "HOUR"
     },
     {
-      "name": "ONE_DAY"
+      "name": "DAY"
     }
   ]
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
index 9a270e21..a1b4c425 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
@@ -3,7 +3,8 @@
 import com.google.gson.reflect.TypeToken;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.BarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.enums.BarsTimeFrame;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.enums.BarAdjustment;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.enums.BarTimePeriod;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.LatestQuoteResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.QuotesResponse;
@@ -180,26 +181,34 @@ public LatestQuoteResponse getLatestQuote(String symbol) throws AlpacaClientExce
     /**
      * Gets {@link Bar} aggregate historical data for the requested security.
      *
-     * @param symbol    the symbol to query for
-     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted.
-     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted.
-     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
-     *                  is given
-     * @param pageToken pagination token to continue from
-     * @param timeFrame the {@link BarsTimeFrame} for the aggregation
+     * @param symbol                the symbol to query for
+     * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
+     *                              not accepted.
+     * @param end                   filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are
+     *                              not accepted.
+     * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
+     *                              <code>null</code> is given
+     * @param pageToken             pagination token to continue from
+     * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
+     *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
+     *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
+     * @param barTimePeriod         the {@link BarTimePeriod} e.g. for <code>15Min</code> bars, you would supply {@link
+     *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
+     *                              <code>barTimePeriodDuration</code> parameter.
+     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is {@link
+     *                              BarAdjustment#RAW}.
      *
      * @return the {@link BarsResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
     public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
-            String pageToken, BarsTimeFrame timeFrame) throws AlpacaClientException {
+            String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod, BarAdjustment barAdjustment)
+            throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(start);
         checkNotNull(end);
-        checkNotNull(timeFrame);
+        checkNotNull(barTimePeriod);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
@@ -208,7 +217,7 @@ public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime en
 
         urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
         urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-        urlBuilder.addQueryParameter("timeframe", timeFrame.toString());
+        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
 
         if (limit != null) {
             urlBuilder.addQueryParameter("limit", limit.toString());
@@ -218,6 +227,10 @@ public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime en
             urlBuilder.addQueryParameter("page_token", pageToken);
         }
 
+        if (barAdjustment != null) {
+            urlBuilder.addQueryParameter("adjustment", barAdjustment.toString());
+        }
+
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java
index 676537ba..26d72861 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java
@@ -13,7 +13,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
@@ -98,6 +97,11 @@ public List<ClosePositionOrder> closeAll(Boolean cancelOrders) throws AlpacaClie
      * positions.
      *
      * @param symbolOrAssetID the symbol or {@link Asset#getId()}
+     * @param quantity        the number of shares to liquidate. Can accept up to <code>9</code> decimal points. Cannot
+     *                        work with <code>percentage</code>.
+     * @param percentage      the percentage of the position to liquidate. Must be between <code>0</code> and
+     *                        <code>100</code>. Would only sell fractional if position is originally fractional. Can
+     *                        accept up to <code>9</code> decimal points. Cannot work with <code>quantity</code>.
      *
      * @return a closing {@link Position} {@link Order}
      *
@@ -105,7 +109,6 @@ public List<ClosePositionOrder> closeAll(Boolean cancelOrders) throws AlpacaClie
      */
     public Order close(String symbolOrAssetID, Integer quantity, Double percentage) throws AlpacaClientException {
         checkNotNull(symbolOrAssetID);
-        checkArgument(quantity != null ^ percentage != null, "Either 'quantity' or 'percentage' are required.");
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)

From 1d7609275498816d2e97a7310b3ff853600196e3 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Mon, 13 Sep 2021 15:26:13 -0600
Subject: [PATCH 12/85] Update to 8.3

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 7886cd50..946e09d6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.2'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>8.2</version>
+    <version>8.3</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index cbace30e..1eb30114 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '8.2-SNAPSHOT'
+version = '8.3-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 3064fa3d51298f355c966aadb7fe88c390dc0f0e Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Mon, 20 Sep 2021 08:11:41 -0600
Subject: [PATCH 13/85] Fix getSnapshot path segment typo

---
 .../jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
index a1b4c425..45fc4a08 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
@@ -279,7 +279,7 @@ public Snapshot getSnapshot(String symbol) throws AlpacaClientException {
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
                 .addPathSegment(symbol)
-                .addPathSegment("shapshot");
+                .addPathSegment("snapshot");
 
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()

From 9bceea3171eee8078549d02f30138b18ef729dc0 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <Petersoj@users.noreply.github.com>
Date: Mon, 20 Sep 2021 08:12:54 -0600
Subject: [PATCH 14/85] Update to 8.3.1

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 946e09d6..b8ac95e1 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3.1'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>8.3</version>
+    <version>8.3.1</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index 1eb30114..d5566da8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '8.3-SNAPSHOT'
+version = '8.3.1-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 5df5e105706f7135df28255f1019137d8db93e52 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 12 Oct 2021 17:20:56 -0600
Subject: [PATCH 15/85] Update README, cleanup AlpacaAPITest

---
 README.md                                     |  3 +-
 .../alpaca/test/live/AlpacaAPITest.java       | 41 +++++++++++--------
 2 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md
index b8ac95e1..f571e41e 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ data_api_type = <must be either "iex" or "sip">
 The default values for `alpaca.properties` can be found [here](src/main/resources/alpaca.default.properties).
 
 # Logger
-For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
+For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Logback](http://logback.qos.ch/), [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
 
 # Examples
 Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson/alpaca-java) for all classes and method signatures.
@@ -471,6 +471,7 @@ Note that the live tests will modify your account minimally. It's meant to test
 - Use [TA4j](https://github.com/ta4j/ta4j) `Num` interface instead of `Double` for number variables so that users can use either `Double` or `BigDecimal` for performance or precision in price data.
 - Add [TimeSeriesDataStore](https://github.com/Petersoj/TimeSeriesDataStore) using Alpaca Data API
 
+# Contributing
 Contributions are welcome!
 
 If you are creating a Pull Request, be sure to create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork. 
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
index 00fe9e96..326a6aaf 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
@@ -1,5 +1,6 @@
 package net.jacobpeterson.alpaca.test.live;
 
+import net.jacobpeterson.alpaca.AlpacaAPI;
 import net.jacobpeterson.alpaca.model.endpoint.account.Account;
 import net.jacobpeterson.alpaca.model.endpoint.accountactivities.AccountActivity;
 import net.jacobpeterson.alpaca.model.endpoint.accountactivities.NonTradeActivity;
@@ -12,21 +13,28 @@
 import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
 import net.jacobpeterson.alpaca.model.endpoint.order.Order;
 import net.jacobpeterson.alpaca.model.endpoint.order.enums.CurrentOrderStatus;
-import net.jacobpeterson.alpaca.AlpacaAPI;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AccountActivitiesEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.AccountConfigurationEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.AccountEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.ClockEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountActivitiesEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.OrdersEndpoint;
-import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.time.ZonedDateTime;
+import java.util.Collection;
 import java.util.List;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * {@link AlpacaAPITest} tests live endpoints using Alpaca Paper credentials given in the
@@ -35,14 +43,14 @@
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
 public class AlpacaAPITest {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPITest.class);
-    private static final int RATE_LIMIT_MILLIS = 200; // Wait 200ms between every test to prevent rate-limiting
-
     static {
         // Log trace-level
         System.setProperty("org.slf4j.simpleLogger.log.net.jacobpeterson", "trace");
     }
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPITest.class);
+    private static final int RATE_LIMIT_MILLIS = 200; // Wait 200ms between every test to prevent rate-limiting
+
     private static AlpacaAPI alpacaAPI;
     private static boolean marketOpen;
     private static AccountConfiguration accountConfiguration;
@@ -83,7 +91,7 @@ public void afterEach() {
      */
     @Test
     @org.junit.jupiter.api.Order(1)
-    public void test_ClockEndpoint_get() throws AlpacaClientException {
+    public void testClockEndpointGet() throws AlpacaClientException {
         Clock clock = alpacaAPI.clock().get();
         assertNotNull(clock);
 
@@ -110,7 +118,7 @@ public void test_ClockEndpoint_get() throws AlpacaClientException {
      */
     @Test
     @SuppressWarnings("ResultOfMethodCallIgnored")
-    public void test_AccountEndpoint_get() throws AlpacaClientException, NumberFormatException {
+    public void testAccountEndpointGet() throws AlpacaClientException, NumberFormatException {
         Account account = alpacaAPI.account().get();
         assertNotNull(account);
 
@@ -148,14 +156,13 @@ public void test_AccountEndpoint_get() throws AlpacaClientException, NumberForma
     }
 
     /**
-     * Tests @{@link AccountActivitiesEndpoint#get(ZonedDateTime,
-     * ZonedDateTime, ZonedDateTime, SortDirection, Integer, String, ActivityType...)} one {@link AccountActivity}
-     * exists until now.
+     * Tests @{@link AccountActivitiesEndpoint#get(ZonedDateTime, ZonedDateTime, ZonedDateTime, SortDirection, Integer,
+     * String, ActivityType...)} one {@link AccountActivity} exists until now.
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
     @Test
-    public void test_AccountActivitiesEndpoint_get_One_Activity_Exists_Until_Now() throws AlpacaClientException {
+    public void testAccountActivitiesEndpointGetOneActivityExistsUntilNow() throws AlpacaClientException {
         List<AccountActivity> accountActivities = alpacaAPI.accountActivities().get(
                 null,
                 ZonedDateTime.now(),
@@ -203,7 +210,7 @@ public void test_AccountActivitiesEndpoint_get_One_Activity_Exists_Until_Now() t
      */
     @Test
     @org.junit.jupiter.api.Order(1)
-    public void test_AccountConfigurationEndpoint_get() throws AlpacaClientException {
+    public void testAccountConfigurationEndpointGet() throws AlpacaClientException {
         AccountConfiguration accountConfiguration = alpacaAPI.accountConfiguration().get();
         assertNotNull(accountConfiguration);
 
@@ -224,7 +231,7 @@ public void test_AccountConfigurationEndpoint_get() throws AlpacaClientException
      */
     @Test
     @org.junit.jupiter.api.Order(2)
-    public void test_AccountConfigurationEndpoint_set() throws AlpacaClientException {
+    public void testAccountConfigurationEndpointSet() throws AlpacaClientException {
         if (accountConfiguration == null) {
             AccountConfiguration newAccountConfiguration = new AccountConfiguration(
                     DTBPCheck.BOTH,
@@ -240,12 +247,12 @@ public void test_AccountConfigurationEndpoint_set() throws AlpacaClientException
 
     /**
      * Test {@link OrdersEndpoint#get(CurrentOrderStatus, Integer, ZonedDateTime, ZonedDateTime, SortDirection, Boolean,
-     * List)} one {@link Order} exists until now.
+     * Collection)} one {@link Order} exists until now.
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
     @Test
-    public void test_OrdersEndpoint_get_One_Order_Exists_Until_now() throws AlpacaClientException {
+    public void testOrdersEndpointGetOneOrderExistsUntilNow() throws AlpacaClientException {
         List<Order> orders = alpacaAPI.orders().get(
                 CurrentOrderStatus.ALL,
                 1,

From 95cc9352c032cdbc089c151da43557fa1bcabfca Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Thu, 11 Nov 2021 00:08:48 -0700
Subject: [PATCH 16/85] Add more notes about AlpacaAPI constructors in README,
 fix javadocs

---
 README.md                                     |  9 ++++--
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 32 +++++++++----------
 2 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md
index f571e41e..a0dce667 100644
--- a/README.md
+++ b/README.md
@@ -60,10 +60,15 @@ Example usage:
 // This constructor uses the 'alpaca.properties' file on the classpath for configuration
 AlpacaAPI alpacaAPI = new AlpacaAPI();
 
-// This constructor passes in a 'keyID' and 'secret' String
+// This constructor passes in a 'keyID' and 'secretKey' and uses the endpoint API type and data API
+// type defined in the 'alpaca.properties' file (which default to 'paper' and 'iex' respectively)
 String keyID = "<some key ID>";
 String secretKey = "<some secret>";
-AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secret);
+AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey);
+
+// This constructor passes in a 'keyID' and 'secretKey' and uses the passed in endpoint API type
+// and data API type (which are 'LIVE' and 'SIP' respectively in this example)
+AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey, EndpointAPIType.LIVE, DataAPIType.SIP);
 
 // This constructor is for OAuth tokens
 String oAuthToken = "<some OAuth token>";
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 616e53e4..b874d794 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -75,11 +75,11 @@ public AlpacaAPI() {
     /**
      * Instantiates a new {@link AlpacaAPI}.
      *
-     * @param keyID  the key ID
-     * @param secret the secret
+     * @param keyID     the key ID
+     * @param secretKey the secret key
      */
-    public AlpacaAPI(String keyID, String secret) {
-        this(null, keyID, secret, null,
+    public AlpacaAPI(String keyID, String secretKey) {
+        this(null, keyID, secretKey, null,
                 AlpacaProperties.ENDPOINT_API_TYPE,
                 AlpacaProperties.DATA_API_TYPE);
     }
@@ -88,12 +88,12 @@ public AlpacaAPI(String keyID, String secret) {
      * Instantiates a new {@link AlpacaAPI}.
      *
      * @param keyID           the key ID
-     * @param secret          the secret
+     * @param secretKey       the secret key
      * @param endpointAPIType the {@link EndpointAPIType}
      * @param dataAPIType     the {@link DataAPIType}
      */
-    public AlpacaAPI(String keyID, String secret, EndpointAPIType endpointAPIType, DataAPIType dataAPIType) {
-        this(null, keyID, secret, null, endpointAPIType, dataAPIType);
+    public AlpacaAPI(String keyID, String secretKey, EndpointAPIType endpointAPIType, DataAPIType dataAPIType) {
+        this(null, keyID, secretKey, null, endpointAPIType, dataAPIType);
     }
 
     /**
@@ -155,11 +155,9 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
             brokerClient = new AlpacaClient(okHttpClient, keyID, secretKey,
                     brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
             dataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_2_PATH_SEGMENT);
-            marketDataWebsocket = new MarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
         } else {
             brokerClient = new AlpacaClient(okHttpClient, oAuthToken, brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
             dataClient = null;
-            marketDataWebsocket = null;
         }
 
         accountEndpoint = new AccountEndpoint(brokerClient);
@@ -174,10 +172,12 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         accountActivitiesEndpoint = new AccountActivitiesEndpoint(brokerClient);
         portfolioHistoryEndpoint = new PortfolioHistoryEndpoint(brokerClient);
         streamingWebsocket = new StreamingWebsocket(okHttpClient, brokerHostSubdomain, keyID, secretKey, oAuthToken);
+        marketDataWebsocket = dataClient == null ? null :
+                new MarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
     }
 
     /**
-     * @return {@link AccountEndpoint}
+     * @return the {@link AccountEndpoint}
      */
     public AccountEndpoint account() {
         return accountEndpoint;
@@ -191,42 +191,42 @@ public MarketDataEndpoint marketData() {
     }
 
     /**
-     * @return {@link OrdersEndpoint}
+     * @return the {@link OrdersEndpoint}
      */
     public OrdersEndpoint orders() {
         return ordersEndpoint;
     }
 
     /**
-     * @return {@link PositionsEndpoint}
+     * @return the {@link PositionsEndpoint}
      */
     public PositionsEndpoint positions() {
         return positionsEndpoint;
     }
 
     /**
-     * @return {@link AssetsEndpoint}
+     * @return the {@link AssetsEndpoint}
      */
     public AssetsEndpoint assets() {
         return assetsEndpoint;
     }
 
     /**
-     * @return {@link WatchlistEndpoint}
+     * @return the {@link WatchlistEndpoint}
      */
     public WatchlistEndpoint watchlist() {
         return watchlistEndpoint;
     }
 
     /**
-     * @return {@link CalendarEndpoint}
+     * @return the {@link CalendarEndpoint}
      */
     public CalendarEndpoint calendar() {
         return calendarEndpoint;
     }
 
     /**
-     * @return {@link ClockEndpoint}
+     * @return the {@link ClockEndpoint}
      */
     public ClockEndpoint clock() {
         return clockEndpoint;

From a55256c88fda84cdec0fd22f275d2bfaac2d17ca Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 19 Nov 2021 09:19:34 -0700
Subject: [PATCH 17/85] Add SSP and SPLIT ActivityType

---
 .../account_activities/enums/activity_type.json        | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/schema_json/endpoint/account_activities/enums/activity_type.json b/schema_json/endpoint/account_activities/enums/activity_type.json
index 03c44af1..9fae432b 100644
--- a/schema_json/endpoint/account_activities/enums/activity_type.json
+++ b/schema_json/endpoint/account_activities/enums/activity_type.json
@@ -35,7 +35,9 @@
     "FEE",
     "REORG",
     "SC",
-    "SSO"
+    "SSO",
+    "SSP",
+    "SPLIT"
   ],
   "javaEnums": [
     {
@@ -139,6 +141,12 @@
     },
     {
       "description": "Stock spinoff."
+    },
+    {
+      "description": "Stock split."
+    },
+    {
+      "description": "Stock split."
     }
   ]
 }

From 1bde139826f8dc4efcddd0f1d7f2b758ad53b284 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 19 Nov 2021 09:20:48 -0700
Subject: [PATCH 18/85] Update to 8.3.2

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index a0dce667..d1443c3d 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3.1'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3.2'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>8.3.1</version>
+    <version>8.3.2</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index d5566da8..da880edf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '8.3.1-SNAPSHOT'
+version = '8.3.2-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 6a45b2bcd2fbdba6c04135a905cf2559152b5e95 Mon Sep 17 00:00:00 2001
From: kurru <thekurru@gmail.com>
Date: Sat, 4 Dec 2021 16:11:11 -0800
Subject: [PATCH 19/85] define new account status enum

---
 schema_json/endpoint/account/enums/account_status.json | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/schema_json/endpoint/account/enums/account_status.json b/schema_json/endpoint/account/enums/account_status.json
index 65963f0d..61f7a081 100644
--- a/schema_json/endpoint/account/enums/account_status.json
+++ b/schema_json/endpoint/account/enums/account_status.json
@@ -9,7 +9,8 @@
     "APPROVAL_PENDING",
     "ACTIVE",
     "REJECTED",
-    "DISABLED"
+    "DISABLED",
+    "ACTION_REQUIRED"
   ],
   "javaEnums": [
     {
@@ -35,6 +36,9 @@
     },
     {
       "description": "The account has been disabled."
+    },
+    {
+      "description": "The account requires manual attention."
     }
   ]
 }

From 6ea0ff40050666fc15348717f020047c94a346ce Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 5 Dec 2021 17:01:03 -0700
Subject: [PATCH 20/85] Update to 8.3.3

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index d1443c3d..2be165a6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3.2'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3.3'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>8.3.2</version>
+    <version>8.3.3</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index da880edf..88519ffb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '8.3.2-SNAPSHOT'
+version = '8.3.3-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 6f8f037a349ba47638a4a2672e9d946b73df45fd Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 19 Dec 2021 21:49:15 -0700
Subject: [PATCH 21/85] Add more descriptive field names in JSON schemas, fix
 javadoc formatting

---
 README.md                                     |  2 +-
 .../non_trade_activity.json                   |  1 +
 .../account_activities/trade_activity.json    |  3 +++
 .../market_data/historical/bar/bar.json       |  8 ++++++++
 .../market_data/historical/quote/quote.json   |  8 ++++++++
 .../market_data/historical/trade/trade.json   |  7 +++++++
 .../realtime/quote/quote_message.json         |  4 ++--
 schema_json/endpoint/order/order.json         |  8 ++++----
 .../portfolio_history_response.json           |  1 +
 schema_json/endpoint/position/position.json   |  6 ++++++
 .../streaming/trade/trade_update.json         |  1 +
 .../alpaca/properties/AlpacaProperties.java   | 20 ++++++++++++++-----
 .../endpoint/PortfolioHistoryEndpoint.java    |  4 ++--
 .../util/doc/Documentation2JSONSchema.java    |  3 ++-
 .../alpaca/util/format/FormatUtil.java        |  4 +++-
 .../alpaca/util/gson/GsonUtil.java            |  7 ++++++-
 .../gson/{ => adapters}/LocalDateAdapter.java |  2 +-
 .../gson/{ => adapters}/LocalTimeAdapter.java |  2 +-
 .../{ => adapters}/ZonedDateTimeAdapter.java  |  2 +-
 .../alpaca/util/okhttp/JSONBodyBuilder.java   |  4 +++-
 .../alpaca/websocket/AlpacaWebsocket.java     |  4 +++-
 .../alpaca/test/live/AlpacaAPITest.java       | 12 +++++------
 22 files changed, 85 insertions(+), 28 deletions(-)
 rename src/main/java/net/jacobpeterson/alpaca/util/gson/{ => adapters}/LocalDateAdapter.java (95%)
 rename src/main/java/net/jacobpeterson/alpaca/util/gson/{ => adapters}/LocalTimeAdapter.java (95%)
 rename src/main/java/net/jacobpeterson/alpaca/util/gson/{ => adapters}/ZonedDateTimeAdapter.java (95%)

diff --git a/README.md b/README.md
index 2be165a6..de6fdc01 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ If you are using Maven as your build tool, add the following dependency to your
 Note that you don't have to use the Maven Central artifacts and instead can just install a clone of this project to your local Maven repository as shown in the [Building](#building) section.
 
 # Configuration
-Creating an `alpaca.properties` file on the classpath with the following format allows you to easily load properties using the `AlpacaAPI` default constructor:
+Creating an `alpaca.properties` file on the classpath (e.g. in `src/main/resources/alpaca.properties`) with the following format allows you to easily load properties using the `AlpacaAPI` default constructor:
 ```
 key_id = <your Key ID>
 secret_key = <your Secret Key>
diff --git a/schema_json/endpoint/account_activities/non_trade_activity.json b/schema_json/endpoint/account_activities/non_trade_activity.json
index c66a15af..81d8c0fa 100644
--- a/schema_json/endpoint/account_activities/non_trade_activity.json
+++ b/schema_json/endpoint/account_activities/non_trade_activity.json
@@ -19,6 +19,7 @@
     },
     "qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "quantity",
       "title": "For dividend activities, the number of shares that contributed to the payment. Not present for other activity types."
     },
     "per_share_amount": {
diff --git a/schema_json/endpoint/account_activities/trade_activity.json b/schema_json/endpoint/account_activities/trade_activity.json
index 0c6dd5f4..1697ad1c 100644
--- a/schema_json/endpoint/account_activities/trade_activity.json
+++ b/schema_json/endpoint/account_activities/trade_activity.json
@@ -7,10 +7,12 @@
   "properties": {
     "cum_qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "cumulativeQuantity",
       "title": "The cumulative quantity of shares involved in the execution."
     },
     "leaves_qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "remainingQuantity",
       "title": "For partially_filled orders, the quantity of shares that are left to be filled."
     },
     "price": {
@@ -19,6 +21,7 @@
     },
     "qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "quantity",
       "title": "The number of shares involved in the trade execution."
     },
     "side": {
diff --git a/schema_json/endpoint/market_data/historical/bar/bar.json b/schema_json/endpoint/market_data/historical/bar/bar.json
index 6be1fe66..55423fde 100644
--- a/schema_json/endpoint/market_data/historical/bar/bar.json
+++ b/schema_json/endpoint/market_data/historical/bar/bar.json
@@ -4,34 +4,42 @@
   "properties": {
     "t": {
       "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
       "title": "Timestamp with nanosecond precision."
     },
     "o": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "open",
       "title": "Open price."
     },
     "h": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "high",
       "title": "High price."
     },
     "l": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "low",
       "title": "Low price."
     },
     "c": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "close",
       "title": "Close price."
     },
     "v": {
       "existingJavaType": "java.lang.Long",
+      "javaName": "volume",
       "title": "Volume."
     },
     "n": {
       "existingJavaType": "java.lang.Long",
+      "javaName": "tradeCount",
       "title": "Trade count."
     },
     "vw": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "vwap",
       "title": "VWAP (Volume Weighted Average Price)."
     }
   }
diff --git a/schema_json/endpoint/market_data/historical/quote/quote.json b/schema_json/endpoint/market_data/historical/quote/quote.json
index 7a238f6e..059c9746 100644
--- a/schema_json/endpoint/market_data/historical/quote/quote.json
+++ b/schema_json/endpoint/market_data/historical/quote/quote.json
@@ -4,34 +4,42 @@
   "properties": {
     "t": {
       "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
       "title": "Timestamp with nanosecond precision."
     },
     "ax": {
       "existingJavaType": "java.lang.String",
+      "javaName": "askExchange",
       "title": "Ask exchange."
     },
     "ap": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
       "title": "Ask price."
     },
     "as": {
       "existingJavaType": "java.lang.Integer",
+      "javaName": "askSize",
       "title": "Ask size."
     },
     "bx": {
       "existingJavaType": "java.lang.String",
+      "javaName": "bidExchange",
       "title": "Bid exchange."
     },
     "bp": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
       "title": "Bid price."
     },
     "bs": {
       "existingJavaType": "java.lang.Integer",
+      "javaName": "bidSize",
       "title": "Bid size."
     },
     "c": {
       "existingJavaType": "java.util.ArrayList<String>",
+      "javaName": "conditions",
       "title": "The {@link java.util.ArrayList} of quote conditions."
     }
   }
diff --git a/schema_json/endpoint/market_data/historical/trade/trade.json b/schema_json/endpoint/market_data/historical/trade/trade.json
index c9303e7b..f7ef48ca 100644
--- a/schema_json/endpoint/market_data/historical/trade/trade.json
+++ b/schema_json/endpoint/market_data/historical/trade/trade.json
@@ -4,30 +4,37 @@
   "properties": {
     "t": {
       "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
       "title": "Timestamp with nanosecond precision."
     },
     "x": {
       "existingJavaType": "java.lang.String",
+      "javaName": "exchange",
       "title": "Exchange where the trade happened."
     },
     "p": {
       "existingJavaType": "java.lang.Double",
+      "javaName": "price",
       "title": "Trade price."
     },
     "s": {
       "existingJavaType": "java.lang.Integer",
+      "javaName": "size",
       "title": "Trade size."
     },
     "c": {
       "existingJavaType": "java.util.ArrayList<String>",
+      "javaName": "conditions",
       "title": "The {@link java.util.ArrayList} of trade conditions."
     },
     "i": {
       "existingJavaType": "java.lang.Long",
+      "javaName": "tradeID",
       "title": "Trade ID."
     },
     "z": {
       "existingJavaType": "java.lang.String",
+      "javaName": "tape",
       "title": "Tape."
     }
   }
diff --git a/schema_json/endpoint/market_data/realtime/quote/quote_message.json b/schema_json/endpoint/market_data/realtime/quote/quote_message.json
index a8ff47b3..f1f9606d 100644
--- a/schema_json/endpoint/market_data/realtime/quote/quote_message.json
+++ b/schema_json/endpoint/market_data/realtime/quote/quote_message.json
@@ -7,7 +7,7 @@
   "properties": {
     "ax": {
       "existingJavaType": "java.lang.String",
-      "javaName": "askExchangeCode",
+      "javaName": "askExchange",
       "title": "Ask exchange code."
     },
     "ap": {
@@ -22,7 +22,7 @@
     },
     "bx": {
       "existingJavaType": "java.lang.String",
-      "javaName": "bidExchangeCode",
+      "javaName": "bidExchange",
       "title": "Bid exchange code."
     },
     "bp": {
diff --git a/schema_json/endpoint/order/order.json b/schema_json/endpoint/order/order.json
index 4af8dde8..c0af5330 100644
--- a/schema_json/endpoint/order/order.json
+++ b/schema_json/endpoint/order/order.json
@@ -68,24 +68,23 @@
     },
     "qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "quantity",
       "title": "Ordered quantity. If entered, notional will be null. Can take up to 9 decimal points."
     },
     "filled_qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "filledQuantity",
       "title": "Filled quantity."
     },
     "filled_avg_price": {
       "existingJavaType": "java.lang.String",
+      "javaName": "averageFillPrice",
       "title": "Filled average price."
     },
     "order_class": {
       "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderClass",
       "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderClass}. For details of non-simple order classes, please see Bracket Order Overview."
     },
-    "order_type": {
-      "existingJavaType": "java.lang.String",
-      "title": "(Deprecated with just type field below.)."
-    },
     "type": {
       "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderType",
       "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderType}."
@@ -128,6 +127,7 @@
     },
     "hwm": {
       "existingJavaType": "java.lang.String",
+      "javaName": "highWaterMark",
       "title": "High Water Mark - The highest (lowest) market price seen since the trailing stop order was submitted."
     }
   }
diff --git a/schema_json/endpoint/portfolio_history/portfolio_history_response.json b/schema_json/endpoint/portfolio_history/portfolio_history_response.json
index d43235f7..d3ca8177 100644
--- a/schema_json/endpoint/portfolio_history/portfolio_history_response.json
+++ b/schema_json/endpoint/portfolio_history/portfolio_history_response.json
@@ -16,6 +16,7 @@
     },
     "profit_loss_pct": {
       "existingJavaType": "java.util.ArrayList<java.lang.Double>",
+      "javaName": "profitLossPercent",
       "title": "Profit/loss in percentage from the base value."
     },
     "base_value": {
diff --git a/schema_json/endpoint/position/position.json b/schema_json/endpoint/position/position.json
index bee7e824..2f1eb603 100644
--- a/schema_json/endpoint/position/position.json
+++ b/schema_json/endpoint/position/position.json
@@ -20,10 +20,12 @@
     },
     "avg_entry_price": {
       "existingJavaType": "java.lang.String",
+      "javaName": "averageEntryPrice",
       "title": "Average entry price of the position."
     },
     "qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "quantity",
       "title": "The number of shares."
     },
     "side": {
@@ -40,18 +42,22 @@
     },
     "unrealized_pl": {
       "existingJavaType": "java.lang.String",
+      "javaName": "unrealizedProfitLoss",
       "title": "Unrealized profit/loss in dollars."
     },
     "unrealized_plpc": {
       "existingJavaType": "java.lang.String",
+      "javaName": "unrealizedProfitLossPercent",
       "title": "Unrealized profit/loss percent (by a factor of 1)."
     },
     "unrealized_intraday_pl": {
       "existingJavaType": "java.lang.String",
+      "javaName": "unrealizedIntradayProfitLoss",
       "title": "Unrealized profit/loss in dollars for the day."
     },
     "unrealized_intraday_plpc": {
       "existingJavaType": "java.lang.String",
+      "javaName": "unrealizedIntradayProfitLossPercent",
       "title": "Unrealized profit/loss percent (by a factor of 1)."
     },
     "current_price": {
diff --git a/schema_json/endpoint/streaming/trade/trade_update.json b/schema_json/endpoint/streaming/trade/trade_update.json
index 46188863..339ea01c 100644
--- a/schema_json/endpoint/streaming/trade/trade_update.json
+++ b/schema_json/endpoint/streaming/trade/trade_update.json
@@ -16,6 +16,7 @@
     },
     "position_qty": {
       "existingJavaType": "java.lang.String",
+      "javaName": "positionQuantity",
       "title": "The position quantity."
     },
     "order": {
diff --git a/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java b/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
index 2b8d0b79..0c121157 100644
--- a/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
+++ b/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
@@ -15,31 +15,41 @@ public class AlpacaProperties {
     private static final String ALPACA_DEFAULT_PROPERTIES_FILE = "alpaca.default.properties";
 
     private static final String KEY_ID_KEY = "key_id";
-    /** The value of {@link #KEY_ID_KEY} in {@link #ALPACA_PROPERTIES_FILE}. */
+    /**
+     * The value of {@link #KEY_ID_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
+     */
     public static final String KEY_ID = getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
             KEY_ID_KEY);
 
     private static final String SECRET_KEY_KEY = "secret_key";
-    /** The value of {@link #SECRET_KEY_KEY} in {@link #ALPACA_PROPERTIES_FILE}. */
+    /**
+     * The value of {@link #SECRET_KEY_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
+     */
     public static final String SECRET_KEY = getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
             SECRET_KEY_KEY);
 
     private static final String ENDPOINT_API_TYPE_KEY = "endpoint_api_type";
-    /** The value of {@link #ENDPOINT_API_TYPE_KEY} in {@link #ALPACA_PROPERTIES_FILE}. */
+    /**
+     * The value of {@link #ENDPOINT_API_TYPE_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
+     */
     public static final EndpointAPIType ENDPOINT_API_TYPE = EndpointAPIType.fromValue(getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
             ENDPOINT_API_TYPE_KEY));
 
     private static final String DATA_API_TYPE_KEY = "data_api_type";
-    /** The value of {@link #DATA_API_TYPE_KEY} in {@link #ALPACA_PROPERTIES_FILE}. */
+    /**
+     * The value of {@link #DATA_API_TYPE_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
+     */
     public static final DataAPIType DATA_API_TYPE = DataAPIType.fromValue(getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
             DATA_API_TYPE_KEY));
 
     private static final String USER_AGENT_KEY = "user_agent";
-    /** The value of {@link #USER_AGENT_KEY} in {@link #ALPACA_PROPERTIES_FILE}. */
+    /**
+     * The value of {@link #USER_AGENT_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
+     */
     public static final String USER_AGENT = getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
             USER_AGENT_KEY);
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java
index 9bab136d..1d01c19d 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java
@@ -81,7 +81,7 @@ public PortfolioHistory get(Integer periodLength, PortfolioPeriodUnit periodUnit
         // Check if any response arrays differ in size
         checkState(response.getTimestamp().size() == response.getEquity().size() &&
                         response.getEquity().size() == response.getProfitLoss().size() &&
-                        response.getProfitLoss().size() == response.getProfitLossPct().size(),
+                        response.getProfitLoss().size() == response.getProfitLossPercent().size(),
                 "Response arrays should not differ in size!");
 
         // Add all data points into one POJO
@@ -91,7 +91,7 @@ public PortfolioHistory get(Integer periodLength, PortfolioPeriodUnit periodUnit
                     Instant.ofEpochSecond(response.getTimestamp().get(index)).atZone(FormatUtil.NEW_YORK_ZONED_ID),
                     response.getEquity().get(index),
                     response.getProfitLoss().get(index),
-                    response.getProfitLossPct().get(index)));
+                    response.getProfitLossPercent().get(index)));
         }
 
         return new PortfolioHistory(
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java b/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java
index 29fbf597..ba0e2208 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java
@@ -16,7 +16,8 @@
 import java.io.IOException;
 
 /**
- * The type {@link Documentation2JSONSchema}.
+ * The {@link Documentation2JSONSchema} is used strictly to expedite the process of creating JSON schemas for POJO
+ * generation in this library using Alpaca's website documentation.
  */
 public class Documentation2JSONSchema {
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
index b5a53a27..e797c294 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
@@ -14,7 +14,9 @@
  */
 public class FormatUtil {
 
-    /** The {@link ZoneId} for <code>America/New_York</code>. */
+    /**
+     * The {@link ZoneId} for <code>America/New_York</code>.
+     */
     public static final ZoneId NEW_YORK_ZONED_ID = ZoneId.of("America/New_York");
 
     // Alpaca uses the following rounding mechanics with respect to buy orders: (1) rounded down to two decimal
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java
index 7b94a344..dc86e2c1 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java
@@ -2,6 +2,9 @@
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import net.jacobpeterson.alpaca.util.gson.adapters.LocalDateAdapter;
+import net.jacobpeterson.alpaca.util.gson.adapters.LocalTimeAdapter;
+import net.jacobpeterson.alpaca.util.gson.adapters.ZonedDateTimeAdapter;
 
 import java.time.LocalDate;
 import java.time.LocalTime;
@@ -12,7 +15,9 @@
  */
 public class GsonUtil {
 
-    /** A {@link Gson} instance with registered date/time adapters. */
+    /**
+     * A {@link Gson} instance with registered date/time adapters.
+     */
     public static final Gson GSON = new GsonBuilder()
             .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter())
             .registerTypeAdapter(LocalDate.class, new LocalDateAdapter())
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/LocalDateAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java
similarity index 95%
rename from src/main/java/net/jacobpeterson/alpaca/util/gson/LocalDateAdapter.java
rename to src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java
index d1dc5956..a9475c17 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/LocalDateAdapter.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.util.gson;
+package net.jacobpeterson.alpaca.util.gson.adapters;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonDeserializationContext;
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/LocalTimeAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java
similarity index 95%
rename from src/main/java/net/jacobpeterson/alpaca/util/gson/LocalTimeAdapter.java
rename to src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java
index 960ccda2..9d6bb735 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/LocalTimeAdapter.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.util.gson;
+package net.jacobpeterson.alpaca.util.gson.adapters;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonDeserializationContext;
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/ZonedDateTimeAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java
similarity index 95%
rename from src/main/java/net/jacobpeterson/alpaca/util/gson/ZonedDateTimeAdapter.java
rename to src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java
index 7f1cf2a7..e10f1be1 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/ZonedDateTimeAdapter.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.util.gson;
+package net.jacobpeterson.alpaca.util.gson.adapters;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonDeserializationContext;
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java b/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java
index c6f54498..11944345 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java
@@ -10,7 +10,9 @@
  */
 public class JSONBodyBuilder {
 
-    /** The UTF-8 JSON {@link MediaType}. */
+    /**
+     * The UTF-8 JSON {@link MediaType}.
+     */
     public static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
 
     private String bodyJSON;
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index 331d89da..232d23f2 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -42,7 +42,9 @@ public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageList
      */
     public static final String WEBSOCKET_NORMAL_CLOSURE_MESSAGE = "Normal closure";
 
-    /** Defines the maximum number of reconnection attempts to be made by an {@link AlpacaWebsocket}. */
+    /**
+     * Defines the maximum number of reconnection attempts to be made by an {@link AlpacaWebsocket}.
+     */
     public static int MAX_RECONNECT_ATTEMPTS = 5;
 
     /**
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
index 326a6aaf..dafd1016 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
@@ -181,10 +181,10 @@ public void testAccountActivitiesEndpointGetOneActivityExistsUntilNow() throws A
             TradeActivity tradeActivity = (TradeActivity) accountActivity;
             assertNotNull(tradeActivity.getActivityType());
             assertNotNull(tradeActivity.getId());
-            assertNotNull(tradeActivity.getCumQty());
-            assertNotNull(tradeActivity.getLeavesQty());
+            assertNotNull(tradeActivity.getCumulativeQuantity());
+            assertNotNull(tradeActivity.getRemainingQuantity());
             assertNotNull(tradeActivity.getPrice());
-            assertNotNull(tradeActivity.getQty());
+            assertNotNull(tradeActivity.getQuantity());
             assertNotNull(tradeActivity.getSide());
             assertNotNull(tradeActivity.getSymbol());
             assertNotNull(tradeActivity.getTransactionTime());
@@ -197,7 +197,7 @@ public void testAccountActivitiesEndpointGetOneActivityExistsUntilNow() throws A
             assertNotNull(nonTradeActivity.getDate());
             assertNotNull(nonTradeActivity.getNetAmount());
             assertNotNull(nonTradeActivity.getSymbol());
-            assertNotNull(nonTradeActivity.getQty());
+            assertNotNull(nonTradeActivity.getQuantity());
             assertNotNull(nonTradeActivity.getPerShareAmount());
             assertNotNull(nonTradeActivity.getDescription());
         }
@@ -277,8 +277,8 @@ public void testOrdersEndpointGetOneOrderExistsUntilNow() throws AlpacaClientExc
         assertNotNull(order.getAssetId());
         assertNotNull(order.getSymbol());
         assertNotNull(order.getAssetClass());
-        assertNotNull(order.getQty());
-        assertNotNull(order.getFilledQty());
+        assertNotNull(order.getQuantity());
+        assertNotNull(order.getFilledQuantity());
         assertNotNull(order.getType());
         assertNotNull(order.getSide());
         assertNotNull(order.getTimeInForce());

From f376d402dea974dab193fb4765cd08ff1e9d98b2 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 20 Dec 2021 02:03:01 -0700
Subject: [PATCH 22/85] Refactor, add CryptoMarketDataEndpoint

---
 README.md                                     |  43 ++-
 schema_json/endpoint/asset/asset.json         |   5 +-
 .../endpoint/asset/enums/asset_class.json     |   8 +
 .../bar => common}/enums/bar_time_period.json |   2 +-
 .../crypto/historical/bar/bar.json            |  51 +++
 .../crypto/historical/bar/bars_response.json  |  18 ++
 .../crypto/historical/enums/exchange.json     |  20 ++
 .../quote/latest_quote_response.json          |  14 +
 .../crypto/historical/quote/quote.json        |  36 +++
 .../historical/quote/quotes_response.json     |  18 ++
 .../historical/trade/enums/taker_side.json    |  16 +
 .../trade/latest_trade_response.json          |  14 +
 .../crypto/historical/trade/trade.json        |  36 +++
 .../historical/trade/trades_response.json     |  18 ++
 .../crypto/historical/xbbo/xbbo.json          |  41 +++
 .../crypto/historical/xbbo/xbbo_response.json |  14 +
 .../{ => stock}/historical/bar/bar.json       |   0
 .../historical/bar/bars_response.json         |   4 +-
 .../historical/bar/enums/bar_adjustment.json  |   0
 .../quote/latest_quote_response.json          |   4 +-
 .../{ => stock}/historical/quote/quote.json   |   0
 .../historical/quote/quotes_response.json     |   4 +-
 .../historical/snapshot/snapshot.json         |  20 +-
 .../trade/latest_trade_response.json          |   4 +-
 .../{ => stock}/historical/trade/trade.json   |   0
 .../historical/trade/trades_response.json     |   4 +-
 .../{ => stock}/realtime/bar/bar_message.json |   2 +-
 .../realtime/control/error_message.json       |   2 +-
 .../control/subscriptions_message.json        |   2 +-
 .../realtime/control/success_message.json     |   2 +-
 .../enums/market_data_message_type.json       |   0
 .../realtime/market_data_message.json         |   4 +-
 .../realtime/quote/quote_message.json         |   2 +-
 .../{ => stock}/realtime/symbol_message.json  |   2 +-
 .../realtime/trade/trade_message.json         |   2 +-
 schema_json/endpoint/position/position.json   |   2 +-
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |  57 ++--
 .../alpaca/rest/AlpacaClient.java             |  10 -
 .../alpaca/rest/AlpacaClientException.java    |  20 --
 .../endpoint/AccountActivitiesEndpoint.java   |   2 +-
 .../alpaca/rest/endpoint/AssetsEndpoint.java  |   7 +-
 .../alpaca/rest/endpoint/OrdersEndpoint.java  |   4 +-
 .../marketdata/CryptoMarketDataEndpoint.java  | 296 ++++++++++++++++++
 .../StockMarketDataEndpoint.java}             |  44 +--
 .../alpaca/websocket/AlpacaWebsocket.java     |  20 --
 .../marketdata/MarketDataListener.java        |   4 +-
 .../marketdata/MarketDataWebsocket.java       |  16 +-
 .../MarketDataWebsocketInterface.java         |  10 +-
 48 files changed, 744 insertions(+), 160 deletions(-)
 create mode 100644 schema_json/endpoint/asset/enums/asset_class.json
 rename schema_json/endpoint/market_data/{historical/bar => common}/enums/bar_time_period.json (75%)
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/bar/bar.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/enums/exchange.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/quote.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/trade.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
 rename schema_json/endpoint/market_data/{ => stock}/historical/bar/bar.json (100%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/bar/bars_response.json (81%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/bar/enums/bar_adjustment.json (100%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/quote/latest_quote_response.json (82%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/quote/quote.json (100%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/quote/quotes_response.json (80%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/snapshot/snapshot.json (67%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/trade/latest_trade_response.json (82%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/trade/trade.json (100%)
 rename schema_json/endpoint/market_data/{ => stock}/historical/trade/trades_response.json (80%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/bar/bar_message.json (95%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/control/error_message.json (91%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/control/subscriptions_message.json (94%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/control/success_message.json (89%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/enums/market_data_message_type.json (100%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/market_data_message.json (75%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/quote/quote_message.json (97%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/symbol_message.json (89%)
 rename schema_json/endpoint/market_data/{ => stock}/realtime/trade/trade_message.json (96%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{MarketDataEndpoint.java => marketdata/StockMarketDataEndpoint.java} (86%)

diff --git a/README.md b/README.md
index de6fdc01..a0947ec0 100644
--- a/README.md
+++ b/README.md
@@ -100,15 +100,42 @@ try {
 }
 ```
 
-## [`MarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java)
-The Data API v2 provides market data through an easy-to-use API for historical data.
+## [`CryptoMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java)
+Alpaca provides cryptocurrency data from multiple venues/exchanges, namely: Coinbase, ErisX, and FTX. Note that it does not route orders to all venues.
+
+Example usage:
+```java
+try {
+    // Get BTCUSD 50 one-hour bars starting on 12/18/2021 from Coinbase and print them out
+    BarsResponse btcBarsResponse = alpacaAPI.cryptoMarketData().getBars(
+            "BTCUSD",
+            Arrays.asList(Exchange.COINBASE),
+            ZonedDateTime.of(2021, 12, 18, 0, 0, 0, 0, ZoneId.of("America/New_York")),
+            50,
+            null,
+            1,
+            BarTimePeriod.HOUR);
+    btcBarsResponse.getBars().forEach(System.out::println);
+
+    // Get the Best Bid and Offer across multiple exchanges (XBBO) and print it out
+    XbboResponse etcXBBO = alpacaAPI.cryptoMarketData().getXBBO(
+            "ETHUSD",
+            null);
+    System.out.println(etcXBBO);
+} catch (AlpacaClientException exception) {
+    exception.printStackTrace();
+}
+```
+
+## [`StockMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java)
+The Data API v2 provides market data through an easy-to-use API for historical stock market data.
 
 Example usage:
 ```java
 try {
     // Get AAPL one hour, split-adjusted bars from 7/6/2021 market open
     // to 7/8/2021 market close and print them out
-    BarsResponse aaplBarsResponse = alpacaAPI.marketData().getBars(
+    BarsResponse aaplBarsResponse = alpacaAPI.stockMarketData().getBars(
             "AAPL",
             ZonedDateTime.of(2021, 7, 6, 9, 30, 0, 0, ZoneId.of("America/New_York")),
             ZonedDateTime.of(2021, 7, 8, 12 + 4, 0, 0, 0, ZoneId.of("America/New_York")),
@@ -120,7 +147,7 @@ try {
     aaplBarsResponse.getBars().forEach(System.out::println);
 
     // Get AAPL first 10 trades on 7/8/2021 at market open and print them out
-    TradesResponse aaplTradesResponse = alpacaAPI.marketData().getTrades(
+    TradesResponse aaplTradesResponse = alpacaAPI.stockMarketData().getTrades(
             "AAPL",
             ZonedDateTime.of(2021, 7, 8, 9, 30, 0, 0, ZoneId.of("America/New_York")),
             ZonedDateTime.of(2021, 7, 8, 9, 31, 0, 0, ZoneId.of("America/New_York")),
@@ -129,11 +156,11 @@ try {
     aaplTradesResponse.getTrades().forEach(System.out::println);
 
     // Print out latest AAPL trade
-    Trade latestAAPLTrade = alpacaAPI.marketData().getLatestTrade("AAPL").getTrade();
+    Trade latestAAPLTrade = alpacaAPI.stockMarketData().getLatestTrade("AAPL").getTrade();
     System.out.printf("Latest AAPL Trade: %s\n", latestAAPLTrade);
 
     // Print out snapshot of AAPL, GME, and TSLA
-    Map<String, Snapshot> snapshots = alpacaAPI.marketData()
+    Map<String, Snapshot> snapshots = alpacaAPI.stockMarketData()
             .getSnapshots(Arrays.asList("AAPL", "GME", "TSLA"));
     snapshots.forEach((symbol, snapshot) ->
             System.out.printf("Symbol: %s\nSnapshot: %s\n\n", symbol, snapshot));
@@ -229,8 +256,8 @@ The Assets API serves as the master list of assets available for trade and data
 Example usage:
 ```java
 try {
-    // Print out a CSV of all active US equity 'Asset's
-    List<Asset> activeUSEquities = alpacaAPI.assets().get(AssetStatus.ACTIVE, null);
+    // Print out a CSV of all active US Equity 'Asset's
+    List<Asset> activeUSEquities = alpacaAPI.assets().get(AssetStatus.ACTIVE, AssetClass.US_EQUITY);
     System.out.println(activeUSEquities
             .stream().map(Asset::getSymbol)
             .collect(Collectors.joining(", ")));
diff --git a/schema_json/endpoint/asset/asset.json b/schema_json/endpoint/asset/asset.json
index b563bf4a..f5ab8b9c 100644
--- a/schema_json/endpoint/asset/asset.json
+++ b/schema_json/endpoint/asset/asset.json
@@ -7,8 +7,9 @@
       "title": "Asset ID."
     },
     "class": {
-      "existingJavaType": "java.lang.String",
-      "title": "\"us_equity\""
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetClass",
+      "javaName": "assetClass",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetClass}."
     },
     "exchange": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/asset/enums/asset_class.json b/schema_json/endpoint/asset/enums/asset_class.json
new file mode 100644
index 00000000..ac36f8e7
--- /dev/null
+++ b/schema_json/endpoint/asset/enums/asset_class.json
@@ -0,0 +1,8 @@
+{
+  "type": "string",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/assets/\">Assets</a>.",
+  "enum": [
+    "us_equity",
+    "crypto"
+  ]
+}
diff --git a/schema_json/endpoint/market_data/historical/bar/enums/bar_time_period.json b/schema_json/endpoint/market_data/common/enums/bar_time_period.json
similarity index 75%
rename from schema_json/endpoint/market_data/historical/bar/enums/bar_time_period.json
rename to schema_json/endpoint/market_data/common/enums/bar_time_period.json
index 627d9413..cccf0508 100644
--- a/schema_json/endpoint/market_data/historical/bar/enums/bar_time_period.json
+++ b/schema_json/endpoint/market_data/common/enums/bar_time_period.json
@@ -1,6 +1,6 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "enum": [
     "Min",
     "Hour",
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/bar.json b/schema_json/endpoint/market_data/crypto/historical/bar/bar.json
new file mode 100644
index 00000000..c677ba92
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/bar.json
@@ -0,0 +1,51 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    },
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange."
+    },
+    "o": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "open",
+      "title": "Open price."
+    },
+    "h": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "high",
+      "title": "High price."
+    },
+    "l": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "low",
+      "title": "Low price."
+    },
+    "c": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "close",
+      "title": "Close price."
+    },
+    "v": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "volume",
+      "title": "Volume."
+    },
+    "n": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeCount",
+      "title": "Trade count."
+    },
+    "vw": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "vwap",
+      "title": "VWAP (Volume Weighted Average Price)."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json b/schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json
new file mode 100644
index 00000000..21f5cddb
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json
@@ -0,0 +1,18 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "properties": {
+    "bars": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.Bar>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.Bar}s."
+    },
+    "symbol": {
+      "existingJavaType": "java.lang.String",
+      "title": "Symbol that was queried."
+    },
+    "next_page_token": {
+      "existingJavaType": "java.lang.String",
+      "title": "Token that can be used to query the next page."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/enums/exchange.json b/schema_json/endpoint/market_data/crypto/historical/enums/exchange.json
new file mode 100644
index 00000000..1ea41bd6
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/enums/exchange.json
@@ -0,0 +1,20 @@
+{
+  "type": "string",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "enum": [
+    "ERSX",
+    "FTX",
+    "CBSE"
+  ],
+  "javaEnums": [
+    {
+      "name": "ERISX"
+    },
+    {
+      "name": "FTX"
+    },
+    {
+      "name": "COINBASE"
+    }
+  ]
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json
new file mode 100644
index 00000000..044d252f
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json
@@ -0,0 +1,14 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "symbol": {
+      "existingJavaType": "java.lang.String",
+      "title": "Symbol that was queried."
+    },
+    "quote": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote}."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/quote.json b/schema_json/endpoint/market_data/crypto/historical/quote/quote.json
new file mode 100644
index 00000000..ecca5a55
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/quote.json
@@ -0,0 +1,36 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    },
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange."
+    },
+    "ap": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
+      "title": "Ask price."
+    },
+    "as": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askSize",
+      "title": "Ask size."
+    },
+    "bp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
+      "title": "Bid price."
+    },
+    "bs": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidSize",
+      "title": "Bid size."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json
new file mode 100644
index 00000000..212635c3
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json
@@ -0,0 +1,18 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "quotes": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote}s."
+    },
+    "symbol": {
+      "existingJavaType": "java.lang.String",
+      "title": "Symbol that was queried."
+    },
+    "next_page_token": {
+      "existingJavaType": "java.lang.String",
+      "title": "Token that can be used to query the next page."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json b/schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json
new file mode 100644
index 00000000..9604bc54
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json
@@ -0,0 +1,16 @@
+{
+  "type": "string",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "enum": [
+    "B",
+    "S"
+  ],
+  "javaEnums": [
+    {
+      "name": "BUY"
+    },
+    {
+      "name": "SELL"
+    }
+  ]
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json
new file mode 100644
index 00000000..089f9528
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json
@@ -0,0 +1,14 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "symbol": {
+      "existingJavaType": "java.lang.String",
+      "title": "Symbol that was queried."
+    },
+    "trade": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade}."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/trade.json b/schema_json/endpoint/market_data/crypto/historical/trade/trade.json
new file mode 100644
index 00000000..56fef69b
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/trade.json
@@ -0,0 +1,36 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    },
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange where the trade happened."
+    },
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "Trade price."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "size",
+      "title": "Trade size."
+    },
+    "tks": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.enums.TakerSide",
+      "javaName": "takerSide",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.enums.TakerSide} of the trade."
+    },
+    "i": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeID",
+      "title": "Trade ID."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json
new file mode 100644
index 00000000..68a770a5
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json
@@ -0,0 +1,18 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "trades": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade}s."
+    },
+    "symbol": {
+      "existingJavaType": "java.lang.String",
+      "title": "Symbol that was queried."
+    },
+    "next_page_token": {
+      "existingJavaType": "java.lang.String",
+      "title": "Token that can be used to query the next page."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
new file mode 100644
index 00000000..9646a1ee
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
@@ -0,0 +1,41 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    },
+    "ax": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "javaName": "askExchange",
+      "title": "Ask Exchange."
+    },
+    "ap": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
+      "title": "Ask price."
+    },
+    "as": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askSize",
+      "title": "Ask size."
+    },
+    "bx": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "javaName": "bidExchange",
+      "title": "Bid Exchange."
+    },
+    "bp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
+      "title": "Bid price."
+    },
+    "bs": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidSize",
+      "title": "Bid size."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
new file mode 100644
index 00000000..b3bbe427
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
@@ -0,0 +1,14 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "properties": {
+    "symbol": {
+      "existingJavaType": "java.lang.String",
+      "title": "Symbol that was queried."
+    },
+    "xbbo": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.Xbbo",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.Xbbo}."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/historical/bar/bar.json b/schema_json/endpoint/market_data/stock/historical/bar/bar.json
similarity index 100%
rename from schema_json/endpoint/market_data/historical/bar/bar.json
rename to schema_json/endpoint/market_data/stock/historical/bar/bar.json
diff --git a/schema_json/endpoint/market_data/historical/bar/bars_response.json b/schema_json/endpoint/market_data/stock/historical/bar/bars_response.json
similarity index 81%
rename from schema_json/endpoint/market_data/historical/bar/bars_response.json
rename to schema_json/endpoint/market_data/stock/historical/bar/bars_response.json
index a876dcf8..ebd21e82 100644
--- a/schema_json/endpoint/market_data/historical/bar/bars_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/bar/bars_response.json
@@ -3,8 +3,8 @@
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
   "properties": {
     "bars": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/historical/bar/enums/bar_adjustment.json b/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
similarity index 100%
rename from schema_json/endpoint/market_data/historical/bar/enums/bar_adjustment.json
rename to schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
diff --git a/schema_json/endpoint/market_data/historical/quote/latest_quote_response.json b/schema_json/endpoint/market_data/stock/historical/quote/latest_quote_response.json
similarity index 82%
rename from schema_json/endpoint/market_data/historical/quote/latest_quote_response.json
rename to schema_json/endpoint/market_data/stock/historical/quote/latest_quote_response.json
index 54477417..1250c5f8 100644
--- a/schema_json/endpoint/market_data/historical/quote/latest_quote_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/quote/latest_quote_response.json
@@ -7,8 +7,8 @@
       "title": "Symbol that was queried."
     },
     "quote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/historical/quote/quote.json b/schema_json/endpoint/market_data/stock/historical/quote/quote.json
similarity index 100%
rename from schema_json/endpoint/market_data/historical/quote/quote.json
rename to schema_json/endpoint/market_data/stock/historical/quote/quote.json
diff --git a/schema_json/endpoint/market_data/historical/quote/quotes_response.json b/schema_json/endpoint/market_data/stock/historical/quote/quotes_response.json
similarity index 80%
rename from schema_json/endpoint/market_data/historical/quote/quotes_response.json
rename to schema_json/endpoint/market_data/stock/historical/quote/quotes_response.json
index 88c1286e..6ba96682 100644
--- a/schema_json/endpoint/market_data/historical/quote/quotes_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/quote/quotes_response.json
@@ -3,8 +3,8 @@
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
   "properties": {
     "quotes": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/historical/snapshot/snapshot.json b/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
similarity index 67%
rename from schema_json/endpoint/market_data/historical/snapshot/snapshot.json
rename to schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
index c2a0acf0..f42b18af 100644
--- a/schema_json/endpoint/market_data/historical/snapshot/snapshot.json
+++ b/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
@@ -3,24 +3,24 @@
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
   "properties": {
     "latestTrade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade}."
     },
     "latestQuote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote}."
     },
     "minuteBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar",
-      "title": "The minute {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar",
+      "title": "The minute {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}."
     },
     "dailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar",
-      "title": "The daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar",
+      "title": "The daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}."
     },
     "prevDailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar",
-      "title": "The previous daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar",
+      "title": "The previous daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/historical/trade/latest_trade_response.json b/schema_json/endpoint/market_data/stock/historical/trade/latest_trade_response.json
similarity index 82%
rename from schema_json/endpoint/market_data/historical/trade/latest_trade_response.json
rename to schema_json/endpoint/market_data/stock/historical/trade/latest_trade_response.json
index 3025a719..f5be1b9d 100644
--- a/schema_json/endpoint/market_data/historical/trade/latest_trade_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/trade/latest_trade_response.json
@@ -7,8 +7,8 @@
       "title": "Symbol that was queried."
     },
     "trade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/historical/trade/trade.json b/schema_json/endpoint/market_data/stock/historical/trade/trade.json
similarity index 100%
rename from schema_json/endpoint/market_data/historical/trade/trade.json
rename to schema_json/endpoint/market_data/stock/historical/trade/trade.json
diff --git a/schema_json/endpoint/market_data/historical/trade/trades_response.json b/schema_json/endpoint/market_data/stock/historical/trade/trades_response.json
similarity index 80%
rename from schema_json/endpoint/market_data/historical/trade/trades_response.json
rename to schema_json/endpoint/market_data/stock/historical/trade/trades_response.json
index 928f6f24..503b6d9f 100644
--- a/schema_json/endpoint/market_data/historical/trade/trades_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/trade/trades_response.json
@@ -3,8 +3,8 @@
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
   "properties": {
     "trades": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/realtime/bar/bar_message.json b/schema_json/endpoint/market_data/stock/realtime/bar/bar_message.json
similarity index 95%
rename from schema_json/endpoint/market_data/realtime/bar/bar_message.json
rename to schema_json/endpoint/market_data/stock/realtime/bar/bar_message.json
index cc6aa003..7abf464d 100644
--- a/schema_json/endpoint/market_data/realtime/bar/bar_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/bar/bar_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.SymbolMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/market_data/realtime/control/error_message.json b/schema_json/endpoint/market_data/stock/realtime/control/error_message.json
similarity index 91%
rename from schema_json/endpoint/market_data/realtime/control/error_message.json
rename to schema_json/endpoint/market_data/stock/realtime/control/error_message.json
index 6dca33c4..1ef275ab 100644
--- a/schema_json/endpoint/market_data/realtime/control/error_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/control/error_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/market_data/realtime/control/subscriptions_message.json b/schema_json/endpoint/market_data/stock/realtime/control/subscriptions_message.json
similarity index 94%
rename from schema_json/endpoint/market_data/realtime/control/subscriptions_message.json
rename to schema_json/endpoint/market_data/stock/realtime/control/subscriptions_message.json
index 6602ed55..76bfc479 100644
--- a/schema_json/endpoint/market_data/realtime/control/subscriptions_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/control/subscriptions_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/market_data/realtime/control/success_message.json b/schema_json/endpoint/market_data/stock/realtime/control/success_message.json
similarity index 89%
rename from schema_json/endpoint/market_data/realtime/control/success_message.json
rename to schema_json/endpoint/market_data/stock/realtime/control/success_message.json
index 76ba8a8c..d7225e02 100644
--- a/schema_json/endpoint/market_data/realtime/control/success_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/control/success_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/market_data/realtime/enums/market_data_message_type.json b/schema_json/endpoint/market_data/stock/realtime/enums/market_data_message_type.json
similarity index 100%
rename from schema_json/endpoint/market_data/realtime/enums/market_data_message_type.json
rename to schema_json/endpoint/market_data/stock/realtime/enums/market_data_message_type.json
diff --git a/schema_json/endpoint/market_data/realtime/market_data_message.json b/schema_json/endpoint/market_data/stock/realtime/market_data_message.json
similarity index 75%
rename from schema_json/endpoint/market_data/realtime/market_data_message.json
rename to schema_json/endpoint/market_data/stock/realtime/market_data_message.json
index 3e43d789..5ee64bef 100644
--- a/schema_json/endpoint/market_data/realtime/market_data_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/market_data_message.json
@@ -3,9 +3,9 @@
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
     "T": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.enums.MarketDataMessageType",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType",
       "javaName": "messageType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.enums.MarketDataMessageType}."
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/realtime/quote/quote_message.json b/schema_json/endpoint/market_data/stock/realtime/quote/quote_message.json
similarity index 97%
rename from schema_json/endpoint/market_data/realtime/quote/quote_message.json
rename to schema_json/endpoint/market_data/stock/realtime/quote/quote_message.json
index f1f9606d..3c7c650c 100644
--- a/schema_json/endpoint/market_data/realtime/quote/quote_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/quote/quote_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.SymbolMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/market_data/realtime/symbol_message.json b/schema_json/endpoint/market_data/stock/realtime/symbol_message.json
similarity index 89%
rename from schema_json/endpoint/market_data/realtime/symbol_message.json
rename to schema_json/endpoint/market_data/stock/realtime/symbol_message.json
index 961d15c0..acc6327c 100644
--- a/schema_json/endpoint/market_data/realtime/symbol_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/symbol_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/market_data/realtime/trade/trade_message.json b/schema_json/endpoint/market_data/stock/realtime/trade/trade_message.json
similarity index 96%
rename from schema_json/endpoint/market_data/realtime/trade/trade_message.json
rename to schema_json/endpoint/market_data/stock/realtime/trade/trade_message.json
index 7d01e2b8..e72f5859 100644
--- a/schema_json/endpoint/market_data/realtime/trade/trade_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/trade/trade_message.json
@@ -1,7 +1,7 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.SymbolMessage"
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
   "properties": {
diff --git a/schema_json/endpoint/position/position.json b/schema_json/endpoint/position/position.json
index 2f1eb603..7ebb619f 100644
--- a/schema_json/endpoint/position/position.json
+++ b/schema_json/endpoint/position/position.json
@@ -12,7 +12,7 @@
     },
     "exchange": {
       "existingJavaType": "java.lang.String",
-      "title": "Exchange name of the asset."
+      "title": "Exchange name of the asset (\"ErisX\" for crypto)."
     },
     "asset_class": {
       "existingJavaType": "java.lang.String",
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index b874d794..67813131 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -12,11 +12,12 @@
 import net.jacobpeterson.alpaca.rest.endpoint.AssetsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.CalendarEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.ClockEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.MarketDataEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.OrdersEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.PortfolioHistoryEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.PositionsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.WatchlistEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.CryptoMarketDataEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
@@ -42,13 +43,16 @@ public class AlpacaAPI {
     private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPI.class);
 
     private static final String VERSION_2_PATH_SEGMENT = "v2";
+    private static final String VERSION_1_BETA_1_PATH_SEGMENT = "v1beta1";
 
     private final OkHttpClient okHttpClient;
     private final AlpacaClient brokerClient;
-    private final AlpacaClient dataClient;
+    private final AlpacaClient cryptoDataClient;
+    private final AlpacaClient stockDataClient;
     // Ordering of fields/methods below are analogous to the ordering in the Alpaca documentation
     private final AccountEndpoint accountEndpoint;
-    private final MarketDataEndpoint marketDataEndpoint;
+    private final CryptoMarketDataEndpoint cryptoMarketDataEndpoint;
+    private final StockMarketDataEndpoint stockMarketDataEndpoint;
     private final OrdersEndpoint ordersEndpoint;
     private final PositionsEndpoint positionsEndpoint;
     private final AssetsEndpoint assetsEndpoint;
@@ -154,14 +158,17 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         if (oAuthToken == null) {
             brokerClient = new AlpacaClient(okHttpClient, keyID, secretKey,
                     brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
-            dataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_2_PATH_SEGMENT);
+            cryptoDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_1_PATH_SEGMENT);
+            stockDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_2_PATH_SEGMENT);
         } else {
             brokerClient = new AlpacaClient(okHttpClient, oAuthToken, brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
-            dataClient = null;
+            cryptoDataClient = null;
+            stockDataClient = null;
         }
 
         accountEndpoint = new AccountEndpoint(brokerClient);
-        marketDataEndpoint = dataClient == null ? null : new MarketDataEndpoint(dataClient);
+        cryptoMarketDataEndpoint = cryptoDataClient == null ? null : new CryptoMarketDataEndpoint(cryptoDataClient);
+        stockMarketDataEndpoint = stockDataClient == null ? null : new StockMarketDataEndpoint(stockDataClient);
         ordersEndpoint = new OrdersEndpoint(brokerClient);
         positionsEndpoint = new PositionsEndpoint(brokerClient);
         assetsEndpoint = new AssetsEndpoint(brokerClient);
@@ -172,7 +179,7 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         accountActivitiesEndpoint = new AccountActivitiesEndpoint(brokerClient);
         portfolioHistoryEndpoint = new PortfolioHistoryEndpoint(brokerClient);
         streamingWebsocket = new StreamingWebsocket(okHttpClient, brokerHostSubdomain, keyID, secretKey, oAuthToken);
-        marketDataWebsocket = dataClient == null ? null :
+        marketDataWebsocket = stockDataClient == null ? null :
                 new MarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
     }
 
@@ -184,10 +191,17 @@ public AccountEndpoint account() {
     }
 
     /**
-     * @return the {@link MarketDataEndpoint}
+     * @return the {@link CryptoMarketDataEndpoint}
      */
-    public MarketDataEndpoint marketData() {
-        return marketDataEndpoint;
+    public CryptoMarketDataEndpoint cryptoMarketData() {
+        return cryptoMarketDataEndpoint;
+    }
+
+    /**
+     * @return the {@link StockMarketDataEndpoint}
+     */
+    public StockMarketDataEndpoint stockMarketData() {
+        return stockMarketDataEndpoint;
     }
 
     /**
@@ -267,30 +281,19 @@ public MarketDataWebsocketInterface marketDataStreaming() {
         return marketDataWebsocket;
     }
 
-    /**
-     * Gets {@link #okHttpClient}.
-     *
-     * @return the {@link OkHttpClient}
-     */
     public OkHttpClient getOkHttpClient() {
         return okHttpClient;
     }
 
-    /**
-     * Gets {@link #brokerClient}.
-     *
-     * @return the broker {@link AlpacaClient}
-     */
     public AlpacaClient getBrokerClient() {
         return brokerClient;
     }
 
-    /**
-     * Gets {@link #dataClient}.
-     *
-     * @return the data {@link AlpacaClient}
-     */
-    public AlpacaClient getDataClient() {
-        return dataClient;
+    public AlpacaClient getCryptoDataClient() {
+        return cryptoDataClient;
+    }
+
+    public AlpacaClient getStockDataClient() {
+        return stockDataClient;
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
index 90e7265a..12cb6a6f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
@@ -222,20 +222,10 @@ public Response executeRequest(Request request) throws IOException {
         return okHttpClient.newCall(request).execute();
     }
 
-    /**
-     * Gets {@link #baseURL}.
-     *
-     * @return the base {@link HttpUrl}
-     */
     public HttpUrl getBaseURL() {
         return baseURL;
     }
 
-    /**
-     * Gets {@link #requestHeaders}.
-     *
-     * @return the request {@link Headers}
-     */
     public Headers getRequestHeaders() {
         return requestHeaders;
     }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
index bcf89c2b..2114b1e8 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
@@ -120,38 +120,18 @@ private String parseAPIErrorResponse() {
         return messageBuilder.toString();
     }
 
-    /**
-     * Gets the {@link #responseStatusCode}.
-     *
-     * @return an {@link Integer}
-     */
     public Integer getResponseStatusCode() {
         return responseStatusCode;
     }
 
-    /**
-     * Gets the {@link #responseStatusMessage}.
-     *
-     * @return a {@link String}
-     */
     public String getResponseStatusMessage() {
         return responseStatusMessage;
     }
 
-    /**
-     * Gets the {@link #apiResponseCode}.
-     *
-     * @return an {@link Integer}
-     */
     public Integer getAPIResponseCode() {
         return apiResponseCode;
     }
 
-    /**
-     * Gets the {@link #apiResponseMessage}.
-     *
-     * @return a {@link String}
-     */
     public String getAPIResponseMessage() {
         return apiResponseMessage;
     }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java
index d1e5b88b..adbe8414 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java
@@ -56,7 +56,7 @@ public AccountActivitiesEndpoint(AlpacaClient alpacaClient) {
      * @param sortDirection the {@link SortDirection} (defaults to {@link SortDirection#DESCENDING} if unspecified.)
      * @param pageSize      the maximum number of entries to return in the response. (See the section on paging.)
      * @param pageToken     the ID of the end of your current page of results. (See the section on paging.)
-     * @param activityTypes the {@link ActivityType}s (null for all {@link ActivityType}s)
+     * @param activityTypes the {@link ActivityType}s (<code>null</code> for all {@link ActivityType}s)
      *
      * @return a {@link List} of {@link AccountActivity}s
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java
index c0642115..6cbf3298 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java
@@ -2,6 +2,7 @@
 
 import com.google.gson.reflect.TypeToken;
 import net.jacobpeterson.alpaca.model.endpoint.asset.Asset;
+import net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetClass;
 import net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetStatus;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
@@ -31,13 +32,13 @@ public AssetsEndpoint(AlpacaClient alpacaClient) {
      * Get a list of {@link Asset}s.
      *
      * @param assetStatus the {@link AssetStatus}. By default, all {@link AssetStatus}es are included.
-     * @param assetClass  the asset class. Defaults to "us_equity".
+     * @param assetClass  the {@link AssetClass} (<code>null</code> for {@link AssetClass#US_EQUITY})
      *
      * @return a {@link List} of {@link Asset}s
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public List<Asset> get(AssetStatus assetStatus, String assetClass) throws AlpacaClientException {
+    public List<Asset> get(AssetStatus assetStatus, AssetClass assetClass) throws AlpacaClientException {
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment);
 
@@ -46,7 +47,7 @@ public List<Asset> get(AssetStatus assetStatus, String assetClass) throws Alpaca
         }
 
         if (assetClass != null) {
-            urlBuilder.addQueryParameter("asset_class", assetClass);
+            urlBuilder.addQueryParameter("asset_class", assetClass.toString());
         }
 
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java
index a542d12d..8715d1fc 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java
@@ -52,7 +52,9 @@ public OrdersEndpoint(AlpacaClient alpacaClient) {
      * @param direction the chronological order of response based on the submission time. Defaults to {@link
      *                  SortDirection#DESCENDING}.
      * @param nested    if true, the result will roll up multi-leg orders under the legs field of primary order.
-     * @param symbols   a {@link Collection} of symbols to filter the result by (null for no filter).
+     * @param symbols   a {@link Collection} of symbols to filter by (e.g. "AAPL,TSLA,MSFT"). A currency pair is
+     *                  required for crypto orders (e.g. "BTCUSD,BCHUSD,LTCUSD,ETCUSD"). <code>null</code> for no
+     *                  filter.
      *
      * @return a {@link List} of {@link Order}s
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java
new file mode 100644
index 00000000..c044d1f8
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java
@@ -0,0 +1,296 @@
+package net.jacobpeterson.alpaca.rest.endpoint.marketdata;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.enums.BarTimePeriod;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.Bar;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.BarsResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestQuoteResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.QuotesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestTradeResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.TradesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.XbboResponse;
+import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
+import net.jacobpeterson.alpaca.util.format.FormatUtil;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+
+import java.time.ZonedDateTime;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link AlpacaEndpoint} for
+ * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/">Historical
+ * Crypto Market Data API</a>.
+ */
+public class CryptoMarketDataEndpoint extends AlpacaEndpoint {
+
+    /**
+     * Instantiates a new {@link CryptoMarketDataEndpoint}.
+     *
+     * @param alpacaClient the {@link AlpacaClient}
+     */
+    public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
+        super(alpacaClient, "crypto");
+    }
+
+    /**
+     * Gets {@link Trade} historical data for the requested crypto symbol.
+     *
+     * @param symbol    the symbol to query for
+     * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all exchanges.
+     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
+     *                  accepted. <code>null</code> for the current day in Central Time.
+     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
+     *                  accepted. <code>null</code> for now.
+     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
+     *                  is given
+     * @param pageToken pagination token to continue from
+     *
+     * @return the {@link TradesResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public TradesResponse getTrades(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
+            ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
+        checkNotNull(symbol);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(symbol)
+                .addPathSegment("trades");
+
+        if (exchanges != null) {
+            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
+                    .map(Exchange::value)
+                    .collect(Collectors.joining(",")));
+        }
+
+        if (start != null) {
+            urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
+        }
+
+        if (end != null) {
+            urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
+        }
+
+        if (limit != null) {
+            urlBuilder.addQueryParameter("limit", limit.toString());
+        }
+
+        if (pageToken != null) {
+            urlBuilder.addQueryParameter("page_token", pageToken);
+        }
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, TradesResponse.class);
+    }
+
+    /**
+     * Gets the latest {@link Trade} for the requested security.
+     *
+     * @param symbol   the symbol to query for
+     * @param exchange the {@link Exchange} to filter the latest {@link Trade} by.
+     *
+     * @return the {@link LatestTradeResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public LatestTradeResponse getLatestTrade(String symbol, Exchange exchange) throws AlpacaClientException {
+        checkNotNull(symbol);
+        checkNotNull(exchange);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(symbol)
+                .addPathSegment("trades")
+                .addPathSegment("latest");
+
+        urlBuilder.addQueryParameter("exchange", exchange.toString());
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, LatestTradeResponse.class);
+    }
+
+    /**
+     * Gets {@link Quote} historical data for the requested crypto symbol.
+     *
+     * @param symbol    the symbol to query for
+     * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all exchanges.
+     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
+     *                  accepted. <code>null</code> for the current day in Central Time.
+     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
+     *                  accepted. <code>null</code> for now.
+     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
+     *                  is given
+     * @param pageToken pagination token to continue from
+     *
+     * @return the {@link QuotesResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public QuotesResponse getQuotes(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
+            ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
+        checkNotNull(symbol);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(symbol)
+                .addPathSegment("quotes");
+
+        if (exchanges != null) {
+            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
+                    .map(Exchange::value)
+                    .collect(Collectors.joining(",")));
+        }
+
+        if (start != null) {
+            urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
+        }
+
+        if (end != null) {
+            urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
+        }
+
+        if (limit != null) {
+            urlBuilder.addQueryParameter("limit", limit.toString());
+        }
+
+        if (pageToken != null) {
+            urlBuilder.addQueryParameter("page_token", pageToken);
+        }
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, QuotesResponse.class);
+    }
+
+    /**
+     * Gets the latest {@link Quote} for the requested security.
+     *
+     * @param symbol   the symbol to query for
+     * @param exchange the {@link Exchange} to filter the latest {@link Quote} by.
+     *
+     * @return the {@link LatestQuoteResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public LatestQuoteResponse getLatestQuote(String symbol, Exchange exchange) throws AlpacaClientException {
+        checkNotNull(symbol);
+        checkNotNull(exchange);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(symbol)
+                .addPathSegment("quotes")
+                .addPathSegment("latest");
+
+        urlBuilder.addQueryParameter("exchange", exchange.toString());
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, LatestQuoteResponse.class);
+    }
+
+    /**
+     * Gets {@link Bar} aggregate historical data for the requested crypto.
+     *
+     * @param symbol                the symbol to query for
+     * @param exchanges             a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all
+     *                              exchanges.
+     * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
+     *                              not accepted. <code>null</code> for now.
+     * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
+     *                              <code>null</code> is given
+     * @param pageToken             pagination token to continue from
+     * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
+     *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
+     *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
+     * @param barTimePeriod         the {@link BarTimePeriod} e.g. for <code>15Min</code> bars, you would supply {@link
+     *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
+     *                              <code>barTimePeriodDuration</code> parameter.
+     *
+     * @return the {@link BarsResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public BarsResponse getBars(String symbol, Collection<Exchange> exchanges, ZonedDateTime start, Integer limit,
+            String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod) throws AlpacaClientException {
+        checkNotNull(symbol);
+        checkNotNull(barTimePeriod);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(symbol)
+                .addPathSegment("bars");
+
+        if (exchanges != null) {
+            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
+                    .map(Exchange::value)
+                    .collect(Collectors.joining(",")));
+        }
+
+        if (start != null) {
+            urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
+        }
+
+        if (limit != null) {
+            urlBuilder.addQueryParameter("limit", limit.toString());
+        }
+
+        if (pageToken != null) {
+            urlBuilder.addQueryParameter("page_token", pageToken);
+        }
+
+        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, BarsResponse.class);
+    }
+
+    /**
+     * Returns the XBBO for a crypto symbol that calculates the Best Bid and Offer across multiple exchanges.
+     *
+     * @param symbol    the symbol to query for
+     * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. If <code>null</code>, then only the
+     *                  exchanges that can be traded on Alpaca are included in the calculation.
+     *
+     * @return the {@link LatestQuoteResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public XbboResponse getXBBO(String symbol, Collection<Exchange> exchanges) throws AlpacaClientException {
+        checkNotNull(symbol);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(symbol)
+                .addPathSegment("xbbo")
+                .addPathSegment("latest");
+
+        if (exchanges != null) {
+            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
+                    .map(Exchange::value)
+                    .collect(Collectors.joining(",")));
+        }
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, XbboResponse.class);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java
similarity index 86%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java
index 45fc4a08..f203a5d2 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/MarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java
@@ -1,19 +1,20 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.marketdata;
 
 import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.Bar;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.BarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.enums.BarAdjustment;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.bar.enums.BarTimePeriod;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.LatestQuoteResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.Quote;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.quote.QuotesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.snapshot.Snapshot;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.LatestTradeResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.Trade;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.historical.trade.TradesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.enums.BarTimePeriod;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.BarsResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.enums.BarAdjustment;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.LatestQuoteResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.QuotesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.snapshot.Snapshot;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.LatestTradeResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.TradesResponse;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.util.format.FormatUtil;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
@@ -31,14 +32,14 @@
  * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/">Historical
  * Market Data API v2</a>.
  */
-public class MarketDataEndpoint extends AlpacaEndpoint {
+public class StockMarketDataEndpoint extends AlpacaEndpoint {
 
     /**
-     * Instantiates a new {@link MarketDataEndpoint}.
+     * Instantiates a new {@link StockMarketDataEndpoint}.
      *
      * @param alpacaClient the {@link AlpacaClient}
      */
-    public MarketDataEndpoint(AlpacaClient alpacaClient) {
+    public StockMarketDataEndpoint(AlpacaClient alpacaClient) {
         super(alpacaClient, "stocks");
     }
 
@@ -87,7 +88,7 @@ public TradesResponse getTrades(String symbol, ZonedDateTime start, ZonedDateTim
     }
 
     /**
-     * The Latest Trade API provides the latest {@link Trade} data for a given ticker symbol.
+     * Gets the latest {@link Trade} for the requested security.
      *
      * @param symbol the symbol to query for
      *
@@ -155,7 +156,7 @@ public QuotesResponse getQuotes(String symbol, ZonedDateTime start, ZonedDateTim
     }
 
     /**
-     * The Latest Quote API provides the latest quote data for a given ticker symbol.
+     * Gets the latest {@link Quote} for the requested security.
      *
      * @param symbol the symbol to query for
      *
@@ -217,7 +218,6 @@ public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime en
 
         urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
         urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
 
         if (limit != null) {
             urlBuilder.addQueryParameter("limit", limit.toString());
@@ -227,6 +227,8 @@ public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime en
             urlBuilder.addQueryParameter("page_token", pageToken);
         }
 
+        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
+
         if (barAdjustment != null) {
             urlBuilder.addQueryParameter("adjustment", barAdjustment.toString());
         }
@@ -238,8 +240,7 @@ public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime en
     }
 
     /**
-     * The Snapshot API for one ticker provides the latest trade, latest quote, minute bar daily bar, and previous daily
-     * bar data for a given ticker symbol
+     * Gets {@link Snapshot}s of the requested securities.
      *
      * @param symbols a {@link Collection} of symbols to query for
      *
@@ -264,8 +265,7 @@ public Map<String, Snapshot> getSnapshots(Collection<String> symbols) throws Alp
     }
 
     /**
-     * The Snapshot API for one ticker provides the latest trade, latest quote, minute bar daily bar and previous daily
-     * bar data for a given ticker symbol
+     * Gets a {@link Snapshot} of the requested security.
      *
      * @param symbol the symbol to query for
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index 232d23f2..18387bcd 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -275,38 +275,18 @@ public void setListener(L listener) {
         this.listener = listener;
     }
 
-    /**
-     * Gets {@link #websocketStateListener}.
-     *
-     * @return the {@link WebsocketStateListener}
-     */
     public WebsocketStateListener getWebsocketStateListener() {
         return websocketStateListener;
     }
 
-    /**
-     * Sets {@link #websocketStateListener}.
-     *
-     * @param websocketStateListener an {@link WebsocketStateListener}
-     */
     public void setWebsocketStateListener(WebsocketStateListener websocketStateListener) {
         this.websocketStateListener = websocketStateListener;
     }
 
-    /**
-     * Gets {@link #automaticallyReconnect}.
-     *
-     * @return a boolean
-     */
     public boolean doesAutomaticallyReconnect() {
         return automaticallyReconnect;
     }
 
-    /**
-     * Sets {@link #automaticallyReconnect}.
-     *
-     * @param automaticallyReconnect the boolean
-     */
     public void setAutomaticallyReconnect(boolean automaticallyReconnect) {
         this.automaticallyReconnect = automaticallyReconnect;
     }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
index c1138209..09bf4266 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
@@ -1,7 +1,7 @@
 package net.jacobpeterson.alpaca.websocket.marketdata;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.enums.MarketDataMessageType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketMessageListener;
 
 /**
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
index 48953560..28cce551 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -5,14 +5,14 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.bar.BarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.control.ErrorMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.control.SubscriptionsMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.control.SuccessMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.enums.MarketDataMessageType;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.quote.QuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.trade.TradeMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.bar.BarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.ErrorMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SuccessMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.quote.QuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.trade.TradeMessage;
 import net.jacobpeterson.alpaca.model.properties.DataAPIType;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import okhttp3.HttpUrl;
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
index 9ac90112..2eb52e63 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
@@ -1,10 +1,10 @@
 package net.jacobpeterson.alpaca.websocket.marketdata;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.control.ErrorMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.control.SubscriptionsMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.control.SuccessMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.realtime.enums.MarketDataMessageType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.ErrorMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SuccessMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
 
 import java.util.Collection;

From 95b36855c9a95e783f37196984da04d7b56880a9 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 21 Dec 2021 18:21:33 -0700
Subject: [PATCH 23/85] Refactor, abstract models and endpoints, implement
 realtime crypto

---
 README.md                                     |  2 +-
 schema_json/endpoint/account/account.json     |  4 +-
 .../endpoint/{asset => assets}/asset.json     |  8 +--
 .../{asset => assets}/enums/asset_class.json  |  0
 .../{asset => assets}/enums/asset_status.json |  0
 .../{stock => common}/historical/bar/bar.json | 10 ++-
 .../bar}/enums/bar_time_period.json           |  0
 .../common/historical/quote/quote.json        | 21 ++++++
 .../common/historical/trade/trade.json        | 21 ++++++
 .../realtime/bar/bar_message.json             |  9 +--
 .../realtime/control/error_message.json       |  4 +-
 .../control/subscriptions_message.json        |  4 +-
 .../realtime/control/success_message.json     |  4 +-
 .../enums/market_data_message_type.json       |  2 +-
 .../realtime/market_data_message.json         |  6 +-
 .../common/realtime/quote/quote_message.json  | 24 +++++++
 .../realtime/symbol_message.json              |  4 +-
 .../common/realtime/trade/trade_message.json  | 24 +++++++
 .../enums/exchange.json                       |  2 +-
 .../trade => common}/enums/taker_side.json    |  2 +-
 .../crypto/historical/bar/bar.json            | 51 --------------
 .../crypto/historical/bar/crypto_bar.json     | 19 +++++
 ...esponse.json => crypto_bars_response.json} |  6 +-
 .../crypto/historical/quote/crypto_quote.json | 24 +++++++
 ...ponse.json => crypto_quotes_response.json} |  6 +-
 ...json => latest_crypto_quote_response.json} |  6 +-
 .../crypto/historical/quote/quote.json        | 36 ----------
 .../crypto/historical/trade/crypto_trade.json | 24 +++++++
 ...ponse.json => crypto_trades_response.json} |  6 +-
 ...json => latest_crypto_trade_response.json} |  6 +-
 .../crypto/historical/trade/trade.json        | 36 ----------
 .../crypto/historical/xbbo/xbbo.json          |  6 +-
 .../crypto/historical/xbbo/xbbo_response.json |  2 +-
 .../realtime/bar/crypto_bar_message.json      | 29 ++++++++
 .../realtime/quote/crypto_quote_message.json  | 24 +++++++
 .../realtime/trade/crypto_trade_message.json  | 24 +++++++
 .../historical/bar/enums/bar_adjustment.json  |  2 +-
 .../stock/historical/bar/stock_bar.json       | 14 ++++
 ...response.json => stock_bars_response.json} |  6 +-
 ....json => latest_stock_quote_response.json} |  6 +-
 .../quote/{quote.json => stock_quote.json}    | 20 ++----
 ...sponse.json => stock_quotes_response.json} |  6 +-
 .../stock/historical/snapshot/snapshot.json   | 22 +++---
 ....json => latest_stock_trade_response.json} |  6 +-
 .../trade/{trade.json => stock_trade.json}    | 20 ++----
 ...sponse.json => stock_trades_response.json} |  6 +-
 .../stock/realtime/bar/stock_bar_message.json | 14 ++++
 ..._message.json => stock_quote_message.json} | 19 +----
 ..._message.json => stock_trade_message.json} | 19 +----
 .../{order => orders}/cancelled_order.json    |  4 +-
 .../enums/current_order_status.json           |  0
 .../{order => orders}/enums/order_class.json  |  0
 .../{order => orders}/enums/order_side.json   |  0
 .../{order => orders}/enums/order_status.json |  0
 .../enums/order_time_in_force.json            |  0
 .../{order => orders}/enums/order_type.json   |  0
 .../endpoint/{order => orders}/order.json     | 24 +++----
 .../close_position_order.json                 |  4 +-
 .../{position => positions}/position.json     |  0
 .../streaming/trade/trade_update.json         |  4 +-
 schema_json/endpoint/watchlist/watchlist.json |  2 +-
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 50 ++++++++-----
 .../{ => account}/AccountEndpoint.java        |  3 +-
 .../AccountActivitiesEndpoint.java            |  3 +-
 .../AccountConfigurationEndpoint.java         |  3 +-
 .../endpoint/{ => assets}/AssetsEndpoint.java |  9 +--
 .../{ => calendar}/CalendarEndpoint.java      |  3 +-
 .../endpoint/{ => clock}/ClockEndpoint.java   |  3 +-
 .../CryptoMarketDataEndpoint.java             | 70 +++++++++----------
 .../{ => stock}/StockMarketDataEndpoint.java  | 60 ++++++++--------
 .../endpoint/{ => orders}/OrdersEndpoint.java | 19 ++---
 .../PortfolioHistoryEndpoint.java             |  3 +-
 .../{ => positions}/PositionsEndpoint.java    | 11 +--
 .../{ => watchlist}/WatchlistEndpoint.java    |  5 +-
 .../marketdata/MarketDataListener.java        |  4 +-
 .../marketdata/MarketDataWebsocket.java       | 70 ++++++++++++-------
 .../MarketDataWebsocketInterface.java         | 10 +--
 .../crypto/CryptoMarketDataWebsocket.java     | 27 +++++++
 .../stock/StockMarketDataWebsocket.java       | 30 ++++++++
 .../alpaca/test/live/AlpacaAPITest.java       | 14 ++--
 80 files changed, 612 insertions(+), 439 deletions(-)
 rename schema_json/endpoint/{asset => assets}/asset.json (93%)
 rename schema_json/endpoint/{asset => assets}/enums/asset_class.json (100%)
 rename schema_json/endpoint/{asset => assets}/enums/asset_status.json (100%)
 rename schema_json/endpoint/market_data/{stock => common}/historical/bar/bar.json (83%)
 rename schema_json/endpoint/market_data/common/{ => historical/bar}/enums/bar_time_period.json (100%)
 create mode 100644 schema_json/endpoint/market_data/common/historical/quote/quote.json
 create mode 100644 schema_json/endpoint/market_data/common/historical/trade/trade.json
 rename schema_json/endpoint/market_data/{stock => common}/realtime/bar/bar_message.json (77%)
 rename schema_json/endpoint/market_data/{stock => common}/realtime/control/error_message.json (76%)
 rename schema_json/endpoint/market_data/{stock => common}/realtime/control/subscriptions_message.json (84%)
 rename schema_json/endpoint/market_data/{stock => common}/realtime/control/success_message.json (71%)
 rename schema_json/endpoint/market_data/{stock => common}/realtime/enums/market_data_message_type.json (83%)
 rename schema_json/endpoint/market_data/{stock => common}/realtime/market_data_message.json (59%)
 create mode 100644 schema_json/endpoint/market_data/common/realtime/quote/quote_message.json
 rename schema_json/endpoint/market_data/{stock => common}/realtime/symbol_message.json (71%)
 create mode 100644 schema_json/endpoint/market_data/common/realtime/trade/trade_message.json
 rename schema_json/endpoint/market_data/crypto/{historical => common}/enums/exchange.json (74%)
 rename schema_json/endpoint/market_data/crypto/{historical/trade => common}/enums/taker_side.json (69%)
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/bar/bar.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
 rename schema_json/endpoint/market_data/crypto/historical/bar/{bars_response.json => crypto_bars_response.json} (76%)
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
 rename schema_json/endpoint/market_data/crypto/historical/quote/{quotes_response.json => crypto_quotes_response.json} (75%)
 rename schema_json/endpoint/market_data/crypto/historical/quote/{latest_quote_response.json => latest_crypto_quote_response.json} (64%)
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/quote.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
 rename schema_json/endpoint/market_data/crypto/historical/trade/{trades_response.json => crypto_trades_response.json} (75%)
 rename schema_json/endpoint/market_data/crypto/historical/trade/{latest_trade_response.json => latest_crypto_trade_response.json} (64%)
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/trade.json
 create mode 100644 schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
 create mode 100644 schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
 create mode 100644 schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
 create mode 100644 schema_json/endpoint/market_data/stock/historical/bar/stock_bar.json
 rename schema_json/endpoint/market_data/stock/historical/bar/{bars_response.json => stock_bars_response.json} (85%)
 rename schema_json/endpoint/market_data/stock/historical/quote/{latest_quote_response.json => latest_stock_quote_response.json} (76%)
 rename schema_json/endpoint/market_data/stock/historical/quote/{quote.json => stock_quote.json} (66%)
 rename schema_json/endpoint/market_data/stock/historical/quote/{quotes_response.json => stock_quotes_response.json} (84%)
 rename schema_json/endpoint/market_data/stock/historical/trade/{latest_trade_response.json => latest_stock_trade_response.json} (76%)
 rename schema_json/endpoint/market_data/stock/historical/trade/{trade.json => stock_trade.json} (63%)
 rename schema_json/endpoint/market_data/stock/historical/trade/{trades_response.json => stock_trades_response.json} (84%)
 create mode 100644 schema_json/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
 rename schema_json/endpoint/market_data/stock/realtime/quote/{quote_message.json => stock_quote_message.json} (65%)
 rename schema_json/endpoint/market_data/stock/realtime/trade/{trade_message.json => stock_trade_message.json} (59%)
 rename schema_json/endpoint/{order => orders}/cancelled_order.json (92%)
 rename schema_json/endpoint/{order => orders}/enums/current_order_status.json (100%)
 rename schema_json/endpoint/{order => orders}/enums/order_class.json (100%)
 rename schema_json/endpoint/{order => orders}/enums/order_side.json (100%)
 rename schema_json/endpoint/{order => orders}/enums/order_status.json (100%)
 rename schema_json/endpoint/{order => orders}/enums/order_time_in_force.json (100%)
 rename schema_json/endpoint/{order => orders}/enums/order_type.json (100%)
 rename schema_json/endpoint/{order => orders}/order.json (89%)
 rename schema_json/endpoint/{position => positions}/close_position_order.json (91%)
 rename schema_json/endpoint/{position => positions}/position.json (100%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => account}/AccountEndpoint.java (90%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => accountactivities}/AccountActivitiesEndpoint.java (97%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => accountconfiguration}/AccountConfigurationEndpoint.java (94%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => assets}/AssetsEndpoint.java (88%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => calendar}/CalendarEndpoint.java (94%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => clock}/ClockEndpoint.java (91%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/{ => crypto}/CryptoMarketDataEndpoint.java (83%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/{ => stock}/StockMarketDataEndpoint.java (84%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => orders}/OrdersEndpoint.java (98%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => portfoliohistory}/PortfolioHistoryEndpoint.java (97%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => positions}/PositionsEndpoint.java (92%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/endpoint/{ => watchlist}/WatchlistEndpoint.java (97%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java

diff --git a/README.md b/README.md
index a0947ec0..ffbea854 100644
--- a/README.md
+++ b/README.md
@@ -101,7 +101,7 @@ try {
 ```
 
 ## [`CryptoMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java)
-Alpaca provides cryptocurrency data from multiple venues/exchanges, namely: Coinbase, ErisX, and FTX. Note that it does not route orders to all venues.
+Alpaca provides cryptocurrency data from multiple venues/exchanges, namely: Coinbase, ErisX, and FTX.
 
 Example usage:
 ```java
diff --git a/schema_json/endpoint/account/account.json b/schema_json/endpoint/account/account.json
index a6e6d506..7e558fe2 100644
--- a/schema_json/endpoint/account/account.json
+++ b/schema_json/endpoint/account/account.json
@@ -56,11 +56,11 @@
     },
     "long_market_value": {
       "existingJavaType": "java.lang.String",
-      "title": "Real-time MtM value of all long positions held in the account."
+      "title": "Realtime MtM value of all long positions held in the account."
     },
     "short_market_value": {
       "existingJavaType": "java.lang.String",
-      "title": "Real-time MtM value of all short positions held in the account."
+      "title": "Realtime MtM value of all short positions held in the account."
     },
     "equity": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/asset/asset.json b/schema_json/endpoint/assets/asset.json
similarity index 93%
rename from schema_json/endpoint/asset/asset.json
rename to schema_json/endpoint/assets/asset.json
index f5ab8b9c..fa65e096 100644
--- a/schema_json/endpoint/asset/asset.json
+++ b/schema_json/endpoint/assets/asset.json
@@ -7,9 +7,9 @@
       "title": "Asset ID."
     },
     "class": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetClass",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetClass",
       "javaName": "assetClass",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetClass}."
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetClass}."
     },
     "exchange": {
       "existingJavaType": "java.lang.String",
@@ -24,8 +24,8 @@
       "title": "The official name of the asset."
     },
     "status": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetStatus",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetStatus}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetStatus",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetStatus}."
     },
     "tradable": {
       "existingJavaType": "java.lang.Boolean",
diff --git a/schema_json/endpoint/asset/enums/asset_class.json b/schema_json/endpoint/assets/enums/asset_class.json
similarity index 100%
rename from schema_json/endpoint/asset/enums/asset_class.json
rename to schema_json/endpoint/assets/enums/asset_class.json
diff --git a/schema_json/endpoint/asset/enums/asset_status.json b/schema_json/endpoint/assets/enums/asset_status.json
similarity index 100%
rename from schema_json/endpoint/asset/enums/asset_status.json
rename to schema_json/endpoint/assets/enums/asset_status.json
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/bar.json b/schema_json/endpoint/market_data/common/historical/bar/bar.json
similarity index 83%
rename from schema_json/endpoint/market_data/stock/historical/bar/bar.json
rename to schema_json/endpoint/market_data/common/historical/bar/bar.json
index 55423fde..b0284b57 100644
--- a/schema_json/endpoint/market_data/stock/historical/bar/bar.json
+++ b/schema_json/endpoint/market_data/common/historical/bar/bar.json
@@ -1,6 +1,9 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "t": {
       "existingJavaType": "java.time.ZonedDateTime",
@@ -27,11 +30,6 @@
       "javaName": "close",
       "title": "Close price."
     },
-    "v": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "volume",
-      "title": "Volume."
-    },
     "n": {
       "existingJavaType": "java.lang.Long",
       "javaName": "tradeCount",
diff --git a/schema_json/endpoint/market_data/common/enums/bar_time_period.json b/schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/enums/bar_time_period.json
rename to schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
diff --git a/schema_json/endpoint/market_data/common/historical/quote/quote.json b/schema_json/endpoint/market_data/common/historical/quote/quote.json
new file mode 100644
index 00000000..ed75e88f
--- /dev/null
+++ b/schema_json/endpoint/market_data/common/historical/quote/quote.json
@@ -0,0 +1,21 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    },
+    "ap": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
+      "title": "Ask price."
+    },
+    "bp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
+      "title": "Bid price."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/common/historical/trade/trade.json b/schema_json/endpoint/market_data/common/historical/trade/trade.json
new file mode 100644
index 00000000..1cacafa5
--- /dev/null
+++ b/schema_json/endpoint/market_data/common/historical/trade/trade.json
@@ -0,0 +1,21 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    },
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "Trade price."
+    },
+    "i": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeID",
+      "title": "Trade ID."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/stock/realtime/bar/bar_message.json b/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
similarity index 77%
rename from schema_json/endpoint/market_data/stock/realtime/bar/bar_message.json
rename to schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
index 7abf464d..fcf5f6c6 100644
--- a/schema_json/endpoint/market_data/stock/realtime/bar/bar_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
@@ -1,9 +1,9 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.SymbolMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "o": {
       "existingJavaType": "java.lang.Double",
@@ -25,11 +25,6 @@
       "javaName": "close",
       "title": "Close price."
     },
-    "v": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "volume",
-      "title": "Volume."
-    },
     "t": {
       "existingJavaType": "java.time.ZonedDateTime",
       "javaName": "timestamp",
diff --git a/schema_json/endpoint/market_data/stock/realtime/control/error_message.json b/schema_json/endpoint/market_data/common/realtime/control/error_message.json
similarity index 76%
rename from schema_json/endpoint/market_data/stock/realtime/control/error_message.json
rename to schema_json/endpoint/market_data/common/realtime/control/error_message.json
index 1ef275ab..39b9e7f2 100644
--- a/schema_json/endpoint/market_data/stock/realtime/control/error_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/control/error_message.json
@@ -1,9 +1,9 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "code": {
       "existingJavaType": "java.lang.Integer",
diff --git a/schema_json/endpoint/market_data/stock/realtime/control/subscriptions_message.json b/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
similarity index 84%
rename from schema_json/endpoint/market_data/stock/realtime/control/subscriptions_message.json
rename to schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
index 76bfc479..c302083a 100644
--- a/schema_json/endpoint/market_data/stock/realtime/control/subscriptions_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
@@ -1,9 +1,9 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "trades": {
       "existingJavaType": "java.util.ArrayList<java.lang.String>",
diff --git a/schema_json/endpoint/market_data/stock/realtime/control/success_message.json b/schema_json/endpoint/market_data/common/realtime/control/success_message.json
similarity index 71%
rename from schema_json/endpoint/market_data/stock/realtime/control/success_message.json
rename to schema_json/endpoint/market_data/common/realtime/control/success_message.json
index d7225e02..2edd335b 100644
--- a/schema_json/endpoint/market_data/stock/realtime/control/success_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/control/success_message.json
@@ -1,9 +1,9 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "msg": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/stock/realtime/enums/market_data_message_type.json b/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
similarity index 83%
rename from schema_json/endpoint/market_data/stock/realtime/enums/market_data_message_type.json
rename to schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
index 40368ec9..f55a5190 100644
--- a/schema_json/endpoint/market_data/stock/realtime/enums/market_data_message_type.json
+++ b/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
@@ -1,6 +1,6 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "enum": [
     "success",
     "error",
diff --git a/schema_json/endpoint/market_data/stock/realtime/market_data_message.json b/schema_json/endpoint/market_data/common/realtime/market_data_message.json
similarity index 59%
rename from schema_json/endpoint/market_data/stock/realtime/market_data_message.json
rename to schema_json/endpoint/market_data/common/realtime/market_data_message.json
index 5ee64bef..d41cd481 100644
--- a/schema_json/endpoint/market_data/stock/realtime/market_data_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/market_data_message.json
@@ -1,11 +1,11 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "T": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType",
       "javaName": "messageType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType}."
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/common/realtime/quote/quote_message.json b/schema_json/endpoint/market_data/common/realtime/quote/quote_message.json
new file mode 100644
index 00000000..d6bdaabc
--- /dev/null
+++ b/schema_json/endpoint/market_data/common/realtime/quote/quote_message.json
@@ -0,0 +1,24 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.SymbolMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
+  "properties": {
+    "ap": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
+      "title": "Ask price."
+    },
+    "bp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
+      "title": "Bid price."
+    },
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/stock/realtime/symbol_message.json b/schema_json/endpoint/market_data/common/realtime/symbol_message.json
similarity index 71%
rename from schema_json/endpoint/market_data/stock/realtime/symbol_message.json
rename to schema_json/endpoint/market_data/common/realtime/symbol_message.json
index acc6327c..a6f40f69 100644
--- a/schema_json/endpoint/market_data/stock/realtime/symbol_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/symbol_message.json
@@ -1,9 +1,9 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "S": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/common/realtime/trade/trade_message.json b/schema_json/endpoint/market_data/common/realtime/trade/trade_message.json
new file mode 100644
index 00000000..ca01450f
--- /dev/null
+++ b/schema_json/endpoint/market_data/common/realtime/trade/trade_message.json
@@ -0,0 +1,24 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.SymbolMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
+  "properties": {
+    "i": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "tradeID",
+      "title": "Trade ID."
+    },
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "Trade price."
+    },
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp with nanosecond precision."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/enums/exchange.json b/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
similarity index 74%
rename from schema_json/endpoint/market_data/crypto/historical/enums/exchange.json
rename to schema_json/endpoint/market_data/crypto/common/enums/exchange.json
index 1ea41bd6..ff9d94e5 100644
--- a/schema_json/endpoint/market_data/crypto/historical/enums/exchange.json
+++ b/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
@@ -1,6 +1,6 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/\">Crypto Data</a>.",
   "enum": [
     "ERSX",
     "FTX",
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json b/schema_json/endpoint/market_data/crypto/common/enums/taker_side.json
similarity index 69%
rename from schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json
rename to schema_json/endpoint/market_data/crypto/common/enums/taker_side.json
index 9604bc54..8c8e4adf 100644
--- a/schema_json/endpoint/market_data/crypto/historical/trade/enums/taker_side.json
+++ b/schema_json/endpoint/market_data/crypto/common/enums/taker_side.json
@@ -1,6 +1,6 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/\">Crypto Data</a>.",
   "enum": [
     "B",
     "S"
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/bar.json b/schema_json/endpoint/market_data/crypto/historical/bar/bar.json
deleted file mode 100644
index c677ba92..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/bar/bar.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange."
-    },
-    "o": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "open",
-      "title": "Open price."
-    },
-    "h": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "high",
-      "title": "High price."
-    },
-    "l": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "low",
-      "title": "Low price."
-    },
-    "c": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "close",
-      "title": "Close price."
-    },
-    "v": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "volume",
-      "title": "Volume."
-    },
-    "n": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "tradeCount",
-      "title": "Trade count."
-    },
-    "vw": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "vwap",
-      "title": "VWAP (Volume Weighted Average Price)."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
new file mode 100644
index 00000000..e23c3ec6
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
@@ -0,0 +1,19 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.Bar"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange."
+    },
+    "v": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "volume",
+      "title": "Volume."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
similarity index 76%
rename from schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json
rename to schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
index 21f5cddb..677e6633 100644
--- a/schema_json/endpoint/market_data/crypto/historical/bar/bars_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
@@ -1,10 +1,10 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "bars": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.Bar>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.Bar}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
new file mode 100644
index 00000000..099df001
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
@@ -0,0 +1,24 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.quote.Quote"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange."
+    },
+    "as": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askSize",
+      "title": "Ask size."
+    },
+    "bs": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidSize",
+      "title": "Bid size."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
similarity index 75%
rename from schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json
rename to schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
index 212635c3..e8a67bfd 100644
--- a/schema_json/endpoint/market_data/crypto/historical/quote/quotes_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
@@ -1,10 +1,10 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "quotes": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
similarity index 64%
rename from schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json
rename to schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
index 044d252f..64bf1d75 100644
--- a/schema_json/endpoint/market_data/crypto/historical/quote/latest_quote_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
@@ -1,14 +1,14 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
       "title": "Symbol that was queried."
     },
     "quote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/quote.json b/schema_json/endpoint/market_data/crypto/historical/quote/quote.json
deleted file mode 100644
index ecca5a55..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/quote/quote.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange."
-    },
-    "ap": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askPrice",
-      "title": "Ask price."
-    },
-    "as": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askSize",
-      "title": "Ask size."
-    },
-    "bp": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidPrice",
-      "title": "Bid price."
-    },
-    "bs": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidSize",
-      "title": "Bid size."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
new file mode 100644
index 00000000..0b0c1b53
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
@@ -0,0 +1,24 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.trade.Trade"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange where the trade happened."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "size",
+      "title": "Trade size."
+    },
+    "tks": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide",
+      "javaName": "takerSide",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide} of the trade."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
similarity index 75%
rename from schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json
rename to schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
index 68a770a5..a512c775 100644
--- a/schema_json/endpoint/market_data/crypto/historical/trade/trades_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
@@ -1,10 +1,10 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "trades": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
similarity index 64%
rename from schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json
rename to schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
index 089f9528..512c6a18 100644
--- a/schema_json/endpoint/market_data/crypto/historical/trade/latest_trade_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
@@ -1,14 +1,14 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
       "title": "Symbol that was queried."
     },
     "trade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/trade.json b/schema_json/endpoint/market_data/crypto/historical/trade/trade.json
deleted file mode 100644
index 56fef69b..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/trade/trade.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange where the trade happened."
-    },
-    "p": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "price",
-      "title": "Trade price."
-    },
-    "s": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "size",
-      "title": "Trade size."
-    },
-    "tks": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.enums.TakerSide",
-      "javaName": "takerSide",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.enums.TakerSide} of the trade."
-    },
-    "i": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "tradeID",
-      "title": "Trade ID."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
index 9646a1ee..d279722f 100644
--- a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
+++ b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
@@ -1,6 +1,6 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "t": {
       "existingJavaType": "java.time.ZonedDateTime",
@@ -8,7 +8,7 @@
       "title": "Timestamp with nanosecond precision."
     },
     "ax": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
       "javaName": "askExchange",
       "title": "Ask Exchange."
     },
@@ -23,7 +23,7 @@
       "title": "Ask size."
     },
     "bx": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
       "javaName": "bidExchange",
       "title": "Bid Exchange."
     },
diff --git a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
index b3bbe427..2fdc8f28 100644
--- a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
@@ -1,6 +1,6 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Crypto Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json b/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
new file mode 100644
index 00000000..3ef4ac16
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
@@ -0,0 +1,29 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
+  "properties": {
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange."
+    },
+    "v": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "volume",
+      "title": "Volume."
+    },
+    "n": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeCount",
+      "title": "Trade count."
+    },
+    "vw": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "vwap",
+      "title": "VWAP (Volume Weighted Average Price)."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json b/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
new file mode 100644
index 00000000..9d9a2618
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
@@ -0,0 +1,24 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
+  "properties": {
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange."
+    },
+    "as": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askSize",
+      "title": "Ask size."
+    },
+    "bs": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidSize",
+      "title": "Bid size."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json b/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
new file mode 100644
index 00000000..ec4f5227
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
@@ -0,0 +1,24 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
+  "properties": {
+    "x": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
+      "javaName": "exchange",
+      "title": "Exchange where the trade happened."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "size",
+      "title": "Trade size."
+    },
+    "tks": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide",
+      "javaName": "takerSide",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide} of the trade."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json b/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
index 4f28b6ed..d42b31b2 100644
--- a/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
+++ b/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
@@ -1,6 +1,6 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "enum": [
     "raw",
     "split",
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/stock_bar.json b/schema_json/endpoint/market_data/stock/historical/bar/stock_bar.json
new file mode 100644
index 00000000..ae851453
--- /dev/null
+++ b/schema_json/endpoint/market_data/stock/historical/bar/stock_bar.json
@@ -0,0 +1,14 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.Bar"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
+  "properties": {
+    "v": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "volume",
+      "title": "Volume."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/bars_response.json b/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
similarity index 85%
rename from schema_json/endpoint/market_data/stock/historical/bar/bars_response.json
rename to schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
index ebd21e82..b23db483 100644
--- a/schema_json/endpoint/market_data/stock/historical/bar/bars_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
@@ -1,10 +1,10 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
     "bars": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/stock/historical/quote/latest_quote_response.json b/schema_json/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
similarity index 76%
rename from schema_json/endpoint/market_data/stock/historical/quote/latest_quote_response.json
rename to schema_json/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
index 1250c5f8..4a9a3992 100644
--- a/schema_json/endpoint/market_data/stock/historical/quote/latest_quote_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
@@ -1,14 +1,14 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
       "title": "Symbol that was queried."
     },
     "quote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/stock/historical/quote/quote.json b/schema_json/endpoint/market_data/stock/historical/quote/stock_quote.json
similarity index 66%
rename from schema_json/endpoint/market_data/stock/historical/quote/quote.json
rename to schema_json/endpoint/market_data/stock/historical/quote/stock_quote.json
index 059c9746..164f093f 100644
--- a/schema_json/endpoint/market_data/stock/historical/quote/quote.json
+++ b/schema_json/endpoint/market_data/stock/historical/quote/stock_quote.json
@@ -1,22 +1,15 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.quote.Quote"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
     "ax": {
       "existingJavaType": "java.lang.String",
       "javaName": "askExchange",
       "title": "Ask exchange."
     },
-    "ap": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askPrice",
-      "title": "Ask price."
-    },
     "as": {
       "existingJavaType": "java.lang.Integer",
       "javaName": "askSize",
@@ -27,11 +20,6 @@
       "javaName": "bidExchange",
       "title": "Bid exchange."
     },
-    "bp": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidPrice",
-      "title": "Bid price."
-    },
     "bs": {
       "existingJavaType": "java.lang.Integer",
       "javaName": "bidSize",
diff --git a/schema_json/endpoint/market_data/stock/historical/quote/quotes_response.json b/schema_json/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
similarity index 84%
rename from schema_json/endpoint/market_data/stock/historical/quote/quotes_response.json
rename to schema_json/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
index 6ba96682..418dcf6b 100644
--- a/schema_json/endpoint/market_data/stock/historical/quote/quotes_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
@@ -1,10 +1,10 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
     "quotes": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json b/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
index f42b18af..9617b3de 100644
--- a/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
+++ b/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
@@ -1,26 +1,26 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
     "latestTrade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade}."
     },
     "latestQuote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote}."
     },
     "minuteBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar",
-      "title": "The minute {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar",
+      "title": "The minute {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}."
     },
     "dailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar",
-      "title": "The daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar",
+      "title": "The daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}."
     },
     "prevDailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar",
-      "title": "The previous daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar",
+      "title": "The previous daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/stock/historical/trade/latest_trade_response.json b/schema_json/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
similarity index 76%
rename from schema_json/endpoint/market_data/stock/historical/trade/latest_trade_response.json
rename to schema_json/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
index f5be1b9d..b4765b53 100644
--- a/schema_json/endpoint/market_data/stock/historical/trade/latest_trade_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
@@ -1,14 +1,14 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
       "title": "Symbol that was queried."
     },
     "trade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade",
+      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade}."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/stock/historical/trade/trade.json b/schema_json/endpoint/market_data/stock/historical/trade/stock_trade.json
similarity index 63%
rename from schema_json/endpoint/market_data/stock/historical/trade/trade.json
rename to schema_json/endpoint/market_data/stock/historical/trade/stock_trade.json
index f7ef48ca..ea3e96cf 100644
--- a/schema_json/endpoint/market_data/stock/historical/trade/trade.json
+++ b/schema_json/endpoint/market_data/stock/historical/trade/stock_trade.json
@@ -1,22 +1,15 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.trade.Trade"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
     "x": {
       "existingJavaType": "java.lang.String",
       "javaName": "exchange",
       "title": "Exchange where the trade happened."
     },
-    "p": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "price",
-      "title": "Trade price."
-    },
     "s": {
       "existingJavaType": "java.lang.Integer",
       "javaName": "size",
@@ -27,11 +20,6 @@
       "javaName": "conditions",
       "title": "The {@link java.util.ArrayList} of trade conditions."
     },
-    "i": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "tradeID",
-      "title": "Trade ID."
-    },
     "z": {
       "existingJavaType": "java.lang.String",
       "javaName": "tape",
diff --git a/schema_json/endpoint/market_data/stock/historical/trade/trades_response.json b/schema_json/endpoint/market_data/stock/historical/trade/stock_trades_response.json
similarity index 84%
rename from schema_json/endpoint/market_data/stock/historical/trade/trades_response.json
rename to schema_json/endpoint/market_data/stock/historical/trade/stock_trades_response.json
index 503b6d9f..9b08b1da 100644
--- a/schema_json/endpoint/market_data/stock/historical/trade/trades_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/trade/stock_trades_response.json
@@ -1,10 +1,10 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
   "properties": {
     "trades": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade}s."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade}s."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/stock/realtime/bar/stock_bar_message.json b/schema_json/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
new file mode 100644
index 00000000..6bfbf62a
--- /dev/null
+++ b/schema_json/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
@@ -0,0 +1,14 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage"
+  },
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Realtime Stock Market Data</a>.",
+  "properties": {
+    "v": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "volume",
+      "title": "Volume."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/stock/realtime/quote/quote_message.json b/schema_json/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
similarity index 65%
rename from schema_json/endpoint/market_data/stock/realtime/quote/quote_message.json
rename to schema_json/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
index 3c7c650c..2633ccde 100644
--- a/schema_json/endpoint/market_data/stock/realtime/quote/quote_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
@@ -1,20 +1,15 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Realtime Stock Market Data</a>.",
   "properties": {
     "ax": {
       "existingJavaType": "java.lang.String",
       "javaName": "askExchange",
       "title": "Ask exchange code."
     },
-    "ap": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askPrice",
-      "title": "Ask price."
-    },
     "as": {
       "existingJavaType": "java.lang.Integer",
       "javaName": "askSize",
@@ -25,21 +20,11 @@
       "javaName": "bidExchange",
       "title": "Bid exchange code."
     },
-    "bp": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidPrice",
-      "title": "Bid price."
-    },
     "bs": {
       "existingJavaType": "java.lang.Integer",
       "javaName": "bidSize",
       "title": "Bid size."
     },
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
     "c": {
       "existingJavaType": "java.util.ArrayList<String>",
       "javaName": "conditions",
diff --git a/schema_json/endpoint/market_data/stock/realtime/trade/trade_message.json b/schema_json/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
similarity index 59%
rename from schema_json/endpoint/market_data/stock/realtime/trade/trade_message.json
rename to schema_json/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
index e72f5859..c3b0bc51 100644
--- a/schema_json/endpoint/market_data/stock/realtime/trade/trade_message.json
+++ b/schema_json/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
@@ -1,35 +1,20 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Real-time Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Realtime Stock Market Data</a>.",
   "properties": {
-    "i": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "tradeID",
-      "title": "Trade ID."
-    },
     "x": {
       "existingJavaType": "java.lang.String",
       "javaName": "exchange",
       "title": "Exchange code where the trade occurred."
     },
-    "p": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "price",
-      "title": "Trade price."
-    },
     "s": {
       "existingJavaType": "java.lang.Integer",
       "javaName": "size",
       "title": "Trade size."
     },
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
     "c": {
       "existingJavaType": "java.util.ArrayList<String>",
       "javaName": "conditions",
diff --git a/schema_json/endpoint/order/cancelled_order.json b/schema_json/endpoint/orders/cancelled_order.json
similarity index 92%
rename from schema_json/endpoint/order/cancelled_order.json
rename to schema_json/endpoint/orders/cancelled_order.json
index be79774c..cd34b921 100644
--- a/schema_json/endpoint/order/cancelled_order.json
+++ b/schema_json/endpoint/orders/cancelled_order.json
@@ -11,9 +11,9 @@
       "title": "The HTTP status code of the cancelled order."
     },
     "body": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.Order",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.Order",
       "javaName": "order",
-      "title": "The cancelled {@link net.jacobpeterson.alpaca.model.endpoint.order.Order}."
+      "title": "The cancelled {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order}."
     }
   }
 }
diff --git a/schema_json/endpoint/order/enums/current_order_status.json b/schema_json/endpoint/orders/enums/current_order_status.json
similarity index 100%
rename from schema_json/endpoint/order/enums/current_order_status.json
rename to schema_json/endpoint/orders/enums/current_order_status.json
diff --git a/schema_json/endpoint/order/enums/order_class.json b/schema_json/endpoint/orders/enums/order_class.json
similarity index 100%
rename from schema_json/endpoint/order/enums/order_class.json
rename to schema_json/endpoint/orders/enums/order_class.json
diff --git a/schema_json/endpoint/order/enums/order_side.json b/schema_json/endpoint/orders/enums/order_side.json
similarity index 100%
rename from schema_json/endpoint/order/enums/order_side.json
rename to schema_json/endpoint/orders/enums/order_side.json
diff --git a/schema_json/endpoint/order/enums/order_status.json b/schema_json/endpoint/orders/enums/order_status.json
similarity index 100%
rename from schema_json/endpoint/order/enums/order_status.json
rename to schema_json/endpoint/orders/enums/order_status.json
diff --git a/schema_json/endpoint/order/enums/order_time_in_force.json b/schema_json/endpoint/orders/enums/order_time_in_force.json
similarity index 100%
rename from schema_json/endpoint/order/enums/order_time_in_force.json
rename to schema_json/endpoint/orders/enums/order_time_in_force.json
diff --git a/schema_json/endpoint/order/enums/order_type.json b/schema_json/endpoint/orders/enums/order_type.json
similarity index 100%
rename from schema_json/endpoint/order/enums/order_type.json
rename to schema_json/endpoint/orders/enums/order_type.json
diff --git a/schema_json/endpoint/order/order.json b/schema_json/endpoint/orders/order.json
similarity index 89%
rename from schema_json/endpoint/order/order.json
rename to schema_json/endpoint/orders/order.json
index c0af5330..6a6f85fd 100644
--- a/schema_json/endpoint/order/order.json
+++ b/schema_json/endpoint/orders/order.json
@@ -82,20 +82,20 @@
       "title": "Filled average price."
     },
     "order_class": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderClass",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderClass}. For details of non-simple order classes, please see Bracket Order Overview."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderClass",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderClass}. For details of non-simple order classes, please see Bracket Order Overview."
     },
     "type": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderType}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderType",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderType}."
     },
     "side": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderSide",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderSide}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderSide",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderSide}."
     },
     "time_in_force": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderTimeInForce",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderTimeInForce}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderTimeInForce",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderTimeInForce}."
     },
     "limit_price": {
       "existingJavaType": "java.lang.String",
@@ -106,16 +106,16 @@
       "title": "Stop price."
     },
     "status": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderStatus",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderStatus}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderStatus",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderStatus}."
     },
     "extended_hours": {
       "existingJavaType": "java.lang.Boolean",
       "title": "If true, eligible for execution outside regular trading hours."
     },
     "legs": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.order.Order>",
-      "title": "When querying non-simple order_class orders in a nested style, an {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.order.Order} entities associated with this order. Otherwise, null."
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.orders.Order>",
+      "title": "When querying non-simple order_class orders in a nested style, an {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order} entities associated with this order. Otherwise, null."
     },
     "trail_percent": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/position/close_position_order.json b/schema_json/endpoint/positions/close_position_order.json
similarity index 91%
rename from schema_json/endpoint/position/close_position_order.json
rename to schema_json/endpoint/positions/close_position_order.json
index 5ce79575..3ffba883 100644
--- a/schema_json/endpoint/position/close_position_order.json
+++ b/schema_json/endpoint/positions/close_position_order.json
@@ -11,9 +11,9 @@
       "title": "The HTTP status code of the position-closing order."
     },
     "body": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.Order",
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.Order",
       "javaName": "order",
-      "title": "The position-closing {@link net.jacobpeterson.alpaca.model.endpoint.order.Order}."
+      "title": "The position-closing {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order}."
     }
   }
 }
diff --git a/schema_json/endpoint/position/position.json b/schema_json/endpoint/positions/position.json
similarity index 100%
rename from schema_json/endpoint/position/position.json
rename to schema_json/endpoint/positions/position.json
diff --git a/schema_json/endpoint/streaming/trade/trade_update.json b/schema_json/endpoint/streaming/trade/trade_update.json
index 339ea01c..32f6c324 100644
--- a/schema_json/endpoint/streaming/trade/trade_update.json
+++ b/schema_json/endpoint/streaming/trade/trade_update.json
@@ -20,8 +20,8 @@
       "title": "The position quantity."
     },
     "order": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.order.Order",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.order.Order}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.Order",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order}."
     }
   }
 }
diff --git a/schema_json/endpoint/watchlist/watchlist.json b/schema_json/endpoint/watchlist/watchlist.json
index 0cfa52a0..7c7e8602 100644
--- a/schema_json/endpoint/watchlist/watchlist.json
+++ b/schema_json/endpoint/watchlist/watchlist.json
@@ -23,7 +23,7 @@
       "title": "Account ID."
     },
     "assets": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.asset.Asset>",
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.assets.Asset>",
       "title": "The content of this watchlist, in the order as registered by the client."
     }
   }
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 67813131..63980318 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -5,22 +5,23 @@
 import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
 import net.jacobpeterson.alpaca.properties.AlpacaProperties;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountActivitiesEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountConfigurationEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.accountactivities.AccountActivitiesEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AssetsEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.CalendarEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.ClockEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.OrdersEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.PortfolioHistoryEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.PositionsEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.WatchlistEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.CryptoMarketDataEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.StockMarketDataEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.assets.AssetsEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.calendar.CalendarEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.portfoliohistory.PortfolioHistoryEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.positions.PositionsEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.watchlist.WatchlistEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
-import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
+import net.jacobpeterson.alpaca.websocket.marketdata.crypto.CryptoMarketDataWebsocket;
+import net.jacobpeterson.alpaca.websocket.marketdata.stock.StockMarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocket;
 import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocketInterface;
 import okhttp3.OkHttpClient;
@@ -63,7 +64,8 @@ public class AlpacaAPI {
     private final AccountActivitiesEndpoint accountActivitiesEndpoint;
     private final PortfolioHistoryEndpoint portfolioHistoryEndpoint;
     private final StreamingWebsocket streamingWebsocket;
-    private final MarketDataWebsocket marketDataWebsocket;
+    private final CryptoMarketDataWebsocket cryptoMarketDataWebsocket;
+    private final StockMarketDataWebsocket stockMarketDataWebsocket;
 
     /**
      * Instantiates a new {@link AlpacaAPI} using properties specified in <code>alpaca.properties</code> file (or their
@@ -178,9 +180,12 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         accountConfigurationEndpoint = new AccountConfigurationEndpoint(brokerClient);
         accountActivitiesEndpoint = new AccountActivitiesEndpoint(brokerClient);
         portfolioHistoryEndpoint = new PortfolioHistoryEndpoint(brokerClient);
+
         streamingWebsocket = new StreamingWebsocket(okHttpClient, brokerHostSubdomain, keyID, secretKey, oAuthToken);
-        marketDataWebsocket = stockDataClient == null ? null :
-                new MarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
+        cryptoMarketDataWebsocket = cryptoDataClient == null ? null :
+                new CryptoMarketDataWebsocket(okHttpClient, keyID, secretKey);
+        stockMarketDataWebsocket = stockDataClient == null ? null :
+                new StockMarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
     }
 
     /**
@@ -275,10 +280,17 @@ public StreamingWebsocketInterface streaming() {
     }
 
     /**
-     * @return the {@link MarketDataWebsocketInterface}
+     * @return the Crypto {@link MarketDataWebsocketInterface}
+     */
+    public MarketDataWebsocketInterface cryptoMarketDataStreaming() {
+        return cryptoMarketDataWebsocket;
+    }
+
+    /**
+     * @return the Stock {@link MarketDataWebsocketInterface}
      */
-    public MarketDataWebsocketInterface marketDataStreaming() {
-        return marketDataWebsocket;
+    public MarketDataWebsocketInterface stockMarketDataStreaming() {
+        return stockMarketDataWebsocket;
     }
 
     public OkHttpClient getOkHttpClient() {
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java
similarity index 90%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java
index ff57ff67..8054ab4f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java
@@ -1,8 +1,9 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.account;
 
 import net.jacobpeterson.alpaca.model.endpoint.account.Account;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java
similarity index 97%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java
index adbe8414..c56375bf 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.accountactivities;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
@@ -10,6 +10,7 @@
 import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 import org.slf4j.Logger;
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountConfigurationEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java
similarity index 94%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountConfigurationEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java
index 2d7c5de0..a319e650 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountConfigurationEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java
@@ -1,8 +1,9 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration;
 
 import net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.AccountConfiguration;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.util.okhttp.JSONBodyBuilder;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
similarity index 88%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
index 6cbf3298..bfcfaffe 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
@@ -1,11 +1,12 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.assets;
 
 import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.asset.Asset;
-import net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetClass;
-import net.jacobpeterson.alpaca.model.endpoint.asset.enums.AssetStatus;
+import net.jacobpeterson.alpaca.model.endpoint.assets.Asset;
+import net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetClass;
+import net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetStatus;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/CalendarEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
similarity index 94%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/CalendarEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
index 03bb2641..58ad41af 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/CalendarEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
@@ -1,9 +1,10 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.calendar;
 
 import com.google.gson.reflect.TypeToken;
 import net.jacobpeterson.alpaca.model.endpoint.calendar.Calendar;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/ClockEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java
similarity index 91%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/ClockEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java
index 6e5e9ae4..368edc13 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/ClockEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java
@@ -1,8 +1,9 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.clock;
 
 import net.jacobpeterson.alpaca.model.endpoint.clock.Clock;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
similarity index 83%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
index c044d1f8..b3136b17 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
@@ -1,15 +1,15 @@
-package net.jacobpeterson.alpaca.rest.endpoint.marketdata;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.enums.BarTimePeriod;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.Bar;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.BarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.enums.Exchange;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestQuoteResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.Quote;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.QuotesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestTradeResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.Trade;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.TradesResponse;
+package net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBarsResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuotesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestCryptoQuoteResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTradesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestCryptoTradeResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.XbboResponse;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
@@ -41,7 +41,7 @@ public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
     }
 
     /**
-     * Gets {@link Trade} historical data for the requested crypto symbol.
+     * Gets {@link CryptoTrade} historical data for the requested crypto symbol.
      *
      * @param symbol    the symbol to query for
      * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all exchanges.
@@ -53,11 +53,11 @@ public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
      *                  is given
      * @param pageToken pagination token to continue from
      *
-     * @return the {@link TradesResponse}
+     * @return the {@link CryptoTradesResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public TradesResponse getTrades(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
+    public CryptoTradesResponse getTrades(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
             ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
         checkNotNull(symbol);
 
@@ -91,20 +91,20 @@ public TradesResponse getTrades(String symbol, Collection<Exchange> exchanges, Z
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, TradesResponse.class);
+        return alpacaClient.requestObject(request, CryptoTradesResponse.class);
     }
 
     /**
-     * Gets the latest {@link Trade} for the requested security.
+     * Gets the latest {@link CryptoTrade} for the requested security.
      *
      * @param symbol   the symbol to query for
-     * @param exchange the {@link Exchange} to filter the latest {@link Trade} by.
+     * @param exchange the {@link Exchange} to filter the latest {@link CryptoTrade} by.
      *
-     * @return the {@link LatestTradeResponse}
+     * @return the {@link LatestCryptoTradeResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestTradeResponse getLatestTrade(String symbol, Exchange exchange) throws AlpacaClientException {
+    public LatestCryptoTradeResponse getLatestTrade(String symbol, Exchange exchange) throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(exchange);
 
@@ -119,11 +119,11 @@ public LatestTradeResponse getLatestTrade(String symbol, Exchange exchange) thro
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, LatestTradeResponse.class);
+        return alpacaClient.requestObject(request, LatestCryptoTradeResponse.class);
     }
 
     /**
-     * Gets {@link Quote} historical data for the requested crypto symbol.
+     * Gets {@link CryptoQuote} historical data for the requested crypto symbol.
      *
      * @param symbol    the symbol to query for
      * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all exchanges.
@@ -135,11 +135,11 @@ public LatestTradeResponse getLatestTrade(String symbol, Exchange exchange) thro
      *                  is given
      * @param pageToken pagination token to continue from
      *
-     * @return the {@link QuotesResponse}
+     * @return the {@link CryptoQuotesResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public QuotesResponse getQuotes(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
+    public CryptoQuotesResponse getQuotes(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
             ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
         checkNotNull(symbol);
 
@@ -173,20 +173,20 @@ public QuotesResponse getQuotes(String symbol, Collection<Exchange> exchanges, Z
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, QuotesResponse.class);
+        return alpacaClient.requestObject(request, CryptoQuotesResponse.class);
     }
 
     /**
-     * Gets the latest {@link Quote} for the requested security.
+     * Gets the latest {@link CryptoQuote} for the requested security.
      *
      * @param symbol   the symbol to query for
-     * @param exchange the {@link Exchange} to filter the latest {@link Quote} by.
+     * @param exchange the {@link Exchange} to filter the latest {@link CryptoQuote} by.
      *
-     * @return the {@link LatestQuoteResponse}
+     * @return the {@link LatestCryptoQuoteResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestQuoteResponse getLatestQuote(String symbol, Exchange exchange) throws AlpacaClientException {
+    public LatestCryptoQuoteResponse getLatestQuote(String symbol, Exchange exchange) throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(exchange);
 
@@ -201,11 +201,11 @@ public LatestQuoteResponse getLatestQuote(String symbol, Exchange exchange) thro
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, LatestQuoteResponse.class);
+        return alpacaClient.requestObject(request, LatestCryptoQuoteResponse.class);
     }
 
     /**
-     * Gets {@link Bar} aggregate historical data for the requested crypto.
+     * Gets {@link CryptoBar} aggregate historical data for the requested crypto.
      *
      * @param symbol                the symbol to query for
      * @param exchanges             a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all
@@ -222,11 +222,11 @@ public LatestQuoteResponse getLatestQuote(String symbol, Exchange exchange) thro
      *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
      *                              <code>barTimePeriodDuration</code> parameter.
      *
-     * @return the {@link BarsResponse}
+     * @return the {@link CryptoBarsResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public BarsResponse getBars(String symbol, Collection<Exchange> exchanges, ZonedDateTime start, Integer limit,
+    public CryptoBarsResponse getBars(String symbol, Collection<Exchange> exchanges, ZonedDateTime start, Integer limit,
             String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod) throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(barTimePeriod);
@@ -259,7 +259,7 @@ public BarsResponse getBars(String symbol, Collection<Exchange> exchanges, Zoned
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, BarsResponse.class);
+        return alpacaClient.requestObject(request, CryptoBarsResponse.class);
     }
 
     /**
@@ -269,7 +269,7 @@ public BarsResponse getBars(String symbol, Collection<Exchange> exchanges, Zoned
      * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. If <code>null</code>, then only the
      *                  exchanges that can be traded on Alpaca are included in the calculation.
      *
-     * @return the {@link LatestQuoteResponse}
+     * @return the {@link XbboResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
similarity index 84%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
index f203a5d2..fe8a846e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
@@ -1,17 +1,17 @@
-package net.jacobpeterson.alpaca.rest.endpoint.marketdata;
+package net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock;
 
 import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.enums.BarTimePeriod;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.Bar;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.BarsResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBarsResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.enums.BarAdjustment;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.LatestQuoteResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.Quote;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.QuotesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.LatestStockQuoteResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuotesResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.snapshot.Snapshot;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.LatestTradeResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.Trade;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.TradesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.LatestStockTradeResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTradesResponse;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
 import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
@@ -44,7 +44,7 @@ public StockMarketDataEndpoint(AlpacaClient alpacaClient) {
     }
 
     /**
-     * Gets {@link Trade} historical data for the requested security.
+     * Gets {@link StockTrade} historical data for the requested security.
      *
      * @param symbol    the symbol to query for
      * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
@@ -55,11 +55,11 @@ public StockMarketDataEndpoint(AlpacaClient alpacaClient) {
      *                  is given
      * @param pageToken pagination token to continue from
      *
-     * @return the {@link TradesResponse}
+     * @return the {@link StockTradesResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public TradesResponse getTrades(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
+    public StockTradesResponse getTrades(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
             String pageToken) throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(start);
@@ -84,19 +84,19 @@ public TradesResponse getTrades(String symbol, ZonedDateTime start, ZonedDateTim
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, TradesResponse.class);
+        return alpacaClient.requestObject(request, StockTradesResponse.class);
     }
 
     /**
-     * Gets the latest {@link Trade} for the requested security.
+     * Gets the latest {@link StockTrade} for the requested security.
      *
      * @param symbol the symbol to query for
      *
-     * @return the {@link LatestTradeResponse}
+     * @return the {@link LatestStockTradeResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestTradeResponse getLatestTrade(String symbol) throws AlpacaClientException {
+    public LatestStockTradeResponse getLatestTrade(String symbol) throws AlpacaClientException {
         checkNotNull(symbol);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
@@ -108,11 +108,11 @@ public LatestTradeResponse getLatestTrade(String symbol) throws AlpacaClientExce
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, LatestTradeResponse.class);
+        return alpacaClient.requestObject(request, LatestStockTradeResponse.class);
     }
 
     /**
-     * Gets {@link Quote} (NBBO or National Best Bid and Offer) historical data for the requested security.
+     * Gets {@link StockQuote} (NBBO or National Best Bid and Offer) historical data for the requested security.
      *
      * @param symbol    the symbol to query for
      * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
@@ -123,11 +123,11 @@ public LatestTradeResponse getLatestTrade(String symbol) throws AlpacaClientExce
      *                  is given
      * @param pageToken pagination token to continue from
      *
-     * @return the {@link QuotesResponse}
+     * @return the {@link StockQuotesResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public QuotesResponse getQuotes(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
+    public StockQuotesResponse getQuotes(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
             String pageToken) throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(start);
@@ -152,19 +152,19 @@ public QuotesResponse getQuotes(String symbol, ZonedDateTime start, ZonedDateTim
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, QuotesResponse.class);
+        return alpacaClient.requestObject(request, StockQuotesResponse.class);
     }
 
     /**
-     * Gets the latest {@link Quote} for the requested security.
+     * Gets the latest {@link StockQuote} for the requested security.
      *
      * @param symbol the symbol to query for
      *
-     * @return the {@link LatestQuoteResponse}
+     * @return the {@link LatestStockQuoteResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestQuoteResponse getLatestQuote(String symbol) throws AlpacaClientException {
+    public LatestStockQuoteResponse getLatestQuote(String symbol) throws AlpacaClientException {
         checkNotNull(symbol);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
@@ -176,11 +176,11 @@ public LatestQuoteResponse getLatestQuote(String symbol) throws AlpacaClientExce
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, LatestQuoteResponse.class);
+        return alpacaClient.requestObject(request, LatestStockQuoteResponse.class);
     }
 
     /**
-     * Gets {@link Bar} aggregate historical data for the requested security.
+     * Gets {@link StockBar} aggregate historical data for the requested security.
      *
      * @param symbol                the symbol to query for
      * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
@@ -199,11 +199,11 @@ public LatestQuoteResponse getLatestQuote(String symbol) throws AlpacaClientExce
      * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is {@link
      *                              BarAdjustment#RAW}.
      *
-     * @return the {@link BarsResponse}
+     * @return the {@link StockBarsResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
+    public StockBarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
             String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod, BarAdjustment barAdjustment)
             throws AlpacaClientException {
         checkNotNull(symbol);
@@ -236,7 +236,7 @@ public BarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime en
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, BarsResponse.class);
+        return alpacaClient.requestObject(request, StockBarsResponse.class);
     }
 
     /**
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
similarity index 98%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
index 8715d1fc..90159bb4 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
@@ -1,19 +1,20 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.orders;
 
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
 import com.google.gson.reflect.TypeToken;
 import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
-import net.jacobpeterson.alpaca.model.endpoint.order.CancelledOrder;
-import net.jacobpeterson.alpaca.model.endpoint.order.Order;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.CurrentOrderStatus;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderClass;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderSide;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderStatus;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderTimeInForce;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.OrderType;
+import net.jacobpeterson.alpaca.model.endpoint.orders.CancelledOrder;
+import net.jacobpeterson.alpaca.model.endpoint.orders.Order;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.CurrentOrderStatus;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderClass;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderSide;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderStatus;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderTimeInForce;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderType;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.util.format.FormatUtil;
 import net.jacobpeterson.alpaca.util.okhttp.JSONBodyBuilder;
 import okhttp3.HttpUrl;
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java
similarity index 97%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java
index 1d01c19d..722299f0 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.portfoliohistory;
 
 import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistory;
 import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistoryDataPoint;
@@ -7,6 +7,7 @@
 import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.enums.PortfolioTimeFrame;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.util.format.FormatUtil;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
similarity index 92%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
index 26d72861..cccfdfb8 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
@@ -1,12 +1,13 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.positions;
 
 import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.asset.Asset;
-import net.jacobpeterson.alpaca.model.endpoint.order.Order;
-import net.jacobpeterson.alpaca.model.endpoint.position.ClosePositionOrder;
-import net.jacobpeterson.alpaca.model.endpoint.position.Position;
+import net.jacobpeterson.alpaca.model.endpoint.assets.Asset;
+import net.jacobpeterson.alpaca.model.endpoint.orders.Order;
+import net.jacobpeterson.alpaca.model.endpoint.positions.ClosePositionOrder;
+import net.jacobpeterson.alpaca.model.endpoint.positions.Position;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/WatchlistEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
similarity index 97%
rename from src/main/java/net/jacobpeterson/alpaca/rest/endpoint/WatchlistEndpoint.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
index aeadb183..8f8762eb 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/WatchlistEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
@@ -1,11 +1,12 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
+package net.jacobpeterson.alpaca.rest.endpoint.watchlist;
 
 import com.google.gson.JsonArray;
 import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.asset.Asset;
+import net.jacobpeterson.alpaca.model.endpoint.assets.Asset;
 import net.jacobpeterson.alpaca.model.endpoint.watchlist.Watchlist;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.util.okhttp.JSONBodyBuilder;
 import okhttp3.HttpUrl;
 import okhttp3.Request;
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
index 09bf4266..0f760d7e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
@@ -1,7 +1,7 @@
 package net.jacobpeterson.alpaca.websocket.marketdata;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketMessageListener;
 
 /**
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
index 28cce551..836a452a 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -5,15 +5,14 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.bar.BarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.ErrorMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SubscriptionsMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SuccessMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.quote.QuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.trade.TradeMessage;
-import net.jacobpeterson.alpaca.model.properties.DataAPIType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.ErrorMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SuccessMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
@@ -22,6 +21,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
@@ -37,10 +37,11 @@
 /**
  * {@link MarketDataWebsocket} is an {@link AlpacaWebsocket} implementation and provides the {@link
  * MarketDataWebsocketInterface} interface for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/">Real-time
- * Market Data</a>
+ * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/">Realtime Market Data</a> for both crypto
+ * and stocks.
  */
-public class MarketDataWebsocket extends AlpacaWebsocket<MarketDataMessageType, MarketDataMessage, MarketDataListener>
+public abstract class MarketDataWebsocket
+        extends AlpacaWebsocket<MarketDataMessageType, MarketDataMessage, MarketDataListener>
         implements MarketDataWebsocketInterface {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(MarketDataWebsocket.class);
@@ -55,18 +56,17 @@ public class MarketDataWebsocket extends AlpacaWebsocket<MarketDataMessageType,
             MarketDataMessageType.BAR);
 
     /**
-     * Creates a {@link HttpUrl} for {@link MarketDataWebsocket} with the given <code>dataAPIType</code>.
+     * Creates a {@link HttpUrl} for {@link MarketDataWebsocket} with the given <code>websocketURLPathSegments</code>.
      *
-     * @param dataAPIType the {@link DataAPIType}
+     * @param websocketURLPathSegments the websocket URL path segments
      *
      * @return a {@link HttpUrl}
      */
-    private static HttpUrl createWebsocketURL(DataAPIType dataAPIType) {
+    private static HttpUrl createWebsocketURL(String websocketURLPathSegments) {
         return new HttpUrl.Builder()
                 .scheme("https") // HttpUrl.Builder doesn't recognize "wss" scheme, but "https" works fine
                 .host("stream.data.alpaca.markets")
-                .addPathSegment("v2")
-                .addPathSegment(dataAPIType.toString())
+                .addPathSegments(websocketURLPathSegments)
                 .build();
     }
 
@@ -74,23 +74,37 @@ private static HttpUrl createWebsocketURL(DataAPIType dataAPIType) {
     private final Set<String> subscribedTrades;
     private final Set<String> subscribedQuotes;
     private final Set<String> subscribedBars;
+    private final Type tradeClassType;
+    private final Type quoteClassType;
+    private final Type barClassType;
 
     /**
      * Instantiates a new {@link MarketDataWebsocket}.
      *
-     * @param okHttpClient the {@link OkHttpClient}
-     * @param dataAPIType  the {@link DataAPIType}
-     * @param keyID        the key ID
-     * @param secretKey    the secret key
+     * @param okHttpClient                the {@link OkHttpClient}
+     * @param websocketURLPathSegments    the websocket URL path segments
+     * @param websocketMarketDataTypeName the websocket market data type name {@link String}
+     * @param keyID                       the key ID
+     * @param secretKey                   the secret key
+     * @param tradeClass                  the {@link TradeMessage} {@link Class} to deserialize data into
+     * @param quoteClass                  the {@link QuoteMessage} {@link Class} to deserialize data into
+     * @param barClass                    the {@link BarMessage} {@link Class} to deserialize data into
      */
-    public MarketDataWebsocket(OkHttpClient okHttpClient, DataAPIType dataAPIType,
-            String keyID, String secretKey) {
-        super(okHttpClient, createWebsocketURL(dataAPIType), "Market Data", keyID, secretKey, null);
+    public MarketDataWebsocket(OkHttpClient okHttpClient, String websocketURLPathSegments,
+            String websocketMarketDataTypeName, String keyID, String secretKey,
+            Class<? extends TradeMessage> tradeClass, Class<? extends QuoteMessage> quoteClass,
+            Class<? extends BarMessage> barClass) {
+        super(okHttpClient, createWebsocketURL(websocketURLPathSegments), websocketMarketDataTypeName + " Market Data",
+                keyID, secretKey, null);
 
         listenedMarketDataMessageTypes = new HashSet<>();
         subscribedTrades = new HashSet<>();
         subscribedQuotes = new HashSet<>();
         subscribedBars = new HashSet<>();
+
+        this.tradeClassType = tradeClass;
+        this.quoteClassType = quoteClass;
+        this.barClassType = barClass;
     }
 
     @Override
@@ -139,6 +153,8 @@ protected void sendAuthenticationMessage() {
     // This websocket uses string frames and not binary frames.
     @Override
     public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
+        LOGGER.trace("{}", message);
+
         JsonElement messageElement = JsonParser.parseString(message);
         checkState(messageElement instanceof JsonArray, "Message must be a JsonArray! Received: %s", messageElement);
 
@@ -198,13 +214,13 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
                             subscribedBars);
                     break;
                 case TRADE:
-                    marketDataMessage = GSON.fromJson(messageObject, TradeMessage.class);
+                    marketDataMessage = GSON.fromJson(messageObject, tradeClassType);
                     break;
                 case QUOTE:
-                    marketDataMessage = GSON.fromJson(messageObject, QuoteMessage.class);
+                    marketDataMessage = GSON.fromJson(messageObject, quoteClassType);
                     break;
                 case BAR:
-                    marketDataMessage = GSON.fromJson(messageObject, BarMessage.class);
+                    marketDataMessage = GSON.fromJson(messageObject, barClassType);
                     break;
                 default:
                     LOGGER.error("Message type {} not implemented!", marketDataMessageType);
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
index 2eb52e63..5ffcaf3c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
@@ -1,10 +1,10 @@
 package net.jacobpeterson.alpaca.websocket.marketdata;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.ErrorMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SubscriptionsMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.control.SuccessMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.enums.MarketDataMessageType;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.ErrorMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SuccessMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
 
 import java.util.Collection;
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
new file mode 100644
index 00000000..fa61d1ec
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
@@ -0,0 +1,27 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.crypto;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.bar.CryptoBarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.quote.CryptoQuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.trade.CryptoTradeMessage;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.OkHttpClient;
+
+/**
+ * {@link CryptoMarketDataWebsocket} is a {@link MarketDataWebsocket} for
+ * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/">Realtime
+ * Crypto Market Data</a>
+ */
+public class CryptoMarketDataWebsocket extends MarketDataWebsocket {
+
+    /**
+     * Instantiates a new {@link CryptoMarketDataWebsocket}.
+     *
+     * @param okHttpClient the {@link OkHttpClient}
+     * @param keyID        the key ID
+     * @param secretKey    the secret key
+     */
+    public CryptoMarketDataWebsocket(OkHttpClient okHttpClient, String keyID, String secretKey) {
+        super(okHttpClient, "v1beta1/crypto", "Crypto", keyID, secretKey, CryptoTradeMessage.class,
+                CryptoQuoteMessage.class, CryptoBarMessage.class);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java
new file mode 100644
index 00000000..4b897f0d
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java
@@ -0,0 +1,30 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.stock;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.bar.StockBarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.quote.StockQuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.trade.StockTradeMessage;
+import net.jacobpeterson.alpaca.model.properties.DataAPIType;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.OkHttpClient;
+
+/**
+ * {@link StockMarketDataWebsocket} is a {@link MarketDataWebsocket} for
+ * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/">Realtime
+ * Stock Market Data</a>
+ */
+public class StockMarketDataWebsocket extends MarketDataWebsocket {
+
+    /**
+     * Instantiates a new {@link StockMarketDataWebsocket}.
+     *
+     * @param okHttpClient the {@link OkHttpClient}
+     * @param dataAPIType  the {@link DataAPIType}
+     * @param keyID        the key ID
+     * @param secretKey    the secret key
+     */
+    public StockMarketDataWebsocket(OkHttpClient okHttpClient, DataAPIType dataAPIType,
+            String keyID, String secretKey) {
+        super(okHttpClient, "v2/" + dataAPIType.toString(), "Stock", keyID, secretKey, StockTradeMessage.class,
+                StockQuoteMessage.class, StockBarMessage.class);
+    }
+}
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
index dafd1016..f9760235 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
@@ -11,14 +11,14 @@
 import net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.TradeConfirmEmail;
 import net.jacobpeterson.alpaca.model.endpoint.clock.Clock;
 import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
-import net.jacobpeterson.alpaca.model.endpoint.order.Order;
-import net.jacobpeterson.alpaca.model.endpoint.order.enums.CurrentOrderStatus;
+import net.jacobpeterson.alpaca.model.endpoint.orders.Order;
+import net.jacobpeterson.alpaca.model.endpoint.orders.enums.CurrentOrderStatus;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountActivitiesEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountConfigurationEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AccountEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.ClockEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.OrdersEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.accountactivities.AccountActivitiesEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;

From e39ce37b58f1d40ded1ad6a6fb220978469bfd40 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Wed, 22 Dec 2021 13:22:34 -0700
Subject: [PATCH 24/85] Update README, fix some market_data schema JSONs

---
 README.md                                     | 68 ++++++++++++-------
 .../common/historical/bar/bar.json            |  3 -
 .../common/realtime/bar/bar_message.json      | 10 +++
 .../realtime/bar/crypto_bar_message.json      | 10 ---
 .../marketdata/MarketDataWebsocket.java       |  2 -
 .../streaming/StreamingWebsocket.java         |  6 +-
 6 files changed, 54 insertions(+), 45 deletions(-)

diff --git a/README.md b/README.md
index ffbea854..59df6b86 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ See the [OkHttpClient Documentation](https://square.github.io/okhttp/4.x/okhttp/
 ## [`AlpacaClientException`](src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java)
 [`AlpacaClientException`](src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java) is thrown anytime an exception occurs when using various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java). It should be caught and handled within your trading algorithm application accordingly.
 
-## [`AccountEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountEndpoint.java)
+## [`AccountEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java)
 The Account API serves important information related to an account, including account status, funds available for trade, funds available for withdrawal, and various flags relevant to an account's ability to trade.
 
 Example usage:
@@ -100,14 +100,14 @@ try {
 }
 ```
 
-## [`CryptoMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/CryptoMarketDataEndpoint.java)
+## [`CryptoMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java)
 Alpaca provides cryptocurrency data from multiple venues/exchanges, namely: Coinbase, ErisX, and FTX.
 
 Example usage:
 ```java
 try {
     // Get BTCUSD 50 one-hour bars starting on 12/18/2021 from Coinbase and print them out
-    BarsResponse btcBarsResponse = alpacaAPI.cryptoMarketData().getBars(
+    CryptoBarsResponse btcBarsResponse = alpacaAPI.cryptoMarketData().getBars(
             "BTCUSD",
             Arrays.asList(Exchange.COINBASE),
             ZonedDateTime.of(2021, 12, 18, 0, 0, 0, 0, ZoneId.of("America/New_York")),
@@ -127,7 +127,7 @@ try {
 }
 ```
 
-## [`StockMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/StockMarketDataEndpoint.java)
+## [`StockMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java)
 The Data API v2 provides market data through an easy-to-use API for historical stock market data.
 
 Example usage:
@@ -135,7 +135,7 @@ Example usage:
 try {
     // Get AAPL one hour, split-adjusted bars from 7/6/2021 market open
     // to 7/8/2021 market close and print them out
-    BarsResponse aaplBarsResponse = alpacaAPI.stockMarketData().getBars(
+    StockBarsResponse aaplBarsResponse = alpacaAPI.stockMarketData().getBars(
             "AAPL",
             ZonedDateTime.of(2021, 7, 6, 9, 30, 0, 0, ZoneId.of("America/New_York")),
             ZonedDateTime.of(2021, 7, 8, 12 + 4, 0, 0, 0, ZoneId.of("America/New_York")),
@@ -147,7 +147,7 @@ try {
     aaplBarsResponse.getBars().forEach(System.out::println);
 
     // Get AAPL first 10 trades on 7/8/2021 at market open and print them out
-    TradesResponse aaplTradesResponse = alpacaAPI.stockMarketData().getTrades(
+    StockTradesResponse aaplTradesResponse = alpacaAPI.stockMarketData().getTrades(
             "AAPL",
             ZonedDateTime.of(2021, 7, 8, 9, 30, 0, 0, ZoneId.of("America/New_York")),
             ZonedDateTime.of(2021, 7, 8, 9, 31, 0, 0, ZoneId.of("America/New_York")),
@@ -169,7 +169,7 @@ try {
 }
 ```
 
-## [`OrdersEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/OrdersEndpoint.java)
+## [`OrdersEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java)
 The Orders API allows a user to monitor, place, and cancel their orders with Alpaca.
 
 Example usage:
@@ -229,7 +229,7 @@ try {
 }
 ```
 
-## [`PositionsEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PositionsEndpoint.java)
+## [`PositionsEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java)
 The Positions API provides information about an account's current open positions.
 
 Example usage:
@@ -250,7 +250,7 @@ try {
 }
 ```
 
-## [`AssetsEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AssetsEndpoint.java)
+## [`AssetsEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java)
 The Assets API serves as the master list of assets available for trade and data consumption from Alpaca.
 
 Example usage:
@@ -270,7 +270,7 @@ try {
 }
 ```
 
-## [`WatchlistEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/WatchlistEndpoint.java)
+## [`WatchlistEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java)
 The Watchlist API provides CRUD operation for the account's watchlist.
 
 Example usage:
@@ -304,7 +304,7 @@ try {
 }
 ```
 
-## [`CalendarEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/CalendarEndpoint.java)
+## [`CalendarEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java)
 The calendar API serves the full list of market days from 1970 to 2029.
 
 Example usage:
@@ -320,7 +320,7 @@ try {
 }
 ```
 
-## [`ClockEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/ClockEndpoint.java)
+## [`ClockEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java)
 The clock API serves the current market timestamp, whether the market is currently open, as well as the times of the next market open and close.
 
 Example usage:
@@ -334,7 +334,7 @@ try {
 }
 ```
 
-## [`AccountConfigurationEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountConfigurationEndpoint.java)
+## [`AccountConfigurationEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java)
 The Account Configuration API provides custom configurations about your trading account settings. These configurations control various allow you to modify settings to suit your trading needs.
 
 Example usage:
@@ -349,7 +349,7 @@ try {
 }
 ```
 
-## [`AccountActivitiesEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AccountActivitiesEndpoint.java)
+## [`AccountActivitiesEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java)
 The Account Activities API provides access to a historical record of transaction activities that have impacted your account.
 
 Example usage:
@@ -376,7 +376,7 @@ try {
 }
 ```
 
-## [`PortfolioHistoryEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/PortfolioHistoryEndpoint.java)
+## [`PortfolioHistoryEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java)
 The Portfolio History API returns the timeseries data for equity and profit loss information of the account.
 
 Example usage:
@@ -399,7 +399,7 @@ try {
 ```
 
 ## [`StreamingWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java)
-Alpaca offers WebSocket streaming of order updates.
+Alpaca offers websocket streaming of order updates.
 
 Example usage:
 ```java
@@ -433,7 +433,7 @@ Thread.sleep(5000);
 alpacaAPI.streaming().disconnect();
 ```
 
-## [`MarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java)
+## [`StockMarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java)
 Alpaca's Data API v2 provides websocket streaming for trades, quotes, and minute bars. This helps receive the most up-to-date market information that could help your trading strategy to act upon certain market movement.
 
 Example usage:
@@ -441,27 +441,27 @@ Example usage:
 // Add a 'MarketDataListener' that simply prints market data information
 MarketDataListener marketDataListener = (messageType, message) ->
         System.out.printf("%s: %s\n", messageType.name(), message);
-alpacaAPI.marketDataStreaming().setListener(marketDataListener);
+alpacaAPI.stockMarketDataStreaming().setListener(marketDataListener);
 
 // Listen to 'SubscriptionsMessage', 'SuccessMessage', and 'ErrorMessage' control messages
 // that contain information about the stream's current state. Note that these are subscribed
 // to before the websocket is connected since these messages usually are sent
-// upon websocket connection. 
-alpacaAPI.marketDataStreaming().subscribeToControl(
+// upon websocket connection.
+alpacaAPI.stockMarketDataStreaming().subscribeToControl(
         MarketDataMessageType.SUCCESS,
         MarketDataMessageType.SUBSCRIPTION,
         MarketDataMessageType.ERROR);
 
 // Connect the websocket and confirm authentication
-alpacaAPI.marketDataStreaming().connect();
-alpacaAPI.marketDataStreaming().waitForAuthorization(5, TimeUnit.SECONDS);
-if (!alpacaAPI.marketDataStreaming().isValid()) {
+alpacaAPI.stockMarketDataStreaming().connect();
+alpacaAPI.stockMarketDataStreaming().waitForAuthorization(5, TimeUnit.SECONDS);
+if (!alpacaAPI.stockMarketDataStreaming().isValid()) {
     System.out.println("Websocket not valid!");
     return;
 }
 
-// Listen to the AAPL and TSLA trades and all ('*') bars.
-alpacaAPI.marketDataStreaming().subscribe(
+// Listen to AAPL and TSLA trades and all bars via the wildcard operator ('*').
+alpacaAPI.stockMarketDataStreaming().subscribe(
         Arrays.asList("AAPL", "TSLA"),
         null,
         Arrays.asList("*"));
@@ -470,7 +470,23 @@ alpacaAPI.marketDataStreaming().subscribe(
 Thread.sleep(5000);
 
 // Manually disconnect the websocket
-alpacaAPI.marketDataStreaming().disconnect();
+alpacaAPI.stockMarketDataStreaming().disconnect();
+```
+
+## [`CryptoMarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java)
+Alpaca also offers cryptocurrency websocket streaming for trades, quotes, and minute bars.
+
+The usage is identical to the [`StockMarketDataWebsocket`](https://github.com/Petersoj/alpaca-java/#stockmarketdatawebsocket) usage except that accessing the websocket instance via the `AlpacaAPI` instance is done using: `alpacaAPI.cryptoMarketDataStreaming()` instead of `alpacaAPI.stockMarketDataStreaming()`.
+
+Example usage:
+```java
+// The 'CryptoMarketDataWebsocket' setup is identical to the 'StockMarketDataWebsocket' setup 
+
+// Listen to BTCUSD and ETHUSD trades and all bars via the wildcard operator ('*').
+alpacaAPI.cryptoMarketDataStreaming().subscribe(
+        Arrays.asList("BTCUSD", "ETHUSD"),
+        null,
+        Arrays.asList("*"));
 ```
 
 # Building
diff --git a/schema_json/endpoint/market_data/common/historical/bar/bar.json b/schema_json/endpoint/market_data/common/historical/bar/bar.json
index b0284b57..b2f3b42f 100644
--- a/schema_json/endpoint/market_data/common/historical/bar/bar.json
+++ b/schema_json/endpoint/market_data/common/historical/bar/bar.json
@@ -1,8 +1,5 @@
 {
   "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage"
-  },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "t": {
diff --git a/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json b/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
index fcf5f6c6..9df2a564 100644
--- a/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
@@ -29,6 +29,16 @@
       "existingJavaType": "java.time.ZonedDateTime",
       "javaName": "timestamp",
       "title": "The timestamp."
+    },
+    "n": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeCount",
+      "title": "Trade count."
+    },
+    "vw": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "vwap",
+      "title": "VWAP (Volume Weighted Average Price)."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json b/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
index 3ef4ac16..8886e9d4 100644
--- a/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
+++ b/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
@@ -14,16 +14,6 @@
       "existingJavaType": "java.lang.Double",
       "javaName": "volume",
       "title": "Volume."
-    },
-    "n": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "tradeCount",
-      "title": "Trade count."
-    },
-    "vw": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "vwap",
-      "title": "VWAP (Volume Weighted Average Price)."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
index 836a452a..9241f066 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -172,7 +172,6 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
             switch (marketDataMessageType) {
                 case SUCCESS:
                     marketDataMessage = GSON.fromJson(messageObject, SuccessMessage.class);
-                    LOGGER.debug("{}", marketDataMessage);
 
                     if (isSuccessMessageAuthenticated((SuccessMessage) marketDataMessage)) {
                         LOGGER.info("{} websocket authenticated.", websocketName);
@@ -202,7 +201,6 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
                     break;
                 case SUBSCRIPTION:
                     marketDataMessage = GSON.fromJson(messageObject, SubscriptionsMessage.class);
-                    LOGGER.debug("{}", marketDataMessage);
 
                     // Update 'listenedMarketDataMessageTypes' and the associated subscribed symbols lists
                     SubscriptionsMessage subscriptionsMessage = (SubscriptionsMessage) marketDataMessage;
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
index 57ede8a4..cfbf2106 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
@@ -136,7 +136,8 @@ protected void sendAuthenticationMessage() {
     // This websocket uses binary frames and not text frames.
     @Override
     public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) {
-        String message = byteString.utf8();
+        final String message = byteString.utf8();
+        LOGGER.trace("{}", message);
 
         JsonElement messageElement = JsonParser.parseString(message);
         checkState(messageElement instanceof JsonObject, "Message must be a JsonObject! Received: %s", messageElement);
@@ -160,7 +161,6 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteStri
                     LOGGER.error("{} websocket not authenticated! Received: {}.", websocketName, streamingMessage);
                 } else {
                     LOGGER.info("{} websocket authenticated.", websocketName);
-                    LOGGER.debug("{}", streamingMessage);
                 }
 
                 if (authenticationMessageFuture != null) {
@@ -169,7 +169,6 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteStri
                 break;
             case LISTENING:
                 streamingMessage = GSON.fromJson(messageObject, ListeningMessage.class);
-                LOGGER.debug("{}", streamingMessage);
 
                 // Remove all 'StreamingMessageType's that are no longer listened to and add new ones
                 List<StreamingMessageType> currentTypes = ((ListeningMessage) streamingMessage).getData().getStreams();
@@ -180,7 +179,6 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteStri
                 break;
             case TRADE_UPDATES:
                 streamingMessage = GSON.fromJson(messageObject, TradeUpdateMessage.class);
-                LOGGER.debug("{}", streamingMessage);
                 break;
             default:
                 throw new UnsupportedOperationException();

From 24065531f43cdc8d97486226b9211f67de83970c Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Wed, 22 Dec 2021 13:23:45 -0700
Subject: [PATCH 25/85] Update to 9.0.0

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 59df6b86..0b17443a 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '8.3.3'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.0.0'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>8.3.3</version>
+    <version>9.0.0</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index 88519ffb..fcbdf568 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '8.3.3-SNAPSHOT'
+version = '9.0.0-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 1da8305d39d994e8e97a3180c8fb3c9812171db8 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 27 Dec 2021 00:18:18 -0700
Subject: [PATCH 26/85] Add multi-symbol getBars method, make some inline
 instantiations static

---
 README.md                                     |  5 +-
 .../stock/historical/bar/enums/bar_feed.json  |  8 ++
 .../bar/multi_stock_bars_response.json        | 14 +++
 .../alpaca/rest/AlpacaClient.java             |  3 +
 .../rest/endpoint/assets/AssetsEndpoint.java  |  5 +-
 .../endpoint/calendar/CalendarEndpoint.java   |  5 +-
 .../stock/StockMarketDataEndpoint.java        | 89 +++++++++++++++++--
 .../rest/endpoint/orders/OrdersEndpoint.java  | 13 ++-
 .../endpoint/positions/PositionsEndpoint.java | 11 ++-
 .../endpoint/watchlist/WatchlistEndpoint.java | 10 ++-
 10 files changed, 144 insertions(+), 19 deletions(-)
 create mode 100644 schema_json/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
 create mode 100644 schema_json/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json

diff --git a/README.md b/README.md
index 0b17443a..197064ec 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,7 @@ Example usage:
 ```java
 try {
     // Get AAPL one hour, split-adjusted bars from 7/6/2021 market open
-    // to 7/8/2021 market close and print them out
+    // to 7/8/2021 market close from the SIP feed and print them out
     StockBarsResponse aaplBarsResponse = alpacaAPI.stockMarketData().getBars(
             "AAPL",
             ZonedDateTime.of(2021, 7, 6, 9, 30, 0, 0, ZoneId.of("America/New_York")),
@@ -143,7 +143,8 @@ try {
             null,
             1,
             BarTimePeriod.HOUR,
-            BarAdjustment.SPLIT);
+            BarAdjustment.SPLIT,
+            BarFeed.SIP);
     aaplBarsResponse.getBars().forEach(System.out::println);
 
     // Get AAPL first 10 trades on 7/8/2021 at market open and print them out
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_feed.json b/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
new file mode 100644
index 00000000..87d8a1a4
--- /dev/null
+++ b/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
@@ -0,0 +1,8 @@
+{
+  "type": "string",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
+  "enum": [
+    "IEX",
+    "SIP"
+  ]
+}
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json b/schema_json/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
new file mode 100644
index 00000000..65028159
--- /dev/null
+++ b/schema_json/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
@@ -0,0 +1,14 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
+  "properties": {
+    "bars": {
+      "existingJavaType": "java.util.HashMap<java.lang.String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar>>",
+      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}s of {@link java.lang.String} symbols {@link java.util.HashMap}."
+    },
+    "next_page_token": {
+      "existingJavaType": "java.lang.String",
+      "title": "Token that can be used to query the next page."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
index 12cb6a6f..7fe5cc27 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
@@ -25,6 +25,9 @@
  */
 public class AlpacaClient {
 
+    public static final Predicate<Integer> STATUS_CODE_200_OR_207 = (code) -> code == 200 || code == 207;
+    public static final Predicate<Integer> STATUS_CODE_200_OR_204 = (code) -> code == 200 || code == 204;
+
     private final OkHttpClient okHttpClient;
     private final HttpUrl baseURL;
     private final Headers requestHeaders;
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
index bfcfaffe..82763589 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
@@ -10,6 +10,7 @@
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -20,6 +21,8 @@
  */
 public class AssetsEndpoint extends AlpacaEndpoint {
 
+    private static final Type ASSET_ARRAYLIST_TYPE = new TypeToken<ArrayList<Asset>>() {}.getType();
+
     /**
      * Instantiates a new {@link AssetsEndpoint}.
      *
@@ -54,7 +57,7 @@ public List<Asset> get(AssetStatus assetStatus, AssetClass assetClass) throws Al
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, new TypeToken<ArrayList<Asset>>() {}.getType());
+        return alpacaClient.requestObject(request, ASSET_ARRAYLIST_TYPE);
     }
 
     /**
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
index 58ad41af..cc1cb5d2 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
@@ -8,6 +8,7 @@
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
+import java.lang.reflect.Type;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
@@ -18,6 +19,8 @@
  */
 public class CalendarEndpoint extends AlpacaEndpoint {
 
+    private static final Type CALENDAR_ARRAYLIST_TYPE = new TypeToken<ArrayList<Calendar>>() {}.getType();
+
     /**
      * Instantiates a new {@link CalendarEndpoint}.
      *
@@ -63,6 +66,6 @@ public List<Calendar> get(LocalDate start, LocalDate end) throws AlpacaClientExc
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, new TypeToken<ArrayList<Calendar>>() {}.getType());
+        return alpacaClient.requestObject(request, CALENDAR_ARRAYLIST_TYPE);
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
index fe8a846e..d47b167d 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
@@ -2,9 +2,11 @@
 
 import com.google.gson.reflect.TypeToken;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.MultiStockBarsResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBarsResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.enums.BarAdjustment;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.enums.BarFeed;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.LatestStockQuoteResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuotesResponse;
@@ -19,6 +21,7 @@
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
+import java.lang.reflect.Type;
 import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.HashMap;
@@ -34,6 +37,9 @@
  */
 public class StockMarketDataEndpoint extends AlpacaEndpoint {
 
+    private static final Type SNAPSHOTS_OF_STRINGS_HASHMAP_TYPE =
+            new TypeToken<HashMap<String, Snapshot>>() {}.getType();
+
     /**
      * Instantiates a new {@link StockMarketDataEndpoint}.
      *
@@ -193,19 +199,21 @@ public LatestStockQuoteResponse getLatestQuote(String symbol) throws AlpacaClien
      * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
      *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
      *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod} e.g. for <code>15Min</code> bars, you would supply {@link
+     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply {@link
      *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
      *                              <code>barTimePeriodDuration</code> parameter.
      * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is {@link
-     *                              BarAdjustment#RAW}.
+     *                              BarAdjustment#RAW}
+     * @param barFeed               defaults to {@link BarFeed#IEX} for Free users and {@link BarFeed#SIP} for users
+     *                              with an Unlimited subscription
      *
      * @return the {@link StockBarsResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
     public StockBarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
-            String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod, BarAdjustment barAdjustment)
-            throws AlpacaClientException {
+            String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod, BarAdjustment barAdjustment,
+            BarFeed barFeed) throws AlpacaClientException {
         checkNotNull(symbol);
         checkNotNull(start);
         checkNotNull(end);
@@ -233,12 +241,83 @@ public StockBarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTi
             urlBuilder.addQueryParameter("adjustment", barAdjustment.toString());
         }
 
+        if (barFeed != null) {
+            urlBuilder.addQueryParameter("feed", barFeed.toString());
+        }
+
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
         return alpacaClient.requestObject(request, StockBarsResponse.class);
     }
 
+    /**
+     * Gets {@link StockBar} aggregate historical data for the requested securities.
+     *
+     * @param symbols               a {@link Collection} of symbols to query for
+     * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
+     *                              not accepted.
+     * @param end                   filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are
+     *                              not accepted.
+     * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
+     *                              <code>null</code> is given
+     * @param pageToken             pagination token to continue from
+     * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
+     *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
+     *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
+     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply {@link
+     *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
+     *                              <code>barTimePeriodDuration</code> parameter.
+     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is {@link
+     *                              BarAdjustment#RAW}
+     * @param barFeed               defaults to {@link BarFeed#IEX} for Free users and {@link BarFeed#SIP} for users
+     *                              with an Unlimited subscription
+     *
+     * @return the {@link MultiStockBarsResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public MultiStockBarsResponse getBars(Collection<String> symbols, ZonedDateTime start, ZonedDateTime end,
+            Integer limit, String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod,
+            BarAdjustment barAdjustment, BarFeed barFeed) throws AlpacaClientException {
+        checkNotNull(symbols);
+        checkArgument(!symbols.isEmpty(), "'symbols' cannot be empty!");
+        checkNotNull(start);
+        checkNotNull(end);
+        checkNotNull(barTimePeriod);
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment("bars");
+
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
+        urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
+        urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
+
+        if (limit != null) {
+            urlBuilder.addQueryParameter("limit", limit.toString());
+        }
+
+        if (pageToken != null) {
+            urlBuilder.addQueryParameter("page_token", pageToken);
+        }
+
+        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
+
+        if (barAdjustment != null) {
+            urlBuilder.addQueryParameter("adjustment", barAdjustment.toString());
+        }
+
+        if (barFeed != null) {
+            urlBuilder.addQueryParameter("feed", barFeed.toString());
+        }
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, MultiStockBarsResponse.class);
+    }
+
     /**
      * Gets {@link Snapshot}s of the requested securities.
      *
@@ -261,7 +340,7 @@ public Map<String, Snapshot> getSnapshots(Collection<String> symbols) throws Alp
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, new TypeToken<HashMap<String, Snapshot>>() {}.getType());
+        return alpacaClient.requestObject(request, SNAPSHOTS_OF_STRINGS_HASHMAP_TYPE);
     }
 
     /**
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
index 90159bb4..c177db71 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
@@ -20,6 +20,7 @@
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
+import java.lang.reflect.Type;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
@@ -28,12 +29,17 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_204;
+import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_207;
 
 /**
  * {@link AlpacaEndpoint} for <a href="https://alpaca.markets/docs/api-documentation/api-v2/orders/">Orders</a>.
  */
 public class OrdersEndpoint extends AlpacaEndpoint {
 
+    private static final Type ORDER_ARRAYLIST_TYPE = new TypeToken<ArrayList<Order>>() {}.getType();
+    private static final Type CANCELLED_ORDER_ARRAYLIST_TYPE = new TypeToken<ArrayList<CancelledOrder>>() {}.getType();
+
     /**
      * Instantiates a new {@link OrdersEndpoint}.
      *
@@ -97,7 +103,7 @@ public List<Order> get(CurrentOrderStatus status, Integer limit, ZonedDateTime a
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, new TypeToken<ArrayList<Order>>() {}.getType());
+        return alpacaClient.requestObject(request, ORDER_ARRAYLIST_TYPE);
     }
 
     /**
@@ -615,8 +621,7 @@ public List<CancelledOrder> cancelAll() throws AlpacaClientException {
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .delete()
                 .build();
-        return alpacaClient.requestObject(request, (code) -> code == 200 || code == 207,
-                new TypeToken<ArrayList<CancelledOrder>>() {}.getType());
+        return alpacaClient.requestObject(request, STATUS_CODE_200_OR_207, CANCELLED_ORDER_ARRAYLIST_TYPE);
     }
 
     /**
@@ -637,6 +642,6 @@ public void cancel(String orderID) throws AlpacaClientException {
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .delete()
                 .build();
-        alpacaClient.requestVoid(request, (code) -> code == 200 || code == 204);
+        alpacaClient.requestVoid(request, STATUS_CODE_200_OR_204);
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
index cccfdfb8..4ea3b096 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
@@ -11,16 +11,22 @@
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_207;
 
 /**
  * {@link AlpacaEndpoint} for <a href="https://alpaca.markets/docs/api-documentation/api-v2/positions/">Positions</a>.
  */
 public class PositionsEndpoint extends AlpacaEndpoint {
 
+    private static final Type POSITION_ARRAYLIST_TYPE = new TypeToken<ArrayList<Position>>() {}.getType();
+    private static final Type CLOSED_POSITION_ORDER_ARRAYLIST_TYPE =
+            new TypeToken<ArrayList<ClosePositionOrder>>() {}.getType();
+
     /**
      * Instantiates a new {@link PositionsEndpoint}.
      *
@@ -43,7 +49,7 @@ public List<Position> get() throws AlpacaClientException {
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, new TypeToken<ArrayList<Position>>() {}.getType());
+        return alpacaClient.requestObject(request, POSITION_ARRAYLIST_TYPE);
     }
 
     /**
@@ -89,8 +95,7 @@ public List<ClosePositionOrder> closeAll(Boolean cancelOrders) throws AlpacaClie
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .delete()
                 .build();
-        return alpacaClient.requestObject(request, (code) -> code == 200 || code == 207,
-                new TypeToken<ArrayList<ClosePositionOrder>>() {}.getType());
+        return alpacaClient.requestObject(request, STATUS_CODE_200_OR_207, CLOSED_POSITION_ORDER_ARRAYLIST_TYPE);
     }
 
     /**
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
index 8f8762eb..605d651b 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
@@ -11,12 +11,14 @@
 import okhttp3.HttpUrl;
 import okhttp3.Request;
 
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_204;
 
 /**
  * {@link AlpacaEndpoint} for <a href="https://docs.alpaca.markets/api-documentation/api-v2/watchlist/">Watchlists</a>
@@ -24,6 +26,8 @@
  */
 public class WatchlistEndpoint extends AlpacaEndpoint {
 
+    private static final Type WATCHLIST_ARRAYLIST_TYPE = new TypeToken<ArrayList<Watchlist>>() {}.getType();
+
     /**
      * Instantiates a new {@link WatchlistEndpoint}.
      *
@@ -46,7 +50,7 @@ public List<Watchlist> get() throws AlpacaClientException {
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, new TypeToken<ArrayList<Watchlist>>() {}.getType());
+        return alpacaClient.requestObject(request, WATCHLIST_ARRAYLIST_TYPE);
     }
 
     /**
@@ -183,7 +187,7 @@ public void delete(String watchlistID) throws AlpacaClientException {
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .delete()
                 .build();
-        alpacaClient.requestVoid(request, (code) -> code == 200 || code == 204);
+        alpacaClient.requestVoid(request, STATUS_CODE_200_OR_204);
     }
 
     /**
@@ -207,6 +211,6 @@ public Watchlist removeSymbol(String watchlistID, String symbol) throws AlpacaCl
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .delete()
                 .build();
-        return alpacaClient.requestObject(request, (code) -> code == 200 || code == 204, Watchlist.class);
+        return alpacaClient.requestObject(request, STATUS_CODE_200_OR_204, Watchlist.class);
     }
 }

From d3acf99dd783f2d4c2c34bcc354e83bad7fd5696 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 27 Dec 2021 00:18:57 -0700
Subject: [PATCH 27/85] Update to 9.1.0

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 197064ec..e1a3d562 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.0.0'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.0'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.0.0</version>
+    <version>9.1.0</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index fcbdf568..e9552238 100644
--- a/build.gradle
+++ b/build.gradle
@@ -30,7 +30,7 @@ tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'js
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '9.0.0-SNAPSHOT'
+version = '9.1.0-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 0e15c393deef5e87198b223355324464510b1714 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Thu, 10 Mar 2022 22:13:39 -0700
Subject: [PATCH 28/85] Update SLF4j version, generateBuilders in POJOs, update
 a Javadoc

---
 README.md                                            | 12 ++++++------
 build.gradle                                         |  7 +++++--
 .../stock/historical/bar/stock_bars_response.json    |  2 +-
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index e1a3d562..396ee4cc 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
     <a href="https://javadoc.io/doc/net.jacobpeterson/alpaca-java" target="_blank"><img src="https://javadoc.io/badge/net.jacobpeterson/alpaca-java.svg" alt="Javadocs"></a>
     <a href="https://travis-ci.com/github/Petersoj/alpaca-java" target="_blank"><img src="https://travis-ci.com/Petersoj/alpaca-java.svg?branch=master" alt="Build Status"></a>
     <a href="https://codecov.io/gh/petersoj/alpaca-java"><img src="https://codecov.io/gh/petersoj/alpaca-java/branch/master/graph/badge.svg" alt="CodeCov badge"/></a>
-    <a href="https://opensource.org/licenses/MIT" target="_blank"><img alt="GitHub" src="https://img.shields.io/github/license/petersoj/alpaca-java"></a>    
+    <a href="https://opensource.org/licenses/MIT" target="_blank"><img alt="GitHub" src="https://img.shields.io/github/license/petersoj/alpaca-java"></a>
 </p>
 
 # Overview
@@ -51,7 +51,7 @@ For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an
 Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson/alpaca-java) for all classes and method signatures.
 
 ## [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java)
-The [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) class contains several instances of various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java) and [`AlpacaWebsockets`](src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java) to interface with Alpaca. You will generally only need one instance of this class in your application. Note that many methods inside the various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java) allow `null` to be passed in as a parameter if it is optional. 
+The [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) class contains several instances of various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java) and [`AlpacaWebsockets`](src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java) to interface with Alpaca. You will generally only need one instance of this class in your application. Note that many methods inside the various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java) allow `null` to be passed in as a parameter if it is optional.
 
 The Alpaca API specification is located [here](https://docs.alpaca.markets/api-documentation/api-v2/) and the [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) Javadoc is located [here](https://javadoc.io/doc/net.jacobpeterson/alpaca-java/latest/net/jacobpeterson/alpaca/AlpacaAPI.html).
 
@@ -409,10 +409,10 @@ StreamingListener streamingListener = (messageType, message) ->
         System.out.printf("%s: %s\n", messageType.name(), message);
 alpacaAPI.streaming().setListener(streamingListener);
 
-// Listen 'AuthorizationMessage' and 'ListeningMessage' messages that contain 
+// Listen 'AuthorizationMessage' and 'ListeningMessage' messages that contain
 // information about the stream's current state. Note that these are subscribed
 // to before the websocket is connected since these messages usually are sent
-// upon websocket connection. 
+// upon websocket connection.
 alpacaAPI.streaming().streams(StreamingMessageType.AUTHORIZATION,
         StreamingMessageType.LISTENING);
 
@@ -481,7 +481,7 @@ The usage is identical to the [`StockMarketDataWebsocket`](https://github.com/Pe
 
 Example usage:
 ```java
-// The 'CryptoMarketDataWebsocket' setup is identical to the 'StockMarketDataWebsocket' setup 
+// The 'CryptoMarketDataWebsocket' setup is identical to the 'StockMarketDataWebsocket' setup
 
 // Listen to BTCUSD and ETHUSD trades and all bars via the wildcard operator ('*').
 alpacaAPI.cryptoMarketDataStreaming().subscribe(
@@ -523,4 +523,4 @@ Note that the live tests will modify your account minimally. It's meant to test
 # Contributing
 Contributions are welcome!
 
-If you are creating a Pull Request, be sure to create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork. 
+If you are creating a Pull Request, be sure to create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork.
diff --git a/build.gradle b/build.gradle
index e9552238..bfd19fbd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,7 +26,6 @@ plugins {
 }
 
 apply plugin: 'jsonschema2pojo'
-tasks.getByName("generateJsonSchema2Pojo").enabled(false) // Disable default 'jsonSchema2Pojo' task
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
@@ -38,7 +37,7 @@ repositories {
 
 dependencies {
     // Logging framework
-    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.31'
+    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32'
     // This is a bridge for the Apache Jakarta Commons Logging library used in dependencies to actually use SLF4j instead
     implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.31'
 
@@ -178,6 +177,9 @@ processResources {
 // START POJO generation
 //
 
+// Disable default 'jsonSchema2Pojo' task
+tasks.getByName("generateJsonSchema2Pojo").enabled(false)
+
 // The generated POJOs will be in a package structure analogous to the path in the 'schema_json/' directory
 // See: https://github.com/joelittlejohn/jsonschema2pojo/wiki/Reference
 
@@ -240,6 +242,7 @@ jsonSchema2Pojo {
     includeGetters = true
     includeSetters = true
     includeCopyConstructor = true
+    generateBuilders = true
 }
 
 //
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json b/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
index b23db483..5add43d2 100644
--- a/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
+++ b/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
@@ -4,7 +4,7 @@
   "properties": {
     "bars": {
       "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}s."
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}s. This may be <code>null</code> if no bars exist."
     },
     "symbol": {
       "existingJavaType": "java.lang.String",

From 04545575ea122a1b5ace2077b02a94e9d1664b98 Mon Sep 17 00:00:00 2001
From: jessethouin <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>
Date: Thu, 21 Apr 2022 16:18:38 -0400
Subject: [PATCH 29/85] Updated requestLimitOrder to take Double type quantity
 for fractional limit orders.

---
 .../alpaca/rest/endpoint/orders/OrdersEndpoint.java           | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
index c177db71..3212bf75 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
@@ -285,9 +285,9 @@ public Order requestNotionalMarketOrder(String symbol, Double notional, OrderSid
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public Order requestLimitOrder(String symbol, Integer quantity, OrderSide side, OrderTimeInForce timeInForce,
+    public Order requestLimitOrder(String symbol, Double quantity, OrderSide side, OrderTimeInForce timeInForce,
             Double limitPrice, Boolean extendedHours) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.LIMIT, timeInForce, limitPrice,
+        return requestOrder(symbol, quantity, null, side, OrderType.LIMIT, timeInForce, limitPrice,
                 null, null, null, extendedHours, null, OrderClass.SIMPLE, null, null, null);
     }
 

From aac872e095e3af3deafe3cdd46043ca5ba2c3cb1 Mon Sep 17 00:00:00 2001
From: Dario Arena <dario.arena87@gmail.com>
Date: Fri, 3 Jun 2022 13:24:38 +0200
Subject: [PATCH 30/85] #121 - use double type quantity in
 PositionsEndpoint.close()

---
 .../alpaca/rest/endpoint/positions/PositionsEndpoint.java       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
index 4ea3b096..fc8eda81 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
@@ -113,7 +113,7 @@ public List<ClosePositionOrder> closeAll(Boolean cancelOrders) throws AlpacaClie
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public Order close(String symbolOrAssetID, Integer quantity, Double percentage) throws AlpacaClientException {
+    public Order close(String symbolOrAssetID, Double quantity, Double percentage) throws AlpacaClientException {
         checkNotNull(symbolOrAssetID);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()

From 43206bdc629be8a33bd28b4dc967d0f0ff63fa08 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 20 Jun 2022 09:37:36 -0600
Subject: [PATCH 31/85] Update dependency versions

---
 build.gradle | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/build.gradle b/build.gradle
index bfd19fbd..80c4aa13 100644
--- a/build.gradle
+++ b/build.gradle
@@ -37,22 +37,22 @@ repositories {
 
 dependencies {
     // Logging framework
-    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32'
+    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36'
     // This is a bridge for the Apache Jakarta Commons Logging library used in dependencies to actually use SLF4j instead
-    implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.31'
+    implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.36'
 
     // Guava and GSON
-    implementation group: 'com.google.guava', name: 'guava', version: '30.1.1-jre'
-    implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.7'
+    implementation group: 'com.google.guava', name: 'guava', version: '31.1-jre'
+    implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
 
     // OkHttp
-    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.9.1'
+    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.10.0'
     implementation group: 'com.github.devcsrj', name: 'slf4j-okhttp3-logging-interceptor', version: '1.0.1'
 
     // Unit test dependencies
-    testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.30'
-    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.7.0'
-    testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.7.0'
+    testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.36'
+    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.8.2'
+    testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.6.1'
 }
 
 // Exclude any SLF4j-implementation transitive dependencies so users can use a logging framework of their choice

From 4b9970ed0d50df1e18c0980dcf44d3f8797fdc00 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 9 Aug 2022 18:09:04 -0600
Subject: [PATCH 32/85] Update to 9.1.1

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 396ee4cc..e3f98186 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.0'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.1'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.1.0</version>
+    <version>9.1.1</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index 80c4aa13..692c91f3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ apply plugin: 'jsonschema2pojo'
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '9.1.0-SNAPSHOT'
+version = '9.1.1-SNAPSHOT'
 
 repositories {
     mavenCentral()

From f058227f9d6da94c0c70ddfc9d348d8ee76c004c Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 16 Aug 2022 22:47:36 -0600
Subject: [PATCH 33/85] Fix FTX crypto exchange enum name

---
 .../endpoint/market_data/crypto/common/enums/exchange.json      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/schema_json/endpoint/market_data/crypto/common/enums/exchange.json b/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
index ff9d94e5..b17a92f8 100644
--- a/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
+++ b/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
@@ -3,7 +3,7 @@
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/\">Crypto Data</a>.",
   "enum": [
     "ERSX",
-    "FTX",
+    "FTXU",
     "CBSE"
   ],
   "javaEnums": [

From becccd909f93a1bc58ca6326feae5e7bb56fe629 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 16 Aug 2022 22:48:12 -0600
Subject: [PATCH 34/85] Update to 9.1.2

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index e3f98186..057e7f0e 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.1'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.2'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.1.1</version>
+    <version>9.1.2</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index 692c91f3..bacd0079 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ apply plugin: 'jsonschema2pojo'
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '9.1.1-SNAPSHOT'
+version = '9.1.2-SNAPSHOT'
 
 repositories {
     mavenCentral()

From aad1af49b522ebe0d683a0e8cbc8f65140b06755 Mon Sep 17 00:00:00 2001
From: Kurru <thekurru@gmail.com>
Date: Fri, 9 Dec 2022 12:10:46 -0800
Subject: [PATCH 35/85] Add nullable annotations on various fields/methods in
 AlpacaClientException

---
 .../alpaca/rest/AlpacaClientException.java           | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
index 2114b1e8..8e34795c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
@@ -3,6 +3,9 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
+
+import javax.annotation.Nullable;
+
 import okhttp3.Response;
 import okhttp3.ResponseBody;
 
@@ -11,10 +14,15 @@ public class AlpacaClientException extends Exception {
     private static final String CODE_KEY = "code";
     private static final String MESSAGE_KEY = "message";
 
+    @Nullable
     private String responseBody;
+    @Nullable
     private Integer responseStatusCode;
+    @Nullable
     private String responseStatusMessage;
+    @Nullable
     private Integer apiResponseCode;
+    @Nullable
     private String apiResponseMessage;
 
     /**
@@ -120,18 +128,22 @@ private String parseAPIErrorResponse() {
         return messageBuilder.toString();
     }
 
+    @Nullable
     public Integer getResponseStatusCode() {
         return responseStatusCode;
     }
 
+    @Nullable
     public String getResponseStatusMessage() {
         return responseStatusMessage;
     }
 
+    @Nullable
     public Integer getAPIResponseCode() {
         return apiResponseCode;
     }
 
+    @Nullable
     public String getAPIResponseMessage() {
         return apiResponseMessage;
     }

From 00c3bea9e44de3eb4cb687eb989add1f5191b95a Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 13 Dec 2022 20:27:31 -0700
Subject: [PATCH 36/85] Remove redundant 'Nullable' annotation from private
 fields

---
 .../alpaca/rest/AlpacaClientException.java             | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
index 8e34795c..4c902b05 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
@@ -3,26 +3,20 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
-
-import javax.annotation.Nullable;
-
 import okhttp3.Response;
 import okhttp3.ResponseBody;
 
+import javax.annotation.Nullable;
+
 public class AlpacaClientException extends Exception {
 
     private static final String CODE_KEY = "code";
     private static final String MESSAGE_KEY = "message";
 
-    @Nullable
     private String responseBody;
-    @Nullable
     private Integer responseStatusCode;
-    @Nullable
     private String responseStatusMessage;
-    @Nullable
     private Integer apiResponseCode;
-    @Nullable
     private String apiResponseMessage;
 
     /**

From 32e6f149f9ba212ea207edda415aab17b6263204 Mon Sep 17 00:00:00 2001
From: Doug Graham <dsgraham@gmail.com>
Date: Wed, 15 Mar 2023 08:28:19 -0500
Subject: [PATCH 37/85] This should fix Issue #131

---
 schema_json/endpoint/positions/position.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/schema_json/endpoint/positions/position.json b/schema_json/endpoint/positions/position.json
index 7ebb619f..1217ef51 100644
--- a/schema_json/endpoint/positions/position.json
+++ b/schema_json/endpoint/positions/position.json
@@ -28,6 +28,11 @@
       "javaName": "quantity",
       "title": "The number of shares."
     },
+    "qty_available": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "quantity",
+      "title": "Total number of shares available minus open orders."
+    },
     "side": {
       "existingJavaType": "java.lang.String",
       "title": "\"long\"."

From b171b097f2117f1cfbc71ba9f9725cdca1f8ba67 Mon Sep 17 00:00:00 2001
From: Doug Graham <dsgraham@gmail.com>
Date: Wed, 15 Mar 2023 08:32:32 -0500
Subject: [PATCH 38/85] Needs a unique POJO name

---
 schema_json/endpoint/positions/position.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/schema_json/endpoint/positions/position.json b/schema_json/endpoint/positions/position.json
index 1217ef51..8ae71b33 100644
--- a/schema_json/endpoint/positions/position.json
+++ b/schema_json/endpoint/positions/position.json
@@ -30,7 +30,7 @@
     },
     "qty_available": {
       "existingJavaType": "java.lang.String",
-      "javaName": "quantity",
+      "javaName": "quantityAvailable",
       "title": "Total number of shares available minus open orders."
     },
     "side": {

From c23c6e7d05416f7f022d8f144872e2aeee89491e Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 20 Mar 2023 09:13:29 -0600
Subject: [PATCH 39/85] Update to 9.1.3

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 057e7f0e..98b9acf0 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.2'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.3'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.1.2</version>
+    <version>9.1.3</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index bacd0079..c7ec09bf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ apply plugin: 'jsonschema2pojo'
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '9.1.2-SNAPSHOT'
+version = '9.1.3-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 60f2deb5a5d9a7dea89bdbec091cedd9c94f677a Mon Sep 17 00:00:00 2001
From: Balaji Kandavel <balajikandavel@gmail.com>
Date: Wed, 10 May 2023 23:09:49 -0400
Subject: [PATCH 40/85] Add week and month timeframes

https://alpaca.markets/docs/api-references/market-data-api/stock-pricing-data/historical/#bars
---
 .../common/historical/bar/enums/bar_time_period.json   | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json b/schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
index cccf0508..0305b24e 100644
--- a/schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
+++ b/schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
@@ -4,7 +4,9 @@
   "enum": [
     "Min",
     "Hour",
-    "Day"
+    "Day",
+    "Week",
+    "Month"
   ],
   "javaEnums": [
     {
@@ -15,6 +17,12 @@
     },
     {
       "name": "DAY"
+    },
+    {
+      "name": "WEEK"
+    },
+    {
+      "name": "MONTH"
     }
   ]
 }

From a5638b3800e495866a7fde5d8a656bde2017712b Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 26 May 2023 08:55:11 -0600
Subject: [PATCH 41/85] Update to 9.1.4

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 98b9acf0..a29ffc56 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.3'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.4'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.1.3</version>
+    <version>9.1.4</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index c7ec09bf..588393d3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ apply plugin: 'jsonschema2pojo'
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '9.1.3-SNAPSHOT'
+version = '9.1.4-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 1403348b3b74fe87c2769af2f187588631edda27 Mon Sep 17 00:00:00 2001
From: Jesse Thouin <jthouin@amazon.com>
Date: Fri, 7 Jul 2023 15:26:25 -0400
Subject: [PATCH 42/85] Updated to use v1beta3 version of Alpaca APIs.

---
 .../crypto/common/enums/exchange.json         |  20 ----
 .../crypto/historical/bar/crypto_bar.json     |   7 +-
 .../historical/bar/crypto_bars_response.json  |   8 +-
 .../crypto/historical/quote/crypto_quote.json |   7 +-
 .../quote/crypto_quotes_response.json         |   8 +-
 .../quote/latest_crypto_quote_response.json   |   2 +-
 .../crypto/historical/trade/crypto_trade.json |   7 +-
 .../trade/crypto_trades_response.json         |   8 +-
 .../trade/latest_crypto_trade_response.json   |   2 +-
 .../crypto/historical/xbbo/xbbo.json          |  41 -------
 .../crypto/historical/xbbo/xbbo_response.json |  14 ---
 .../realtime/bar/crypto_bar_message.json      |   5 -
 .../realtime/quote/crypto_quote_message.json  |   5 -
 .../realtime/trade/crypto_trade_message.json  |   5 -
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |   4 +-
 .../crypto/CryptoMarketDataEndpoint.java      | 107 +++++-------------
 .../crypto/CryptoMarketDataWebsocket.java     |   4 +-
 17 files changed, 42 insertions(+), 212 deletions(-)
 delete mode 100644 schema_json/endpoint/market_data/crypto/common/enums/exchange.json
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json

diff --git a/schema_json/endpoint/market_data/crypto/common/enums/exchange.json b/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
deleted file mode 100644
index b17a92f8..00000000
--- a/schema_json/endpoint/market_data/crypto/common/enums/exchange.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/\">Crypto Data</a>.",
-  "enum": [
-    "ERSX",
-    "FTXU",
-    "CBSE"
-  ],
-  "javaEnums": [
-    {
-      "name": "ERISX"
-    },
-    {
-      "name": "FTX"
-    },
-    {
-      "name": "COINBASE"
-    }
-  ]
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
index e23c3ec6..1fbf8b5d 100644
--- a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
@@ -3,13 +3,8 @@
   "extends": {
     "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.Bar"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange."
-    },
     "v": {
       "existingJavaType": "java.lang.Double",
       "javaName": "volume",
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
index 677e6633..8461d5cc 100644
--- a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
@@ -1,15 +1,11 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "bars": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar>",
+      "existingJavaType": "java.util.Map<String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar>>",
       "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar}s."
     },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
     "next_page_token": {
       "existingJavaType": "java.lang.String",
       "title": "Token that can be used to query the next page."
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
index 099df001..97e50e0b 100644
--- a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
@@ -3,13 +3,8 @@
   "extends": {
     "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.quote.Quote"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange."
-    },
     "as": {
       "existingJavaType": "java.lang.Double",
       "javaName": "askSize",
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
index e8a67bfd..a5010f8c 100644
--- a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
@@ -1,15 +1,11 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "quotes": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote>",
+      "existingJavaType": "java.util.Map<String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote>>",
       "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}s."
     },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
     "next_page_token": {
       "existingJavaType": "java.lang.String",
       "title": "Token that can be used to query the next page."
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
index 64bf1d75..6db62c25 100644
--- a/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
@@ -1,6 +1,6 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
index 0b0c1b53..8e5bf0da 100644
--- a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
@@ -3,13 +3,8 @@
   "extends": {
     "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.trade.Trade"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange where the trade happened."
-    },
     "s": {
       "existingJavaType": "java.lang.Double",
       "javaName": "size",
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
index a512c775..0a007487 100644
--- a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
@@ -1,15 +1,11 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "trades": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade>",
+      "existingJavaType": "java.util.Map<String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade>>",
       "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}s."
     },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
     "next_page_token": {
       "existingJavaType": "java.lang.String",
       "title": "Token that can be used to query the next page."
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
index 512c6a18..b49e4ef7 100644
--- a/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
@@ -1,6 +1,6 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
   "properties": {
     "symbol": {
       "existingJavaType": "java.lang.String",
diff --git a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
deleted file mode 100644
index d279722f..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "ax": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "askExchange",
-      "title": "Ask Exchange."
-    },
-    "ap": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askPrice",
-      "title": "Ask price."
-    },
-    "as": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askSize",
-      "title": "Ask size."
-    },
-    "bx": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "bidExchange",
-      "title": "Bid Exchange."
-    },
-    "bp": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidPrice",
-      "title": "Bid price."
-    },
-    "bs": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidSize",
-      "title": "Bid size."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json b/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
deleted file mode 100644
index 2fdc8f28..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/xbbo/xbbo_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "xbbo": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.Xbbo",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.Xbbo}."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json b/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
index 8886e9d4..82cfe47f 100644
--- a/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
+++ b/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
@@ -5,11 +5,6 @@
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
   "properties": {
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange."
-    },
     "v": {
       "existingJavaType": "java.lang.Double",
       "javaName": "volume",
diff --git a/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json b/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
index 9d9a2618..1eac99c5 100644
--- a/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
+++ b/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
@@ -5,11 +5,6 @@
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
   "properties": {
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange."
-    },
     "as": {
       "existingJavaType": "java.lang.Double",
       "javaName": "askSize",
diff --git a/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json b/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
index ec4f5227..09ee5a87 100644
--- a/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
+++ b/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
@@ -5,11 +5,6 @@
   },
   "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
   "properties": {
-    "x": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange",
-      "javaName": "exchange",
-      "title": "Exchange where the trade happened."
-    },
     "s": {
       "existingJavaType": "java.lang.Double",
       "javaName": "size",
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 63980318..c71f496b 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -44,7 +44,7 @@ public class AlpacaAPI {
     private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPI.class);
 
     private static final String VERSION_2_PATH_SEGMENT = "v2";
-    private static final String VERSION_1_BETA_1_PATH_SEGMENT = "v1beta1";
+    private static final String VERSION_1_BETA_3_PATH_SEGMENT = "v1beta3";
 
     private final OkHttpClient okHttpClient;
     private final AlpacaClient brokerClient;
@@ -160,7 +160,7 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         if (oAuthToken == null) {
             brokerClient = new AlpacaClient(okHttpClient, keyID, secretKey,
                     brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
-            cryptoDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_1_PATH_SEGMENT);
+            cryptoDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_3_PATH_SEGMENT);
             stockDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_2_PATH_SEGMENT);
         } else {
             brokerClient = new AlpacaClient(okHttpClient, oAuthToken, brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
index b3136b17..0062d3fe 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
@@ -1,7 +1,6 @@
 package net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto;
 
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.Exchange;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBarsResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote;
@@ -10,7 +9,6 @@
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTradesResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestCryptoTradeResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.xbbo.XbboResponse;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
 import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
@@ -26,10 +24,11 @@
 
 /**
  * {@link AlpacaEndpoint} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/historical/">Historical
+ * <a href="https://docs.alpaca.markets/docs/crypto-pricing-data">Historical
  * Crypto Market Data API</a>.
  */
 public class CryptoMarketDataEndpoint extends AlpacaEndpoint {
+    public static final String LOCALE = "us";
 
     /**
      * Instantiates a new {@link CryptoMarketDataEndpoint}.
@@ -43,8 +42,7 @@ public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
     /**
      * Gets {@link CryptoTrade} historical data for the requested crypto symbol.
      *
-     * @param symbol    the symbol to query for
-     * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all exchanges.
+     * @param symbols   a {@link Collection} of symbols to query for
      * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
      *                  accepted. <code>null</code> for the current day in Central Time.
      * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
@@ -57,20 +55,16 @@ public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public CryptoTradesResponse getTrades(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
+    public CryptoTradesResponse getTrades(Collection<String> symbols, ZonedDateTime start,
             ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
-        checkNotNull(symbol);
+        checkNotNull(symbols);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
+                .addPathSegment(LOCALE)
                 .addPathSegment("trades");
 
-        if (exchanges != null) {
-            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
-                    .map(Exchange::value)
-                    .collect(Collectors.joining(",")));
-        }
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         if (start != null) {
             urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
@@ -97,24 +91,22 @@ public CryptoTradesResponse getTrades(String symbol, Collection<Exchange> exchan
     /**
      * Gets the latest {@link CryptoTrade} for the requested security.
      *
-     * @param symbol   the symbol to query for
-     * @param exchange the {@link Exchange} to filter the latest {@link CryptoTrade} by.
+     * @param symbols   a {@link Collection} of symbols to query for
      *
      * @return the {@link LatestCryptoTradeResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestCryptoTradeResponse getLatestTrade(String symbol, Exchange exchange) throws AlpacaClientException {
-        checkNotNull(symbol);
-        checkNotNull(exchange);
+    public LatestCryptoTradeResponse getLatestTrade(Collection<String> symbols) throws AlpacaClientException {
+        checkNotNull(symbols);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
+                .addPathSegment(LOCALE)
                 .addPathSegment("trades")
                 .addPathSegment("latest");
 
-        urlBuilder.addQueryParameter("exchange", exchange.toString());
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
@@ -125,8 +117,7 @@ public LatestCryptoTradeResponse getLatestTrade(String symbol, Exchange exchange
     /**
      * Gets {@link CryptoQuote} historical data for the requested crypto symbol.
      *
-     * @param symbol    the symbol to query for
-     * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all exchanges.
+     * @param symbols   a {@link Collection} of symbols to query for
      * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
      *                  accepted. <code>null</code> for the current day in Central Time.
      * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
@@ -139,20 +130,16 @@ public LatestCryptoTradeResponse getLatestTrade(String symbol, Exchange exchange
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public CryptoQuotesResponse getQuotes(String symbol, Collection<Exchange> exchanges, ZonedDateTime start,
+    public CryptoQuotesResponse getQuotes(Collection<String> symbols, ZonedDateTime start,
             ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
-        checkNotNull(symbol);
+        checkNotNull(symbols);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
+                .addPathSegment(LOCALE)
                 .addPathSegment("quotes");
 
-        if (exchanges != null) {
-            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
-                    .map(Exchange::value)
-                    .collect(Collectors.joining(",")));
-        }
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         if (start != null) {
             urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
@@ -179,24 +166,22 @@ public CryptoQuotesResponse getQuotes(String symbol, Collection<Exchange> exchan
     /**
      * Gets the latest {@link CryptoQuote} for the requested security.
      *
-     * @param symbol   the symbol to query for
-     * @param exchange the {@link Exchange} to filter the latest {@link CryptoQuote} by.
+     * @param symbols   a {@link Collection} of symbols to query for
      *
      * @return the {@link LatestCryptoQuoteResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestCryptoQuoteResponse getLatestQuote(String symbol, Exchange exchange) throws AlpacaClientException {
-        checkNotNull(symbol);
-        checkNotNull(exchange);
+    public LatestCryptoQuoteResponse getLatestQuote(Collection<String> symbols) throws AlpacaClientException {
+        checkNotNull(symbols);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
+                .addPathSegment(LOCALE)
                 .addPathSegment("quotes")
                 .addPathSegment("latest");
 
-        urlBuilder.addQueryParameter("exchange", exchange.toString());
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
@@ -207,9 +192,7 @@ public LatestCryptoQuoteResponse getLatestQuote(String symbol, Exchange exchange
     /**
      * Gets {@link CryptoBar} aggregate historical data for the requested crypto.
      *
-     * @param symbol                the symbol to query for
-     * @param exchanges             a {@link Collection} of {@link Exchange}s to filter by. <code>null</code> for all
-     *                              exchanges.
+     * @param symbols               a {@link Collection} of symbols to query for
      * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
      *                              not accepted. <code>null</code> for now.
      * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
@@ -226,21 +209,17 @@ public LatestCryptoQuoteResponse getLatestQuote(String symbol, Exchange exchange
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public CryptoBarsResponse getBars(String symbol, Collection<Exchange> exchanges, ZonedDateTime start, Integer limit,
+    public CryptoBarsResponse getBars(Collection<String> symbols, ZonedDateTime start, Integer limit,
             String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod) throws AlpacaClientException {
-        checkNotNull(symbol);
+        checkNotNull(symbols);
         checkNotNull(barTimePeriod);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
+                .addPathSegment(LOCALE)
                 .addPathSegment("bars");
 
-        if (exchanges != null) {
-            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
-                    .map(Exchange::value)
-                    .collect(Collectors.joining(",")));
-        }
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         if (start != null) {
             urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
@@ -261,36 +240,4 @@ public CryptoBarsResponse getBars(String symbol, Collection<Exchange> exchanges,
                 .build();
         return alpacaClient.requestObject(request, CryptoBarsResponse.class);
     }
-
-    /**
-     * Returns the XBBO for a crypto symbol that calculates the Best Bid and Offer across multiple exchanges.
-     *
-     * @param symbol    the symbol to query for
-     * @param exchanges a {@link Collection} of {@link Exchange}s to filter by. If <code>null</code>, then only the
-     *                  exchanges that can be traded on Alpaca are included in the calculation.
-     *
-     * @return the {@link XbboResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public XbboResponse getXBBO(String symbol, Collection<Exchange> exchanges) throws AlpacaClientException {
-        checkNotNull(symbol);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("xbbo")
-                .addPathSegment("latest");
-
-        if (exchanges != null) {
-            urlBuilder.addQueryParameter("exchanges", exchanges.stream()
-                    .map(Exchange::value)
-                    .collect(Collectors.joining(",")));
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, XbboResponse.class);
-    }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
index fa61d1ec..27168332 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
@@ -8,7 +8,7 @@
 
 /**
  * {@link CryptoMarketDataWebsocket} is a {@link MarketDataWebsocket} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/">Realtime
+ * <a href="https://docs.alpaca.markets/docs/real-time-crypto-pricing-data">Realtime
  * Crypto Market Data</a>
  */
 public class CryptoMarketDataWebsocket extends MarketDataWebsocket {
@@ -21,7 +21,7 @@ public class CryptoMarketDataWebsocket extends MarketDataWebsocket {
      * @param secretKey    the secret key
      */
     public CryptoMarketDataWebsocket(OkHttpClient okHttpClient, String keyID, String secretKey) {
-        super(okHttpClient, "v1beta1/crypto", "Crypto", keyID, secretKey, CryptoTradeMessage.class,
+        super(okHttpClient, "v1beta3/crypto/us", "Crypto", keyID, secretKey, CryptoTradeMessage.class,
                 CryptoQuoteMessage.class, CryptoBarMessage.class);
     }
 }

From 4aab2d5f98adde2957f4bf3845426d20ff16d098 Mon Sep 17 00:00:00 2001
From: Jesse Thouin <jthouin@amazon.com>
Date: Tue, 11 Jul 2023 11:39:41 -0400
Subject: [PATCH 43/85] Further updates to match v1beta3 crypto APIs.

---
 .../bar/latest_crypto_bars_response.json      |  10 ++
 .../orderbook/crypto_orderbook.json           |  21 +++
 .../orderbook/crypto_orderbook_entry.json     |  16 ++
 .../latest_crypto_orderbooks_response.json    |  10 ++
 .../quote/latest_crypto_quote_response.json   |  14 --
 .../quote/latest_crypto_quotes_response.json  |  10 ++
 .../historical/snapshot/crypto_snapshot.json  |  31 ++++
 .../snapshot/crypto_snapshots_response.json   |  10 ++
 .../trade/latest_crypto_trade_response.json   |  14 --
 .../trade/latest_crypto_trades_response.json  |  10 ++
 .../crypto/CryptoMarketDataEndpoint.java      | 147 +++++++++++++++---
 11 files changed, 247 insertions(+), 46 deletions(-)
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
 create mode 100644 schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json

diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json b/schema_json/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
new file mode 100644
index 00000000..b502ddc3
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
@@ -0,0 +1,10 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "bars": {
+      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar>",
+      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar}s."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json b/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
new file mode 100644
index 00000000..04409010
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
@@ -0,0 +1,21 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://docs.alpaca.markets/reference/v1beta3cryptolatestorderbooks\">Latest Orderbook</a>.",
+  "properties": {
+    "t": {
+      "existingJavaType": "java.time.ZonedDateTime",
+      "javaName": "timestamp",
+      "title": "Timestamp in RFC-3339 format with nanosecond precision."
+    },
+    "b": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry>",
+      "javaName": "bidSide",
+      "title": "An {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry}s for the buy/bid side."
+    },
+    "a": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry>",
+      "javaName": "askSide",
+      "title": "An {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry}s for the ask/sell side."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json b/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
new file mode 100644
index 00000000..5387ecc7
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
@@ -0,0 +1,16 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://docs.alpaca.markets/reference/v1beta3cryptolatestorderbooks\">Latest Orderbook</a>.",
+  "properties": {
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "Price."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "size",
+      "title": "Size."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json b/schema_json/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
new file mode 100644
index 00000000..31c605d3
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
@@ -0,0 +1,10 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "orderbooks": {
+      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook>",
+      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook}s."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
deleted file mode 100644
index 6db62c25..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quote_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "quote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
new file mode 100644
index 00000000..995ccd02
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
@@ -0,0 +1,10 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "quotes": {
+      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote>",
+      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}s."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json b/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
new file mode 100644
index 00000000..9ff6ab0d
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
@@ -0,0 +1,31 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://docs.alpaca.markets/reference/v1beta3cryptosnapshots\">Snapshots</a>.",
+  "properties": {
+    "dailyBar": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar",
+      "javaName": "dailyBar",
+      "title": "OHLC aggregate of all the trades in a given interval."
+    },
+    "latestQuote": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote",
+      "javaName": "latestQuote",
+      "title": "The best bid and ask information for a given security."
+    },
+    "latestTrade": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade",
+      "javaName": "latestTrade",
+      "title": "A crypto trade"
+    },
+    "minuteBar": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar",
+      "javaName": "minuteBar",
+      "title": "OHLC aggregate of all the trades in a given interval."
+    },
+    "prevDailyBar": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar",
+      "javaName": "prevDailyBar",
+      "title": "OHLC aggregate of all the trades in a given interval."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json b/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
new file mode 100644
index 00000000..3ec3f3b8
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
@@ -0,0 +1,10 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "snapshots": {
+      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot>",
+      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot}s."
+    }
+  }
+}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
deleted file mode 100644
index b49e4ef7..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trade_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "trade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}."
-    }
-  }
-}
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
new file mode 100644
index 00000000..4e6749c2
--- /dev/null
+++ b/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
@@ -0,0 +1,10 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
+  "properties": {
+    "trades": {
+      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade>",
+      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}s."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
index 0062d3fe..bebe7a87 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
@@ -3,12 +3,17 @@
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBarsResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.LatestCryptoBarsResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.LatestCryptoOrderbooksResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuotesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestCryptoQuoteResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestCryptoQuotesResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshotsResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTradesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestCryptoTradeResponse;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestCryptoTradesResponse;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
 import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
@@ -18,7 +23,6 @@
 
 import java.time.ZonedDateTime;
 import java.util.Collection;
-import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -40,7 +44,7 @@ public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
     }
 
     /**
-     * Gets {@link CryptoTrade} historical data for the requested crypto symbol.
+     * Gets {@link CryptoTrade} historical data for the requested crypto symbols.
      *
      * @param symbols   a {@link Collection} of symbols to query for
      * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
@@ -58,6 +62,9 @@ public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
     public CryptoTradesResponse getTrades(Collection<String> symbols, ZonedDateTime start,
             ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
         checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
@@ -89,33 +96,65 @@ public CryptoTradesResponse getTrades(Collection<String> symbols, ZonedDateTime
     }
 
     /**
-     * Gets the latest {@link CryptoTrade} for the requested security.
+     * Gets the latest {@link CryptoTrade}s for the requested securities.
+     *
+     * @param symbols   a {@link Collection} of symbols to query for
+     *
+     * @return the {@link LatestCryptoTradesResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public LatestCryptoTradesResponse getLatestTrades(Collection<String> symbols) throws AlpacaClientException {
+        checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(LOCALE)
+                .addPathSegment("latest")
+                .addPathSegment("trades");
+
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, LatestCryptoTradesResponse.class);
+    }
+
+    /**
+     * Gets the latest {@link CryptoBar}s for the requested securities.
      *
      * @param symbols   a {@link Collection} of symbols to query for
      *
-     * @return the {@link LatestCryptoTradeResponse}
+     * @return the {@link LatestCryptoBarsResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestCryptoTradeResponse getLatestTrade(Collection<String> symbols) throws AlpacaClientException {
+    public LatestCryptoBarsResponse getLatestBars(Collection<String> symbols) throws AlpacaClientException {
         checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
                 .addPathSegment(LOCALE)
-                .addPathSegment("trades")
-                .addPathSegment("latest");
+                .addPathSegment("latest")
+                .addPathSegment("bars");
 
         urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, LatestCryptoTradeResponse.class);
+        return alpacaClient.requestObject(request, LatestCryptoBarsResponse.class);
     }
 
     /**
-     * Gets {@link CryptoQuote} historical data for the requested crypto symbol.
+     * Gets {@link CryptoQuote} historical data for the requested crypto symbols.
      *
      * @param symbols   a {@link Collection} of symbols to query for
      * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
@@ -133,6 +172,9 @@ public LatestCryptoTradeResponse getLatestTrade(Collection<String> symbols) thro
     public CryptoQuotesResponse getQuotes(Collection<String> symbols, ZonedDateTime start,
             ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
         checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
@@ -164,29 +206,89 @@ public CryptoQuotesResponse getQuotes(Collection<String> symbols, ZonedDateTime
     }
 
     /**
-     * Gets the latest {@link CryptoQuote} for the requested security.
+     * Gets the latest {@link CryptoQuote}s for the requested securities.
+     *
+     * @param symbols   a {@link Collection} of symbols to query for
+     *
+     * @return the {@link LatestCryptoQuotesResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public LatestCryptoQuotesResponse getLatestQuotes(Collection<String> symbols) throws AlpacaClientException {
+        checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(LOCALE)
+                .addPathSegment("latest")
+                .addPathSegment("quotes");
+
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, LatestCryptoQuotesResponse.class);
+    }
+
+    /**
+     * Gets the latest {@link CryptoSnapshot} for the requested securities.
+     *
+     * @param symbols   a {@link Collection} of symbols to query for
+     *
+     * @return the {@link CryptoSnapshotsResponse}
+     *
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public CryptoSnapshotsResponse getSnapshots(Collection<String> symbols) throws AlpacaClientException {
+        checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(LOCALE)
+                .addPathSegment("snapshots");
+
+        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, CryptoSnapshotsResponse.class);
+    }
+
+    /**
+     * Gets the latest {@link CryptoOrderbook}s for the requested securities.
      *
      * @param symbols   a {@link Collection} of symbols to query for
      *
-     * @return the {@link LatestCryptoQuoteResponse}
+     * @return the {@link LatestCryptoOrderbooksResponse}
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public LatestCryptoQuoteResponse getLatestQuote(Collection<String> symbols) throws AlpacaClientException {
+    public LatestCryptoOrderbooksResponse getLatestOrderbooks(Collection<String> symbols) throws AlpacaClientException {
         checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
                 .addPathSegment(endpointPathSegment)
                 .addPathSegment(LOCALE)
-                .addPathSegment("quotes")
-                .addPathSegment("latest");
+                .addPathSegment("latest")
+                .addPathSegment("orderbooks");
 
         urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
 
         Request request = alpacaClient.requestBuilder(urlBuilder.build())
                 .get()
                 .build();
-        return alpacaClient.requestObject(request, LatestCryptoQuoteResponse.class);
+        return alpacaClient.requestObject(request, LatestCryptoOrderbooksResponse.class);
     }
 
     /**
@@ -195,6 +297,8 @@ public LatestCryptoQuoteResponse getLatestQuote(Collection<String> symbols) thro
      * @param symbols               a {@link Collection} of symbols to query for
      * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
      *                              not accepted. <code>null</code> for now.
+     * @param end                   filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are
+     *                              not accepted. <code>null</code> for now.
      * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
      *                              <code>null</code> is given
      * @param pageToken             pagination token to continue from
@@ -209,9 +313,12 @@ public LatestCryptoQuoteResponse getLatestQuote(Collection<String> symbols) thro
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
-    public CryptoBarsResponse getBars(Collection<String> symbols, ZonedDateTime start, Integer limit,
+    public CryptoBarsResponse getBars(Collection<String> symbols, ZonedDateTime start, ZonedDateTime end, Integer limit,
             String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod) throws AlpacaClientException {
         checkNotNull(symbols);
+        if (symbols.isEmpty()) {
+            throw new AlpacaClientException("Collection symbols must not be empty.");
+        }
         checkNotNull(barTimePeriod);
 
         HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
@@ -225,6 +332,10 @@ public CryptoBarsResponse getBars(Collection<String> symbols, ZonedDateTime star
             urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
         }
 
+        if (end != null) {
+            urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
+        }
+
         if (limit != null) {
             urlBuilder.addQueryParameter("limit", limit.toString());
         }

From eb7f551f547bbaf6015797b2972aa2bed1fdb50c Mon Sep 17 00:00:00 2001
From: Jesse Thouin <jthouin@amazon.com>
Date: Thu, 13 Jul 2023 16:54:02 -0400
Subject: [PATCH 44/85] Removed getQuotes as it doesn't have a matching API
 call with Alpaca.

---
 .../quote/crypto_quotes_response.json         | 14 -----
 .../crypto/CryptoMarketDataEndpoint.java      | 53 -------------------
 2 files changed, 67 deletions(-)
 delete mode 100644 schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json

diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json b/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
deleted file mode 100644
index a5010f8c..00000000
--- a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quotes_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "quotes": {
-      "existingJavaType": "java.util.Map<String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote>>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}s."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
index bebe7a87..c1a8947f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
@@ -7,7 +7,6 @@
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.LatestCryptoOrderbooksResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuotesResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestCryptoQuotesResponse;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshotsResponse;
@@ -153,58 +152,6 @@ public LatestCryptoBarsResponse getLatestBars(Collection<String> symbols) throws
         return alpacaClient.requestObject(request, LatestCryptoBarsResponse.class);
     }
 
-    /**
-     * Gets {@link CryptoQuote} historical data for the requested crypto symbols.
-     *
-     * @param symbols   a {@link Collection} of symbols to query for
-     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted. <code>null</code> for the current day in Central Time.
-     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted. <code>null</code> for now.
-     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
-     *                  is given
-     * @param pageToken pagination token to continue from
-     *
-     * @return the {@link CryptoQuotesResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public CryptoQuotesResponse getQuotes(Collection<String> symbols, ZonedDateTime start,
-            ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("quotes");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        if (start != null) {
-            urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        }
-
-        if (end != null) {
-            urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-        }
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, CryptoQuotesResponse.class);
-    }
-
     /**
      * Gets the latest {@link CryptoQuote}s for the requested securities.
      *

From 6fdb6fcab02b68a0c54eeeabaa95c5d651b75ce7 Mon Sep 17 00:00:00 2001
From: Jesse Thouin <jthouin@amazon.com>
Date: Sat, 15 Jul 2023 12:50:29 -0400
Subject: [PATCH 45/85] Alpaca removed OrderTimeInForce.DAY as a valid time in
 force for crypto orders.

---
 .../alpaca/rest/endpoint/orders/OrdersEndpoint.java       | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
index 3212bf75..d2d355dd 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
@@ -242,7 +242,7 @@ public Order requestMarketOrder(String symbol, Integer quantity, OrderSide side,
      * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls {@link
      * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
      * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and {@link
-     * OrderTimeInForce#DAY} with a fractional quantity so check the Javadoc for that method for parameter details.
+     * OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a fractional quantity so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -250,7 +250,7 @@ public Order requestMarketOrder(String symbol, Integer quantity, OrderSide side,
      */
     public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderSide side)
             throws AlpacaClientException {
-        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.DAY, null, null, null,
+        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null, null, null,
                 null, null, null, null, null, null, null);
     }
 
@@ -259,7 +259,7 @@ public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderS
      * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls {@link
      * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
      * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and {@link
-     * OrderTimeInForce#DAY} with a notional dollar amount so check the Javadoc for that method for the parameter
+     * OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a notional dollar amount so check the Javadoc for that method for the parameter
      * details.
      *
      * @return the requested {@link Order}
@@ -268,7 +268,7 @@ public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderS
      */
     public Order requestNotionalMarketOrder(String symbol, Double notional, OrderSide side)
             throws AlpacaClientException {
-        return requestOrder(symbol, null, notional, side, OrderType.MARKET, OrderTimeInForce.DAY, null, null, null,
+        return requestOrder(symbol, null, notional, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null, null, null,
                 null, null, null, null, null, null, null);
     }
 

From 9ca1bcd9dec7f7fab6dd58da1ab4bc8d3722dd73 Mon Sep 17 00:00:00 2001
From: Daniel Mejer <dmejer@wp.pl>
Date: Mon, 7 Aug 2023 18:13:31 +0200
Subject: [PATCH 46/85] Add builder to AlpacaAPI

---
 README.md                                     |  6 +++
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 53 +++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/README.md b/README.md
index a29ffc56..aff83e53 100644
--- a/README.md
+++ b/README.md
@@ -73,6 +73,12 @@ AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey, EndpointAPIType.LIVE, Data
 // This constructor is for OAuth tokens
 String oAuthToken = "<some OAuth token>";
 AlpacaAPI alpacaAPI = new AlpacaAPI(oAuthToken);
+
+// The following approach uses builder pattern. Attributes not provided explicitly are retrieved from 'alpaca.properties' file
+AlpacaAPI alpacaAPI = AlpacaAPI.builder()
+    .withEndpointAPIType(EndpointAPIType.PAPER)
+    .build();
+
 ```
 
 Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests. These threads persist even if the main thread exists so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) so your program can exit without calling `System.exit()`, use the following snippet:
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 63980318..60b47293 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -78,6 +78,17 @@ public AlpacaAPI() {
                 AlpacaProperties.DATA_API_TYPE);
     }
 
+    /**
+     * Instantiates a new {@link AlpacaAPI} using properties specified in <code>Builder</code>, otherwise from
+     * <code>alpaca.properties</code> file (or their associated defaults).
+     */
+    private AlpacaAPI(Builder builder) {
+        this(builder.keyID,
+                builder.secretKey,
+                builder.endpointAPIType,
+                builder.dataAPIType);
+    }
+
     /**
      * Instantiates a new {@link AlpacaAPI}.
      *
@@ -308,4 +319,46 @@ public AlpacaClient getCryptoDataClient() {
     public AlpacaClient getStockDataClient() {
         return stockDataClient;
     }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder {
+        private String keyID;
+        private String secretKey;
+        private EndpointAPIType endpointAPIType;
+        private DataAPIType dataAPIType;
+
+        private Builder() {
+            this.keyID = AlpacaProperties.KEY_ID;
+            this.secretKey = AlpacaProperties.SECRET_KEY;
+            this.endpointAPIType = AlpacaProperties.ENDPOINT_API_TYPE;
+            this.dataAPIType = AlpacaProperties.DATA_API_TYPE;
+        }
+
+        public Builder withKeyID(String val) {
+            keyID = val;
+            return this;
+        }
+
+        public Builder withSecretKey(String val) {
+            secretKey = val;
+            return this;
+        }
+
+        public Builder withEndpointAPIType(EndpointAPIType val) {
+            endpointAPIType = val;
+            return this;
+        }
+
+        public Builder withDataAPIType(DataAPIType val) {
+            dataAPIType = val;
+            return this;
+        }
+
+        public AlpacaAPI build() {
+            return new AlpacaAPI(this);
+        }
+    }
 }

From 854a5ddf55af940d406b60d0828986984e52ca56 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 20 Aug 2023 16:19:10 -0600
Subject: [PATCH 47/85] Minor documentation updates

---
 README.md                                     |  2 +-
 build.gradle                                  |  1 +
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 23 +++++++++++++------
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index aff83e53..a6c51d49 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey, EndpointAPIType.LIVE, Data
 String oAuthToken = "<some OAuth token>";
 AlpacaAPI alpacaAPI = new AlpacaAPI(oAuthToken);
 
-// The following approach uses builder pattern. Attributes not provided explicitly are retrieved from 'alpaca.properties' file
+// This approach uses a builder pattern. Attributes not provided explicitly are retrieved from 'alpaca.properties' file
 AlpacaAPI alpacaAPI = AlpacaAPI.builder()
     .withEndpointAPIType(EndpointAPIType.PAPER)
     .build();
diff --git a/build.gradle b/build.gradle
index 588393d3..a40691fa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -237,6 +237,7 @@ jsonSchema2Pojo {
     annotationStyle = 'gson'
     sourceType = 'jsonschema'
     customDateTimePattern = "yyyy-MM-ddTHH:mm:ssZ"
+    includeGeneratedAnnotation = false
     includeConstructors = true
     serializable = true
     includeGetters = true
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 60b47293..c043b78f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -5,19 +5,19 @@
 import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
 import net.jacobpeterson.alpaca.properties.AlpacaProperties;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.accountactivities.AccountActivitiesEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.assets.AssetsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.calendar.CalendarEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.portfoliohistory.PortfolioHistoryEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.positions.PositionsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.watchlist.WatchlistEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
 import net.jacobpeterson.alpaca.websocket.marketdata.crypto.CryptoMarketDataWebsocket;
@@ -32,8 +32,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * The {@link AlpacaAPI} class contains several instances of various {@link AlpacaEndpoint}s and {@link
- * AlpacaWebsocket}s to interface with Alpaca. You will generally only need one instance of this class in your
+ * The {@link AlpacaAPI} class contains several instances of various {@link AlpacaEndpoint}s and
+ * {@link AlpacaWebsocket}s to interface with Alpaca. You will generally only need one instance of this class in your
  * application. Note that many methods inside the various {@link AlpacaEndpoint}s allow <code>null<code/> to be passed
  * in as a parameter if it is optional.
  *
@@ -79,7 +79,7 @@ public AlpacaAPI() {
     }
 
     /**
-     * Instantiates a new {@link AlpacaAPI} using properties specified in <code>Builder</code>, otherwise from
+     * Instantiates a new {@link AlpacaAPI} using properties specified in the given {@link Builder}, otherwise from
      * <code>alpaca.properties</code> file (or their associated defaults).
      */
     private AlpacaAPI(Builder builder) {
@@ -320,11 +320,20 @@ public AlpacaClient getStockDataClient() {
         return stockDataClient;
     }
 
+    /**
+     * Creates a {@link Builder} for {@link AlpacaAPI}.
+     *
+     * @return a {@link Builder}
+     */
     public static Builder builder() {
         return new Builder();
     }
 
+    /**
+     * A builder for {@link AlpacaAPI}
+     */
     public static final class Builder {
+
         private String keyID;
         private String secretKey;
         private EndpointAPIType endpointAPIType;

From 9d253ff4f5acf7d2ff65cbfe903720e19299fd1d Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 20 Aug 2023 16:33:26 -0600
Subject: [PATCH 48/85] Auto-format

---
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |  12 +-
 .../alpaca/rest/AlpacaClient.java             |  16 +-
 .../crypto/CryptoMarketDataEndpoint.java      |  15 +-
 .../stock/StockMarketDataEndpoint.java        |  16 +-
 .../rest/endpoint/orders/OrdersEndpoint.java  | 154 +++++++++---------
 .../marketdata/MarketDataWebsocket.java       |   8 +-
 .../streaming/StreamingWebsocket.java         |   4 +-
 src/main/resources/alpaca.default.properties  |   1 -
 .../alpaca/test/live/AlpacaAPITest.java       |   9 +-
 9 files changed, 120 insertions(+), 115 deletions(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index c71f496b..6ae47c47 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -5,19 +5,19 @@
 import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
 import net.jacobpeterson.alpaca.properties.AlpacaProperties;
 import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.accountactivities.AccountActivitiesEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.assets.AssetsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.calendar.CalendarEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.portfoliohistory.PortfolioHistoryEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.positions.PositionsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.watchlist.WatchlistEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
 import net.jacobpeterson.alpaca.websocket.marketdata.crypto.CryptoMarketDataWebsocket;
@@ -32,8 +32,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * The {@link AlpacaAPI} class contains several instances of various {@link AlpacaEndpoint}s and {@link
- * AlpacaWebsocket}s to interface with Alpaca. You will generally only need one instance of this class in your
+ * The {@link AlpacaAPI} class contains several instances of various {@link AlpacaEndpoint}s and
+ * {@link AlpacaWebsocket}s to interface with Alpaca. You will generally only need one instance of this class in your
  * application. Note that many methods inside the various {@link AlpacaEndpoint}s allow <code>null<code/> to be passed
  * in as a parameter if it is optional.
  *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
index 7fe5cc27..dc11df3f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
@@ -127,8 +127,8 @@ public <T> T requestObject(Request request, Type type) throws AlpacaClientExcept
      *
      * @param <T>           the type of object
      * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the {@link
-     *                      Predicate#test(Object)} returns false
+     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
+     *                      {@link Predicate#test(Object)} returns false
      * @param type          the object {@link Type}
      *
      * @return the requested object
@@ -151,8 +151,8 @@ public JsonElement requestJSON(Request request) throws AlpacaClientException {
      * Requests a {@link JsonElement} given a {@link Request}.
      *
      * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the {@link
-     *                      Predicate#test(Object)} returns false
+     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
+     *                      {@link Predicate#test(Object)} returns false
      *
      * @return the {@link JsonElement}
      *
@@ -178,8 +178,8 @@ public JsonElement requestJSON(Request request, Predicate<Integer> isSuccessCode
      * Sends a {@link Request} and ignores any {@link ResponseBody}.
      *
      * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the {@link
-     *                      Predicate#test(Object)} returns false
+     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
+     *                      {@link Predicate#test(Object)} returns false
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
@@ -193,8 +193,8 @@ public void requestVoid(Request request, Predicate<Integer> isSuccessCode) throw
      * Sends a {@link Request} and returns a {@link Response}.
      *
      * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the {@link
-     *                      Predicate#test(Object)} returns false
+     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
+     *                      {@link Predicate#test(Object)} returns false
      *
      * @return the {@link Response}. <strong>Be sure to call {@link Response#close()} after use.</strong>
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
index c1a8947f..f429a32c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
@@ -31,6 +31,7 @@
  * Crypto Market Data API</a>.
  */
 public class CryptoMarketDataEndpoint extends AlpacaEndpoint {
+
     public static final String LOCALE = "us";
 
     /**
@@ -97,7 +98,7 @@ public CryptoTradesResponse getTrades(Collection<String> symbols, ZonedDateTime
     /**
      * Gets the latest {@link CryptoTrade}s for the requested securities.
      *
-     * @param symbols   a {@link Collection} of symbols to query for
+     * @param symbols a {@link Collection} of symbols to query for
      *
      * @return the {@link LatestCryptoTradesResponse}
      *
@@ -126,7 +127,7 @@ public LatestCryptoTradesResponse getLatestTrades(Collection<String> symbols) th
     /**
      * Gets the latest {@link CryptoBar}s for the requested securities.
      *
-     * @param symbols   a {@link Collection} of symbols to query for
+     * @param symbols a {@link Collection} of symbols to query for
      *
      * @return the {@link LatestCryptoBarsResponse}
      *
@@ -155,7 +156,7 @@ public LatestCryptoBarsResponse getLatestBars(Collection<String> symbols) throws
     /**
      * Gets the latest {@link CryptoQuote}s for the requested securities.
      *
-     * @param symbols   a {@link Collection} of symbols to query for
+     * @param symbols a {@link Collection} of symbols to query for
      *
      * @return the {@link LatestCryptoQuotesResponse}
      *
@@ -184,7 +185,7 @@ public LatestCryptoQuotesResponse getLatestQuotes(Collection<String> symbols) th
     /**
      * Gets the latest {@link CryptoSnapshot} for the requested securities.
      *
-     * @param symbols   a {@link Collection} of symbols to query for
+     * @param symbols a {@link Collection} of symbols to query for
      *
      * @return the {@link CryptoSnapshotsResponse}
      *
@@ -212,7 +213,7 @@ public CryptoSnapshotsResponse getSnapshots(Collection<String> symbols) throws A
     /**
      * Gets the latest {@link CryptoOrderbook}s for the requested securities.
      *
-     * @param symbols   a {@link Collection} of symbols to query for
+     * @param symbols a {@link Collection} of symbols to query for
      *
      * @return the {@link LatestCryptoOrderbooksResponse}
      *
@@ -252,8 +253,8 @@ public LatestCryptoOrderbooksResponse getLatestOrderbooks(Collection<String> sym
      * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
      *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
      *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod} e.g. for <code>15Min</code> bars, you would supply {@link
-     *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
+     * @param barTimePeriod         the {@link BarTimePeriod} e.g. for <code>15Min</code> bars, you would supply
+     *                              {@link BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
      *                              <code>barTimePeriodDuration</code> parameter.
      *
      * @return the {@link CryptoBarsResponse}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
index d47b167d..3111dde7 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
@@ -199,11 +199,11 @@ public LatestStockQuoteResponse getLatestQuote(String symbol) throws AlpacaClien
      * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
      *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
      *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply {@link
-     *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
+     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply
+     *                              {@link BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
      *                              <code>barTimePeriodDuration</code> parameter.
-     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is {@link
-     *                              BarAdjustment#RAW}
+     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is
+     *                              {@link BarAdjustment#RAW}
      * @param barFeed               defaults to {@link BarFeed#IEX} for Free users and {@link BarFeed#SIP} for users
      *                              with an Unlimited subscription
      *
@@ -265,11 +265,11 @@ public StockBarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTi
      * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
      *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
      *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply {@link
-     *                              BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
+     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply
+     *                              {@link BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
      *                              <code>barTimePeriodDuration</code> parameter.
-     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is {@link
-     *                              BarAdjustment#RAW}
+     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is
+     *                              {@link BarAdjustment#RAW}
      * @param barFeed               defaults to {@link BarFeed#IEX} for Free users and {@link BarFeed#SIP} for users
      *                              with an Unlimited subscription
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
index d2d355dd..0ad4a66e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
@@ -56,8 +56,8 @@ public OrdersEndpoint(AlpacaClient alpacaClient) {
      * @param limit     the maximum number of orders in response. Defaults to 50 and max is 500.
      * @param after     the response will include only ones submitted after this timestamp (exclusive)
      * @param until     the response will include only ones submitted until this timestamp (exclusive)
-     * @param direction the chronological order of response based on the submission time. Defaults to {@link
-     *                  SortDirection#DESCENDING}.
+     * @param direction the chronological order of response based on the submission time. Defaults to
+     *                  {@link SortDirection#DESCENDING}.
      * @param nested    if true, the result will roll up multi-leg orders under the legs field of primary order.
      * @param symbols   a {@link Collection} of symbols to filter by (e.g. "AAPL,TSLA,MSFT"). A currency pair is
      *                  required for crypto orders (e.g. "BTCUSD,BCHUSD,LTCUSD,ETCUSD"). <code>null</code> for no
@@ -120,19 +120,19 @@ public List<Order> get(CurrentOrderStatus status, Integer limit, ZonedDateTime a
      * @param timeInForce          the {@link OrderTimeInForce}
      * @param limitPrice           required if type is {@link OrderType#LIMIT} or {@link OrderType#STOP_LIMIT}
      * @param stopPrice            required if type is {@link OrderType#STOP} or {@link OrderType#STOP_LIMIT}
-     * @param trailPrice           this or <code>trail_percent</code> is required if type is {@link
-     *                             OrderType#TRAILING_STOP}
-     * @param trailPercent         this or <code>trail_price</code> is required if type is {@link
-     *                             OrderType#TRAILING_STOP}
+     * @param trailPrice           this or <code>trail_percent</code> is required if type is
+     *                             {@link OrderType#TRAILING_STOP}
+     * @param trailPercent         this or <code>trail_price</code> is required if type is
+     *                             {@link OrderType#TRAILING_STOP}
      * @param extendedHours        (default) false. If true, order will be eligible to execute in premarket/afterhours.
      *                             Only works with type {@link OrderType#LIMIT} and {@link OrderTimeInForce#DAY}.
      * @param clientOrderId        a unique identifier for the order. Automatically generated if <code>null</code>.
      * @param orderClass           the {@link OrderClass}. For details of non-simple order classes, please see "Bracket
      *                             Order Overview" on the Alpaca Docs.
-     * @param takeProfitLimitPrice additional parameter for take-profit leg of advanced orders. Required for {@link
-     *                             OrderClass#BRACKET}.
-     * @param stopLossStopPrice    additional parameters for stop-loss leg of advanced orders. Required for {@link
-     *                             OrderClass#BRACKET}.
+     * @param takeProfitLimitPrice additional parameter for take-profit leg of advanced orders. Required for
+     *                             {@link OrderClass#BRACKET}.
+     * @param stopLossStopPrice    additional parameters for stop-loss leg of advanced orders. Required for
+     *                             {@link OrderClass#BRACKET}.
      * @param stopLossLimitPrice   additional parameters for stop-loss leg of advanced orders. The stop-loss order
      *                             becomes a stop-limit order if specified.
      *
@@ -222,10 +222,10 @@ public Order requestOrder(String symbol, Double quantity, Double notional, Order
 
     /**
      * A market order is a request to buy or sell a security at the currently available market price. It provides the
-     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls {@link
-     * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
-     * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} so check the Javadoc for that
-     * method for parameter details.
+     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} so check the Javadoc
+     * for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -239,10 +239,11 @@ public Order requestMarketOrder(String symbol, Integer quantity, OrderSide side,
 
     /**
      * A market order is a request to buy or sell a security at the currently available market price. It provides the
-     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls {@link
-     * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
-     * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and {@link
-     * OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a fractional quantity so check the Javadoc for that method for parameter details.
+     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and
+     * {@link OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a fractional quantity so check the Javadoc for that method for
+     * parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -250,17 +251,18 @@ public Order requestMarketOrder(String symbol, Integer quantity, OrderSide side,
      */
     public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderSide side)
             throws AlpacaClientException {
-        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null, null, null,
+        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null,
+                null, null,
                 null, null, null, null, null, null, null);
     }
 
     /**
      * A market order is a request to buy or sell a security at the currently available market price. It provides the
-     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls {@link
-     * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
-     * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and {@link
-     * OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a notional dollar amount so check the Javadoc for that method for the parameter
-     * details.
+     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and
+     * {@link OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a notional dollar amount so check the Javadoc for that method
+     * for the parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -268,7 +270,8 @@ public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderS
      */
     public Order requestNotionalMarketOrder(String symbol, Double notional, OrderSide side)
             throws AlpacaClientException {
-        return requestOrder(symbol, null, notional, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null, null, null,
+        return requestOrder(symbol, null, notional, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null,
+                null, null,
                 null, null, null, null, null, null, null);
     }
 
@@ -276,10 +279,10 @@ public Order requestNotionalMarketOrder(String symbol, Double notional, OrderSid
      * A limit order is an order to buy or sell at a specified price or better. A buy limit order (a limit order to buy)
      * is executed at the specified limit price or lower (i.e., better). Conversely, a sell limit order (a limit order
      * to sell) is executed at the specified limit price or higher (better). Unlike a market order, you have to specify
-     * the limit price parameter when submitting your order. This method calls {@link #requestOrder(String, Double,
-     * Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass,
-     * Double, Double, Double)} with {@link OrderType#LIMIT} so check the Javadoc for that method for the parameter
-     * details.
+     * the limit price parameter when submitting your order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} so check the Javadoc
+     * for that method for the parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -296,10 +299,10 @@ public Order requestLimitOrder(String symbol, Double quantity, OrderSide side, O
      * ensuring a higher probability of achieving a predetermined entry or exit price. Once the market price crosses the
      * specified stop price, the stop order becomes a market order. Alpaca converts buy stop orders into stop limit
      * orders with a limit price that is 4% higher than a stop price &lt; $50 (or 2.5% higher than a stop price &gt;=
-     * $50). Sell stop orders are not converted into stop limit orders. This method calls {@link #requestOrder(String,
-     * Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double, Boolean, String,
-     * OrderClass, Double, Double, Double)} with {@link OrderType#STOP} so check the Javadoc for that method for
-     * parameter details.
+     * $50). Sell stop orders are not converted into stop limit orders. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP} so check the Javadoc
+     * for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -315,10 +318,10 @@ public Order requestStopOrder(String symbol, Integer quantity, OrderSide side, O
      * A stop-limit order is a conditional trade over a set time frame that combines the features of a stop order with
      * those of a limit order and is used to mitigate risk. The stop-limit order will be executed at a specified limit
      * price, or better, after a given stop price has been reached. Once the stop price is reached, the stop-limit order
-     * becomes a limit order to buy or sell at the limit price or better. This method calls {@link #requestOrder(String,
-     * Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double, Boolean, String,
-     * OrderClass, Double, Double, Double)} with {@link OrderType#STOP_LIMIT} so check the Javadoc for that method for
-     * parameter details.
+     * becomes a limit order to buy or sell at the limit price or better. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP_LIMIT} so check the
+     * Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -332,10 +335,10 @@ public Order requestStopLimitOrder(String symbol, Integer quantity, OrderSide si
 
     /**
      * A bracket order is a chain of three orders that can be used to manage your position entry and exit. It is a
-     * common use case of an OTOCO (One Triggers OCO {One Cancels Other}) order. This method calls {@link
-     * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
-     * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and with parameters for a
-     * bracket order so check the Javadoc for that method for parameter details.
+     * common use case of an OTOCO (One Triggers OCO {One Cancels Other}) order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and with parameters
+     * for a bracket order so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -351,10 +354,10 @@ public Order requestMarketBracketOrder(String symbol, Integer quantity, OrderSid
 
     /**
      * A bracket order is a chain of three orders that can be used to manage your position entry and exit. It is a
-     * common use case of an OTOCO (One Triggers OCO {One Cancels Other}) order. This method calls {@link
-     * #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double, Double,
-     * Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters for a
-     * bracket order so check the Javadoc for that method for parameter details.
+     * common use case of an OTOCO (One Triggers OCO {One Cancels Other}) order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters
+     * for a bracket order so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -372,10 +375,10 @@ public Order requestLimitBracketOrder(String symbol, Integer quantity, OrderSide
      * OCO (One-Cancels-Other) is another type of advanced order type. This is a set of two orders with the same side
      * (buy/buy or sell/sell) and currently only exit order is supported. In other words, this is the second part of the
      * bracket orders where the entry order is already filled, and you can submit the take-profit and stop-loss in one
-     * order submission. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for the parameter
-     * details.
+     * order submission. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with parameters for a
+     * {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for the parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -391,10 +394,10 @@ public Order requestOCOOrder(String symbol, Integer quantity, OrderSide side,
 
     /**
      * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * {@link OrderType#LIMIT} and with parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for
-     * that method for parameter details.
+     * addition to the entry order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters
+     * for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -410,10 +413,10 @@ public Order requestOTOMarketOrder(String symbol, Integer quantity, OrderSide si
 
     /**
      * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * {@link OrderType#LIMIT} and with parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for
-     * that method for parameter details.
+     * addition to the entry order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters
+     * for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -429,10 +432,10 @@ public Order requestOTOLimitOrder(String symbol, Integer quantity, OrderSide sid
 
     /**
      * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * {@link OrderType#STOP} and with parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for
-     * that method for parameter details.
+     * addition to the entry order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP} and with parameters for
+     * a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -448,10 +451,11 @@ public Order requestOTOStopOrder(String symbol, Integer quantity, OrderSide side
 
     /**
      * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * {@link OrderType#STOP_LIMIT} and with parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc
-     * for that method for parameter details.
+     * addition to the entry order. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP_LIMIT} and with
+     * parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter
+     * details.
      *
      * @return the requested {@link Order}
      *
@@ -468,10 +472,10 @@ public Order requestOTOStopLimitOrder(String symbol, Integer quantity, OrderSide
 
     /**
      * Trailing stop orders allow you to continuously and automatically keep updating the stop price threshold based on
-     * the stock price movement. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * {@link OrderType#TRAILING_STOP} and with parameters for a {@link OrderType#TRAILING_STOP} so check the Javadoc
-     * for that method for parameter details.
+     * the stock price movement. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#TRAILING_STOP} and with
+     * parameters for a {@link OrderType#TRAILING_STOP} so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -485,10 +489,10 @@ public Order requestTrailingStopPriceOrder(String symbol, Integer quantity, Orde
 
     /**
      * Trailing stop orders allow you to continuously and automatically keep updating the stop price threshold based on
-     * the stock price movement. This method calls {@link #requestOrder(String, Double, Double, OrderSide, OrderType,
-     * OrderTimeInForce, Double, Double, Double, Double, Boolean, String, OrderClass, Double, Double, Double)} with
-     * {@link OrderType#TRAILING_STOP} and with parameters for a {@link OrderType#TRAILING_STOP} so check the Javadoc
-     * for that method for parameter details.
+     * the stock price movement. This method calls
+     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
+     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#TRAILING_STOP} and with
+     * parameters for a {@link OrderType#TRAILING_STOP} so check the Javadoc for that method for parameter details.
      *
      * @return the requested {@link Order}
      *
@@ -625,8 +629,8 @@ public List<CancelledOrder> cancelAll() throws AlpacaClientException {
     }
 
     /**
-     * Attempts to cancel an open {@link Order}. If the {@link Order} is no longer cancelable (example: {@link
-     * Order#getStatus()} <code>==</code> {@link OrderStatus#FILLED}), the server will respond with status
+     * Attempts to cancel an open {@link Order}. If the {@link Order} is no longer cancelable (example:
+     * {@link Order#getStatus()} <code>==</code> {@link OrderStatus#FILLED}), the server will respond with status
      * <code>422</code>, and reject the request.
      *
      * @param orderID the {@link Order#getId()}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
index 9241f066..9c924c11 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -35,8 +35,8 @@
 import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
 
 /**
- * {@link MarketDataWebsocket} is an {@link AlpacaWebsocket} implementation and provides the {@link
- * MarketDataWebsocketInterface} interface for
+ * {@link MarketDataWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
+ * {@link MarketDataWebsocketInterface} interface for
  * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/">Realtime Market Data</a> for both crypto
  * and stocks.
  */
@@ -243,8 +243,8 @@ private boolean isSuccessMessageAuthenticated(SuccessMessage successMessage) {
     }
 
     /**
-     * Handles a {@link SubscriptionsMessage} for updating {@link #listenedMarketDataMessageTypes} and returns a {@link
-     * List} of currently subscribe symbols or <code>null</code>.
+     * Handles a {@link SubscriptionsMessage} for updating {@link #listenedMarketDataMessageTypes} and returns a
+     * {@link List} of currently subscribe symbols or <code>null</code>.
      *
      * @param marketDataMessageType   the {@link MarketDataMessageType}
      * @param newSubscribedSymbols    the new subscribed symbols from {@link SubscriptionsMessage}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
index cfbf2106..92a15c0c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
@@ -35,8 +35,8 @@
 import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
 
 /**
- * {@link StreamingWebsocket} is an {@link AlpacaWebsocket} implementation and provides the {@link
- * StreamingWebsocketInterface} interface for
+ * {@link StreamingWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
+ * {@link StreamingWebsocketInterface} interface for
  * <a href="https://alpaca.markets/docs/api-documentation/api-v2/streaming/">Streaming</a>
  */
 public class StreamingWebsocket extends AlpacaWebsocket<StreamingMessageType, StreamingMessage, StreamingListener>
diff --git a/src/main/resources/alpaca.default.properties b/src/main/resources/alpaca.default.properties
index 0cef7e43..7da46f1c 100644
--- a/src/main/resources/alpaca.default.properties
+++ b/src/main/resources/alpaca.default.properties
@@ -4,7 +4,6 @@
 #endpoint_api_type = <must be either "paper" or "live">
 #data_api_type = <must be either "iex" or "sip">
 #user_agent = <some User Agent string>
-
 #Defaults:
 endpoint_api_type=paper
 data_api_type=iex
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
index f9760235..9e6d6136 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
@@ -15,7 +15,6 @@
 import net.jacobpeterson.alpaca.model.endpoint.orders.enums.CurrentOrderStatus;
 import net.jacobpeterson.alpaca.rest.AlpacaClientException;
 import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.accountactivities.AccountActivitiesEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
@@ -156,8 +155,9 @@ public void testAccountEndpointGet() throws AlpacaClientException, NumberFormatE
     }
 
     /**
-     * Tests @{@link AccountActivitiesEndpoint#get(ZonedDateTime, ZonedDateTime, ZonedDateTime, SortDirection, Integer,
-     * String, ActivityType...)} one {@link AccountActivity} exists until now.
+     * Tests
+     * @{@link AccountActivitiesEndpoint#get(ZonedDateTime, ZonedDateTime, ZonedDateTime, SortDirection, Integer,
+     *String, ActivityType...)} one {@link AccountActivity} exists until now.
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
      */
@@ -246,7 +246,8 @@ public void testAccountConfigurationEndpointSet() throws AlpacaClientException {
     }
 
     /**
-     * Test {@link OrdersEndpoint#get(CurrentOrderStatus, Integer, ZonedDateTime, ZonedDateTime, SortDirection, Boolean,
+     * Test
+     * {@link OrdersEndpoint#get(CurrentOrderStatus, Integer, ZonedDateTime, ZonedDateTime, SortDirection, Boolean,
      * Collection)} one {@link Order} exists until now.
      *
      * @throws AlpacaClientException thrown for {@link AlpacaClientException}s

From cf282160700a5edf396a3ac0de0d32a7e8d88e19 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 20 Aug 2023 16:36:10 -0600
Subject: [PATCH 49/85] Update `.editorconfig`

---
 .editorconfig | 1067 +++++++++++++++++++++++++++++++++++--------------
 README.md     |    1 -
 2 files changed, 776 insertions(+), 292 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 604f1553..818a4c9e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -11,8 +11,33 @@ ij_formatter_off_tag = @formatter:off
 ij_formatter_on_tag = @formatter:on
 ij_formatter_tags_enabled = true
 ij_smart_tabs = false
+ij_visual_guides = 
 ij_wrap_on_typing = false
 
+[*.css]
+ij_css_align_closing_brace_with_properties = false
+ij_css_blank_lines_around_nested_selector = 1
+ij_css_blank_lines_between_blocks = 1
+ij_css_block_comment_add_space = false
+ij_css_brace_placement = end_of_line
+ij_css_enforce_quotes_on_format = false
+ij_css_hex_color_long_format = false
+ij_css_hex_color_lower_case = false
+ij_css_hex_color_short_format = false
+ij_css_hex_color_upper_case = false
+ij_css_keep_blank_lines_in_code = 2
+ij_css_keep_indents_on_empty_lines = false
+ij_css_keep_single_line_blocks = false
+ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
+ij_css_space_after_colon = true
+ij_css_space_before_opening_brace = true
+ij_css_use_double_quotes = true
+ij_css_value_alignment = do_not_align
+
+[*.feature]
+indent_size = 2
+ij_gherkin_keep_indents_on_empty_lines = false
+
 [*.java]
 ij_java_align_consecutive_assignments = false
 ij_java_align_consecutive_variable_declarations = false
@@ -22,17 +47,21 @@ ij_java_align_multiline_array_initializer_expression = false
 ij_java_align_multiline_assignment = false
 ij_java_align_multiline_binary_operation = false
 ij_java_align_multiline_chained_methods = false
+ij_java_align_multiline_deconstruction_list_components = false
 ij_java_align_multiline_extends_list = false
 ij_java_align_multiline_for = false
 ij_java_align_multiline_method_parentheses = false
 ij_java_align_multiline_parameters = false
 ij_java_align_multiline_parameters_in_calls = false
 ij_java_align_multiline_parenthesized_expression = false
+ij_java_align_multiline_records = false
 ij_java_align_multiline_resources = false
 ij_java_align_multiline_ternary_operation = false
+ij_java_align_multiline_text_blocks = false
 ij_java_align_multiline_throws_list = false
 ij_java_align_subsequent_simple_methods = false
 ij_java_align_throws_keyword = false
+ij_java_align_types_in_multi_catch = false
 ij_java_annotation_parameter_wrap = normal
 ij_java_array_initializer_new_line_after_left_brace = false
 ij_java_array_initializer_right_brace_on_new_line = false
@@ -57,18 +86,22 @@ ij_java_blank_lines_before_imports = 1
 ij_java_blank_lines_before_method_body = 0
 ij_java_blank_lines_before_package = 0
 ij_java_block_brace_style = end_of_line
+ij_java_block_comment_add_space = false
 ij_java_block_comment_at_first_column = true
+ij_java_builder_methods = 
 ij_java_call_parameters_new_line_after_left_paren = false
 ij_java_call_parameters_right_paren_on_new_line = false
 ij_java_call_parameters_wrap = normal
 ij_java_case_statement_on_separate_line = true
 ij_java_catch_on_new_line = false
-ij_java_class_annotation_wrap = split_into_lines
+ij_java_class_annotation_wrap = normal
 ij_java_class_brace_style = end_of_line
-ij_java_class_count_to_use_import_on_demand = 20
-ij_java_class_names_in_javadoc = 1
+ij_java_class_count_to_use_import_on_demand = 999
+ij_java_class_names_in_javadoc = 3
+ij_java_deconstruction_list_wrap = normal
 ij_java_do_not_indent_top_level_class_members = false
 ij_java_do_not_wrap_after_single_annotation = false
+ij_java_do_not_wrap_after_single_annotation_in_parameter = false
 ij_java_do_while_brace_force = always
 ij_java_doc_add_blank_line_after_description = true
 ij_java_doc_add_blank_line_after_param_comments = true
@@ -76,7 +109,7 @@ ij_java_doc_add_blank_line_after_return = true
 ij_java_doc_add_p_tag_on_empty_lines = true
 ij_java_doc_align_exception_comments = true
 ij_java_doc_align_param_comments = true
-ij_java_doc_do_not_wrap_if_one_line = true
+ij_java_doc_do_not_wrap_if_one_line = false
 ij_java_doc_enable_formatting = true
 ij_java_doc_enable_leading_asterisks = true
 ij_java_doc_indent_on_continuation = false
@@ -89,10 +122,31 @@ ij_java_doc_param_description_on_new_line = false
 ij_java_doc_preserve_line_breaks = false
 ij_java_doc_use_throws_not_exception_tag = true
 ij_java_else_on_new_line = false
-ij_java_enum_constants_wrap = normal
+ij_java_entity_dd_prefix = 
+ij_java_entity_dd_suffix = EJB
+ij_java_entity_eb_prefix = 
+ij_java_entity_eb_suffix = Bean
+ij_java_entity_hi_prefix = 
+ij_java_entity_hi_suffix = Home
+ij_java_entity_lhi_prefix = Local
+ij_java_entity_lhi_suffix = Home
+ij_java_entity_li_prefix = Local
+ij_java_entity_li_suffix = 
+ij_java_entity_pk_class = java.lang.String
+ij_java_entity_ri_prefix = 
+ij_java_entity_ri_suffix = 
+ij_java_entity_vo_prefix = 
+ij_java_entity_vo_suffix = VO
+ij_java_enum_constants_wrap = split_into_lines
 ij_java_extends_keyword_wrap = normal
 ij_java_extends_list_wrap = normal
-ij_java_field_annotation_wrap = split_into_lines
+ij_java_field_annotation_wrap = normal
+ij_java_field_name_prefix = 
+ij_java_field_name_suffix = 
+ij_java_filter_class_prefix = 
+ij_java_filter_class_suffix = 
+ij_java_filter_dd_prefix = 
+ij_java_filter_dd_suffix = 
 ij_java_finally_on_new_line = false
 ij_java_for_brace_force = always
 ij_java_for_statement_new_line_after_left_paren = false
@@ -109,6 +163,7 @@ ij_java_keep_blank_lines_before_right_brace = 1
 ij_java_keep_blank_lines_between_package_declaration_and_header = 1
 ij_java_keep_blank_lines_in_code = 1
 ij_java_keep_blank_lines_in_declarations = 1
+ij_java_keep_builder_methods_indents = false
 ij_java_keep_control_statement_in_one_line = true
 ij_java_keep_first_column_comment = true
 ij_java_keep_indents_on_empty_lines = false
@@ -118,24 +173,44 @@ ij_java_keep_simple_blocks_in_one_line = true
 ij_java_keep_simple_classes_in_one_line = true
 ij_java_keep_simple_lambdas_in_one_line = true
 ij_java_keep_simple_methods_in_one_line = true
+ij_java_label_indent_absolute = false
+ij_java_label_indent_size = 0
 ij_java_lambda_brace_style = end_of_line
 ij_java_layout_static_imports_separately = true
-ij_java_line_comment_add_space = true
-ij_java_line_comment_at_first_column = false
-ij_java_method_annotation_wrap = split_into_lines
+ij_java_line_comment_add_space = false
+ij_java_line_comment_add_space_on_reformat = false
+ij_java_line_comment_at_first_column = true
+ij_java_listener_class_prefix = 
+ij_java_listener_class_suffix = 
+ij_java_local_variable_name_prefix = 
+ij_java_local_variable_name_suffix = 
+ij_java_message_dd_prefix = 
+ij_java_message_dd_suffix = EJB
+ij_java_message_eb_prefix = 
+ij_java_message_eb_suffix = Bean
+ij_java_method_annotation_wrap = normal
 ij_java_method_brace_style = end_of_line
 ij_java_method_call_chain_wrap = normal
 ij_java_method_parameters_new_line_after_left_paren = false
 ij_java_method_parameters_right_paren_on_new_line = false
 ij_java_method_parameters_wrap = normal
 ij_java_modifier_list_wrap = false
-ij_java_names_count_to_use_import_on_demand = 20
+ij_java_multi_catch_types_wrap = normal
+ij_java_names_count_to_use_import_on_demand = 999
+ij_java_new_line_after_lparen_in_annotation = false
+ij_java_new_line_after_lparen_in_deconstruction_pattern = false
+ij_java_new_line_after_lparen_in_record_header = false
+ij_java_packages_to_use_import_on_demand = 
 ij_java_parameter_annotation_wrap = normal
+ij_java_parameter_name_prefix = 
+ij_java_parameter_name_suffix = 
 ij_java_parentheses_expression_new_line_after_left_paren = false
 ij_java_parentheses_expression_right_paren_on_new_line = false
 ij_java_place_assignment_sign_on_next_line = false
 ij_java_prefer_longer_names = true
 ij_java_prefer_parameters_wrap = false
+ij_java_record_components_wrap = normal
+ij_java_repeat_annotations = 
 ij_java_repeat_synchronized = true
 ij_java_replace_instanceof_and_cast = false
 ij_java_replace_null_check = true
@@ -143,6 +218,27 @@ ij_java_replace_sum_lambda_with_method_ref = true
 ij_java_resource_list_new_line_after_left_paren = false
 ij_java_resource_list_right_paren_on_new_line = false
 ij_java_resource_list_wrap = normal
+ij_java_rparen_on_new_line_in_annotation = false
+ij_java_rparen_on_new_line_in_deconstruction_pattern = false
+ij_java_rparen_on_new_line_in_record_header = false
+ij_java_servlet_class_prefix = 
+ij_java_servlet_class_suffix = 
+ij_java_servlet_dd_prefix = 
+ij_java_servlet_dd_suffix = 
+ij_java_session_dd_prefix = 
+ij_java_session_dd_suffix = EJB
+ij_java_session_eb_prefix = 
+ij_java_session_eb_suffix = Bean
+ij_java_session_hi_prefix = 
+ij_java_session_hi_suffix = Home
+ij_java_session_lhi_prefix = Local
+ij_java_session_lhi_suffix = Home
+ij_java_session_li_prefix = Local
+ij_java_session_li_suffix = 
+ij_java_session_ri_prefix = 
+ij_java_session_ri_suffix = 
+ij_java_session_si_prefix = 
+ij_java_session_si_suffix = Service
 ij_java_space_after_closing_angle_bracket_in_type_argument = false
 ij_java_space_after_colon = true
 ij_java_space_after_comma = true
@@ -160,6 +256,7 @@ ij_java_space_before_class_left_brace = true
 ij_java_space_before_colon = true
 ij_java_space_before_colon_in_foreach = true
 ij_java_space_before_comma = false
+ij_java_space_before_deconstruction_list = false
 ij_java_space_before_do_left_brace = true
 ij_java_space_before_else_keyword = true
 ij_java_space_before_else_left_brace = true
@@ -190,6 +287,7 @@ ij_java_space_within_empty_array_initializer_braces = false
 ij_java_space_within_empty_method_call_parentheses = false
 ij_java_space_within_empty_method_parentheses = false
 ij_java_spaces_around_additive_operators = true
+ij_java_spaces_around_annotation_eq = true
 ij_java_spaces_around_assignment_operators = true
 ij_java_spaces_around_bitwise_operators = true
 ij_java_spaces_around_equality_operators = true
@@ -208,24 +306,31 @@ ij_java_spaces_within_braces = false
 ij_java_spaces_within_brackets = false
 ij_java_spaces_within_cast_parentheses = false
 ij_java_spaces_within_catch_parentheses = false
+ij_java_spaces_within_deconstruction_list = false
 ij_java_spaces_within_for_parentheses = false
 ij_java_spaces_within_if_parentheses = false
 ij_java_spaces_within_method_call_parentheses = false
 ij_java_spaces_within_method_parentheses = false
 ij_java_spaces_within_parentheses = false
+ij_java_spaces_within_record_header = false
 ij_java_spaces_within_switch_parentheses = false
 ij_java_spaces_within_synchronized_parentheses = false
 ij_java_spaces_within_try_parentheses = false
 ij_java_spaces_within_while_parentheses = false
 ij_java_special_else_if_treatment = true
+ij_java_static_field_name_prefix = 
+ij_java_static_field_name_suffix = 
+ij_java_subclass_name_prefix = 
 ij_java_subclass_name_suffix = Impl
 ij_java_ternary_operation_signs_on_next_line = false
 ij_java_ternary_operation_wrap = normal
+ij_java_test_name_prefix = 
 ij_java_test_name_suffix = Test
 ij_java_throws_keyword_wrap = normal
 ij_java_throws_list_wrap = normal
 ij_java_use_external_annotations = false
 ij_java_use_fq_class_names = false
+ij_java_use_relative_indents = false
 ij_java_use_single_class_imports = true
 ij_java_variable_annotation_wrap = normal
 ij_java_visibility = public
@@ -233,209 +338,99 @@ ij_java_while_brace_force = always
 ij_java_while_on_new_line = false
 ij_java_wrap_comments = true
 ij_java_wrap_first_method_in_call_chain = false
-ij_java_wrap_long_lines = true
+ij_java_wrap_long_lines = false
 
-[*.json]
+[*.less]
 indent_size = 2
-ij_json_keep_blank_lines_in_code = 0
-ij_json_keep_indents_on_empty_lines = false
-ij_json_keep_line_breaks = true
-ij_json_space_after_colon = true
-ij_json_space_after_comma = true
-ij_json_space_before_colon = true
-ij_json_space_before_comma = false
-ij_json_spaces_within_braces = false
-ij_json_spaces_within_brackets = false
-ij_json_wrap_long_lines = false
+ij_less_align_closing_brace_with_properties = false
+ij_less_blank_lines_around_nested_selector = 1
+ij_less_blank_lines_between_blocks = 1
+ij_less_block_comment_add_space = false
+ij_less_brace_placement = 0
+ij_less_enforce_quotes_on_format = false
+ij_less_hex_color_long_format = false
+ij_less_hex_color_lower_case = false
+ij_less_hex_color_short_format = false
+ij_less_hex_color_upper_case = false
+ij_less_keep_blank_lines_in_code = 2
+ij_less_keep_indents_on_empty_lines = false
+ij_less_keep_single_line_blocks = false
+ij_less_line_comment_add_space = false
+ij_less_line_comment_at_first_column = false
+ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
+ij_less_space_after_colon = true
+ij_less_space_before_opening_brace = true
+ij_less_use_double_quotes = true
+ij_less_value_alignment = 0
 
-[*.properties]
-ij_properties_align_group_field_declarations = false
+[*.proto]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_protobuf_keep_blank_lines_in_code = 2
+ij_protobuf_keep_indents_on_empty_lines = false
+ij_protobuf_keep_line_breaks = true
+ij_protobuf_space_after_comma = true
+ij_protobuf_space_before_comma = false
+ij_protobuf_spaces_around_assignment_operators = true
+ij_protobuf_spaces_within_braces = false
+ij_protobuf_spaces_within_brackets = false
 
-[*.scala]
+[*.sass]
+indent_size = 2
+ij_sass_align_closing_brace_with_properties = false
+ij_sass_blank_lines_around_nested_selector = 1
+ij_sass_blank_lines_between_blocks = 1
+ij_sass_brace_placement = 0
+ij_sass_enforce_quotes_on_format = false
+ij_sass_hex_color_long_format = false
+ij_sass_hex_color_lower_case = false
+ij_sass_hex_color_short_format = false
+ij_sass_hex_color_upper_case = false
+ij_sass_keep_blank_lines_in_code = 2
+ij_sass_keep_indents_on_empty_lines = false
+ij_sass_keep_single_line_blocks = false
+ij_sass_line_comment_add_space = false
+ij_sass_line_comment_at_first_column = false
+ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
+ij_sass_space_after_colon = true
+ij_sass_space_before_opening_brace = true
+ij_sass_use_double_quotes = true
+ij_sass_value_alignment = 0
+
+[*.scss]
+indent_size = 2
+ij_scss_align_closing_brace_with_properties = false
+ij_scss_blank_lines_around_nested_selector = 1
+ij_scss_blank_lines_between_blocks = 1
+ij_scss_block_comment_add_space = false
+ij_scss_brace_placement = 0
+ij_scss_enforce_quotes_on_format = false
+ij_scss_hex_color_long_format = false
+ij_scss_hex_color_lower_case = false
+ij_scss_hex_color_short_format = false
+ij_scss_hex_color_upper_case = false
+ij_scss_keep_blank_lines_in_code = 2
+ij_scss_keep_indents_on_empty_lines = false
+ij_scss_keep_single_line_blocks = false
+ij_scss_line_comment_add_space = false
+ij_scss_line_comment_at_first_column = false
+ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
+ij_scss_space_after_colon = true
+ij_scss_space_before_opening_brace = true
+ij_scss_use_double_quotes = true
+ij_scss_value_alignment = 0
+
+[*.vue]
 indent_size = 2
 tab_width = 2
-ij_continuation_indent_size = 2
-ij_scala_align_composite_pattern = true
-ij_scala_align_extends_with = 0
-ij_scala_align_group_field_declarations = false
-ij_scala_align_if_else = false
-ij_scala_align_in_columns_case_branch = false
-ij_scala_align_multiline_binary_operation = false
-ij_scala_align_multiline_chained_methods = false
-ij_scala_align_multiline_for = true
-ij_scala_align_multiline_parameters = true
-ij_scala_align_multiline_parameters_in_calls = false
-ij_scala_align_multiline_parenthesized_expression = false
-ij_scala_align_tuple_elements = false
-ij_scala_align_types_in_multiline_declarations = false
-ij_scala_all_other_imports = all other imports
-ij_scala_alternate_continuation_indent_for_params = 4
-ij_scala_binary_operation_wrap = off
-ij_scala_blank_line = _______ blank line _______
-ij_scala_blank_lines_after_anonymous_class_header = 0
-ij_scala_blank_lines_after_class_header = 0
-ij_scala_blank_lines_after_imports = 1
-ij_scala_blank_lines_after_package = 1
-ij_scala_blank_lines_around_class = 1
-ij_scala_blank_lines_around_field = 0
-ij_scala_blank_lines_around_field_in_inner_scopes = 0
-ij_scala_blank_lines_around_field_in_interface = 0
-ij_scala_blank_lines_around_method = 1
-ij_scala_blank_lines_around_method_in_inner_scopes = 1
-ij_scala_blank_lines_around_method_in_interface = 1
-ij_scala_blank_lines_before_imports = 1
-ij_scala_blank_lines_before_method_body = 0
-ij_scala_blank_lines_before_package = 0
-ij_scala_block_brace_style = end_of_line
-ij_scala_block_comment_at_first_column = true
-ij_scala_call_parameters_new_line_after_lparen = 0
-ij_scala_call_parameters_right_paren_on_new_line = false
-ij_scala_call_parameters_wrap = off
-ij_scala_case_clause_brace_force = never
-ij_scala_catch_on_new_line = false
-ij_scala_class_annotation_wrap = split_into_lines
-ij_scala_class_brace_style = end_of_line
-ij_scala_closure_brace_force = never
-ij_scala_do_not_align_block_expr_params = true
-ij_scala_do_not_indent_case_clause_body = false
-ij_scala_do_not_indent_tuples_close_brace = true
-ij_scala_do_while_brace_force = never
-ij_scala_else_on_new_line = false
-ij_scala_enable_scaladoc_formatting = true
-ij_scala_enforce_functional_syntax_for_unit = true
-ij_scala_exclude_prefix = exclude:
-ij_scala_extends_keyword_wrap = off
-ij_scala_extends_list_wrap = off
-ij_scala_field_annotation_wrap = split_into_lines
-ij_scala_finally_brace_force = never
-ij_scala_finally_on_new_line = false
-ij_scala_for_brace_force = never
-ij_scala_for_statement_wrap = off
-ij_scala_formatter = 0
-ij_scala_if_brace_force = never
-ij_scala_indent_braced_function_args = true
-ij_scala_indent_case_from_switch = true
-ij_scala_indent_first_parameter = true
-ij_scala_indent_first_parameter_clause = false
-ij_scala_indent_type_arguments = true
-ij_scala_indent_type_parameters = true
-ij_scala_insert_whitespaces_in_simple_one_line_method = true
-ij_scala_keep_blank_lines_before_right_brace = 2
-ij_scala_keep_blank_lines_in_code = 2
-ij_scala_keep_blank_lines_in_declarations = 2
-ij_scala_keep_comments_on_same_line = true
-ij_scala_keep_first_column_comment = false
-ij_scala_keep_indents_on_empty_lines = false
-ij_scala_keep_line_breaks = true
-ij_scala_keep_one_line_lambdas_in_arg_list = false
-ij_scala_keep_simple_blocks_in_one_line = false
-ij_scala_keep_simple_methods_in_one_line = false
-ij_scala_keep_xml_formatting = false
-ij_scala_line_comment_at_first_column = true
-ij_scala_method_annotation_wrap = split_into_lines
-ij_scala_method_brace_force = never
-ij_scala_method_brace_style = end_of_line
-ij_scala_method_call_chain_wrap = off
-ij_scala_method_parameters_new_line_after_left_paren = false
-ij_scala_method_parameters_right_paren_on_new_line = false
-ij_scala_method_parameters_wrap = off
-ij_scala_modifier_list_wrap = false
-ij_scala_multiline_string_align_dangling_closing_quotes = false
-ij_scala_multiline_string_closing_quotes_on_new_line = true
-ij_scala_multiline_string_insert_margin_on_enter = true
-ij_scala_multiline_string_margin_char = |
-ij_scala_multiline_string_margin_indent = 2
-ij_scala_multiline_string_opening_quotes_on_new_line = true
-ij_scala_multiline_string_process_margin_on_copy_paste = true
-ij_scala_newline_after_annotations = false
-ij_scala_not_continuation_indent_for_params = false
-ij_scala_parameter_annotation_wrap = off
-ij_scala_parentheses_expression_new_line_after_left_paren = false
-ij_scala_parentheses_expression_right_paren_on_new_line = false
-ij_scala_place_closure_parameters_on_new_line = false
-ij_scala_place_self_type_on_new_line = true
-ij_scala_prefer_parameters_wrap = false
-ij_scala_preserve_space_after_method_declaration_name = false
-ij_scala_reformat_on_compile = false
-ij_scala_replace_case_arrow_with_unicode_char = false
-ij_scala_replace_for_generator_arrow_with_unicode_char = false
-ij_scala_replace_lambda_with_greek_letter = false
-ij_scala_replace_map_arrow_with_unicode_char = false
-ij_scala_scalafmt_reformat_on_files_save = false
-ij_scala_scalafmt_show_invalid_code_warnings = true
-ij_scala_scalafmt_use_intellij_formatter_for_range_format = true
-ij_scala_sd_align_exception_comments = true
-ij_scala_sd_align_other_tags_comments = true
-ij_scala_sd_align_parameters_comments = true
-ij_scala_sd_align_return_comments = true
-ij_scala_sd_blank_line_after_parameters_comments = false
-ij_scala_sd_blank_line_after_return_comments = false
-ij_scala_sd_blank_line_before_parameters = false
-ij_scala_sd_blank_line_before_tags = true
-ij_scala_sd_blank_line_between_parameters = false
-ij_scala_sd_keep_blank_lines_between_tags = false
-ij_scala_sd_preserve_spaces_in_tags = false
-ij_scala_space_after_comma = true
-ij_scala_space_after_for_semicolon = true
-ij_scala_space_after_modifiers_constructor = false
-ij_scala_space_after_type_colon = true
-ij_scala_space_before_brace_method_call = true
-ij_scala_space_before_class_left_brace = true
-ij_scala_space_before_infix_like_method_parentheses = false
-ij_scala_space_before_infix_method_call_parentheses = false
-ij_scala_space_before_infix_operator_like_method_call_parentheses = true
-ij_scala_space_before_method_call_parentheses = false
-ij_scala_space_before_method_left_brace = true
-ij_scala_space_before_method_parentheses = false
-ij_scala_space_before_type_colon = false
-ij_scala_space_before_type_parameter_in_def_list = false
-ij_scala_space_before_type_parameter_list = false
-ij_scala_space_inside_closure_braces = true
-ij_scala_space_inside_self_type_braces = true
-ij_scala_space_within_empty_method_call_parentheses = false
-ij_scala_spaces_around_at_in_patterns = false
-ij_scala_spaces_in_imports = false
-ij_scala_spaces_in_one_line_blocks = false
-ij_scala_spaces_within_brackets = false
-ij_scala_spaces_within_for_parentheses = false
-ij_scala_spaces_within_if_parentheses = false
-ij_scala_spaces_within_method_call_parentheses = false
-ij_scala_spaces_within_method_parentheses = false
-ij_scala_spaces_within_parentheses = false
-ij_scala_spaces_within_while_parentheses = false
-ij_scala_special_else_if_treatment = true
-ij_scala_trailing_comma_arg_list_enabled = true
-ij_scala_trailing_comma_import_selector_enabled = false
-ij_scala_trailing_comma_mode = trailing_comma_keep
-ij_scala_trailing_comma_params_enabled = true
-ij_scala_trailing_comma_pattern_arg_list_enabled = false
-ij_scala_trailing_comma_tuple_enabled = false
-ij_scala_trailing_comma_tuple_type_enabled = false
-ij_scala_trailing_comma_type_params_enabled = false
-ij_scala_try_brace_force = never
-ij_scala_type_annotation_exclude_constant = true
-ij_scala_type_annotation_exclude_in_dialect_sources = true
-ij_scala_type_annotation_exclude_in_test_sources = false
-ij_scala_type_annotation_exclude_member_of_anonymous_class = false
-ij_scala_type_annotation_exclude_member_of_private_class = false
-ij_scala_type_annotation_exclude_when_type_is_stable = true
-ij_scala_type_annotation_function_parameter = false
-ij_scala_type_annotation_implicit_modifier = true
-ij_scala_type_annotation_local_definition = false
-ij_scala_type_annotation_private_member = false
-ij_scala_type_annotation_protected_member = true
-ij_scala_type_annotation_public_member = true
-ij_scala_type_annotation_structural_type = true
-ij_scala_type_annotation_underscore_parameter = false
-ij_scala_type_annotation_unit_type = true
-ij_scala_use_alternate_continuation_indent_for_params = false
-ij_scala_use_scaladoc2_formatting = false
-ij_scala_variable_annotation_wrap = off
-ij_scala_while_brace_force = never
-ij_scala_while_on_new_line = false
-ij_scala_wrap_before_with_keyword = false
-ij_scala_wrap_first_method_in_call_chain = false
-ij_scala_wrap_long_lines = false
+ij_continuation_indent_size = 4
+ij_vue_indent_children_of_top_level = template
+ij_vue_interpolation_new_line_after_start_delimiter = true
+ij_vue_interpolation_new_line_before_end_delimiter = true
+ij_vue_interpolation_wrap = off
+ij_vue_keep_indents_on_empty_lines = false
+ij_vue_spaces_within_interpolation_expressions = true
 
 [.editorconfig]
 ij_editorconfig_align_group_field_declarations = false
@@ -445,27 +440,402 @@ ij_editorconfig_space_before_colon = false
 ij_editorconfig_space_before_comma = false
 ij_editorconfig_spaces_around_assignment_operators = true
 
-[{*.gant,*.groovy,*.gradle,*.gdsl,*.gy}]
+[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
+ij_xml_align_attributes = true
+ij_xml_align_text = false
+ij_xml_attribute_wrap = normal
+ij_xml_block_comment_add_space = false
+ij_xml_block_comment_at_first_column = true
+ij_xml_keep_blank_lines = 2
+ij_xml_keep_indents_on_empty_lines = false
+ij_xml_keep_line_breaks = true
+ij_xml_keep_line_breaks_in_text = true
+ij_xml_keep_whitespaces = false
+ij_xml_keep_whitespaces_around_cdata = preserve
+ij_xml_keep_whitespaces_inside_cdata = false
+ij_xml_line_comment_at_first_column = true
+ij_xml_space_after_tag_name = false
+ij_xml_space_around_equals_in_attribute = false
+ij_xml_space_inside_empty_tag = false
+ij_xml_text_wrap = normal
+ij_xml_use_custom_settings = false
+
+[{*.ats,*.cts,*.mts,*.ts}]
+ij_typescript_align_imports = false
+ij_typescript_align_multiline_array_initializer_expression = false
+ij_typescript_align_multiline_binary_operation = false
+ij_typescript_align_multiline_chained_methods = false
+ij_typescript_align_multiline_extends_list = false
+ij_typescript_align_multiline_for = false
+ij_typescript_align_multiline_parameters = false
+ij_typescript_align_multiline_parameters_in_calls = false
+ij_typescript_align_multiline_ternary_operation = false
+ij_typescript_align_object_properties = 0
+ij_typescript_align_union_types = false
+ij_typescript_align_var_statements = 0
+ij_typescript_array_initializer_new_line_after_left_brace = false
+ij_typescript_array_initializer_right_brace_on_new_line = false
+ij_typescript_array_initializer_wrap = normal
+ij_typescript_assignment_wrap = normal
+ij_typescript_binary_operation_sign_on_next_line = false
+ij_typescript_binary_operation_wrap = normal
+ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
+ij_typescript_blank_lines_after_imports = 1
+ij_typescript_blank_lines_around_class = 1
+ij_typescript_blank_lines_around_field = 0
+ij_typescript_blank_lines_around_field_in_interface = 0
+ij_typescript_blank_lines_around_function = 1
+ij_typescript_blank_lines_around_method = 1
+ij_typescript_blank_lines_around_method_in_interface = 1
+ij_typescript_block_brace_style = end_of_line
+ij_typescript_block_comment_add_space = false
+ij_typescript_block_comment_at_first_column = true
+ij_typescript_call_parameters_new_line_after_left_paren = false
+ij_typescript_call_parameters_right_paren_on_new_line = false
+ij_typescript_call_parameters_wrap = normal
+ij_typescript_catch_on_new_line = false
+ij_typescript_chained_call_dot_on_new_line = true
+ij_typescript_class_brace_style = end_of_line
+ij_typescript_comma_on_new_line = false
+ij_typescript_do_while_brace_force = always
+ij_typescript_else_on_new_line = false
+ij_typescript_enforce_trailing_comma = remove
+ij_typescript_enum_constants_wrap = split_into_lines
+ij_typescript_extends_keyword_wrap = normal
+ij_typescript_extends_list_wrap = normal
+ij_typescript_field_prefix = _
+ij_typescript_file_name_style = relaxed
+ij_typescript_finally_on_new_line = false
+ij_typescript_for_brace_force = always
+ij_typescript_for_statement_new_line_after_left_paren = false
+ij_typescript_for_statement_right_paren_on_new_line = false
+ij_typescript_for_statement_wrap = split_into_lines
+ij_typescript_force_quote_style = true
+ij_typescript_force_semicolon_style = true
+ij_typescript_function_expression_brace_style = end_of_line
+ij_typescript_if_brace_force = always
+ij_typescript_import_merge_members = global
+ij_typescript_import_prefer_absolute_path = global
+ij_typescript_import_sort_members = true
+ij_typescript_import_sort_module_name = false
+ij_typescript_import_use_node_resolution = true
+ij_typescript_imports_wrap = normal
+ij_typescript_indent_case_from_switch = true
+ij_typescript_indent_chained_calls = true
+ij_typescript_indent_package_children = 0
+ij_typescript_jsdoc_include_types = false
+ij_typescript_jsx_attribute_value = braces
+ij_typescript_keep_blank_lines_in_code = 1
+ij_typescript_keep_first_column_comment = true
+ij_typescript_keep_indents_on_empty_lines = false
+ij_typescript_keep_line_breaks = true
+ij_typescript_keep_simple_blocks_in_one_line = true
+ij_typescript_keep_simple_methods_in_one_line = true
+ij_typescript_line_comment_add_space = true
+ij_typescript_line_comment_at_first_column = false
+ij_typescript_method_brace_style = end_of_line
+ij_typescript_method_call_chain_wrap = normal
+ij_typescript_method_parameters_new_line_after_left_paren = false
+ij_typescript_method_parameters_right_paren_on_new_line = false
+ij_typescript_method_parameters_wrap = normal
+ij_typescript_object_literal_wrap = on_every_item
+ij_typescript_object_types_wrap = on_every_item
+ij_typescript_parentheses_expression_new_line_after_left_paren = false
+ij_typescript_parentheses_expression_right_paren_on_new_line = false
+ij_typescript_place_assignment_sign_on_next_line = false
+ij_typescript_prefer_as_type_cast = false
+ij_typescript_prefer_explicit_types_function_expression_returns = false
+ij_typescript_prefer_explicit_types_function_returns = false
+ij_typescript_prefer_explicit_types_vars_fields = false
+ij_typescript_prefer_parameters_wrap = false
+ij_typescript_property_prefix = 
+ij_typescript_reformat_c_style_comments = false
+ij_typescript_space_after_colon = true
+ij_typescript_space_after_comma = true
+ij_typescript_space_after_dots_in_rest_parameter = false
+ij_typescript_space_after_generator_mult = true
+ij_typescript_space_after_property_colon = true
+ij_typescript_space_after_quest = true
+ij_typescript_space_after_type_colon = true
+ij_typescript_space_after_unary_not = false
+ij_typescript_space_before_async_arrow_lparen = true
+ij_typescript_space_before_catch_keyword = true
+ij_typescript_space_before_catch_left_brace = true
+ij_typescript_space_before_catch_parentheses = true
+ij_typescript_space_before_class_lbrace = true
+ij_typescript_space_before_class_left_brace = true
+ij_typescript_space_before_colon = true
+ij_typescript_space_before_comma = false
+ij_typescript_space_before_do_left_brace = true
+ij_typescript_space_before_else_keyword = true
+ij_typescript_space_before_else_left_brace = true
+ij_typescript_space_before_finally_keyword = true
+ij_typescript_space_before_finally_left_brace = true
+ij_typescript_space_before_for_left_brace = true
+ij_typescript_space_before_for_parentheses = true
+ij_typescript_space_before_for_semicolon = false
+ij_typescript_space_before_function_left_parenth = true
+ij_typescript_space_before_generator_mult = false
+ij_typescript_space_before_if_left_brace = true
+ij_typescript_space_before_if_parentheses = true
+ij_typescript_space_before_method_call_parentheses = false
+ij_typescript_space_before_method_left_brace = true
+ij_typescript_space_before_method_parentheses = false
+ij_typescript_space_before_property_colon = false
+ij_typescript_space_before_quest = true
+ij_typescript_space_before_switch_left_brace = true
+ij_typescript_space_before_switch_parentheses = true
+ij_typescript_space_before_try_left_brace = true
+ij_typescript_space_before_type_colon = false
+ij_typescript_space_before_unary_not = false
+ij_typescript_space_before_while_keyword = true
+ij_typescript_space_before_while_left_brace = true
+ij_typescript_space_before_while_parentheses = true
+ij_typescript_spaces_around_additive_operators = true
+ij_typescript_spaces_around_arrow_function_operator = true
+ij_typescript_spaces_around_assignment_operators = true
+ij_typescript_spaces_around_bitwise_operators = true
+ij_typescript_spaces_around_equality_operators = true
+ij_typescript_spaces_around_logical_operators = true
+ij_typescript_spaces_around_multiplicative_operators = true
+ij_typescript_spaces_around_relational_operators = true
+ij_typescript_spaces_around_shift_operators = true
+ij_typescript_spaces_around_unary_operator = false
+ij_typescript_spaces_within_array_initializer_brackets = false
+ij_typescript_spaces_within_brackets = false
+ij_typescript_spaces_within_catch_parentheses = false
+ij_typescript_spaces_within_for_parentheses = false
+ij_typescript_spaces_within_if_parentheses = false
+ij_typescript_spaces_within_imports = false
+ij_typescript_spaces_within_interpolation_expressions = false
+ij_typescript_spaces_within_method_call_parentheses = false
+ij_typescript_spaces_within_method_parentheses = false
+ij_typescript_spaces_within_object_literal_braces = false
+ij_typescript_spaces_within_object_type_braces = true
+ij_typescript_spaces_within_parentheses = false
+ij_typescript_spaces_within_switch_parentheses = false
+ij_typescript_spaces_within_type_assertion = false
+ij_typescript_spaces_within_union_types = true
+ij_typescript_spaces_within_while_parentheses = false
+ij_typescript_special_else_if_treatment = true
+ij_typescript_ternary_operation_signs_on_next_line = false
+ij_typescript_ternary_operation_wrap = normal
+ij_typescript_union_types_wrap = on_every_item
+ij_typescript_use_chained_calls_group_indents = false
+ij_typescript_use_double_quotes = true
+ij_typescript_use_explicit_js_extension = auto
+ij_typescript_use_path_mapping = always
+ij_typescript_use_public_modifier = false
+ij_typescript_use_semicolon_after_statement = true
+ij_typescript_var_declaration_wrap = normal
+ij_typescript_while_brace_force = always
+ij_typescript_while_on_new_line = false
+ij_typescript_wrap_comments = true
+
+[{*.bash,*.sh,*.zsh}]
+indent_size = 2
+tab_width = 2
+ij_shell_binary_ops_start_line = false
+ij_shell_keep_column_alignment_padding = false
+ij_shell_minify_program = false
+ij_shell_redirect_followed_by_space = false
+ij_shell_switch_cases_indented = false
+ij_shell_use_unix_line_separator = true
+
+[{*.cjs,*.js}]
+ij_javascript_align_imports = false
+ij_javascript_align_multiline_array_initializer_expression = false
+ij_javascript_align_multiline_binary_operation = false
+ij_javascript_align_multiline_chained_methods = false
+ij_javascript_align_multiline_extends_list = false
+ij_javascript_align_multiline_for = false
+ij_javascript_align_multiline_parameters = false
+ij_javascript_align_multiline_parameters_in_calls = false
+ij_javascript_align_multiline_ternary_operation = false
+ij_javascript_align_object_properties = 0
+ij_javascript_align_union_types = false
+ij_javascript_align_var_statements = 0
+ij_javascript_array_initializer_new_line_after_left_brace = false
+ij_javascript_array_initializer_right_brace_on_new_line = false
+ij_javascript_array_initializer_wrap = normal
+ij_javascript_assignment_wrap = normal
+ij_javascript_binary_operation_sign_on_next_line = false
+ij_javascript_binary_operation_wrap = normal
+ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
+ij_javascript_blank_lines_after_imports = 1
+ij_javascript_blank_lines_around_class = 1
+ij_javascript_blank_lines_around_field = 0
+ij_javascript_blank_lines_around_function = 1
+ij_javascript_blank_lines_around_method = 1
+ij_javascript_block_brace_style = end_of_line
+ij_javascript_block_comment_add_space = false
+ij_javascript_block_comment_at_first_column = true
+ij_javascript_call_parameters_new_line_after_left_paren = false
+ij_javascript_call_parameters_right_paren_on_new_line = false
+ij_javascript_call_parameters_wrap = normal
+ij_javascript_catch_on_new_line = false
+ij_javascript_chained_call_dot_on_new_line = true
+ij_javascript_class_brace_style = end_of_line
+ij_javascript_comma_on_new_line = false
+ij_javascript_do_while_brace_force = always
+ij_javascript_else_on_new_line = false
+ij_javascript_enforce_trailing_comma = remove
+ij_javascript_extends_keyword_wrap = normal
+ij_javascript_extends_list_wrap = normal
+ij_javascript_field_prefix = _
+ij_javascript_file_name_style = relaxed
+ij_javascript_finally_on_new_line = false
+ij_javascript_for_brace_force = always
+ij_javascript_for_statement_new_line_after_left_paren = false
+ij_javascript_for_statement_right_paren_on_new_line = false
+ij_javascript_for_statement_wrap = normal
+ij_javascript_force_quote_style = true
+ij_javascript_force_semicolon_style = true
+ij_javascript_function_expression_brace_style = end_of_line
+ij_javascript_if_brace_force = always
+ij_javascript_import_merge_members = global
+ij_javascript_import_prefer_absolute_path = global
+ij_javascript_import_sort_members = true
+ij_javascript_import_sort_module_name = false
+ij_javascript_import_use_node_resolution = true
+ij_javascript_imports_wrap = normal
+ij_javascript_indent_case_from_switch = true
+ij_javascript_indent_chained_calls = true
+ij_javascript_indent_package_children = 0
+ij_javascript_jsx_attribute_value = braces
+ij_javascript_keep_blank_lines_in_code = 1
+ij_javascript_keep_first_column_comment = true
+ij_javascript_keep_indents_on_empty_lines = false
+ij_javascript_keep_line_breaks = true
+ij_javascript_keep_simple_blocks_in_one_line = true
+ij_javascript_keep_simple_methods_in_one_line = true
+ij_javascript_line_comment_add_space = true
+ij_javascript_line_comment_at_first_column = false
+ij_javascript_method_brace_style = end_of_line
+ij_javascript_method_call_chain_wrap = normal
+ij_javascript_method_parameters_new_line_after_left_paren = false
+ij_javascript_method_parameters_right_paren_on_new_line = false
+ij_javascript_method_parameters_wrap = normal
+ij_javascript_object_literal_wrap = on_every_item
+ij_javascript_object_types_wrap = on_every_item
+ij_javascript_parentheses_expression_new_line_after_left_paren = false
+ij_javascript_parentheses_expression_right_paren_on_new_line = false
+ij_javascript_place_assignment_sign_on_next_line = false
+ij_javascript_prefer_as_type_cast = false
+ij_javascript_prefer_explicit_types_function_expression_returns = false
+ij_javascript_prefer_explicit_types_function_returns = false
+ij_javascript_prefer_explicit_types_vars_fields = false
+ij_javascript_prefer_parameters_wrap = false
+ij_javascript_property_prefix = 
+ij_javascript_reformat_c_style_comments = false
+ij_javascript_space_after_colon = true
+ij_javascript_space_after_comma = true
+ij_javascript_space_after_dots_in_rest_parameter = false
+ij_javascript_space_after_generator_mult = true
+ij_javascript_space_after_property_colon = true
+ij_javascript_space_after_quest = true
+ij_javascript_space_after_type_colon = true
+ij_javascript_space_after_unary_not = false
+ij_javascript_space_before_async_arrow_lparen = true
+ij_javascript_space_before_catch_keyword = true
+ij_javascript_space_before_catch_left_brace = true
+ij_javascript_space_before_catch_parentheses = true
+ij_javascript_space_before_class_lbrace = true
+ij_javascript_space_before_class_left_brace = true
+ij_javascript_space_before_colon = true
+ij_javascript_space_before_comma = false
+ij_javascript_space_before_do_left_brace = true
+ij_javascript_space_before_else_keyword = true
+ij_javascript_space_before_else_left_brace = true
+ij_javascript_space_before_finally_keyword = true
+ij_javascript_space_before_finally_left_brace = true
+ij_javascript_space_before_for_left_brace = true
+ij_javascript_space_before_for_parentheses = true
+ij_javascript_space_before_for_semicolon = false
+ij_javascript_space_before_function_left_parenth = true
+ij_javascript_space_before_generator_mult = false
+ij_javascript_space_before_if_left_brace = true
+ij_javascript_space_before_if_parentheses = true
+ij_javascript_space_before_method_call_parentheses = false
+ij_javascript_space_before_method_left_brace = true
+ij_javascript_space_before_method_parentheses = false
+ij_javascript_space_before_property_colon = false
+ij_javascript_space_before_quest = true
+ij_javascript_space_before_switch_left_brace = true
+ij_javascript_space_before_switch_parentheses = true
+ij_javascript_space_before_try_left_brace = true
+ij_javascript_space_before_type_colon = false
+ij_javascript_space_before_unary_not = false
+ij_javascript_space_before_while_keyword = true
+ij_javascript_space_before_while_left_brace = true
+ij_javascript_space_before_while_parentheses = true
+ij_javascript_spaces_around_additive_operators = true
+ij_javascript_spaces_around_arrow_function_operator = true
+ij_javascript_spaces_around_assignment_operators = true
+ij_javascript_spaces_around_bitwise_operators = true
+ij_javascript_spaces_around_equality_operators = true
+ij_javascript_spaces_around_logical_operators = true
+ij_javascript_spaces_around_multiplicative_operators = true
+ij_javascript_spaces_around_relational_operators = true
+ij_javascript_spaces_around_shift_operators = true
+ij_javascript_spaces_around_unary_operator = false
+ij_javascript_spaces_within_array_initializer_brackets = false
+ij_javascript_spaces_within_brackets = false
+ij_javascript_spaces_within_catch_parentheses = false
+ij_javascript_spaces_within_for_parentheses = false
+ij_javascript_spaces_within_if_parentheses = false
+ij_javascript_spaces_within_imports = false
+ij_javascript_spaces_within_interpolation_expressions = false
+ij_javascript_spaces_within_method_call_parentheses = false
+ij_javascript_spaces_within_method_parentheses = false
+ij_javascript_spaces_within_object_literal_braces = false
+ij_javascript_spaces_within_object_type_braces = true
+ij_javascript_spaces_within_parentheses = false
+ij_javascript_spaces_within_switch_parentheses = false
+ij_javascript_spaces_within_type_assertion = false
+ij_javascript_spaces_within_union_types = true
+ij_javascript_spaces_within_while_parentheses = false
+ij_javascript_special_else_if_treatment = true
+ij_javascript_ternary_operation_signs_on_next_line = false
+ij_javascript_ternary_operation_wrap = normal
+ij_javascript_union_types_wrap = on_every_item
+ij_javascript_use_chained_calls_group_indents = false
+ij_javascript_use_double_quotes = true
+ij_javascript_use_explicit_js_extension = auto
+ij_javascript_use_path_mapping = always
+ij_javascript_use_public_modifier = false
+ij_javascript_use_semicolon_after_statement = true
+ij_javascript_var_declaration_wrap = normal
+ij_javascript_while_brace_force = always
+ij_javascript_while_on_new_line = false
+ij_javascript_wrap_comments = true
+
+[{*.ft,*.vm,*.vsl}]
+ij_vtl_keep_indents_on_empty_lines = false
+
+[{*.gant,*.groovy,*.gy}]
 ij_groovy_align_group_field_declarations = false
 ij_groovy_align_multiline_array_initializer_expression = false
 ij_groovy_align_multiline_assignment = false
 ij_groovy_align_multiline_binary_operation = false
 ij_groovy_align_multiline_chained_methods = false
 ij_groovy_align_multiline_extends_list = false
-ij_groovy_align_multiline_for = true
+ij_groovy_align_multiline_for = false
+ij_groovy_align_multiline_list_or_map = false
 ij_groovy_align_multiline_method_parentheses = false
-ij_groovy_align_multiline_parameters = true
+ij_groovy_align_multiline_parameters = false
 ij_groovy_align_multiline_parameters_in_calls = false
-ij_groovy_align_multiline_resources = true
+ij_groovy_align_multiline_resources = false
 ij_groovy_align_multiline_ternary_operation = false
 ij_groovy_align_multiline_throws_list = false
+ij_groovy_align_named_args_in_map = false
 ij_groovy_align_throws_keyword = false
 ij_groovy_array_initializer_new_line_after_left_brace = false
 ij_groovy_array_initializer_right_brace_on_new_line = false
-ij_groovy_array_initializer_wrap = off
-ij_groovy_assert_statement_wrap = off
-ij_groovy_assignment_wrap = off
-ij_groovy_binary_operation_wrap = off
+ij_groovy_array_initializer_wrap = normal
+ij_groovy_assert_statement_wrap = normal
+ij_groovy_assignment_wrap = normal
+ij_groovy_binary_operation_wrap = normal
 ij_groovy_blank_lines_after_class_header = 0
 ij_groovy_blank_lines_after_imports = 1
 ij_groovy_blank_lines_after_package = 1
@@ -478,55 +848,75 @@ ij_groovy_blank_lines_before_imports = 1
 ij_groovy_blank_lines_before_method_body = 0
 ij_groovy_blank_lines_before_package = 0
 ij_groovy_block_brace_style = end_of_line
+ij_groovy_block_comment_add_space = false
 ij_groovy_block_comment_at_first_column = true
 ij_groovy_call_parameters_new_line_after_left_paren = false
 ij_groovy_call_parameters_right_paren_on_new_line = false
-ij_groovy_call_parameters_wrap = off
+ij_groovy_call_parameters_wrap = normal
 ij_groovy_catch_on_new_line = false
 ij_groovy_class_annotation_wrap = split_into_lines
 ij_groovy_class_brace_style = end_of_line
-ij_groovy_do_while_brace_force = never
+ij_groovy_class_count_to_use_import_on_demand = 999
+ij_groovy_do_while_brace_force = always
 ij_groovy_else_on_new_line = false
-ij_groovy_enum_constants_wrap = off
-ij_groovy_extends_keyword_wrap = off
-ij_groovy_extends_list_wrap = off
+ij_groovy_enable_groovydoc_formatting = true
+ij_groovy_enum_constants_wrap = split_into_lines
+ij_groovy_extends_keyword_wrap = normal
+ij_groovy_extends_list_wrap = normal
 ij_groovy_field_annotation_wrap = split_into_lines
 ij_groovy_finally_on_new_line = false
-ij_groovy_for_brace_force = never
+ij_groovy_for_brace_force = always
 ij_groovy_for_statement_new_line_after_left_paren = false
 ij_groovy_for_statement_right_paren_on_new_line = false
-ij_groovy_for_statement_wrap = off
-ij_groovy_if_brace_force = never
+ij_groovy_for_statement_wrap = normal
+ij_groovy_ginq_general_clause_wrap_policy = 2
+ij_groovy_ginq_having_wrap_policy = 1
+ij_groovy_ginq_indent_having_clause = true
+ij_groovy_ginq_indent_on_clause = true
+ij_groovy_ginq_on_wrap_policy = 1
+ij_groovy_ginq_space_after_keyword = true
+ij_groovy_if_brace_force = always
+ij_groovy_import_annotation_wrap = 2
+ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
 ij_groovy_indent_case_from_switch = true
-ij_groovy_keep_blank_lines_before_right_brace = 2
-ij_groovy_keep_blank_lines_in_code = 2
-ij_groovy_keep_blank_lines_in_declarations = 2
+ij_groovy_indent_label_blocks = true
+ij_groovy_insert_inner_class_imports = false
+ij_groovy_keep_blank_lines_before_right_brace = 1
+ij_groovy_keep_blank_lines_in_code = 1
+ij_groovy_keep_blank_lines_in_declarations = 1
 ij_groovy_keep_control_statement_in_one_line = true
 ij_groovy_keep_first_column_comment = true
 ij_groovy_keep_indents_on_empty_lines = false
 ij_groovy_keep_line_breaks = true
-ij_groovy_keep_multiple_expressions_in_one_line = false
-ij_groovy_keep_simple_blocks_in_one_line = false
+ij_groovy_keep_multiple_expressions_in_one_line = true
+ij_groovy_keep_simple_blocks_in_one_line = true
 ij_groovy_keep_simple_classes_in_one_line = true
 ij_groovy_keep_simple_lambdas_in_one_line = true
 ij_groovy_keep_simple_methods_in_one_line = true
+ij_groovy_label_indent_absolute = false
+ij_groovy_label_indent_size = 0
 ij_groovy_lambda_brace_style = end_of_line
+ij_groovy_layout_static_imports_separately = true
 ij_groovy_line_comment_add_space = false
+ij_groovy_line_comment_add_space_on_reformat = false
 ij_groovy_line_comment_at_first_column = true
 ij_groovy_method_annotation_wrap = split_into_lines
 ij_groovy_method_brace_style = end_of_line
-ij_groovy_method_call_chain_wrap = off
+ij_groovy_method_call_chain_wrap = normal
 ij_groovy_method_parameters_new_line_after_left_paren = false
 ij_groovy_method_parameters_right_paren_on_new_line = false
-ij_groovy_method_parameters_wrap = off
+ij_groovy_method_parameters_wrap = normal
 ij_groovy_modifier_list_wrap = false
-ij_groovy_parameter_annotation_wrap = off
+ij_groovy_names_count_to_use_import_on_demand = 999
+ij_groovy_packages_to_use_import_on_demand = *
+ij_groovy_parameter_annotation_wrap = normal
 ij_groovy_parentheses_expression_new_line_after_left_paren = false
 ij_groovy_parentheses_expression_right_paren_on_new_line = false
 ij_groovy_prefer_parameters_wrap = false
 ij_groovy_resource_list_new_line_after_left_paren = false
 ij_groovy_resource_list_right_paren_on_new_line = false
-ij_groovy_resource_list_wrap = off
+ij_groovy_resource_list_wrap = normal
+ij_groovy_space_after_assert_separator = true
 ij_groovy_space_after_colon = true
 ij_groovy_space_after_comma = true
 ij_groovy_space_after_comma_in_type_arguments = true
@@ -535,10 +925,12 @@ ij_groovy_space_after_quest = true
 ij_groovy_space_after_type_cast = true
 ij_groovy_space_before_annotation_parameter_list = false
 ij_groovy_space_before_array_initializer_left_brace = false
+ij_groovy_space_before_assert_separator = false
 ij_groovy_space_before_catch_keyword = true
 ij_groovy_space_before_catch_left_brace = true
 ij_groovy_space_before_catch_parentheses = true
 ij_groovy_space_before_class_left_brace = true
+ij_groovy_space_before_closure_left_brace = true
 ij_groovy_space_before_colon = true
 ij_groovy_space_before_comma = false
 ij_groovy_space_before_do_left_brace = true
@@ -555,6 +947,7 @@ ij_groovy_space_before_method_call_parentheses = false
 ij_groovy_space_before_method_left_brace = true
 ij_groovy_space_before_method_parentheses = false
 ij_groovy_space_before_quest = true
+ij_groovy_space_before_record_parentheses = false
 ij_groovy_space_before_switch_left_brace = true
 ij_groovy_space_before_switch_parentheses = true
 ij_groovy_space_before_synchronized_left_brace = true
@@ -564,6 +957,8 @@ ij_groovy_space_before_try_parentheses = true
 ij_groovy_space_before_while_keyword = true
 ij_groovy_space_before_while_left_brace = true
 ij_groovy_space_before_while_parentheses = true
+ij_groovy_space_in_named_argument = true
+ij_groovy_space_in_named_argument_before_colon = false
 ij_groovy_space_within_empty_array_initializer_braces = false
 ij_groovy_space_within_empty_method_call_parentheses = false
 ij_groovy_spaces_around_additive_operators = true
@@ -573,6 +968,7 @@ ij_groovy_spaces_around_equality_operators = true
 ij_groovy_spaces_around_lambda_arrow = true
 ij_groovy_spaces_around_logical_operators = true
 ij_groovy_spaces_around_multiplicative_operators = true
+ij_groovy_spaces_around_regex_operators = true
 ij_groovy_spaces_around_relational_operators = true
 ij_groovy_spaces_around_shift_operators = true
 ij_groovy_spaces_within_annotation_parentheses = false
@@ -582,42 +978,110 @@ ij_groovy_spaces_within_brackets = false
 ij_groovy_spaces_within_cast_parentheses = false
 ij_groovy_spaces_within_catch_parentheses = false
 ij_groovy_spaces_within_for_parentheses = false
+ij_groovy_spaces_within_gstring_injection_braces = false
 ij_groovy_spaces_within_if_parentheses = false
+ij_groovy_spaces_within_list_or_map = false
 ij_groovy_spaces_within_method_call_parentheses = false
 ij_groovy_spaces_within_method_parentheses = false
 ij_groovy_spaces_within_parentheses = false
 ij_groovy_spaces_within_switch_parentheses = false
 ij_groovy_spaces_within_synchronized_parentheses = false
 ij_groovy_spaces_within_try_parentheses = false
+ij_groovy_spaces_within_tuple_expression = false
 ij_groovy_spaces_within_while_parentheses = false
 ij_groovy_special_else_if_treatment = true
-ij_groovy_ternary_operation_wrap = off
-ij_groovy_throws_keyword_wrap = off
-ij_groovy_throws_list_wrap = off
-ij_groovy_variable_annotation_wrap = off
-ij_groovy_while_brace_force = never
+ij_groovy_ternary_operation_wrap = normal
+ij_groovy_throws_keyword_wrap = normal
+ij_groovy_throws_list_wrap = normal
+ij_groovy_use_flying_geese_braces = false
+ij_groovy_use_fq_class_names = false
+ij_groovy_use_fq_class_names_in_javadoc = false
+ij_groovy_use_relative_indents = false
+ij_groovy_use_single_class_imports = true
+ij_groovy_variable_annotation_wrap = normal
+ij_groovy_while_brace_force = always
 ij_groovy_while_on_new_line = false
+ij_groovy_wrap_chain_calls_after_dot = false
 ij_groovy_wrap_long_lines = false
 
-[{*.jhm,*.xslt,*.xul,*.tagx,*.rng,*.xsl,*.xsd,*.jspx,*.ant,*.xml,*.tld,*.fxml,*.wsdl,*.jrxml,*.jnlp,*.pom}]
-ij_xml_block_comment_at_first_column = true
-ij_xml_keep_indents_on_empty_lines = false
-ij_xml_line_comment_at_first_column = true
+[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
+indent_size = 2
+ij_json_array_wrapping = split_into_lines
+ij_json_keep_blank_lines_in_code = 0
+ij_json_keep_indents_on_empty_lines = false
+ij_json_keep_line_breaks = false
+ij_json_keep_trailing_comma = false
+ij_json_object_wrapping = split_into_lines
+ij_json_property_alignment = do_not_align
+ij_json_space_after_colon = true
+ij_json_space_after_comma = true
+ij_json_space_before_colon = false
+ij_json_space_before_comma = false
+ij_json_spaces_within_braces = false
+ij_json_spaces_within_brackets = false
+ij_json_wrap_long_lines = false
+
+[{*.htm,*.html,*.ng,*.peb,*.sht,*.shtm,*.shtml}]
+ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
+ij_html_align_attributes = true
+ij_html_align_text = false
+ij_html_attribute_wrap = normal
+ij_html_block_comment_add_space = false
+ij_html_block_comment_at_first_column = true
+ij_html_do_not_align_children_of_min_lines = 0
+ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
+ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
+ij_html_enforce_quotes = true
+ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
+ij_html_keep_blank_lines = 1
+ij_html_keep_indents_on_empty_lines = false
+ij_html_keep_line_breaks = true
+ij_html_keep_line_breaks_in_text = true
+ij_html_keep_whitespaces = false
+ij_html_keep_whitespaces_inside = span,pre,textarea
+ij_html_line_comment_at_first_column = true
+ij_html_new_line_after_last_attribute = never
+ij_html_new_line_before_first_attribute = never
+ij_html_quote_style = double
+ij_html_remove_new_line_before_tags = br
+ij_html_space_after_tag_name = false
+ij_html_space_around_equality_in_attribute = false
+ij_html_space_inside_empty_tag = false
+ij_html_text_wrap = normal
+
+[{*.http,*.rest}]
+indent_size = 0
+ij_continuation_indent_size = 4
+ij_http-request_call_parameters_wrap = normal
+ij_http-request_method_parameters_wrap = split_into_lines
+ij_http-request_space_before_comma = true
+ij_http-request_spaces_around_assignment_operators = true
+
+[{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}]
+ij_jsp_jsp_prefer_comma_separated_import_list = false
+ij_jsp_keep_indents_on_empty_lines = false
+
+[{*.jspx,*.tagx}]
+ij_jspx_keep_indents_on_empty_lines = false
 
 [{*.kt,*.kts}]
 ij_kotlin_align_in_columns_case_branch = false
 ij_kotlin_align_multiline_binary_operation = false
 ij_kotlin_align_multiline_extends_list = false
 ij_kotlin_align_multiline_method_parentheses = false
-ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters = false
 ij_kotlin_align_multiline_parameters_in_calls = false
-ij_kotlin_assignment_wrap = off
+ij_kotlin_allow_trailing_comma = false
+ij_kotlin_allow_trailing_comma_on_call_site = false
+ij_kotlin_assignment_wrap = normal
 ij_kotlin_blank_lines_after_class_header = 0
 ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_add_space = false
 ij_kotlin_block_comment_at_first_column = true
 ij_kotlin_call_parameters_new_line_after_left_paren = false
 ij_kotlin_call_parameters_right_paren_on_new_line = false
-ij_kotlin_call_parameters_wrap = off
+ij_kotlin_call_parameters_wrap = normal
 ij_kotlin_catch_on_new_line = false
 ij_kotlin_class_annotation_wrap = split_into_lines
 ij_kotlin_continuation_indent_for_chained_calls = true
@@ -628,30 +1092,34 @@ ij_kotlin_continuation_indent_in_if_conditions = true
 ij_kotlin_continuation_indent_in_parameter_lists = true
 ij_kotlin_continuation_indent_in_supertype_lists = true
 ij_kotlin_else_on_new_line = false
-ij_kotlin_enum_constants_wrap = off
-ij_kotlin_extends_list_wrap = off
+ij_kotlin_enum_constants_wrap = split_into_lines
+ij_kotlin_extends_list_wrap = normal
 ij_kotlin_field_annotation_wrap = split_into_lines
 ij_kotlin_finally_on_new_line = false
 ij_kotlin_if_rparen_on_new_line = false
 ij_kotlin_import_nested_classes = false
+ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
 ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
-ij_kotlin_keep_blank_lines_before_right_brace = 2
-ij_kotlin_keep_blank_lines_in_code = 2
-ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_blank_lines_before_right_brace = 1
+ij_kotlin_keep_blank_lines_in_code = 1
+ij_kotlin_keep_blank_lines_in_declarations = 1
 ij_kotlin_keep_first_column_comment = true
 ij_kotlin_keep_indents_on_empty_lines = false
 ij_kotlin_keep_line_breaks = true
 ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_break_after_multiline_when_entry = true
 ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_add_space_on_reformat = false
 ij_kotlin_line_comment_at_first_column = true
 ij_kotlin_method_annotation_wrap = split_into_lines
-ij_kotlin_method_call_chain_wrap = off
+ij_kotlin_method_call_chain_wrap = normal
 ij_kotlin_method_parameters_new_line_after_left_paren = false
 ij_kotlin_method_parameters_right_paren_on_new_line = false
-ij_kotlin_method_parameters_wrap = off
-ij_kotlin_name_count_to_use_star_import = 5
-ij_kotlin_name_count_to_use_star_import_for_members = 3
-ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_method_parameters_wrap = normal
+ij_kotlin_name_count_to_use_star_import = 2147483647
+ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
+ij_kotlin_packages_to_use_import_on_demand = 
+ij_kotlin_parameter_annotation_wrap = normal
 ij_kotlin_space_after_comma = true
 ij_kotlin_space_after_extend_colon = true
 ij_kotlin_space_after_type_colon = true
@@ -674,48 +1142,65 @@ ij_kotlin_spaces_around_range = false
 ij_kotlin_spaces_around_relational_operators = true
 ij_kotlin_spaces_around_unary_operator = false
 ij_kotlin_spaces_around_when_arrow = true
-ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_variable_annotation_wrap = normal
 ij_kotlin_while_on_new_line = false
 ij_kotlin_wrap_elvis_expressions = 1
-ij_kotlin_wrap_expression_body_functions = 0
+ij_kotlin_wrap_expression_body_functions = 1
 ij_kotlin_wrap_first_method_in_call_chain = false
 
-[{*.shtm,*.htm,*.sht,*.shtml,*.html}]
-ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
-ij_html_align_attributes = true
-ij_html_align_text = false
-ij_html_attribute_wrap = normal
-ij_html_block_comment_at_first_column = true
-ij_html_do_not_align_children_of_min_lines = 0
-ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
-ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
-ij_html_enforce_quotes = false
-ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
-ij_html_keep_blank_lines = 2
-ij_html_keep_indents_on_empty_lines = false
-ij_html_keep_line_breaks = true
-ij_html_keep_line_breaks_in_text = true
-ij_html_keep_whitespaces = false
-ij_html_keep_whitespaces_inside = span,pre,textarea
-ij_html_line_comment_at_first_column = true
-ij_html_new_line_after_last_attribute = never
-ij_html_new_line_before_first_attribute = never
-ij_html_quote_style = double
-ij_html_remove_new_line_before_tags = br
-ij_html_space_after_tag_name = false
-ij_html_space_around_equality_in_attribute = false
-ij_html_space_inside_empty_tag = false
-ij_html_text_wrap = normal
+[{*.markdown,*.md}]
+ij_markdown_force_one_space_after_blockquote_symbol = true
+ij_markdown_force_one_space_after_header_symbol = true
+ij_markdown_force_one_space_after_list_bullet = true
+ij_markdown_force_one_space_between_words = true
+ij_markdown_format_tables = true
+ij_markdown_insert_quote_arrows_on_wrap = true
+ij_markdown_keep_indents_on_empty_lines = false
+ij_markdown_keep_line_breaks_inside_text_blocks = true
+ij_markdown_max_lines_around_block_elements = 1
+ij_markdown_max_lines_around_header = 1
+ij_markdown_max_lines_between_paragraphs = 1
+ij_markdown_min_lines_around_block_elements = 1
+ij_markdown_min_lines_around_header = 1
+ij_markdown_min_lines_between_paragraphs = 1
+ij_markdown_wrap_text_if_long = true
+ij_markdown_wrap_text_inside_blockquotes = true
 
-[{*.yml,*.yaml}]
+[{*.pb,*.textproto}]
 indent_size = 2
-ij_continuation_indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_prototext_keep_blank_lines_in_code = 2
+ij_prototext_keep_indents_on_empty_lines = false
+ij_prototext_keep_line_breaks = true
+ij_prototext_space_after_colon = true
+ij_prototext_space_after_comma = true
+ij_prototext_space_before_colon = false
+ij_prototext_space_before_comma = false
+ij_prototext_spaces_within_braces = true
+ij_prototext_spaces_within_brackets = false
+
+[{*.properties,spring.handlers,spring.schemas}]
+ij_properties_align_group_field_declarations = false
+ij_properties_keep_blank_lines = false
+ij_properties_key_value_delimiter = equals
+ij_properties_spaces_around_key_value_delimiter = false
+
+[{*.qute.htm,*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}]
+ij_qute_keep_indents_on_empty_lines = false
+
+[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
+ij_toml_keep_indents_on_empty_lines = false
+
+[{*.yaml,*.yml}]
+indent_size = 2
+ij_yaml_align_values_properties = do_not_align
+ij_yaml_autoinsert_sequence_marker = true
+ij_yaml_block_mapping_on_new_line = false
+ij_yaml_indent_sequence_value = true
 ij_yaml_keep_indents_on_empty_lines = false
 ij_yaml_keep_line_breaks = true
-
-[{*.zsh,*.bash,*.sh}]
-ij_shell_binary_ops_start_line = false
-ij_shell_keep_column_alignment_padding = false
-ij_shell_minify_program = false
-ij_shell_redirect_followed_by_space = false
-ij_shell_switch_cases_indented = false
+ij_yaml_sequence_on_new_line = false
+ij_yaml_space_before_colon = false
+ij_yaml_spaces_within_braces = true
+ij_yaml_spaces_within_brackets = true
diff --git a/README.md b/README.md
index a6c51d49..0ec10a33 100644
--- a/README.md
+++ b/README.md
@@ -78,7 +78,6 @@ AlpacaAPI alpacaAPI = new AlpacaAPI(oAuthToken);
 AlpacaAPI alpacaAPI = AlpacaAPI.builder()
     .withEndpointAPIType(EndpointAPIType.PAPER)
     .build();
-
 ```
 
 Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests. These threads persist even if the main thread exists so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) so your program can exit without calling `System.exit()`, use the following snippet:

From 92656f777c005302bb3245bd0a38e6ecfa6918ca Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 20 Aug 2023 16:37:54 -0600
Subject: [PATCH 50/85] Update to 9.2.0

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 0ec10a33..82630ee6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ If you are using Gradle as your build tool, add the following dependency to your
 
 ```
 dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.1.4'
+    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.2.0'
 }
 ```
 
@@ -27,7 +27,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.1.4</version>
+    <version>9.2.0</version>
     <scope>compile</scope>
 </dependency>
 ```
diff --git a/build.gradle b/build.gradle
index a40691fa..a861d055 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ apply plugin: 'jsonschema2pojo'
 
 archivesBaseName = 'alpaca-java'
 group = 'net.jacobpeterson'
-version = '9.1.4-SNAPSHOT'
+version = '9.2.0-SNAPSHOT'
 
 repositories {
     mavenCentral()

From 5d1f23126fb83cec4630630698fcba92fc367d56 Mon Sep 17 00:00:00 2001
From: Jc <jmg129@gmail.com>
Date: Tue, 7 Nov 2023 16:19:17 -0500
Subject: [PATCH 51/85] Fix

---
 .../alpaca/rest/endpoint/orders/OrdersEndpoint.java             | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
index 0ad4a66e..b2a0563d 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
@@ -251,7 +251,7 @@ public Order requestMarketOrder(String symbol, Integer quantity, OrderSide side,
      */
     public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderSide side)
             throws AlpacaClientException {
-        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null,
+        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.DAY, null,
                 null, null,
                 null, null, null, null, null, null, null);
     }

From 8d9ca1434d3bb5f29ca68eac16267d788021140e Mon Sep 17 00:00:00 2001
From: gretard <gretard@users.noreply.github.com>
Date: Tue, 2 Jan 2024 21:00:58 +0200
Subject: [PATCH 52/85] Add corporate actions endpoint for #118

---
 build.gradle                                  |   1 +
 .../common/announcement.json                  |  53 ++++++++
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |   9 ++
 .../CorporateActionsEndpoint.java             | 121 ++++++++++++++++++
 .../alpaca/test/mock/AlpacaAPITest.java       |  13 ++
 .../CorporateActionsEndpointTest.java         | 104 +++++++++++++++
 6 files changed, 301 insertions(+)
 create mode 100644 schema_json/endpoint/corporate_actions/common/announcement.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java
 create mode 100644 src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java

diff --git a/build.gradle b/build.gradle
index a861d055..7c65cc3d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -53,6 +53,7 @@ dependencies {
     testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.36'
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.8.2'
     testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.6.1'
+    testImplementation group: 'com.github.gmazzo.okhttp.mock', name: 'mock-client', version: '2.0.0'
 }
 
 // Exclude any SLF4j-implementation transitive dependencies so users can use a logging framework of their choice
diff --git a/schema_json/endpoint/corporate_actions/common/announcement.json b/schema_json/endpoint/corporate_actions/common/announcement.json
new file mode 100644
index 00000000..14c8e66e
--- /dev/null
+++ b/schema_json/endpoint/corporate_actions/common/announcement.json
@@ -0,0 +1,53 @@
+{
+  "type": "object",
+  "title": "See <a href=\"https://docs.alpaca.markets/reference/get-v2-corporate_actions-announcements\">Corporate announcement</a>.",
+  "properties": {
+    "id": {
+      "existingJavaType": "java.lang.String"
+    },
+    "corporate_actions_id": {
+      "existingJavaType": "java.lang.String"
+    },
+    "ca_type": {
+      "existingJavaType": "java.lang.String"
+    },
+    "ca_sub_type": {
+      "existingJavaType": "java.lang.String"
+    },
+    "initiating_symbol": {
+      "existingJavaType": "java.lang.String"
+    },
+    "initiating_original_cusip": {
+      "existingJavaType": "java.lang.String"
+    },
+    "target_symbol": {
+      "target_symbol": "java.lang.String"
+    },
+    "target_original_cusip": {
+      "target_original_cusip": "java.lang.String"
+    },
+    "declaration_date": {
+      "declaration_date": "java.lang.String"
+    },
+    "expiration_date": {
+      "declaration_date": "java.lang.String"
+    },
+    "record_date": {
+      "declaration_date": "java.lang.String"
+    },
+    "payable_date": {
+      "declaration_date": "java.lang.String"
+    },
+    "cash": {
+      "declaration_date": "java.lang.String"
+    },
+    "old_rate": {
+      "declaration_date": "java.lang.String"
+    },
+    "new_rate": {
+      "declaration_date": "java.lang.String"
+    }
+    
+    
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index d1e75376..922ddd10 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -12,6 +12,7 @@
 import net.jacobpeterson.alpaca.rest.endpoint.assets.AssetsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.calendar.CalendarEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
+import net.jacobpeterson.alpaca.rest.endpoint.corporateactions.CorporateActionsEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
 import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
@@ -67,6 +68,7 @@ public class AlpacaAPI {
     private final CryptoMarketDataWebsocket cryptoMarketDataWebsocket;
     private final StockMarketDataWebsocket stockMarketDataWebsocket;
 
+    private final CorporateActionsEndpoint corporateActionsEndpoint;
     /**
      * Instantiates a new {@link AlpacaAPI} using properties specified in <code>alpaca.properties</code> file (or their
      * associated defaults).
@@ -197,6 +199,7 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
                 new CryptoMarketDataWebsocket(okHttpClient, keyID, secretKey);
         stockMarketDataWebsocket = stockDataClient == null ? null :
                 new StockMarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
+        corporateActionsEndpoint = new CorporateActionsEndpoint(brokerClient);
     }
 
     /**
@@ -320,6 +323,12 @@ public AlpacaClient getStockDataClient() {
         return stockDataClient;
     }
 
+    /**
+     * @return the {@link CorporateActionsEndpoint}
+     */
+    public CorporateActionsEndpoint corporateActions() {
+        return corporateActionsEndpoint;
+    }
     /**
      * Creates a {@link Builder} for {@link AlpacaAPI}.
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java
new file mode 100644
index 00000000..f467c4f0
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java
@@ -0,0 +1,121 @@
+package net.jacobpeterson.alpaca.rest.endpoint.corporateactions;
+
+import com.google.gson.reflect.TypeToken;
+import net.jacobpeterson.alpaca.model.endpoint.corporateactions.common.Announcement;
+import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+
+import java.lang.reflect.Type;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link AlpacaEndpoint} for
+ * <a href="https://docs.alpaca.markets/reference/get-v2-corporate_actions-announcements-id">Corporate actions</a>
+ * Endpoint.
+ */
+public class CorporateActionsEndpoint extends AlpacaEndpoint {
+
+    private static final Type ANNOUNCEMENT_ARRAYLIST_TYPE = new TypeToken<ArrayList<Announcement>>() {}.getType();
+
+    private static final List<String> ALL_CA_TYPES = Arrays.asList("dividend", "merger", "spinoff", "split");
+
+    /**
+     * Instantiates a new {@link AlpacaEndpoint}.
+     *
+     * @param alpacaClient the {@link AlpacaClient}
+     */
+    public CorporateActionsEndpoint(AlpacaClient alpacaClient) {
+        super(alpacaClient, "corporate_actions/announcements");
+    }
+
+    /**
+     * Gets a {@link Announcement} of the requested id.
+     * @param id - announcement id to query for
+     * @return the {@link Announcement}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public Announcement getAnnouncement(String id) throws AlpacaClientException {
+        checkNotNull(id);
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment)
+                .addPathSegment(id);
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, Announcement.class);
+
+    }
+
+    /**
+     * Gets list of all {@link Announcement} within last 90 days
+     * @return a  {@link List} of {@link Announcement}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public List<Announcement> getAnnouncements() throws AlpacaClientException {
+        return this.getAnnouncements(ALL_CA_TYPES, LocalDate.now().minusDays(90), LocalDate.now(), null, null, null);
+    }
+
+    /**
+     *  Gets list of all {@link Announcement} within last 90 days for specific symbol
+     * @param symbol the symbol to query for
+     * @return a  {@link List} of {@link Announcement}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public List<Announcement> getAnnouncements(String symbol) throws AlpacaClientException {
+        return this.getAnnouncements(ALL_CA_TYPES, LocalDate.now().minusDays(90), LocalDate.now(), symbol, null, null);
+    }
+
+    /**
+     *  Gets list of all {@link Announcement} for specific query
+     * @param caTypes - a list of Dividend, Merger, Spinoff, or Split
+     * @param since - the start (inclusive) of the date range when searching corporate action announcements. This should follow the YYYY-MM-DD format. The date range is limited to 90 days
+     * @param until - the end (inclusive) of the date range when searching corporate action announcements. This should follow the YYYY-MM-DD format. The date range is limited to 90 days
+     * @param symbol - the symbol of the company initiating the announcement
+     * @param cusip - the CUSIP of the company initiating the announcement
+     * @param dateType - one of declaration_date, ex_date, record_date, or payable_date
+     * @return a  {@link List} of {@link Announcement}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public List<Announcement> getAnnouncements(
+            List<String> caTypes, LocalDate since, LocalDate until,
+            String symbol,
+            String cusip,
+            String dateType
+            )
+            throws AlpacaClientException {
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment);
+        checkNotNull(caTypes);
+        checkNotNull(since);
+        checkNotNull(until);
+
+
+        urlBuilder.addQueryParameter("ca_types", String.join(",", caTypes));
+
+        urlBuilder.addQueryParameter("since", since.toString());
+        urlBuilder.addQueryParameter("until", until.toString());
+
+        if (symbol != null) {
+            urlBuilder.addQueryParameter("symbol", symbol);
+        }
+        if (cusip != null) {
+            urlBuilder.addQueryParameter("cusip", cusip);
+        }
+        if (dateType !=null) {
+            urlBuilder.addQueryParameter("date_type", dateType);
+        }
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+        return alpacaClient.requestObject(request, ANNOUNCEMENT_ARRAYLIST_TYPE);
+
+    }
+}
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
index d5f4f320..0fbc722a 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
@@ -6,6 +6,8 @@
 import okhttp3.OkHttpClient;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
 /**
  * {@link AlpacaAPITest} tests {@link AlpacaAPI} using mocked objects with Mockito.
  */
@@ -71,4 +73,15 @@ public void testAlpacaAPIConstructor_okHttpClient_keyID_secret_oAuthToken_endpoi
         new AlpacaAPI(okHttpClient, null, null, oAuthToken, EndpointAPIType.PAPER, DataAPIType.IEX);
         new AlpacaAPI(okHttpClient, keyID, secret, null, EndpointAPIType.LIVE, DataAPIType.SIP);
     }
+
+    @Test
+    public void testAlpacaAPIConstructor_corporateActionsEndpoint() {
+        OkHttpClient okHttpClient = new OkHttpClient();
+        String keyID = "ABCDEFGHIJKLM";
+        String secret = "NOPQURSTUVWXYZ";
+
+        AlpacaAPI api = new AlpacaAPI(okHttpClient, keyID, secret, null, EndpointAPIType.LIVE, DataAPIType.SIP);
+
+        assertNotNull(api.corporateActions());
+    }
 }
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
new file mode 100644
index 00000000..fd2dc0ae
--- /dev/null
+++ b/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
@@ -0,0 +1,104 @@
+package net.jacobpeterson.alpaca.test.mock.corporateactions;
+
+import net.jacobpeterson.alpaca.model.endpoint.corporateactions.common.Announcement;
+import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.corporateactions.CorporateActionsEndpoint;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.ResponseBody;
+import okhttp3.mock.MockInterceptor;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CorporateActionsEndpointTest {
+
+    @Test
+    void getAnnouncements_givenSymbol_shouldReturnData() throws AlpacaClientException {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
+
+        java.util.List<Announcement> result = newsEndpoint.getAnnouncements("K");
+
+        assertNotNull(result);
+        assertEquals(3, result.size());
+
+    }
+
+    @Test
+    void getAnnouncements_shouldReturnData() throws AlpacaClientException {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
+
+        java.util.List<Announcement> result = newsEndpoint.getAnnouncements();
+
+        assertNotNull(result);
+        assertEquals(3, result.size());
+
+    }
+
+    @Test
+    void getAnnouncements_validation_caTypes() {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
+
+        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncements(null, LocalDate.now(), LocalDate.now(), null, null, null));
+    }
+
+    @Test
+    void getAnnouncements_validation_since() {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
+
+        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncements(new ArrayList<>(), null, LocalDate.now(), null, null, null));
+    }
+
+    @Test
+    void getAnnouncements_validation_until() {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
+
+        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncements(new ArrayList<>(), LocalDate.now(), null, null, null, null));
+    }
+
+    @Test
+    void getAnnouncement_validation_id() {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockSingleAnnouncementResponse());
+
+        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncement(null));
+    }
+
+    @Test
+    void getAnnouncement_shouldReturnData() throws AlpacaClientException {
+        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockSingleAnnouncementResponse());
+
+        Announcement announcement = newsEndpoint.getAnnouncement("1");
+
+        assertNotNull(announcement);
+        assertEquals(announcement.getId(), "cd0cb768-889f-47a1-a94b-1438017dad77");
+    }
+
+    private static CorporateActionsEndpoint setupEndpoint(String response) {
+        MockInterceptor interceptor = new MockInterceptor();
+        interceptor.addRule()
+                .respond(200)
+                .body(ResponseBody.create(response, MediaType.parse("application/json")));
+
+        OkHttpClient client = new OkHttpClient.Builder()
+                .addInterceptor(interceptor)
+                .build();
+
+        AlpacaClient api = new AlpacaClient(client, "token", "test", "/v1");
+
+        return new CorporateActionsEndpoint(api);
+    }
+
+    private static String mockMultipleAnnouncementsResponse() {
+        return "[{\"id\":\"cd0cb768-889f-47a1-a94b-1438017dad77\",\"corporate_action_id\":\"2642471\",\"ca_type\":\"spinoff\",\"ca_sub_type\":\"spinoff\",\"initiating_symbol\":\"KLG\",\"initiating_original_cusip\":\"92942W107\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"effective_date\":\"2023-09-08\",\"ex_date\":\"2023-10-02\",\"record_date\":\"2023-09-21\",\"payable_date\":\"2023-10-02\",\"cash\":\"0\",\"old_rate\":\"1\",\"new_rate\":\"0.25\"},{\"id\":\"3a7fe233-7766-4f70-915f-9d1a5258eaa9\",\"corporate_action_id\":\"487836108_AD23\",\"ca_type\":\"dividend\",\"ca_sub_type\":\"cash\",\"initiating_symbol\":\"K\",\"initiating_original_cusip\":\"487836108\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"declaration_date\":\"2023-09-19\",\"effective_date\":\"2023-09-21\",\"ex_date\":\"2023-10-02\",\"record_date\":\"2023-09-21\",\"payable_date\":\"2023-10-02\",\"cash\":\"0\",\"old_rate\":\"1\",\"new_rate\":\"1\"},{\"id\":\"ddf64bcb-fa76-4472-a98b-237f220ea38b\",\"corporate_action_id\":\"487836108_AE23\",\"ca_type\":\"dividend\",\"ca_sub_type\":\"cash\",\"initiating_symbol\":\"K\",\"initiating_original_cusip\":\"487836108\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"declaration_date\":\"2023-11-29\",\"effective_date\":\"2023-12-01\",\"ex_date\":\"2023-11-30\",\"record_date\":\"2023-12-01\",\"payable_date\":\"2023-12-15\",\"cash\":\"0.56\",\"old_rate\":\"1\",\"new_rate\":\"1\"}]";
+    }
+
+    private static String mockSingleAnnouncementResponse() {
+        return "{\"id\":\"cd0cb768-889f-47a1-a94b-1438017dad77\",\"corporate_action_id\":\"2642471\",\"ca_type\":\"spinoff\",\"ca_sub_type\":\"spinoff\",\"initiating_symbol\":\"KLG\",\"initiating_original_cusip\":\"92942W107\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"effective_date\":\"2023-09-08\",\"ex_date\":\"2023-10-02\",\"record_date\":\"2023-09-21\",\"payable_date\":\"2023-10-02\",\"cash\":\"0\",\"old_rate\":\"1\",\"new_rate\":\"0.25\"}";
+    }
+}

From 6d5a0436da2de9d6ebadeb5fad76f820a6ea0545 Mon Sep 17 00:00:00 2001
From: gretard <gretard@users.noreply.github.com>
Date: Mon, 8 Jan 2024 20:23:20 +0200
Subject: [PATCH 53/85] Add news actions endpoints for #118

---
 README.md                                     |  33 +++++
 .../control/subscriptions_message.json        |   4 +
 .../enums/market_data_message_type.json       |   6 +-
 .../market_data/news/common/image.json        |  13 ++
 .../market_data/news/common/news_article.json |  39 ++++++
 .../market_data/news/news_response.json       |  12 ++
 .../news/realtime/news_message.json           |  40 ++++++
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |  32 ++++-
 .../marketdata/news/NewsEndpoint.java         | 120 ++++++++++++++++++
 .../marketdata/MarketDataWebsocket.java       |  32 +++--
 .../MarketDataWebsocketInterface.java         |  11 +-
 .../news/NewsMarketDataWebsocket.java         |  15 +++
 .../CorporateActionsEndpointTest.java         |  11 +-
 .../marketdata/news/NewsEndpointTest.java     | 111 ++++++++++++++++
 14 files changed, 461 insertions(+), 18 deletions(-)
 create mode 100644 schema_json/endpoint/market_data/news/common/image.json
 create mode 100644 schema_json/endpoint/market_data/news/common/news_article.json
 create mode 100644 schema_json/endpoint/market_data/news/news_response.json
 create mode 100644 schema_json/endpoint/market_data/news/realtime/news_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java
 create mode 100644 src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java

diff --git a/README.md b/README.md
index 82630ee6..c483ee2d 100644
--- a/README.md
+++ b/README.md
@@ -495,6 +495,39 @@ alpacaAPI.cryptoMarketDataStreaming().subscribe(
         Arrays.asList("*"));
 ```
 
+## [`NewsMarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java)
+Alpaca also offers news websocket streaming for symbols.
+
+The usage is identical to the [`StockMarketDataWebsocket`](https://github.com/Petersoj/alpaca-java/#stockmarketdatawebsocket) usage except that accessing the websocket instance via the `AlpacaAPI` instance is done using: `alpacaAPI.newsMarketDataStreaming()` instead of `alpacaAPI.stockMarketDataStreaming()`.
+
+Example usage:
+```java
+MarketDataListener marketDataListener = (messageType, message) ->
+                System.out.printf("%s: %s\n", messageType.name(), message);
+
+MarketDataWebsocketInterface newsWebsocket = alpacaAPI.newsMarketDataStreaming();
+
+newsWebsocket.setListener(marketDataListener);
+newsWebsocket.subscribeToControl(
+                MarketDataMessageType.SUCCESS,
+                MarketDataMessageType.SUBSCRIPTION,
+                MarketDataMessageType.ERROR);
+
+newsWebsocket.connect();
+newsWebsocket.waitForAuthorization(5, TimeUnit.SECONDS);
+if (!newsWebsocket.isValid()) {
+            System.out.println("Websocket not valid!");
+            return;
+}
+newsWebsocket.subscribe(null, null, null, Arrays.asList("*"));
+
+// Wait a few seconds
+Thread.sleep(5000);
+
+// Manually disconnect the websocket
+newsWebsocket.disconnect();
+```
+
 # Building
 To build this project yourself, clone this repository and run:
 ```
diff --git a/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json b/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
index c302083a..b44cbb85 100644
--- a/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
+++ b/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
@@ -16,6 +16,10 @@
     "bars": {
       "existingJavaType": "java.util.ArrayList<java.lang.String>",
       "title": "The {@link java.util.ArrayList} of subscribed bars."
+    },
+    "news": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of subscribed news."
     }
   }
 }
diff --git a/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json b/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
index f55a5190..6999050d 100644
--- a/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
+++ b/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
@@ -7,7 +7,8 @@
     "subscription",
     "t",
     "q",
-    "b"
+    "b",
+    "n"
   ],
   "javaEnums": [
     {
@@ -27,6 +28,9 @@
     },
     {
       "name": "BAR"
+    },
+    {
+      "name": "NEWS"
     }
   ]
 }
diff --git a/schema_json/endpoint/market_data/news/common/image.json b/schema_json/endpoint/market_data/news/common/image.json
new file mode 100644
index 00000000..2f3bf607
--- /dev/null
+++ b/schema_json/endpoint/market_data/news/common/image.json
@@ -0,0 +1,13 @@
+{
+    "type": "object",
+    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
+    "properties": {
+      "size": {
+        "existingJavaType": "java.lang.String"
+      },
+      "url": {
+        "existingJavaType": "java.lang.String"
+      }
+    }
+  }
+  
\ No newline at end of file
diff --git a/schema_json/endpoint/market_data/news/common/news_article.json b/schema_json/endpoint/market_data/news/common/news_article.json
new file mode 100644
index 00000000..ee80a6d7
--- /dev/null
+++ b/schema_json/endpoint/market_data/news/common/news_article.json
@@ -0,0 +1,39 @@
+{
+    "type": "object",
+    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
+    "properties": {
+      "author": {
+        "existingJavaType": "java.lang.String"
+      },
+      "content": {
+        "existingJavaType": "java.lang.String"
+      },
+      "created_at": {
+        "existingJavaType": "java.time.ZonedDateTime"
+      },
+      "headline": {
+        "existingJavaType": "java.lang.String"
+      },
+      "id": {
+        "existingJavaType": "java.lang.Long"
+      },
+      "images": {
+        "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.news.common.Image>"
+      },
+      "source": {
+        "existingJavaType": "java.lang.String"
+      },
+      "summary": {
+        "existingJavaType": "java.lang.String"
+      },
+      "symbols": {
+        "existingJavaType": "java.util.ArrayList<java.lang.String>"
+      },
+      "updated_at": {
+        "existingJavaType": "java.time.ZonedDateTime"
+      },
+      "url": {
+        "existingJavaType": "java.lang.String"
+      }
+    }
+  }
diff --git a/schema_json/endpoint/market_data/news/news_response.json b/schema_json/endpoint/market_data/news/news_response.json
new file mode 100644
index 00000000..b25ed7f9
--- /dev/null
+++ b/schema_json/endpoint/market_data/news/news_response.json
@@ -0,0 +1,12 @@
+{
+    "type": "object",
+    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
+    "properties": {
+      "news": {
+        "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.news.common.NewsArticle>"
+      },
+      "next_page_token": {
+        "existingJavaType": "java.lang.String"
+      }
+    }
+}
diff --git a/schema_json/endpoint/market_data/news/realtime/news_message.json b/schema_json/endpoint/market_data/news/realtime/news_message.json
new file mode 100644
index 00000000..bee011fe
--- /dev/null
+++ b/schema_json/endpoint/market_data/news/realtime/news_message.json
@@ -0,0 +1,40 @@
+{
+    "type": "object",
+    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
+  },
+  "properties": {
+    "headline": {
+      "existingJavaType": "java.lang.String"
+    },
+    "created_at": {
+      "existingJavaType": "java.time.ZonedDateTime"
+    },
+    "updated_at": {
+      "existingJavaType": "java.time.ZonedDateTime"
+    },
+      "author": {
+        "existingJavaType": "java.lang.String"
+      },
+    "summary": {
+      "existingJavaType": "java.lang.String"
+    },
+      "content": {
+        "existingJavaType": "java.lang.String"
+      },
+
+    "url": {
+      "existingJavaType": "java.lang.String"
+    },
+    "symbols": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>"
+    },
+    "source": {
+      "existingJavaType": "java.lang.String"
+    },
+      "id": {
+        "existingJavaType": "java.lang.Long"
+      }
+    }
+  }
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 922ddd10..4292e85d 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -23,6 +23,8 @@
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
 import net.jacobpeterson.alpaca.websocket.marketdata.crypto.CryptoMarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.stock.StockMarketDataWebsocket;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.news.NewsEndpoint;
+import net.jacobpeterson.alpaca.websocket.marketdata.news.NewsMarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocket;
 import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocketInterface;
 import okhttp3.OkHttpClient;
@@ -46,11 +48,13 @@ public class AlpacaAPI {
 
     private static final String VERSION_2_PATH_SEGMENT = "v2";
     private static final String VERSION_1_BETA_3_PATH_SEGMENT = "v1beta3";
+    private static final String VERSION_1_BETA_1_PATH_SEGMENT = "v1beta1";
 
     private final OkHttpClient okHttpClient;
     private final AlpacaClient brokerClient;
     private final AlpacaClient cryptoDataClient;
     private final AlpacaClient stockDataClient;
+    private final AlpacaClient newsDataClient;
     // Ordering of fields/methods below are analogous to the ordering in the Alpaca documentation
     private final AccountEndpoint accountEndpoint;
     private final CryptoMarketDataEndpoint cryptoMarketDataEndpoint;
@@ -67,8 +71,10 @@ public class AlpacaAPI {
     private final StreamingWebsocket streamingWebsocket;
     private final CryptoMarketDataWebsocket cryptoMarketDataWebsocket;
     private final StockMarketDataWebsocket stockMarketDataWebsocket;
-
     private final CorporateActionsEndpoint corporateActionsEndpoint;
+    private final NewsMarketDataWebsocket newsMarketDataWebsocket;
+    private final NewsEndpoint newsEndpoint;
+
     /**
      * Instantiates a new {@link AlpacaAPI} using properties specified in <code>alpaca.properties</code> file (or their
      * associated defaults).
@@ -175,10 +181,12 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
                     brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
             cryptoDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_3_PATH_SEGMENT);
             stockDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_2_PATH_SEGMENT);
+            newsDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_1_PATH_SEGMENT);
         } else {
             brokerClient = new AlpacaClient(okHttpClient, oAuthToken, brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
             cryptoDataClient = null;
             stockDataClient = null;
+            newsDataClient = null;
         }
 
         accountEndpoint = new AccountEndpoint(brokerClient);
@@ -200,6 +208,8 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         stockMarketDataWebsocket = stockDataClient == null ? null :
                 new StockMarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
         corporateActionsEndpoint = new CorporateActionsEndpoint(brokerClient);
+        newsEndpoint = newsDataClient == null ? null :  new NewsEndpoint(newsDataClient);
+        newsMarketDataWebsocket = newsDataClient == null ? null : new NewsMarketDataWebsocket(okHttpClient,  keyID, secretKey);
     }
 
     /**
@@ -323,12 +333,32 @@ public AlpacaClient getStockDataClient() {
         return stockDataClient;
     }
 
+    public AlpacaClient getNewsDataClient() {
+        return newsDataClient;
+    }
+
     /**
      * @return the {@link CorporateActionsEndpoint}
      */
     public CorporateActionsEndpoint corporateActions() {
         return corporateActionsEndpoint;
     }
+
+    /**
+     * @return the {@link NewsEndpoint}
+     */
+    public NewsEndpoint newsEndpoint() {
+        return newsEndpoint;
+    }
+
+    /**
+     * @return the News {@link MarketDataWebsocketInterface}
+     */
+    public MarketDataWebsocketInterface newsMarketDataStreaming() {
+        return newsMarketDataWebsocket;
+    }
+
+
     /**
      * Creates a {@link Builder} for {@link AlpacaAPI}.
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java
new file mode 100644
index 00000000..88ee1b7c
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java
@@ -0,0 +1,120 @@
+package net.jacobpeterson.alpaca.rest.endpoint.marketdata.news;
+
+import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.NewsResponse;
+import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link AlpacaEndpoint} for
+ * <a href="https://docs.alpaca.markets/reference/news-1">News</a>
+ * Endpoint.
+ */
+public class NewsEndpoint extends AlpacaEndpoint {
+
+    /**
+     * Instantiates a new {@link AlpacaEndpoint}.
+     *
+     * @param alpacaClient the {@link AlpacaClient}
+     */
+    public NewsEndpoint(AlpacaClient alpacaClient) {
+        super(alpacaClient, "news");
+    }
+
+    /**
+     * Gets latest list of news {@link NewsResponse}.
+     * @return the {@link NewsResponse}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}
+     */
+    public NewsResponse getLatestNews() throws AlpacaClientException {
+        return this.getLatestNews(null, null, null, null, null, null, null, null);
+    }
+
+    /**
+     * Gets latest list of news {@link NewsResponse} for specific symbols.
+     * @param symbols list of symbols to query news for
+     * @return the {@link NewsResponse}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public NewsResponse getLatestNews(List<String> symbols) throws AlpacaClientException {
+        checkNotNull(symbols);
+        return this.getLatestNews(null, null, null, symbols, null, null, null, null);
+    }
+
+    /**
+     * Gets latest list of news {@link NewsResponse} for specified token.
+     * @param pageToken the ID of the end of your current page of results. (See the section on paging.)
+     * @return the {@link NewsResponse}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public NewsResponse getLatestNewsPaged(String pageToken) throws AlpacaClientException {
+        checkNotNull(pageToken);
+        return this.getLatestNews(null, null, null, null, null, null, null, pageToken);
+    }
+
+    /**
+     * Gets latest list of news {@link NewsResponse}
+     * @param start The inclusive start of the interval
+     * @param end the inclusive end of the interval
+     * @param sortDirection sort articles by updated date
+     * @param symbols list of symbols to query news for
+     * @param limit Limit of news items to be returned for given page
+     * @param includeContent Boolean indicator to include content for news articles (if available)
+     * @param excludeContentless Boolean indicator to exclude news articles that do not contain content
+     * @param pageToken the ID of the end of your current page of results. (See the section on paging.)
+     * @return the {@link NewsResponse}
+     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
+     */
+    public NewsResponse getLatestNews(
+            LocalDate start, LocalDate end,
+            SortDirection sortDirection,
+            List<String> symbols,
+            Integer limit,
+            Boolean includeContent,
+            Boolean excludeContentless,
+            String pageToken) throws AlpacaClientException {
+
+        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
+                .addPathSegment(endpointPathSegment);
+
+        if (start != null) {
+            urlBuilder.addQueryParameter("start", start.toString());
+        }
+        if (end != null) {
+            urlBuilder.addQueryParameter("end", end.toString());
+        }
+        if (sortDirection != null) {
+            urlBuilder.addQueryParameter("sort", sortDirection.value());
+        }
+        if (symbols != null && !symbols.isEmpty()) {
+            urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
+        }
+        if (limit != null) {
+            urlBuilder.addQueryParameter("limit", Integer.toString(limit));
+        }
+        if (includeContent != null) {
+            urlBuilder.addQueryParameter("include_content", Boolean.toString(includeContent));
+        }
+        if (excludeContentless != null) {
+            urlBuilder.addQueryParameter("exclude_contentless", Boolean.toString(excludeContentless));
+        }
+        if (pageToken != null) {
+            urlBuilder.addQueryParameter("page_token", pageToken);
+        }
+
+        Request request = alpacaClient.requestBuilder(urlBuilder.build())
+                .get()
+                .build();
+
+        return alpacaClient.requestObject(request, NewsResponse.class);
+
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
index 9c924c11..48c297f9 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -13,6 +13,8 @@
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
 import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.common.NewsArticle;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.realtime.NewsMessage;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
@@ -53,7 +55,8 @@ public abstract class MarketDataWebsocket
     private static final List<MarketDataMessageType> SUBSCRIBABLE_MARKET_DATA_MESSAGE_TYPES = Arrays.asList(
             MarketDataMessageType.TRADE,
             MarketDataMessageType.QUOTE,
-            MarketDataMessageType.BAR);
+            MarketDataMessageType.BAR,
+            MarketDataMessageType.NEWS);
 
     /**
      * Creates a {@link HttpUrl} for {@link MarketDataWebsocket} with the given <code>websocketURLPathSegments</code>.
@@ -74,9 +77,11 @@ private static HttpUrl createWebsocketURL(String websocketURLPathSegments) {
     private final Set<String> subscribedTrades;
     private final Set<String> subscribedQuotes;
     private final Set<String> subscribedBars;
+    private final Set<String> subscribedNews;
     private final Type tradeClassType;
     private final Type quoteClassType;
     private final Type barClassType;
+    private final Type newsClassType = NewsMessage.class;
 
     /**
      * Instantiates a new {@link MarketDataWebsocket}.
@@ -101,7 +106,7 @@ public MarketDataWebsocket(OkHttpClient okHttpClient, String websocketURLPathSeg
         subscribedTrades = new HashSet<>();
         subscribedQuotes = new HashSet<>();
         subscribedBars = new HashSet<>();
-
+        subscribedNews = new HashSet<>();
         this.tradeClassType = tradeClass;
         this.quoteClassType = quoteClass;
         this.barClassType = barClass;
@@ -124,7 +129,7 @@ protected void onReconnection() {
         sendAuthenticationMessage();
         if (waitForAuthorization(5, TimeUnit.SECONDS)) {
             subscribeToControl(Iterables.toArray(listenedMarketDataMessageTypes, MarketDataMessageType.class));
-            subscribe(subscribedTrades, subscribedQuotes, subscribedBars);
+            subscribe(subscribedTrades, subscribedQuotes, subscribedBars, subscribedNews);
         }
     }
 
@@ -210,6 +215,8 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
                             subscribedQuotes);
                     handleSubscriptionMessageList(MarketDataMessageType.BAR, subscriptionsMessage.getBars(),
                             subscribedBars);
+                    handleSubscriptionMessageList(MarketDataMessageType.NEWS, subscriptionsMessage.getNews(),
+                            subscribedNews);
                     break;
                 case TRADE:
                     marketDataMessage = GSON.fromJson(messageObject, tradeClassType);
@@ -220,6 +227,9 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
                 case BAR:
                     marketDataMessage = GSON.fromJson(messageObject, barClassType);
                     break;
+                case NEWS:
+                    marketDataMessage = GSON.fromJson(messageObject, newsClassType);
+                    break;
                 default:
                     LOGGER.error("Message type {} not implemented!", marketDataMessageType);
                     continue;
@@ -286,14 +296,14 @@ public void subscribeToControl(MarketDataMessageType... marketDataMessageTypes)
 
     @Override
     public void subscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
-            Collection<String> barSymbols) {
-        sendSubscriptionUpdate(tradeSymbols, quoteSymbols, barSymbols, true);
+            Collection<String> barSymbols, Collection<String> newsSymbols) {
+        sendSubscriptionUpdate(tradeSymbols, quoteSymbols, barSymbols, newsSymbols, true);
     }
 
     @Override
     public void unsubscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
-            Collection<String> barSymbols) {
-        sendSubscriptionUpdate(tradeSymbols, quoteSymbols, barSymbols, false);
+            Collection<String> barSymbols, Collection<String> newsSymbols) {
+        sendSubscriptionUpdate(tradeSymbols, quoteSymbols, barSymbols, newsSymbols, false);
     }
 
     /**
@@ -305,7 +315,7 @@ public void unsubscribe(Collection<String> tradeSymbols, Collection<String> quot
      * @param subscribe    true to subscribe, false to unsubscribe
      */
     private void sendSubscriptionUpdate(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
-            Collection<String> barSymbols, boolean subscribe) {
+            Collection<String> barSymbols, Collection<String> newsSymbols, boolean subscribe) {
         if (!isConnected()) {
             throw new IllegalStateException("This websocket must be connected before sending subscription updates!");
         }
@@ -325,6 +335,7 @@ private void sendSubscriptionUpdate(Collection<String> tradeSymbols, Collection<
         addSubscriptionUpdateList(subscriptionUpdateObject, "trades", tradeSymbols);
         addSubscriptionUpdateList(subscriptionUpdateObject, "quotes", quoteSymbols);
         addSubscriptionUpdateList(subscriptionUpdateObject, "bars", barSymbols);
+        addSubscriptionUpdateList(subscriptionUpdateObject, "news", newsSymbols);
 
         boolean updateExists = subscriptionUpdateObject.size() > 1;
         if (updateExists) {
@@ -368,4 +379,9 @@ public Collection<String> subscribedQuotes() {
     public Collection<String> subscribedBars() {
         return new HashSet<>(subscribedBars);
     }
+
+    @Override
+    public Collection<String> subscribedNews() {
+        return new HashSet<>(subscribedNews);
+    }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
index 5ffcaf3c..d4f7700c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
@@ -33,9 +33,9 @@ public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<M
      * @param quoteSymbols a {@link Collection} of symbols to subscribe to quotes or <code>null</code> for no change
      * @param barSymbols   a {@link Collection} of symbols to subscribe to bars or <code>null</code> for no change
      *
-     * @see #unsubscribe(Collection, Collection, Collection)
+     * @see #unsubscribe(Collection, Collection, Collection, Collection)
      */
-    void subscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols);
+    void subscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols, Collection<String> newsSymbols);
 
     /**
      * Unsubscribes from trades, quotes, or bars according to the given {@link Collection} of symbols.
@@ -49,9 +49,9 @@ public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<M
      *                     change
      * @param barSymbols   a {@link Collection} of symbols to unsubscribe from bars or <code>null</code> for no change
      *
-     * @see #subscribe(Collection, Collection, Collection)
+     * @see #subscribe(Collection, Collection, Collection, Collection)
      */
-    void unsubscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols);
+    void unsubscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols, Collection<String> newsSymbols);
 
     /**
      * Gets all the currently subscribed control {@link MarketDataMessageType}s.
@@ -80,4 +80,7 @@ public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<M
      * @return a {@link Collection} of {@link String}s
      */
     Collection<String> subscribedBars();
+
+    Collection<String> subscribedNews();
+
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java
new file mode 100644
index 00000000..9d2c2620
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java
@@ -0,0 +1,15 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.news;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.OkHttpClient;
+
+public class NewsMarketDataWebsocket extends MarketDataWebsocket {
+    public NewsMarketDataWebsocket(OkHttpClient okHttpClient,
+            String keyID, String secretKey) {
+        super(okHttpClient, "v1beta1/news", "News", keyID, secretKey, TradeMessage.class,
+                QuoteMessage.class, BarMessage.class);
+    }
+}
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
index fd2dc0ae..c895ed03 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
@@ -45,21 +45,24 @@ void getAnnouncements_shouldReturnData() throws AlpacaClientException {
     void getAnnouncements_validation_caTypes() {
         CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
 
-        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncements(null, LocalDate.now(), LocalDate.now(), null, null, null));
+        assertThrows(Exception.class,
+                () -> newsEndpoint.getAnnouncements(null, LocalDate.now(), LocalDate.now(), null, null, null));
     }
 
     @Test
     void getAnnouncements_validation_since() {
         CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
 
-        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncements(new ArrayList<>(), null, LocalDate.now(), null, null, null));
+        assertThrows(Exception.class,
+                () -> newsEndpoint.getAnnouncements(new ArrayList<>(), null, LocalDate.now(), null, null, null));
     }
 
     @Test
     void getAnnouncements_validation_until() {
         CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
 
-        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncements(new ArrayList<>(), LocalDate.now(), null, null, null, null));
+        assertThrows(Exception.class,
+                () -> newsEndpoint.getAnnouncements(new ArrayList<>(), LocalDate.now(), null, null, null, null));
     }
 
     @Test
@@ -76,7 +79,7 @@ void getAnnouncement_shouldReturnData() throws AlpacaClientException {
         Announcement announcement = newsEndpoint.getAnnouncement("1");
 
         assertNotNull(announcement);
-        assertEquals(announcement.getId(), "cd0cb768-889f-47a1-a94b-1438017dad77");
+        assertEquals("cd0cb768-889f-47a1-a94b-1438017dad77", announcement.getId());
     }
 
     private static CorporateActionsEndpoint setupEndpoint(String response) {
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java
new file mode 100644
index 00000000..ef4b553f
--- /dev/null
+++ b/src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java
@@ -0,0 +1,111 @@
+package net.jacobpeterson.alpaca.test.mock.marketdata.news;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.NewsResponse;
+import net.jacobpeterson.alpaca.rest.AlpacaClient;
+import net.jacobpeterson.alpaca.rest.AlpacaClientException;
+import net.jacobpeterson.alpaca.rest.endpoint.marketdata.news.NewsEndpoint;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.ResponseBody;
+import okhttp3.mock.MockInterceptor;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class NewsEndpointTest {
+
+    @Test
+    void getLatestNews_validation_page() {
+        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
+
+        assertThrows(Exception.class, () -> newsEndpoint.getLatestNewsPaged(null));
+    }
+
+    @Test
+    void getLatestNews_validation_symbols() {
+        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
+
+        assertThrows(Exception.class, () -> newsEndpoint.getLatestNews(null));
+    }
+
+    @Test
+    void getLatestNews_shouldReturnData() throws AlpacaClientException {
+        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
+        NewsResponse response = newsEndpoint.getLatestNews(null, null, null, null, null, null, null, null);
+
+        assertNotNull(response);
+        assertEquals("MTcwNDczNjg2NTAwMDAwMDAwMHwzNjU0MDM2Mw==", response.getNextPageToken());
+        assertEquals(1, response.getNews().size());
+        assertEquals(3, response.getNews().get(0).getImages().size());
+
+    }
+
+    @Test
+    void getLatestNews_shouldReturnLatestData() throws AlpacaClientException {
+        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
+        NewsResponse response = newsEndpoint.getLatestNews();
+
+        assertNotNull(response);
+        assertEquals("MTcwNDczNjg2NTAwMDAwMDAwMHwzNjU0MDM2Mw==", response.getNextPageToken());
+        assertEquals(1, response.getNews().size());
+        assertEquals(3, response.getNews().get(0).getImages().size());
+
+    }
+
+    private static NewsEndpoint setupEndpoint(String response) {
+        MockInterceptor interceptor = new MockInterceptor();
+        interceptor.addRule()
+                .respond(200)
+                .body(ResponseBody.create(response, MediaType.parse("application/json")));
+
+        OkHttpClient client = new OkHttpClient.Builder()
+                .addInterceptor(interceptor)
+                .build();
+
+        AlpacaClient api = new AlpacaClient(client, "token", "test", "/v1");
+
+        return new NewsEndpoint(api);
+    }
+
+    private static String mockResponse() {
+        return "{\n" +
+                "  \"news\": [\n" +
+                "    {\n" +
+                "      \"author\": \"Khyathi Dalal\",\n" +
+                "      \"content\": \"\",\n" +
+                "      \"created_at\": \"2024-01-08T18:07:34Z\",\n" +
+                "      \"headline\": \"Solana Drops 9.9%, Solana Memecoins Tank Over 20%: Is This The End Of The Dogecoin Killers?\",\n" +
+                "      \"id\": 36536785,\n" +
+                "      \"images\": [\n" +
+                "        {\n" +
+                "          \"size\": \"large\",\n" +
+                "          \"url\": \"https://cdn.benzinga.com/files/imagecache/2048x1536xUP/images/story/2024/01/08/solana_coin_shutter2_0.jpg\"\n" +
+                "        },\n" +
+                "        {\n" +
+                "          \"size\": \"small\",\n" +
+                "          \"url\": \"https://cdn.benzinga.com/files/imagecache/1024x768xUP/images/story/2024/01/08/solana_coin_shutter2_0.jpg\"\n" +
+                "        },\n" +
+                "        {\n" +
+                "          \"size\": \"thumb\",\n" +
+                "          \"url\": \"https://cdn.benzinga.com/files/imagecache/250x187xUP/images/story/2024/01/08/solana_coin_shutter2_0.jpg\"\n" +
+                "        }\n" +
+                "      ],\n" +
+                "      \"source\": \"benzinga\",\n" +
+                "      \"summary\": \"Solana (CRYPTO: SOL) dropped 1.4% in the past 24 hours, extending its losses over the previous seven days to 9.9%. Solana-based memecoins are particularly hard-hit by the correction, taking much heavier losses than established memecoins.\",\n" +
+                "      \"symbols\": [\n" +
+                "        \"BONKUSD\",\n" +
+                "        \"DOGEUSD\",\n" +
+                "        \"ETHUSD\",\n" +
+                "        \"SHIBUSD\",\n" +
+                "        \"SOLUSD\"\n" +
+                "      ],\n" +
+                "      \"updated_at\": \"2024-01-08T18:07:35Z\",\n" +
+                "      \"url\": \"https://www.benzinga.com/markets/cryptocurrency/24/01/36536785/solana-drops-9-9-solana-memecoins-tank-over-20-is-this-the-end-of-the-dogecoin-killers\"\n" +
+                "    }\n" +
+                "  ],\n" +
+                "  \"next_page_token\": \"MTcwNDczNjg2NTAwMDAwMDAwMHwzNjU0MDM2Mw==\"\n" +
+                "}";
+    }
+}

From 2750936a15114a750ce604e741fb8c6e88d87052 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 1 Mar 2024 17:16:16 -0800
Subject: [PATCH 54/85] Modify PR to account for future project structure
 changes Namely: moving to OpenAPI generated clients, removing unit testing
 for RestAPI

---
 build.gradle                                  |   1 -
 .../alpaca/test/mock/AlpacaAPITest.java       |  13 --
 .../CorporateActionsEndpointTest.java         | 107 -----------------
 .../marketdata/news/NewsEndpointTest.java     | 111 ------------------
 4 files changed, 232 deletions(-)
 delete mode 100644 src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
 delete mode 100644 src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java

diff --git a/build.gradle b/build.gradle
index 7c65cc3d..a861d055 100644
--- a/build.gradle
+++ b/build.gradle
@@ -53,7 +53,6 @@ dependencies {
     testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.36'
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.8.2'
     testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.6.1'
-    testImplementation group: 'com.github.gmazzo.okhttp.mock', name: 'mock-client', version: '2.0.0'
 }
 
 // Exclude any SLF4j-implementation transitive dependencies so users can use a logging framework of their choice
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
index 0fbc722a..d5f4f320 100644
--- a/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
+++ b/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
@@ -6,8 +6,6 @@
 import okhttp3.OkHttpClient;
 import org.junit.jupiter.api.Test;
 
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
 /**
  * {@link AlpacaAPITest} tests {@link AlpacaAPI} using mocked objects with Mockito.
  */
@@ -73,15 +71,4 @@ public void testAlpacaAPIConstructor_okHttpClient_keyID_secret_oAuthToken_endpoi
         new AlpacaAPI(okHttpClient, null, null, oAuthToken, EndpointAPIType.PAPER, DataAPIType.IEX);
         new AlpacaAPI(okHttpClient, keyID, secret, null, EndpointAPIType.LIVE, DataAPIType.SIP);
     }
-
-    @Test
-    public void testAlpacaAPIConstructor_corporateActionsEndpoint() {
-        OkHttpClient okHttpClient = new OkHttpClient();
-        String keyID = "ABCDEFGHIJKLM";
-        String secret = "NOPQURSTUVWXYZ";
-
-        AlpacaAPI api = new AlpacaAPI(okHttpClient, keyID, secret, null, EndpointAPIType.LIVE, DataAPIType.SIP);
-
-        assertNotNull(api.corporateActions());
-    }
 }
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
deleted file mode 100644
index c895ed03..00000000
--- a/src/test/java/net/jacobpeterson/alpaca/test/mock/corporateactions/CorporateActionsEndpointTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package net.jacobpeterson.alpaca.test.mock.corporateactions;
-
-import net.jacobpeterson.alpaca.model.endpoint.corporateactions.common.Announcement;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.corporateactions.CorporateActionsEndpoint;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.ResponseBody;
-import okhttp3.mock.MockInterceptor;
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDate;
-import java.util.ArrayList;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-public class CorporateActionsEndpointTest {
-
-    @Test
-    void getAnnouncements_givenSymbol_shouldReturnData() throws AlpacaClientException {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
-
-        java.util.List<Announcement> result = newsEndpoint.getAnnouncements("K");
-
-        assertNotNull(result);
-        assertEquals(3, result.size());
-
-    }
-
-    @Test
-    void getAnnouncements_shouldReturnData() throws AlpacaClientException {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
-
-        java.util.List<Announcement> result = newsEndpoint.getAnnouncements();
-
-        assertNotNull(result);
-        assertEquals(3, result.size());
-
-    }
-
-    @Test
-    void getAnnouncements_validation_caTypes() {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
-
-        assertThrows(Exception.class,
-                () -> newsEndpoint.getAnnouncements(null, LocalDate.now(), LocalDate.now(), null, null, null));
-    }
-
-    @Test
-    void getAnnouncements_validation_since() {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
-
-        assertThrows(Exception.class,
-                () -> newsEndpoint.getAnnouncements(new ArrayList<>(), null, LocalDate.now(), null, null, null));
-    }
-
-    @Test
-    void getAnnouncements_validation_until() {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockMultipleAnnouncementsResponse());
-
-        assertThrows(Exception.class,
-                () -> newsEndpoint.getAnnouncements(new ArrayList<>(), LocalDate.now(), null, null, null, null));
-    }
-
-    @Test
-    void getAnnouncement_validation_id() {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockSingleAnnouncementResponse());
-
-        assertThrows(Exception.class, () -> newsEndpoint.getAnnouncement(null));
-    }
-
-    @Test
-    void getAnnouncement_shouldReturnData() throws AlpacaClientException {
-        CorporateActionsEndpoint newsEndpoint = setupEndpoint(mockSingleAnnouncementResponse());
-
-        Announcement announcement = newsEndpoint.getAnnouncement("1");
-
-        assertNotNull(announcement);
-        assertEquals("cd0cb768-889f-47a1-a94b-1438017dad77", announcement.getId());
-    }
-
-    private static CorporateActionsEndpoint setupEndpoint(String response) {
-        MockInterceptor interceptor = new MockInterceptor();
-        interceptor.addRule()
-                .respond(200)
-                .body(ResponseBody.create(response, MediaType.parse("application/json")));
-
-        OkHttpClient client = new OkHttpClient.Builder()
-                .addInterceptor(interceptor)
-                .build();
-
-        AlpacaClient api = new AlpacaClient(client, "token", "test", "/v1");
-
-        return new CorporateActionsEndpoint(api);
-    }
-
-    private static String mockMultipleAnnouncementsResponse() {
-        return "[{\"id\":\"cd0cb768-889f-47a1-a94b-1438017dad77\",\"corporate_action_id\":\"2642471\",\"ca_type\":\"spinoff\",\"ca_sub_type\":\"spinoff\",\"initiating_symbol\":\"KLG\",\"initiating_original_cusip\":\"92942W107\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"effective_date\":\"2023-09-08\",\"ex_date\":\"2023-10-02\",\"record_date\":\"2023-09-21\",\"payable_date\":\"2023-10-02\",\"cash\":\"0\",\"old_rate\":\"1\",\"new_rate\":\"0.25\"},{\"id\":\"3a7fe233-7766-4f70-915f-9d1a5258eaa9\",\"corporate_action_id\":\"487836108_AD23\",\"ca_type\":\"dividend\",\"ca_sub_type\":\"cash\",\"initiating_symbol\":\"K\",\"initiating_original_cusip\":\"487836108\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"declaration_date\":\"2023-09-19\",\"effective_date\":\"2023-09-21\",\"ex_date\":\"2023-10-02\",\"record_date\":\"2023-09-21\",\"payable_date\":\"2023-10-02\",\"cash\":\"0\",\"old_rate\":\"1\",\"new_rate\":\"1\"},{\"id\":\"ddf64bcb-fa76-4472-a98b-237f220ea38b\",\"corporate_action_id\":\"487836108_AE23\",\"ca_type\":\"dividend\",\"ca_sub_type\":\"cash\",\"initiating_symbol\":\"K\",\"initiating_original_cusip\":\"487836108\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"declaration_date\":\"2023-11-29\",\"effective_date\":\"2023-12-01\",\"ex_date\":\"2023-11-30\",\"record_date\":\"2023-12-01\",\"payable_date\":\"2023-12-15\",\"cash\":\"0.56\",\"old_rate\":\"1\",\"new_rate\":\"1\"}]";
-    }
-
-    private static String mockSingleAnnouncementResponse() {
-        return "{\"id\":\"cd0cb768-889f-47a1-a94b-1438017dad77\",\"corporate_action_id\":\"2642471\",\"ca_type\":\"spinoff\",\"ca_sub_type\":\"spinoff\",\"initiating_symbol\":\"KLG\",\"initiating_original_cusip\":\"92942W107\",\"target_symbol\":\"K\",\"target_original_cusip\":\"487836108\",\"effective_date\":\"2023-09-08\",\"ex_date\":\"2023-10-02\",\"record_date\":\"2023-09-21\",\"payable_date\":\"2023-10-02\",\"cash\":\"0\",\"old_rate\":\"1\",\"new_rate\":\"0.25\"}";
-    }
-}
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java
deleted file mode 100644
index ef4b553f..00000000
--- a/src/test/java/net/jacobpeterson/alpaca/test/mock/marketdata/news/NewsEndpointTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package net.jacobpeterson.alpaca.test.mock.marketdata.news;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.NewsResponse;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.news.NewsEndpoint;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.ResponseBody;
-import okhttp3.mock.MockInterceptor;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-class NewsEndpointTest {
-
-    @Test
-    void getLatestNews_validation_page() {
-        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
-
-        assertThrows(Exception.class, () -> newsEndpoint.getLatestNewsPaged(null));
-    }
-
-    @Test
-    void getLatestNews_validation_symbols() {
-        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
-
-        assertThrows(Exception.class, () -> newsEndpoint.getLatestNews(null));
-    }
-
-    @Test
-    void getLatestNews_shouldReturnData() throws AlpacaClientException {
-        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
-        NewsResponse response = newsEndpoint.getLatestNews(null, null, null, null, null, null, null, null);
-
-        assertNotNull(response);
-        assertEquals("MTcwNDczNjg2NTAwMDAwMDAwMHwzNjU0MDM2Mw==", response.getNextPageToken());
-        assertEquals(1, response.getNews().size());
-        assertEquals(3, response.getNews().get(0).getImages().size());
-
-    }
-
-    @Test
-    void getLatestNews_shouldReturnLatestData() throws AlpacaClientException {
-        NewsEndpoint newsEndpoint = setupEndpoint(mockResponse());
-        NewsResponse response = newsEndpoint.getLatestNews();
-
-        assertNotNull(response);
-        assertEquals("MTcwNDczNjg2NTAwMDAwMDAwMHwzNjU0MDM2Mw==", response.getNextPageToken());
-        assertEquals(1, response.getNews().size());
-        assertEquals(3, response.getNews().get(0).getImages().size());
-
-    }
-
-    private static NewsEndpoint setupEndpoint(String response) {
-        MockInterceptor interceptor = new MockInterceptor();
-        interceptor.addRule()
-                .respond(200)
-                .body(ResponseBody.create(response, MediaType.parse("application/json")));
-
-        OkHttpClient client = new OkHttpClient.Builder()
-                .addInterceptor(interceptor)
-                .build();
-
-        AlpacaClient api = new AlpacaClient(client, "token", "test", "/v1");
-
-        return new NewsEndpoint(api);
-    }
-
-    private static String mockResponse() {
-        return "{\n" +
-                "  \"news\": [\n" +
-                "    {\n" +
-                "      \"author\": \"Khyathi Dalal\",\n" +
-                "      \"content\": \"\",\n" +
-                "      \"created_at\": \"2024-01-08T18:07:34Z\",\n" +
-                "      \"headline\": \"Solana Drops 9.9%, Solana Memecoins Tank Over 20%: Is This The End Of The Dogecoin Killers?\",\n" +
-                "      \"id\": 36536785,\n" +
-                "      \"images\": [\n" +
-                "        {\n" +
-                "          \"size\": \"large\",\n" +
-                "          \"url\": \"https://cdn.benzinga.com/files/imagecache/2048x1536xUP/images/story/2024/01/08/solana_coin_shutter2_0.jpg\"\n" +
-                "        },\n" +
-                "        {\n" +
-                "          \"size\": \"small\",\n" +
-                "          \"url\": \"https://cdn.benzinga.com/files/imagecache/1024x768xUP/images/story/2024/01/08/solana_coin_shutter2_0.jpg\"\n" +
-                "        },\n" +
-                "        {\n" +
-                "          \"size\": \"thumb\",\n" +
-                "          \"url\": \"https://cdn.benzinga.com/files/imagecache/250x187xUP/images/story/2024/01/08/solana_coin_shutter2_0.jpg\"\n" +
-                "        }\n" +
-                "      ],\n" +
-                "      \"source\": \"benzinga\",\n" +
-                "      \"summary\": \"Solana (CRYPTO: SOL) dropped 1.4% in the past 24 hours, extending its losses over the previous seven days to 9.9%. Solana-based memecoins are particularly hard-hit by the correction, taking much heavier losses than established memecoins.\",\n" +
-                "      \"symbols\": [\n" +
-                "        \"BONKUSD\",\n" +
-                "        \"DOGEUSD\",\n" +
-                "        \"ETHUSD\",\n" +
-                "        \"SHIBUSD\",\n" +
-                "        \"SOLUSD\"\n" +
-                "      ],\n" +
-                "      \"updated_at\": \"2024-01-08T18:07:35Z\",\n" +
-                "      \"url\": \"https://www.benzinga.com/markets/cryptocurrency/24/01/36536785/solana-drops-9-9-solana-memecoins-tank-over-20-is-this-the-end-of-the-dogecoin-killers\"\n" +
-                "    }\n" +
-                "  ],\n" +
-                "  \"next_page_token\": \"MTcwNDczNjg2NTAwMDAwMDAwMHwzNjU0MDM2Mw==\"\n" +
-                "}";
-    }
-}

From 1f6e425087ea963e0497f616bd8f411b09d3f9b5 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 1 Mar 2024 20:33:54 -0800
Subject: [PATCH 55/85] WIP use OpenAPI specification with generated clients

---
 .editorconfig                                 |  10 +-
 .gitattributes                                |  45 +-
 .github/image/logo.png                        | Bin 0 -> 52737 bytes
 .gitignore                                    |   3 +-
 .travis.yml                                   |  24 -
 LICENSE => LICENSE.txt                        |   2 +-
 README.md                                     |  47 +-
 _config.yml                                   |   2 +-
 build.gradle                                  | 376 +++++-----
 buildSrc/build.gradle                         |  29 +
 .../GeneratePOJOsTask.groovy                  | 128 ++++
 .../JSONSchema2POJOAdaptedPlugin.groovy       |  26 +
 .../JSONSchemaFileConfig.groovy               |  31 +
 codecov.yml                                   |   6 -
 gradle/wrapper/.gitignore                     |   1 +
 gradle/wrapper/gradle-wrapper.properties      |   4 +-
 schema_json/endpoint/account/account.json     | 110 ---
 .../account/enums/account_status.json         |  44 --
 .../account_activities/account_activity.json  |  14 -
 .../enums/activity_type.json                  | 152 ----
 .../enums/trade_activity_side.json            |   8 -
 .../enums/trade_activity_type.json            |   8 -
 .../non_trade_activity.json                   |  38 -
 .../account_activities/trade_activity.json    |  48 --
 .../account_configuration.json                |  22 -
 .../enums/dtbp_check.json                     |  10 -
 .../enums/trade_confirm_email.json            |   8 -
 schema_json/endpoint/assets/asset.json        |  51 --
 .../endpoint/assets/enums/asset_class.json    |   8 -
 .../endpoint/assets/enums/asset_status.json   |   8 -
 schema_json/endpoint/calendar/calendar.json   |  18 -
 schema_json/endpoint/clock/clock.json         |  22 -
 .../endpoint/common/enums/sort_direction.json |  16 -
 .../common/announcement.json                  |  53 --
 .../endpoint/orders/cancelled_order.json      |  19 -
 .../orders/enums/current_order_status.json    |   9 -
 .../endpoint/orders/enums/order_class.json    |  24 -
 .../endpoint/orders/enums/order_side.json     |   8 -
 .../endpoint/orders/enums/order_status.json   |  76 --
 .../orders/enums/order_time_in_force.json     |  38 -
 .../endpoint/orders/enums/order_type.json     |  28 -
 schema_json/endpoint/orders/order.json        | 134 ----
 .../enums/portfolio_period_unit.json          |  24 -
 .../enums/portfolio_time_frame.json           |  28 -
 .../portfolio_history/portfolio_history.json  |  18 -
 .../portfolio_history_data_point.json         |  22 -
 .../portfolio_history_response.json           |  31 -
 .../positions/close_position_order.json       |  19 -
 schema_json/endpoint/positions/position.json  |  81 ---
 schema_json/endpoint/watchlist/watchlist.json |  30 -
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 246 +------
 .../common/historical/bar/bar.json            |   0
 .../historical/bar/enums/bar_time_period.json |   0
 .../common/historical/quote/quote.json        |   0
 .../common/historical/trade/trade.json        |   0
 .../common/realtime/bar/bar_message.json      |   0
 .../realtime/control/error_message.json       |   0
 .../control/subscriptions_message.json        |   0
 .../realtime/control/success_message.json     |   0
 .../enums/market_data_message_type.json       |   0
 .../common/realtime/market_data_message.json  |   0
 .../common/realtime/quote/quote_message.json  |   0
 .../common/realtime/symbol_message.json       |   0
 .../common/realtime/trade/trade_message.json  |   0
 .../crypto/common/enums/taker_side.json       |   0
 .../crypto/historical/bar/crypto_bar.json     |   0
 .../historical/bar/crypto_bars_response.json  |   0
 .../bar/latest_crypto_bars_response.json      |   0
 .../orderbook/crypto_orderbook.json           |   0
 .../orderbook/crypto_orderbook_entry.json     |   0
 .../latest_crypto_orderbooks_response.json    |   0
 .../crypto/historical/quote/crypto_quote.json |   0
 .../quote/latest_crypto_quotes_response.json  |   0
 .../historical/snapshot/crypto_snapshot.json  |   0
 .../snapshot/crypto_snapshots_response.json   |   0
 .../crypto/historical/trade/crypto_trade.json |   0
 .../trade/crypto_trades_response.json         |   0
 .../trade/latest_crypto_trades_response.json  |   0
 .../realtime/bar/crypto_bar_message.json      |   0
 .../realtime/quote/crypto_quote_message.json  |   0
 .../realtime/trade/crypto_trade_message.json  |   0
 .../market_data/news/common/image.json        |   0
 .../market_data/news/common/news_article.json |   0
 .../market_data/news/news_response.json       |   0
 .../news/realtime/news_message.json           |   0
 .../historical/bar/enums/bar_adjustment.json  |   0
 .../stock/historical/bar/enums/bar_feed.json  |   0
 .../bar/multi_stock_bars_response.json        |   0
 .../stock/historical/bar/stock_bar.json       |   0
 .../historical/bar/stock_bars_response.json   |   0
 .../quote/latest_stock_quote_response.json    |   0
 .../stock/historical/quote/stock_quote.json   |   0
 .../quote/stock_quotes_response.json          |   0
 .../stock/historical/snapshot/snapshot.json   |   0
 .../trade/latest_stock_trade_response.json    |   0
 .../stock/historical/trade/stock_trade.json   |   0
 .../trade/stock_trades_response.json          |   0
 .../stock/realtime/bar/stock_bar_message.json |   0
 .../realtime/quote/stock_quote_message.json   |   0
 .../realtime/trade/stock_trade_message.json   |   0
 .../authorization/authorization_data.json     |   0
 .../authorization/authorization_message.json  |   0
 .../enums/streaming_message_type.json         |   0
 .../streaming/listening/listening_data.json   |   0
 .../listening/listening_message.json          |   0
 .../endpoint/streaming/streaming_message.json |   0
 .../trade/enums/trade_update_event.json       |   0
 .../streaming/trade/trade_update.json         |   4 +-
 .../streaming/trade/trade_update_message.json |   0
 .../alpaca/properties/AlpacaProperties.java   |  35 +-
 .../alpaca}/properties/data_api_type.json     |   0
 .../alpaca}/properties/endpoint_api_type.json |   0
 .../alpaca/rest/AlpacaClient.java             | 235 -------
 .../alpaca/rest/AlpacaClientException.java    | 144 ----
 .../alpaca/rest/endpoint/AlpacaEndpoint.java  |  23 -
 .../endpoint/account/AccountEndpoint.java     |  39 --
 .../AccountActivitiesEndpoint.java            | 142 ----
 .../AccountConfigurationEndpoint.java         |  64 --
 .../rest/endpoint/assets/AssetsEndpoint.java  |  83 ---
 .../endpoint/calendar/CalendarEndpoint.java   |  71 --
 .../rest/endpoint/clock/ClockEndpoint.java    |  39 --
 .../CorporateActionsEndpoint.java             | 121 ----
 .../crypto/CryptoMarketDataEndpoint.java      | 302 --------
 .../marketdata/news/NewsEndpoint.java         | 120 ----
 .../stock/StockMarketDataEndpoint.java        | 368 ----------
 .../rest/endpoint/orders/OrdersEndpoint.java  | 651 ------------------
 .../PortfolioHistoryEndpoint.java             | 103 ---
 .../endpoint/positions/PositionsEndpoint.java | 136 ----
 .../endpoint/watchlist/WatchlistEndpoint.java | 216 ------
 .../util/doc/Documentation2JSONSchema.java    | 130 ----
 .../alpaca/util/format/FormatUtil.java        |  53 --
 .../alpaca/util/okhttp/JSONBodyBuilder.java   |  65 --
 .../alpaca/util/properties/PropertyUtil.java  |  88 ++-
 .../alpaca/websocket/AlpacaWebsocket.java     |   3 +-
 .../WebsocketStateListener.java               |   2 +-
 .../streaming/StreamingWebsocket.java         |  18 +-
 src/main/resources/alpaca.default.properties  |  12 +-
 .../alpaca/test/live/AlpacaAPITest.java       | 289 --------
 .../alpaca/test/mock/AlpacaAPITest.java       |  74 --
 src/test/resources/alpaca.default.properties  |   5 -
 140 files changed, 528 insertions(+), 5314 deletions(-)
 create mode 100644 .github/image/logo.png
 delete mode 100644 .travis.yml
 rename LICENSE => LICENSE.txt (96%)
 create mode 100644 buildSrc/build.gradle
 create mode 100644 buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy
 create mode 100644 buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy
 create mode 100644 buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy
 delete mode 100644 codecov.yml
 create mode 100644 gradle/wrapper/.gitignore
 delete mode 100644 schema_json/endpoint/account/account.json
 delete mode 100644 schema_json/endpoint/account/enums/account_status.json
 delete mode 100644 schema_json/endpoint/account_activities/account_activity.json
 delete mode 100644 schema_json/endpoint/account_activities/enums/activity_type.json
 delete mode 100644 schema_json/endpoint/account_activities/enums/trade_activity_side.json
 delete mode 100644 schema_json/endpoint/account_activities/enums/trade_activity_type.json
 delete mode 100644 schema_json/endpoint/account_activities/non_trade_activity.json
 delete mode 100644 schema_json/endpoint/account_activities/trade_activity.json
 delete mode 100644 schema_json/endpoint/account_configuration/account_configuration.json
 delete mode 100644 schema_json/endpoint/account_configuration/enums/dtbp_check.json
 delete mode 100644 schema_json/endpoint/account_configuration/enums/trade_confirm_email.json
 delete mode 100644 schema_json/endpoint/assets/asset.json
 delete mode 100644 schema_json/endpoint/assets/enums/asset_class.json
 delete mode 100644 schema_json/endpoint/assets/enums/asset_status.json
 delete mode 100644 schema_json/endpoint/calendar/calendar.json
 delete mode 100644 schema_json/endpoint/clock/clock.json
 delete mode 100644 schema_json/endpoint/common/enums/sort_direction.json
 delete mode 100644 schema_json/endpoint/corporate_actions/common/announcement.json
 delete mode 100644 schema_json/endpoint/orders/cancelled_order.json
 delete mode 100644 schema_json/endpoint/orders/enums/current_order_status.json
 delete mode 100644 schema_json/endpoint/orders/enums/order_class.json
 delete mode 100644 schema_json/endpoint/orders/enums/order_side.json
 delete mode 100644 schema_json/endpoint/orders/enums/order_status.json
 delete mode 100644 schema_json/endpoint/orders/enums/order_time_in_force.json
 delete mode 100644 schema_json/endpoint/orders/enums/order_type.json
 delete mode 100644 schema_json/endpoint/orders/order.json
 delete mode 100644 schema_json/endpoint/portfolio_history/enums/portfolio_period_unit.json
 delete mode 100644 schema_json/endpoint/portfolio_history/enums/portfolio_time_frame.json
 delete mode 100644 schema_json/endpoint/portfolio_history/portfolio_history.json
 delete mode 100644 schema_json/endpoint/portfolio_history/portfolio_history_data_point.json
 delete mode 100644 schema_json/endpoint/portfolio_history/portfolio_history_response.json
 delete mode 100644 schema_json/endpoint/positions/close_position_order.json
 delete mode 100644 schema_json/endpoint/positions/position.json
 delete mode 100644 schema_json/endpoint/watchlist/watchlist.json
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/historical/bar/bar.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/historical/bar/enums/bar_time_period.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/historical/quote/quote.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/historical/trade/trade.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/bar/bar_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/control/error_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/control/subscriptions_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/control/success_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/enums/market_data_message_type.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/market_data_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/quote/quote_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/symbol_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/common/realtime/trade/trade_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/common/enums/taker_side.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/bar/crypto_bar.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/quote/crypto_quote.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/trade/crypto_trade.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/news/common/image.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/news/common/news_article.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/news/news_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/news/realtime/news_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/bar/enums/bar_feed.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/bar/stock_bar.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/bar/stock_bars_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/quote/stock_quote.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/quote/stock_quotes_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/snapshot/snapshot.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/trade/stock_trade.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/historical/trade/stock_trades_response.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/realtime/bar/stock_bar_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/realtime/quote/stock_quote_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/market_data/stock/realtime/trade/stock_trade_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/authorization/authorization_data.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/authorization/authorization_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/enums/streaming_message_type.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/listening/listening_data.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/listening/listening_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/streaming_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/trade/enums/trade_update_event.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/trade/trade_update.json (82%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/endpoint/streaming/trade/trade_update_message.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/properties/data_api_type.json (100%)
 rename {schema_json => src/main/java/net/jacobpeterson/alpaca}/properties/endpoint_api_type.json (100%)
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java
 rename src/main/java/net/jacobpeterson/alpaca/{util/okhttp => websocket}/WebsocketStateListener.java (94%)
 delete mode 100644 src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
 delete mode 100644 src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
 delete mode 100644 src/test/resources/alpaca.default.properties

diff --git a/.editorconfig b/.editorconfig
index 818a4c9e..e8ed66b1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -509,7 +509,7 @@ ij_typescript_finally_on_new_line = false
 ij_typescript_for_brace_force = always
 ij_typescript_for_statement_new_line_after_left_paren = false
 ij_typescript_for_statement_right_paren_on_new_line = false
-ij_typescript_for_statement_wrap = split_into_lines
+ij_typescript_for_statement_wrap = normal
 ij_typescript_force_quote_style = true
 ij_typescript_force_semicolon_style = true
 ij_typescript_function_expression_brace_style = end_of_line
@@ -519,7 +519,7 @@ ij_typescript_import_prefer_absolute_path = global
 ij_typescript_import_sort_members = true
 ij_typescript_import_sort_module_name = false
 ij_typescript_import_use_node_resolution = true
-ij_typescript_imports_wrap = normal
+ij_typescript_imports_wrap = on_every_item
 ij_typescript_indent_case_from_switch = true
 ij_typescript_indent_chained_calls = true
 ij_typescript_indent_package_children = 0
@@ -1023,7 +1023,7 @@ ij_json_wrap_long_lines = false
 
 [{*.htm,*.html,*.ng,*.peb,*.sht,*.shtm,*.shtml}]
 ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
-ij_html_align_attributes = true
+ij_html_align_attributes = false
 ij_html_align_text = false
 ij_html_attribute_wrap = normal
 ij_html_block_comment_add_space = false
@@ -1182,8 +1182,8 @@ ij_prototext_spaces_within_brackets = false
 
 [{*.properties,spring.handlers,spring.schemas}]
 ij_properties_align_group_field_declarations = false
-ij_properties_keep_blank_lines = false
-ij_properties_key_value_delimiter = equals
+ij_properties_keep_blank_lines = true
+ij_properties_key_value_delimiter = colon
 ij_properties_spaces_around_key_value_delimiter = false
 
 [{*.qute.htm,*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}]
diff --git a/.gitattributes b/.gitattributes
index 1730d965..710d818f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,46 +1,7 @@
-# https://www.gitattributes.io/api/java
-#
-# Handle line endings automatically for files detected as text
-# and leave all files detected as binary untouched.
+# Handle line endings automatically for files detected as text and leave all files detected as binary untouched.
 * text=auto
 
-#
-# The above will handle all files NOT found below
-#
-# These files are text and should be normalized (Convert crlf => lf)
-*.css           text
-*.df            text
-*.htm           text
-*.html          text
-*.java          text
-*.js            text
-*.json          text
-*.jsp           text
-*.jspf          text
-*.jspx          text
-*.properties    text
-*.sh            text
-*.tld           text
-*.txt           text
-*.tag           text
-*.tagx          text
-*.xml           text
-*.yml           text
-
-# Declare files that will always have CRLF line endings on checkout.
+# Declare files that should always have CRLF line endings at checkout.
 *.sln           text eol=crlf
 *.bat           text eol=crlf
-
-# These files are binary and should be left untouched
-# (binary is a macro for -text -diff)
-*.class         binary
-*.dll           binary
-*.ear           binary
-*.gif           binary
-*.ico           binary
-*.jar           binary
-*.jpg           binary
-*.jpeg          binary
-*.png           binary
-*.so            binary
-*.war           binary
+*.cmd           text eol=crlf
diff --git a/.github/image/logo.png b/.github/image/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9cdaa37b4314105f5b4663bc274384a7cba552d
GIT binary patch
diff --git a/.gitignore b/.gitignore
index fcbc20f3..985d4c2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@ build/
 .settings
 .DS_Store
 .idea/
-alpaca.properties
+*.properties
+!*.default.properties
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d54435b1..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: java
-install: true
-dist: trusty
-
-jdk:
-  - oraclejdk8
-
-before_script:
-  - chmod +x gradlew
-
-script:
-  - ./gradlew build check jacocoTestReport --stacktrace
-
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
-  
-before_cache:
-  - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock
-  - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
-
-cache:
-  directories:
-    - $HOME/.gradle/caches/
-    - $HOME/.gradle/wrapper/
diff --git a/LICENSE b/LICENSE.txt
similarity index 96%
rename from LICENSE
rename to LICENSE.txt
index 706b1675..38f23352 100644
--- a/LICENSE
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) Jacob Peterson 2021
+Copyright (c) Jacob Peterson
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index c483ee2d..94b7e833 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,13 @@
-<p align="center"><a href="https://petersoj.github.io/alpaca-java/" target="_blank"><img src="https://i.imgur.com/D8XzGvJ.png" alt="Logo"></a></p>
+<p align="center"><a href="https://petersoj.github.io/alpaca-java/" target="_blank"><img src=".github/image/logo.png" alt="Logo"></a></p>
 <p align="center">
+    <a href="https://github.com/Petersoj/alpaca-java" target="_blank"><img alt="GitHub Repository" src="https://img.shields.io/badge/GitHub-000000?logo=github"></a>
     <a href="https://search.maven.org/artifact/net.jacobpeterson/alpaca-java" target="_blank"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/net.jacobpeterson/alpaca-java"></a>
     <a href="https://javadoc.io/doc/net.jacobpeterson/alpaca-java" target="_blank"><img src="https://javadoc.io/badge/net.jacobpeterson/alpaca-java.svg" alt="Javadocs"></a>
-    <a href="https://travis-ci.com/github/Petersoj/alpaca-java" target="_blank"><img src="https://travis-ci.com/Petersoj/alpaca-java.svg?branch=master" alt="Build Status"></a>
-    <a href="https://codecov.io/gh/petersoj/alpaca-java"><img src="https://codecov.io/gh/petersoj/alpaca-java/branch/master/graph/badge.svg" alt="CodeCov badge"/></a>
     <a href="https://opensource.org/licenses/MIT" target="_blank"><img alt="GitHub" src="https://img.shields.io/github/license/petersoj/alpaca-java"></a>
 </p>
 
 # Overview
-This is a Java implementation for the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free stock trading API. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
+This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate REST API clients and implements the websocket streaming interface using [OkHttp](https://square.github.io/okhttp/). This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
 
 Give this repository a star ⭐ if it helped you build a trading algorithm in Java!
 
@@ -16,19 +15,16 @@ Give this repository a star ⭐ if it helped you build a trading algorithm in Ja
 If you are using Gradle as your build tool, add the following dependency to your `build.gradle` file:
 
 ```
-dependencies {
-    implementation group: 'net.jacobpeterson', name: 'alpaca-java', version: '9.2.0'
-}
+implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "10.0.0"
 ```
 
 If you are using Maven as your build tool, add the following dependency to your `pom.xml` file:
 
 ```
 <dependency>
-    <groupId>net.jacobpeterson</groupId>
+    <groupId>net.jacobpeterson.alpaca</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.2.0</version>
-    <scope>compile</scope>
+    <version>10.0.0</version>
 </dependency>
 ```
 
@@ -516,8 +512,8 @@ newsWebsocket.subscribeToControl(
 newsWebsocket.connect();
 newsWebsocket.waitForAuthorization(5, TimeUnit.SECONDS);
 if (!newsWebsocket.isValid()) {
-            System.out.println("Websocket not valid!");
-            return;
+    System.out.println("Websocket not valid!");
+    return;
 }
 newsWebsocket.subscribe(null, null, null, Arrays.asList("*"));
 
@@ -534,31 +530,18 @@ To build this project yourself, clone this repository and run:
 ./gradlew build
 ```
 
-To install built artifacts to your local maven repo, run:
-```
-./gradlew install -x test
-```
-
-# Testing
-
-To run mocked tests using Mockito, run:
-```
-./gradlew test
-```
-Note that mocked tests never send real API requests to Alpaca. Mocked tests are meant to test the basic integrity of this API locally.
-
-To run live endpoint tests with Alpaca Paper credentials, create the `alpaca.properties` file in `src/test/resources` with the corresponding credentials. Then run:
+To install the built artifacts to your local Maven repository on your machine (the `~/.m2/` directory), run:
 ```
-./gradlew test -PtestPackage=live
+./gradlew install
 ```
-Note that the live tests will modify your account minimally. It's meant to test live endpoints on a real paper account during market hours to confirm that API methods are working properly. Please read through the live endpoint tests [here](https://github.com/Petersoj/alpaca-java/tree/master/src/test/java/live/net/jacobpeterson/alpaca) before running this testing suite on your own paper account.
 
 # TODO
-- Finish Unit Testing (both live and mocked)
-- Use [TA4j](https://github.com/ta4j/ta4j) `Num` interface instead of `Double` for number variables so that users can use either `Double` or `BigDecimal` for performance or precision in price data.
-- Add [TimeSeriesDataStore](https://github.com/Petersoj/TimeSeriesDataStore) using Alpaca Data API
+- Implement Unit Testing for REST API and Websocket streaming (both live and mocked)
+- Implement Broker API websockets
 
 # Contributing
 Contributions are welcome!
 
-If you are creating a Pull Request, be sure to create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork.
+When creating a Pull Request, keep the following in mind:
+1. Create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork.
+2. Use the `dev` branch in this repository as the base branch in your Pull Request.
diff --git a/_config.yml b/_config.yml
index 259a24e4..72d781d7 100644
--- a/_config.yml
+++ b/_config.yml
@@ -1 +1 @@
-theme: jekyll-theme-tactile
\ No newline at end of file
+theme: jekyll-theme-tactile
diff --git a/build.gradle b/build.gradle
index a861d055..844d41b9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,35 +1,29 @@
-import org.jsonschema2pojo.gradle.GenerateJsonSchemaJavaTask
+import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
 
-buildscript {
-    repositories {
-        mavenCentral()
-    }
+plugins {
+    id "java"
+    id "java-library"
 
-    dependencies {
-        // For Maven and Maven Central support
-        classpath group: 'com.bmuschko', name: 'gradle-nexus-plugin', version: '2.3.1'
-        // For JSONSchema2POJO
-        classpath group: 'org.jsonschema2pojo', name: 'jsonschema2pojo-gradle-plugin', version: '1.1.1'
-    }
-}
+    // Adapted JSONSchema2POJO plugin
+    id "jsonschema2pojo-adapted"
 
-plugins {
-    id 'java'
+    // OpenAPI Generator
+    id "org.openapi.generator" version "7.3.0"
 
-    // For Maven and Maven Central support
-    id 'maven-publish'
-    id 'com.bmuschko.nexus' version '2.3.1'
-    id 'io.codearte.nexus-staging' version '0.11.0'
+    // Gradle versions plugin
+    id "com.github.ben-manes.versions" version "0.51.0"
 
-    // For code coverage reports
-    id 'jacoco'
+    // For Maven Central publishing
+    id "maven-publish"
+    id "signing"
 }
 
-apply plugin: 'jsonschema2pojo'
+final def projectGroup = "net.jacobpeterson.alpaca"
+final def projectArtifactID = "alpaca-java"
+final def projectVersion = "10.0.0-SNAPSHOT"
 
-archivesBaseName = 'alpaca-java'
-group = 'net.jacobpeterson'
-version = '9.2.0-SNAPSHOT'
+group = projectGroup
+version = projectVersion
 
 repositories {
     mavenCentral()
@@ -37,215 +31,237 @@ repositories {
 
 dependencies {
     // Logging framework
-    implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36'
-    // This is a bridge for the Apache Jakarta Commons Logging library used in dependencies to actually use SLF4j instead
-    implementation group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.36'
+    implementation group: "org.slf4j", name: "slf4j-api", version: "2.1.0-alpha1"
+    // Require dependencies that use the Apache Jakarta Commons Logging library to use SLF4j instead
+    implementation group: "org.slf4j", name: "jcl-over-slf4j", version: "2.1.0-alpha1"
+
+    // Google Guava
+    implementation group: "com.google.guava", name: "guava", version: "33.0.0-jre"
 
-    // Guava and GSON
-    implementation group: 'com.google.guava', name: 'guava', version: '31.1-jre'
-    implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0'
+    // GSON
+    implementation group: "com.google.code.gson", name: "gson", version: "2.10.1"
 
     // OkHttp
-    implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.10.0'
-    implementation group: 'com.github.devcsrj', name: 'slf4j-okhttp3-logging-interceptor', version: '1.0.1'
+    implementation group: "com.squareup.okhttp3", name: "okhttp", version: "5.0.0-alpha.12"
+    implementation group: "com.squareup.okhttp3", name: "logging-interceptor", version: "5.0.0-alpha.12"
 
-    // Unit test dependencies
-    testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.36'
-    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.8.2'
-    testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.6.1'
+    // Libraries used by the OpenAPI generated client libraries
+    implementation group: "jakarta.annotation", name: "jakarta.annotation-api", version: "1.3.5"
+    implementation group: "org.openapitools", name: "jackson-databind-nullable", version: "0.2.6"
+    implementation group: "io.gsonfire", name: "gson-fire", version: "1.9.0"
 }
 
 // Exclude any SLF4j-implementation transitive dependencies so users can use a logging framework of their choice
 configurations.implementation {
-    exclude group: 'commons-logging', module: 'commons-logging'
+    exclude group: "commons-logging", module: "commons-logging"
 }
 
-sourceSets {
-    main {
-        java {
-            srcDirs 'src/main/java'
-        }
-    }
-    test {
-        java {
-            srcDirs 'src/test/java'
-        }
-    }
+java {
+    sourceCompatibility = JavaVersion.VERSION_17
+    targetCompatibility = JavaVersion.VERSION_17
+    withJavadocJar()
+    withSourcesJar()
 }
 
-sourceCompatibility = 1.8
-targetCompatibility = 1.8
-[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
+[compileJava, compileTestJava]*.options*.encoding = "UTF-8"
 
 javadoc {
-    // Explicitly define source version
-    options.addStringOption('source', '8')
-    // Used to suppress Javadoc linting warnings
-    options.addStringOption('Xdoclint:none', '-quiet')
-    // Explicitly define Charset for Javadocs
-    options.addStringOption('charset', 'UTF-8')
-    // Add Java SE 8 link
-    options.addStringOption('link', 'https://docs.oracle.com/javase/8/docs/api/')
+    options.addStringOption("source", "17")
+    options.addStringOption("Xdoclint:none", "-quiet") // Suppress Javadoc linting warnings
+    options.addStringOption("charset", "UTF-8")
+    options.addStringOption("link", "https://docs.oracle.com/javase/8/docs/api/")
 }
 
-test {
-    useJUnitPlatform()
-
-    testLogging {
-        events "passed", "skipped", "failed"
-        exceptionFormat "full"
-    }
+//
+// BEGIN Alpaca OpenAPI Specification (OAS) client generation
+//
 
-    doFirst {
-        def testPackageArgument = "testPackage"
-        filter.includeTestsMatching(project.hasProperty(testPackageArgument) &&
-                project.getProperty(testPackageArgument).equals("live") ? "*.live.*" : "*.mock.*")
+// These IDs come from the URLs on this page: https://docs.alpaca.markets/v1.1/openapi
+final def specIDsOfFileNames = Map.of(
+        "trader.json", "657760422129f9005cf4bd58",
+        "broker.json", "657771fc2471d20070183049",
+        "market-data.json", "6577704d1fb6b9007032bf39")
+final File specDownloadPath = new File(project.layout.buildDirectory.get().getAsFile(),
+        "/download/openapi/")
+final File generatedClientLibrariesPath = new File(project.layout.buildDirectory.get().getAsFile(),
+        "/generated/openapi/")
+
+final def generateAllOpenAPIClientsTasks = tasks.register("generateAllOpenAPIClients") {
+    doLast {
+        fixOpenAPIGeneratedClientIssues(generatedClientLibrariesPath)
     }
 }
+compileJava.dependsOn generateAllOpenAPIClientsTasks
 
-jacocoTestReport {
-    reports {
-        xml.enabled true
-        html.enabled false
+def downloadOpenAPISpecFilesTask = tasks.register("downloadOpenAPISpecFiles") {
+    configure {
+        outputs.dir(specDownloadPath)
     }
-}
-jacocoTestReport.dependsOn check
 
-//
-// START publishing
-//
-modifyPom {
-    project {
-        name = 'alpaca-java'
-        description = 'Java API for Alpaca'
-        url = 'https://github.com/Petersoj/alpaca-java'
-        inceptionYear = '2018'
-
-        licenses {
-            license {
-                name = 'MIT License'
-                url = 'https://opensource.org/licenses/MIT'
+    doLast {
+        // Download spec files
+        specIDsOfFileNames.forEach { fileName, alpacaSpecID ->
+            final def outputFile = new File(specDownloadPath, fileName)
+            final def specURL = "https://docs.alpaca.markets/v1.1/openapi/${alpacaSpecID}"
+            outputFile.getParentFile().mkdirs()
+            try {
+                logger.info("Downloading OpenAPI spec file from: {}", specURL)
+                new URL(specURL).withInputStream { inputStream -> outputFile.withOutputStream { it << inputStream } }
+                logger.info("Successfully downloaded OpenAPI spec file to: {}", outputFile.getPath())
+            } catch (IOException ioException) {
+                throw new RuntimeException("Could not download OpenAPI spec file!", ioException)
             }
         }
 
-        developers {
-            developer {
-                id = 'Petersoj'
-                name = 'Jacob Peterson'
-            }
+        // Fix an issue in the Broker spec file that uses an empty string as an object key for some reason
+        // TODO remove this when issue is fixed by Alpaca
+        final def brokerSpecFile = new File(specDownloadPath, "broker.json")
+        brokerSpecFile.text = brokerSpecFile.text
+                .replace(",\"\":{\"type\":\"string\",\"x-stoplight\":{\"id\":\"1zg5jnb6rzdoo\"}}", "")
+    }
+}
+
+for (def specFileName : specIDsOfFileNames.keySet()) {
+    final def specName = specFileName.replace(".json", "")
+    final def inputSpecFile = new File(specDownloadPath, specFileName)
+    final def outputDirectory = new File(generatedClientLibrariesPath, specName)
 
-            developer {
-                id = 'mainstringargs'
-                name = 'main(String[] args)'
+    // Add source set of OpenAPI generated client library
+    sourceSets {
+        main {
+            java {
+                srcDir new File(outputDirectory, "src/main/java/").getPath()
             }
         }
+    }
 
-        scm {
-            url = "https://github.com/Petersoj/alpaca-java.git"
-            connection = "scm:git:https://github.com/Petersoj/alpaca-java.git"
-            developerConnection = "scm:git:git@github.com/Petersoj/alpaca-java.git"
+    // Register and configure OpenAPI generate task
+    final def generateTask = tasks.register("generateOpenAPIClientFor-" + specName, GenerateTask) {
+        dependsOn {
+            downloadOpenAPISpecFilesTask
         }
-    }
-}
 
-extraArchive {
-    sources = true
-    tests = true
-    javadoc = true
-}
+        it.onlyIf {
+            !downloadOpenAPISpecFilesTask.get().state.upToDate &&
+                    downloadOpenAPISpecFilesTask.get().state.failure == null &&
+                    !downloadOpenAPISpecFilesTask.get().state.skipped &&
+                    downloadOpenAPISpecFilesTask.get().state.executed
+        }
 
-nexus {
-    sign = true
-    repositoryUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'
-    snapshotRepositoryUrl = 'https://oss.sonatype.org/content/repositories/snapshots/'
-}
+        configure {
+            inputs.file(inputSpecFile)
+            outputs.dir(outputDirectory)
+        }
 
-nexusStaging {
-    packageGroup = "net.jacobpeterson"
+        generatorName.set("java")
+        inputSpec.set(inputSpecFile.getPath())
+        outputDir.set(outputDirectory.getPath())
+        final def destinationPackage = projectGroup + ".openapi." + specName.replace("-", "")
+        configOptions.set(Map.of(
+                "artifactId", specName,
+                "artifactVersion", "1.0.0-SNAPSHOT",
+                "groupId", destinationPackage,
+                "invokerPackage", destinationPackage,
+                "apiPackage", destinationPackage + ".api",
+                "modelPackage", destinationPackage + ".model",
+                "library", "okhttp-gson",
+                "documentationProvider", "none"))
+        cleanupOutput.set(true)
+        validateSpec.set(false)
+        skipValidateSpec.set(true)
+        generateModelTests.set(false)
+        generateModelDocumentation.set(false)
+        generateApiTests.set(false)
+        generateApiDocumentation.set(false)
+    }
+    generateAllOpenAPIClientsTasks.configure {
+        dependsOn generateTask
+    }
 }
 
-// Used to prevent accidental publication of Alpaca keys
-processResources {
-    exclude('alpaca.properties')
+static void fixOpenAPIGeneratedClientIssues(File generatedClientLibrariesPath) {
+    // Fix broker spec generated client issues with discriminators
+    // on some enums: https://github.com/OpenAPITools/openapi-generator/issues/806
+    final def brokerTransferDataJavaFile = new File(generatedClientLibrariesPath,
+            "broker/src/main/java/net/jacobpeterson/alpaca/openapi/broker/model/TransferData.java")
+    brokerTransferDataJavaFile.text = brokerTransferDataJavaFile.text
+            .replace("this.transferType = this.getClass().getSimpleName();", "")
+    final def brokerTransferResourceJavaFile = new File(generatedClientLibrariesPath,
+            "broker/src/main/java/net/jacobpeterson/alpaca/openapi/broker/model/TransferResource.java")
+    brokerTransferResourceJavaFile.text = brokerTransferResourceJavaFile.text
+            .replace("this.type = this.getClass().getSimpleName();", "")
 }
 
 //
-// END publishing
+// END Alpaca OpenAPI Specification (OAS) client generation
 //
 
 //
-// START POJO generation
+// BEGIN Publishing
 //
 
-// Disable default 'jsonSchema2Pojo' task
-tasks.getByName("generateJsonSchema2Pojo").enabled(false)
-
-// The generated POJOs will be in a package structure analogous to the path in the 'schema_json/' directory
-// See: https://github.com/joelittlejohn/jsonschema2pojo/wiki/Reference
-
-final def targetDirectoryPath = file("${project.buildDir}/generated-sources/schemajson").getPath()
-
-task generatePOJOs() {
-    final def jsonSourceDirectory = file("${project.projectDir}/schema_json").getPath()
-    final def jsonPackageNamePrefix = "net.jacobpeterson.alpaca.model"
-
-    // Loop through all files in schema JSON file tree
-    file(jsonSourceDirectory).eachFileRecurse { jsonFile ->
-        if (jsonFile.getName().endsWith(".json")) {
-            def startPackageIndex = jsonFile.getAbsolutePath().indexOf(jsonSourceDirectory) + jsonSourceDirectory.length()
-            def targetPackage = jsonPackageNamePrefix + jsonFile.getParentFile().getAbsolutePath()
-                    .substring(startPackageIndex)
-                    .replace(File.separator, '.').replace('-', '').replace('_', '')
-                    .toLowerCase()
-
-            def jsonToPOJOTask = tasks.create('json2POJOTask-' + targetPackage + "." + jsonFile.getName(),
-                    GenerateJsonSchemaJavaTask)
-
-            // Configure this task to participate in incremental builds so that it only executes when changes occur
-            jsonToPOJOTask.configure {
-                inputs.file(jsonFile)
-                outputs.dir(targetDirectoryPath)
+publishing {
+    publications {
+        mavenJava(MavenPublication) {
+            groupId = projectGroup
+            artifactId = projectArtifactID
+            version = projectVersion
+
+            pom {
+                name = projectArtifactID
+                description = "A Java API for Alpaca, the commission free, algo friendly, stock trading broker."
+                url = "https://github.com/Petersoj/alpaca-java"
+                inceptionYear = "2018"
+
+                licenses {
+                    license {
+                        name = "MIT License"
+                        url = "https://opensource.org/licenses/MIT"
+                    }
+                }
+
+                developers {
+                    developer {
+                        id = "Petersoj"
+                        name = "Jacob Peterson"
+                    }
+                    developer {
+                        id = "mainstringargs"
+                        name = "main(String[] args)"
+                    }
+                }
+
+                scm {
+                    url = "https://github.com/Petersoj/alpaca-java.git"
+                    connection = "scm:git:https://github.com/Petersoj/alpaca-java.git"
+                    developerConnection = "scm:git:git@github.com/Petersoj/alpaca-java.git"
+                }
             }
+        }
+    }
 
-            jsonToPOJOTask.doFirst {
-                jsonToPOJOTask.configuration.source = files(jsonFile.getAbsolutePath())
-                jsonToPOJOTask.configuration.targetDirectory = file(targetDirectoryPath)
-                jsonToPOJOTask.configuration.targetPackage = targetPackage
+    repositories {
+        maven {
+            name = "OSSRH"
+            url = projectVersion.contains("SNAPSHOT") ? "https://oss.sonatype.org/content/repositories/snapshots/" :
+                    "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
+            credentials {
+                username = project.property("nexus.username")
+                password = project.property("nexus.password")
             }
-
-            dependsOn jsonToPOJOTask
         }
     }
 }
 
-compileJava {
-    dependsOn generatePOJOs
+signing {
+    sign publishing.publications.mavenJava
 }
 
-sourceSets {
-    main {
-        java {
-            srcDir targetDirectoryPath
-        }
-    }
-}
-
-jsonSchema2Pojo {
-    includeAdditionalProperties = true
-    targetDirectory = file(targetDirectoryPath)
-    propertyWordDelimiters = ['-', '_'] as char[]
-    annotationStyle = 'gson'
-    sourceType = 'jsonschema'
-    customDateTimePattern = "yyyy-MM-ddTHH:mm:ssZ"
-    includeGeneratedAnnotation = false
-    includeConstructors = true
-    serializable = true
-    includeGetters = true
-    includeSetters = true
-    includeCopyConstructor = true
-    generateBuilders = true
+// Used to prevent accidental publication of Alpaca keys
+processResources {
+    exclude("alpaca.properties")
 }
 
 //
-// END POJO generation
+// END Publishing
 //
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 00000000..1ca86d32
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,29 @@
+plugins {
+    id "groovy-gradle-plugin"
+}
+
+gradlePlugin {
+    plugins {
+        jsonschema2pojoAdapted {
+            id = "jsonschema2pojo-adapted"
+            implementationClass =
+                    "net.jacobpeterson.alpaca.buildsrc.plugin.jsonschema2pojoadapted.JSONSchema2POJOAdaptedPlugin"
+        }
+    }
+}
+
+repositories {
+    mavenCentral()
+    gradlePluginPortal()
+}
+
+dependencies {
+    gradleApi()
+
+    // Google Guava
+    implementation group: "com.google.guava", name: "guava", version: "33.0.0-jre"
+
+    // JsonSchema2Pojo
+    implementation group: "org.jsonschema2pojo", name: "jsonschema2pojo-core", version: "1.2.1"
+    implementation group: "org.jsonschema2pojo", name: "jsonschema2pojo-gradle-plugin", version: "1.2.1"
+}
diff --git a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy
new file mode 100644
index 00000000..88ac5a4a
--- /dev/null
+++ b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy
@@ -0,0 +1,128 @@
+package net.jacobpeterson.alpaca.buildsrc.plugin.jsonschema2pojoadapted
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.model.ReplacedBy
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.SourceSetContainer
+import org.gradle.api.tasks.TaskAction
+import org.jsonschema2pojo.GenerationConfig
+import org.jsonschema2pojo.Jsonschema2Pojo
+import org.jsonschema2pojo.gradle.GradleRuleLogger
+
+import java.nio.file.Files
+import java.nio.file.Path
+
+import static JSONSchema2POJOAdaptedPlugin.EXTENSION_KEY
+import static com.google.common.base.Preconditions.checkArgument
+
+/**
+ * {@link GeneratePOJOsTask} is a Gradle task that invokes <code>jsonschema2pojo</code> with special configurations
+ * and generation logic.
+ * TODO publish this plugin somewhere so the code doesn't have to be copied between 'cli' and 'system' Gradle projects.
+ * @see <a href="https://github.com/joelittlejohn/jsonschema2pojo/blob/master/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/GenerateJsonSchemaJavaTask.groovy">GenerateJsonSchemaJavaTask</a>
+ */
+class GeneratePOJOsTask extends DefaultTask {
+
+    @ReplacedBy("configurationString")
+    GenerationConfig configuration
+
+    @Input
+    String getConfigurationString() {
+        configuration.toString()
+    }
+
+    @Internal
+    List<JSONSchemaFileConfig> jsonSchemaFileConfigs
+
+    GeneratePOJOsTask() {
+        group = "Build"
+        description = "Generates Java POJOs from JSON schemas."
+
+        project.afterEvaluate {
+            // Validate plugin configuration
+            configuration = project.extensions.getByName(EXTENSION_KEY) as GenerationConfig
+            checkArgument(!configuration.source.hasNext(), "'source' should not be set for '${name}'.")
+            checkArgument(configuration.targetDirectory == null, "'targetDirectory' should not be set for '${name}'.")
+
+            // Set custom defaults on plugin configuration
+            configuration.targetDirectory = project.file(
+                    "${project.layout.buildDirectory.getAsFile().get().getPath()}/generated-sources/jsonschema2pojo")
+            configuration.targetPackage = configuration.targetPackage == "" ?
+                    "${project.group}.model" : configuration.targetPackage
+            configuration.propertyWordDelimiters = ["-", "_"] as char[]
+            configuration.annotationStyle = "gson"
+            configuration.sourceType = "jsonschema"
+            configuration.customDateTimePattern = "yyyy-MM-ddTHH:mm:ssZ"
+            configuration.includeConstructors = true
+            configuration.serializable = true
+            configuration.includeGetters = true
+            configuration.includeSetters = true
+            configuration.includeCopyConstructor = true
+            configuration.generateBuilders = true
+
+            // Get project main java source set
+            final def projectMainJavaSourceSet =
+                    project.extensions.getByType(SourceSetContainer).named("main").get().java
+
+            // Set task dependents and dependencies
+            project.tasks.named("compileJava").get().dependsOn(this)
+            dependsOn(project.tasks.named("processResources").get())
+
+            // Walk through source files to create a list of JSON schema files to process
+            jsonSchemaFileConfigs = new ArrayList<>()
+            projectMainJavaSourceSet.srcDirs.each { srcDir ->
+                if (!srcDir.exists()) {
+                    return
+                }
+                final def sourceDirPath = srcDir.getAbsolutePath()
+                srcDir.eachFileRecurse { sourceFile ->
+                    final def sourceFilePath = sourceFile.getAbsolutePath()
+                    if (sourceFilePath.endsWith(".json")) {
+                        def targetPackage = sourceFile.getParentFile().getAbsolutePath()
+                                .substring(sourceDirPath.length())
+                                .replace(File.separator, ".").replace("-", "").replace("_", "")
+                                .toLowerCase()
+                        targetPackage = targetPackage.replace("${project.group}", "")
+                        targetPackage = targetPackage.startsWith(".") ? targetPackage.substring(1) : targetPackage
+                        targetPackage = configuration.targetPackage +
+                                (targetPackage.isEmpty() ? "" : targetPackage)
+                        jsonSchemaFileConfigs.add(new JSONSchemaFileConfig(sourceFile, targetPackage))
+
+                        inputs.file(sourceFile)
+                        outputs.file(project.file(configuration.targetDirectory.getAbsolutePath() + File.separator +
+                                targetPackage.replace(".", File.separator) + File.separator +
+                                sourceFile.getName().replace(".json", ".java")))
+                    }
+                }
+            }
+
+            outputs.dir(configuration.targetDirectory)
+            outputs.cacheIf { true }
+
+            projectMainJavaSourceSet.srcDirs(configuration.targetDirectory)
+        }
+    }
+
+    @TaskAction
+    def execute() {
+        logger.info("Deleting existing generated POJOs...")
+        Files.walk(configuration.targetDirectory.toPath())
+                .sorted(Comparator.reverseOrder())
+                .map(Path::toFile)
+                .peek(file -> logger.info("Deleting file: {}", file.getPath()))
+                .forEach(File::delete)
+        logger.info("Deleted existing generated POJOs.")
+
+        logger.info("Generating POJOs from JSON schemas...")
+        final def gradleRuleLogger = new GradleRuleLogger(logger)
+        jsonSchemaFileConfigs.forEach { jsonSchemaFileConfig ->
+            configuration.source = List.of(jsonSchemaFileConfig.sourceFile.toURI().toURL())
+            configuration.targetPackage = jsonSchemaFileConfig.targetPackage
+            Jsonschema2Pojo.generate(configuration, gradleRuleLogger)
+            logger.info("Generated POJO for: {}", jsonSchemaFileConfig.targetPackage + "." +
+                    jsonSchemaFileConfig.sourceFile.getName())
+        }
+        logger.info("Generated POJOs from JSON schemas.")
+    }
+}
diff --git a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy
new file mode 100644
index 00000000..9d53a826
--- /dev/null
+++ b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy
@@ -0,0 +1,26 @@
+package net.jacobpeterson.alpaca.buildsrc.plugin.jsonschema2pojoadapted
+
+import org.gradle.api.GradleException
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.jsonschema2pojo.gradle.JsonSchemaExtension
+
+/**
+ * {@link JSONSchema2POJOAdaptedPlugin} is a Gradle plugin that adapts the <code>jsonschema2pojo</code> Gradle plugin
+ * to add/change some features.
+ */
+class JSONSchema2POJOAdaptedPlugin implements Plugin<Project> {
+
+    public static final EXTENSION_KEY = "jsonSchema2POJOAdaptedConfig"
+
+    @Override
+    void apply(Project project) {
+        project.extensions.create(EXTENSION_KEY, JsonSchemaExtension)
+        if (project.plugins.hasPlugin("java")) {
+            final def generatePOJOsTask = project.tasks.register("generatePOJOs", GeneratePOJOsTask)
+            generatePOJOsTask.get().enabled = true
+        } else {
+            throw new GradleException("'java' Gradle plugin required.")
+        }
+    }
+}
diff --git a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy
new file mode 100644
index 00000000..698a30b9
--- /dev/null
+++ b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy
@@ -0,0 +1,31 @@
+package net.jacobpeterson.alpaca.buildsrc.plugin.jsonschema2pojoadapted
+
+/**
+ * {@link JSONSchemaFileConfig} provides the configuration fields for a single JSON schema file.
+ */
+class JSONSchemaFileConfig {
+
+    private File sourceFile
+    private String targetPackage
+    private String outputFilePath
+
+    /**
+     * Instantiates a new {@link JSONSchemaFileConfig}.
+     *
+     * @param sourceFile the source file
+     * @param targetPackage the target package
+     */
+    JSONSchemaFileConfig(File sourceFile, String targetPackage) {
+        this.sourceFile = sourceFile
+        this.targetPackage = targetPackage
+        this.outputFilePath = outputFilePath
+    }
+
+    File getSourceFile() {
+        return sourceFile
+    }
+
+    String getTargetPackage() {
+        return targetPackage
+    }
+}
diff --git a/codecov.yml b/codecov.yml
deleted file mode 100644
index c749985f..00000000
--- a/codecov.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-coverage:
-  status:
-    # Turn off Codecov status for now
-    project: off
-    patch: off
-comment: false
\ No newline at end of file
diff --git a/gradle/wrapper/.gitignore b/gradle/wrapper/.gitignore
new file mode 100644
index 00000000..351625b1
--- /dev/null
+++ b/gradle/wrapper/.gitignore
@@ -0,0 +1 @@
+!gradle-wrapper.properties
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1f3fdbc5..2ea3535d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
+networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/schema_json/endpoint/account/account.json b/schema_json/endpoint/account/account.json
deleted file mode 100644
index 7e558fe2..00000000
--- a/schema_json/endpoint/account/account.json
+++ /dev/null
@@ -1,110 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account/\">Account</a>.",
-  "properties": {
-    "id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Account ID."
-    },
-    "account_number": {
-      "existingJavaType": "java.lang.String",
-      "title": "Account number."
-    },
-    "status": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.account.enums.AccountStatus",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.account.enums.AccountStatus}."
-    },
-    "currency": {
-      "existingJavaType": "java.lang.String",
-      "title": "\"USD\""
-    },
-    "cash": {
-      "existingJavaType": "java.lang.String",
-      "title": "Cash balance."
-    },
-    "portfolio_value": {
-      "existingJavaType": "java.lang.String",
-      "title": "Total value of cash + holding positions. (This field is deprecated. It is equivalent to the equity field.)"
-    },
-    "pattern_day_trader": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Whether or not the account has been flagged as a pattern day trader."
-    },
-    "trade_suspended_by_user": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "User setting. If true, the account is not allowed to place orders."
-    },
-    "trading_blocked": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "If true, the account is not allowed to place orders."
-    },
-    "transfers_blocked": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "If true, the account is not allowed to request money transfers."
-    },
-    "account_blocked": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "If true, the account activity by user is prohibited."
-    },
-    "created_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Timestamp this account was created at."
-    },
-    "shorting_enabled": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Flag to denote whether or not the account is permitted to short."
-    },
-    "long_market_value": {
-      "existingJavaType": "java.lang.String",
-      "title": "Realtime MtM value of all long positions held in the account."
-    },
-    "short_market_value": {
-      "existingJavaType": "java.lang.String",
-      "title": "Realtime MtM value of all short positions held in the account."
-    },
-    "equity": {
-      "existingJavaType": "java.lang.String",
-      "title": "Cash + long_market_value + short_market_value."
-    },
-    "last_equity": {
-      "existingJavaType": "java.lang.String",
-      "title": "Equity as of previous trading day at 16:00:00 ET."
-    },
-    "multiplier": {
-      "existingJavaType": "java.lang.String",
-      "title": "Buying power multiplier that represents account margin classification; valid values 1 (standard limited margin account with 1x buying power), 2 (reg T margin account with 2x intraday and overnight buying power; this is the default for all non-PDT accounts with $2,000 or more equity), 4 (PDT account with 4x intraday buying power and 2x reg T overnight buying power)."
-    },
-    "buying_power": {
-      "existingJavaType": "java.lang.String",
-      "title": "Current available $ buying power; If multiplier = 4, this is your daytrade buying power which is calculated as (last_equity - (last) maintenance_margin) * 4; If multiplier = 2, buying_power = max(equity – initial_margin,0) * 2; If multiplier = 1, buying_power = cash."
-    },
-    "initial_margin": {
-      "existingJavaType": "java.lang.String",
-      "title": "Reg T initial margin requirement (continuously updated value)."
-    },
-    "maintenance_margin": {
-      "existingJavaType": "java.lang.String",
-      "title": "Maintenance margin requirement (continuously updated value)."
-    },
-    "sma": {
-      "existingJavaType": "java.lang.String",
-      "title": "Value of special memorandum account (will be used at a later date to provide additional buying_power)."
-    },
-    "daytrade_count": {
-      "existingJavaType": "java.lang.Integer",
-      "title": "The current number of daytrades that have been made in the last 5 trading days (inclusive of today)."
-    },
-    "last_maintenance_margin": {
-      "existingJavaType": "java.lang.String",
-      "title": "Your maintenance margin requirement on the previous trading day."
-    },
-    "daytrading_buying_power": {
-      "existingJavaType": "java.lang.String",
-      "title": "Your buying power for day trades (continuously updated value)."
-    },
-    "regt_buying_power": {
-      "existingJavaType": "java.lang.String",
-      "title": "Your buying power under Regulation T (your excess equity - equity minus margin value - times your margin multiplier)."
-    }
-  }
-}
diff --git a/schema_json/endpoint/account/enums/account_status.json b/schema_json/endpoint/account/enums/account_status.json
deleted file mode 100644
index 61f7a081..00000000
--- a/schema_json/endpoint/account/enums/account_status.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account/\">Account</a>.",
-  "enum": [
-    "ONBOARDING",
-    "SUBMISSION_FAILED",
-    "SUBMITTED",
-    "ACCOUNT_UPDATED",
-    "APPROVAL_PENDING",
-    "ACTIVE",
-    "REJECTED",
-    "DISABLED",
-    "ACTION_REQUIRED"
-  ],
-  "javaEnums": [
-    {
-      "description": "The account is onboarding."
-    },
-    {
-      "description": "The account application submission failed for some reason."
-    },
-    {
-      "description": "The account application has been submitted for review."
-    },
-    {
-      "description": "The account information is being updated."
-    },
-    {
-      "description": "The final account approval is pending."
-    },
-    {
-      "description": "The account is active for trading."
-    },
-    {
-      "description": "The account application has been rejected."
-    },
-    {
-      "description": "The account has been disabled."
-    },
-    {
-      "description": "The account requires manual attention."
-    }
-  ]
-}
diff --git a/schema_json/endpoint/account_activities/account_activity.json b/schema_json/endpoint/account_activities/account_activity.json
deleted file mode 100644
index bfa36b8f..00000000
--- a/schema_json/endpoint/account_activities/account_activity.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-activities/\">Account Activities</a>.",
-  "properties": {
-    "activity_type": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.ActivityType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.ActivityType}."
-    },
-    "id": {
-      "existingJavaType": "java.lang.String",
-      "title": "An ID for the activity, always in \"::\" format. Can be sent as page_token in requests to facilitate the paging of results."
-    }
-  }
-}
diff --git a/schema_json/endpoint/account_activities/enums/activity_type.json b/schema_json/endpoint/account_activities/enums/activity_type.json
deleted file mode 100644
index 9fae432b..00000000
--- a/schema_json/endpoint/account_activities/enums/activity_type.json
+++ /dev/null
@@ -1,152 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-activities/\">Account Activities</a>.",
-  "enum": [
-    "FILL",
-    "TRANS",
-    "MISC",
-    "ACATC",
-    "ACATS",
-    "CSD",
-    "CSR",
-    "CSW",
-    "DIV",
-    "DIVCGL",
-    "DIVCGS",
-    "DIVFEE",
-    "DIVFT",
-    "DIVNRA",
-    "DIVROC",
-    "DIVTW",
-    "DIVTXEX",
-    "INT",
-    "INTNRA",
-    "INTTW",
-    "JNL",
-    "JNLC",
-    "JNLS",
-    "MA",
-    "NC",
-    "OPASN",
-    "OPEXP",
-    "OPXRC",
-    "PTC",
-    "PTR",
-    "FEE",
-    "REORG",
-    "SC",
-    "SSO",
-    "SSP",
-    "SPLIT"
-  ],
-  "javaEnums": [
-    {
-      "description": "Order fills (both partial and full fills)."
-    },
-    {
-      "description": "Cash transactions (both CSD and CSW)."
-    },
-    {
-      "description": "Miscellaneous or rarely used activity types (All types except those in TRANS, DIV, or FILL)."
-    },
-    {
-      "description": "ACATS IN/OUT (Cash)."
-    },
-    {
-      "description": "ACATS IN/OUT (Securities)."
-    },
-    {
-      "description": "Cash deposit(+)."
-    },
-    {
-      "description": "Cash receipt(-)."
-    },
-    {
-      "description": "Cash withdrawal(-)."
-    },
-    {
-      "description": "Dividends."
-    },
-    {
-      "description": "Dividend (capital gain long term)."
-    },
-    {
-      "description": "Dividend (capital gain short term)."
-    },
-    {
-      "description": "Dividend fee."
-    },
-    {
-      "description": "Dividend adjusted (Foreign Tax Withheld)."
-    },
-    {
-      "description": "Dividend adjusted (NRA Withheld)."
-    },
-    {
-      "description": "Dividend return of capital."
-    },
-    {
-      "description": "Dividend adjusted (Tefra Withheld)."
-    },
-    {
-      "description": "Dividend (tax exempt)."
-    },
-    {
-      "description": "Interest (credit/margin)."
-    },
-    {
-      "description": "Interest adjusted (NRA Withheld)."
-    },
-    {
-      "description": "Interest adjusted (Tefra Withheld)."
-    },
-    {
-      "description": "Journal entry."
-    },
-    {
-      "description": "Journal entry (cash)."
-    },
-    {
-      "description": "Journal entry (stock)."
-    },
-    {
-      "description": "Merger/Acquisition."
-    },
-    {
-      "description": "Name change."
-    },
-    {
-      "description": "Option assignment."
-    },
-    {
-      "description": "Option expiration."
-    },
-    {
-      "description": "Option exercise."
-    },
-    {
-      "description": "Pass Thru Charge."
-    },
-    {
-      "description": "Pass Thru Rebate."
-    },
-    {
-      "description": "REG/TAF fees."
-    },
-    {
-      "description": "Reorg CA."
-    },
-    {
-      "description": "Symbol change."
-    },
-    {
-      "description": "Stock spinoff."
-    },
-    {
-      "description": "Stock split."
-    },
-    {
-      "description": "Stock split."
-    }
-  ]
-}
diff --git a/schema_json/endpoint/account_activities/enums/trade_activity_side.json b/schema_json/endpoint/account_activities/enums/trade_activity_side.json
deleted file mode 100644
index ddcd4869..00000000
--- a/schema_json/endpoint/account_activities/enums/trade_activity_side.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-activities/\">Account Activities</a>.",
-  "enum": [
-    "buy",
-    "sell"
-  ]
-}
diff --git a/schema_json/endpoint/account_activities/enums/trade_activity_type.json b/schema_json/endpoint/account_activities/enums/trade_activity_type.json
deleted file mode 100644
index 8b92e2b9..00000000
--- a/schema_json/endpoint/account_activities/enums/trade_activity_type.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-activities/\">Account Activities</a>.",
-  "enum": [
-    "fill",
-    "partial_fill"
-  ]
-}
diff --git a/schema_json/endpoint/account_activities/non_trade_activity.json b/schema_json/endpoint/account_activities/non_trade_activity.json
deleted file mode 100644
index 81d8c0fa..00000000
--- a/schema_json/endpoint/account_activities/non_trade_activity.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-activities/\">Account Activities</a>.",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountactivities.AccountActivity"
-  },
-  "properties": {
-    "date": {
-      "existingJavaType": "java.lang.String",
-      "title": "The date on which the activity occurred or on which the transaction associated with the activity settled."
-    },
-    "net_amount": {
-      "existingJavaType": "java.lang.String",
-      "title": "The net amount of money (positive or negative) associated with the activity."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "The symbol of the security involved with the activity. Not present for all activity types."
-    },
-    "qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "quantity",
-      "title": "For dividend activities, the number of shares that contributed to the payment. Not present for other activity types."
-    },
-    "per_share_amount": {
-      "existingJavaType": "java.lang.String",
-      "title": "For dividend activities, the average amount paid per share. Not present for other activity types."
-    },
-    "description": {
-      "existingJavaType": "java.lang.String",
-      "title": "Description field for transaction records (TRANS, CSR, CSD)."
-    },
-    "status": {
-      "existingJavaType": "java.lang.String",
-      "title": "The status."
-    }
-  }
-}
diff --git a/schema_json/endpoint/account_activities/trade_activity.json b/schema_json/endpoint/account_activities/trade_activity.json
deleted file mode 100644
index 1697ad1c..00000000
--- a/schema_json/endpoint/account_activities/trade_activity.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-activities/\">Account Activities</a>.",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountactivities.AccountActivity"
-  },
-  "properties": {
-    "cum_qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "cumulativeQuantity",
-      "title": "The cumulative quantity of shares involved in the execution."
-    },
-    "leaves_qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "remainingQuantity",
-      "title": "For partially_filled orders, the quantity of shares that are left to be filled."
-    },
-    "price": {
-      "existingJavaType": "java.lang.String",
-      "title": "The per-share price that the trade was executed at."
-    },
-    "qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "quantity",
-      "title": "The number of shares involved in the trade execution."
-    },
-    "side": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.TradeActivitySide",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.TradeActivitySide}."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "The symbol of the security being traded."
-    },
-    "transaction_time": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "The time at which the execution occurred."
-    },
-    "order_id": {
-      "existingJavaType": "java.lang.String",
-      "title": "The id for the order that filled. Always in GUID format."
-    },
-    "type": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.TradeActivityType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.TradeActivityType}."
-    }
-  }
-}
diff --git a/schema_json/endpoint/account_configuration/account_configuration.json b/schema_json/endpoint/account_configuration/account_configuration.json
deleted file mode 100644
index 704ee4e1..00000000
--- a/schema_json/endpoint/account_configuration/account_configuration.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-configuration/\">Account Configuration</a>.",
-  "properties": {
-    "dtbp_check": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.DTBPCheck",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.DTBPCheck}. Controls Day Trading Margin Call (DTMC) checks."
-    },
-    "trade_confirm_email": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.TradeConfirmEmail",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.TradeConfirmEmail}. If {@link net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.TradeConfirmEmail#NONE}, emails for order fills are not sent."
-    },
-    "suspend_trade": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "If true, new orders are blocked."
-    },
-    "no_shorting": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "If true, account becomes long-only mode."
-    }
-  }
-}
diff --git a/schema_json/endpoint/account_configuration/enums/dtbp_check.json b/schema_json/endpoint/account_configuration/enums/dtbp_check.json
deleted file mode 100644
index 75940d81..00000000
--- a/schema_json/endpoint/account_configuration/enums/dtbp_check.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "string",
-  "javaName": "DTBPCheck",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-configuration/\">Account Configuration</a>.",
-  "enum": [
-    "both",
-    "entry",
-    "exit"
-  ]
-}
diff --git a/schema_json/endpoint/account_configuration/enums/trade_confirm_email.json b/schema_json/endpoint/account_configuration/enums/trade_confirm_email.json
deleted file mode 100644
index 8f28197c..00000000
--- a/schema_json/endpoint/account_configuration/enums/trade_confirm_email.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/account-configuration/\">Account Configuration</a>.",
-  "enum": [
-    "all",
-    "none"
-  ]
-}
diff --git a/schema_json/endpoint/assets/asset.json b/schema_json/endpoint/assets/asset.json
deleted file mode 100644
index fa65e096..00000000
--- a/schema_json/endpoint/assets/asset.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/assets/\">Assets</a>.",
-  "properties": {
-    "id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Asset ID."
-    },
-    "class": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetClass",
-      "javaName": "assetClass",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetClass}."
-    },
-    "exchange": {
-      "existingJavaType": "java.lang.String",
-      "title": "AMEX, ARCA, BATS, NYSE, NASDAQ or NYSEARCA."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "The symbol."
-    },
-    "name": {
-      "existingJavaType": "java.lang.String",
-      "title": "The official name of the asset."
-    },
-    "status": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetStatus",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetStatus}."
-    },
-    "tradable": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Asset is tradable on Alpaca or not."
-    },
-    "marginable": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Asset is marginable or not."
-    },
-    "shortable": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Asset is shortable or not."
-    },
-    "easy_to_borrow": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Asset is easy-to-borrow or not (filtering for easy_to_borrow = True is the best way to check whether the name is currently available to short at Alpaca)."
-    },
-    "fractionable": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Asset is fractionable or not."
-    }
-  }
-}
diff --git a/schema_json/endpoint/assets/enums/asset_class.json b/schema_json/endpoint/assets/enums/asset_class.json
deleted file mode 100644
index ac36f8e7..00000000
--- a/schema_json/endpoint/assets/enums/asset_class.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/assets/\">Assets</a>.",
-  "enum": [
-    "us_equity",
-    "crypto"
-  ]
-}
diff --git a/schema_json/endpoint/assets/enums/asset_status.json b/schema_json/endpoint/assets/enums/asset_status.json
deleted file mode 100644
index b77af48e..00000000
--- a/schema_json/endpoint/assets/enums/asset_status.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/assets/\">Assets</a>.",
-  "enum": [
-    "active",
-    "inactive"
-  ]
-}
diff --git a/schema_json/endpoint/calendar/calendar.json b/schema_json/endpoint/calendar/calendar.json
deleted file mode 100644
index 96207ded..00000000
--- a/schema_json/endpoint/calendar/calendar.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/calendar/\">Calendar</a>.",
-  "properties": {
-    "date": {
-      "existingJavaType": "java.time.LocalDate",
-      "title": "Date string in \"%Y-%m-%d\" format."
-    },
-    "open": {
-      "existingJavaType": "java.time.LocalTime",
-      "title": "The time the market opens at on this date in \"%H:%M\" format."
-    },
-    "close": {
-      "existingJavaType": "java.time.LocalTime",
-      "title": "The time the market closes at on this date in \"%H:%M\" format."
-    }
-  }
-}
diff --git a/schema_json/endpoint/clock/clock.json b/schema_json/endpoint/clock/clock.json
deleted file mode 100644
index 671d4a19..00000000
--- a/schema_json/endpoint/clock/clock.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/clock/\">Clock</a>.",
-  "properties": {
-    "timestamp": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Current timestamp."
-    },
-    "is_open": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "Whether or not the market is open."
-    },
-    "next_open": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Next market open timestamp."
-    },
-    "next_close": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Next market close timestamp."
-    }
-  }
-}
diff --git a/schema_json/endpoint/common/enums/sort_direction.json b/schema_json/endpoint/common/enums/sort_direction.json
deleted file mode 100644
index 01e5a4eb..00000000
--- a/schema_json/endpoint/common/enums/sort_direction.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "type": "string",
-  "title": "Defines enums for sort directions.",
-  "enum": [
-    "asc",
-    "desc"
-  ],
-  "javaEnums": [
-    {
-      "name": "ASCENDING"
-    },
-    {
-      "name": "DESCENDING"
-    }
-  ]
-}
diff --git a/schema_json/endpoint/corporate_actions/common/announcement.json b/schema_json/endpoint/corporate_actions/common/announcement.json
deleted file mode 100644
index 14c8e66e..00000000
--- a/schema_json/endpoint/corporate_actions/common/announcement.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://docs.alpaca.markets/reference/get-v2-corporate_actions-announcements\">Corporate announcement</a>.",
-  "properties": {
-    "id": {
-      "existingJavaType": "java.lang.String"
-    },
-    "corporate_actions_id": {
-      "existingJavaType": "java.lang.String"
-    },
-    "ca_type": {
-      "existingJavaType": "java.lang.String"
-    },
-    "ca_sub_type": {
-      "existingJavaType": "java.lang.String"
-    },
-    "initiating_symbol": {
-      "existingJavaType": "java.lang.String"
-    },
-    "initiating_original_cusip": {
-      "existingJavaType": "java.lang.String"
-    },
-    "target_symbol": {
-      "target_symbol": "java.lang.String"
-    },
-    "target_original_cusip": {
-      "target_original_cusip": "java.lang.String"
-    },
-    "declaration_date": {
-      "declaration_date": "java.lang.String"
-    },
-    "expiration_date": {
-      "declaration_date": "java.lang.String"
-    },
-    "record_date": {
-      "declaration_date": "java.lang.String"
-    },
-    "payable_date": {
-      "declaration_date": "java.lang.String"
-    },
-    "cash": {
-      "declaration_date": "java.lang.String"
-    },
-    "old_rate": {
-      "declaration_date": "java.lang.String"
-    },
-    "new_rate": {
-      "declaration_date": "java.lang.String"
-    }
-    
-    
-  }
-}
diff --git a/schema_json/endpoint/orders/cancelled_order.json b/schema_json/endpoint/orders/cancelled_order.json
deleted file mode 100644
index cd34b921..00000000
--- a/schema_json/endpoint/orders/cancelled_order.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "properties": {
-    "id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Order ID."
-    },
-    "status": {
-      "existingJavaType": "java.lang.Integer",
-      "title": "The HTTP status code of the cancelled order."
-    },
-    "body": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.Order",
-      "javaName": "order",
-      "title": "The cancelled {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order}."
-    }
-  }
-}
diff --git a/schema_json/endpoint/orders/enums/current_order_status.json b/schema_json/endpoint/orders/enums/current_order_status.json
deleted file mode 100644
index 25a5829f..00000000
--- a/schema_json/endpoint/orders/enums/current_order_status.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "enum": [
-    "open",
-    "closed",
-    "all"
-  ]
-}
diff --git a/schema_json/endpoint/orders/enums/order_class.json b/schema_json/endpoint/orders/enums/order_class.json
deleted file mode 100644
index 6baaf86c..00000000
--- a/schema_json/endpoint/orders/enums/order_class.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "enum": [
-    "simple",
-    "bracket",
-    "oco",
-    "oto"
-  ],
-  "javaEnums": [
-    {
-      "name": "SIMPLE"
-    },
-    {
-      "name": "BRACKET"
-    },
-    {
-      "name": "ONE_CANCELS_OTHER"
-    },
-    {
-      "name": "ONE_TRIGGERS_OTHER"
-    }
-  ]
-}
diff --git a/schema_json/endpoint/orders/enums/order_side.json b/schema_json/endpoint/orders/enums/order_side.json
deleted file mode 100644
index e8b6178f..00000000
--- a/schema_json/endpoint/orders/enums/order_side.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "enum": [
-    "buy",
-    "sell"
-  ]
-}
diff --git a/schema_json/endpoint/orders/enums/order_status.json b/schema_json/endpoint/orders/enums/order_status.json
deleted file mode 100644
index 5174e120..00000000
--- a/schema_json/endpoint/orders/enums/order_status.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "enum": [
-    "new",
-    "partially_filled",
-    "filled",
-    "done_for_day",
-    "canceled",
-    "expired",
-    "replaced",
-    "pending_cancel",
-    "pending_replace",
-    "accepted",
-    "pending_new",
-    "accepted_for_bidding",
-    "stopped",
-    "rejected",
-    "suspended",
-    "calculated",
-    "held"
-  ],
-  "javaEnums": [
-    {
-      "description": "The order has been received by Alpaca, and routed to exchanges for execution. This is the usual initial state of an order."
-    },
-    {
-      "description": "The order has been partially filled."
-    },
-    {
-      "description": "The order has been filled, and no further updates will occur for the order."
-    },
-    {
-      "description": "The order is done executing for the day, and will not receive further updates until the next trading day."
-    },
-    {
-      "description": "The order has been canceled, and no further updates will occur for the order. This can be either due to a cancel request by the user, or the order has been canceled by the exchanges due to its time-in-force."
-    },
-    {
-      "description": "The order has expired, and no further updates will occur for the order."
-    },
-    {
-      "description": "The order was replaced by another order, or was updated due to a market event such as corporate action."
-    },
-    {
-      "description": "The order is waiting to be canceled."
-    },
-    {
-      "description": "The order is waiting to be replaced by another order. The order will reject cancel request while in this state."
-    },
-    {
-      "description": "The order has been received by Alpaca, but hasn’t yet been routed to the execution venue. This could be seen often out side of trading session hours."
-    },
-    {
-      "description": "The order has been received by Alpaca, and routed to the exchanges, but has not yet been accepted for execution. This state only occurs on rare occasions."
-    },
-    {
-      "description": "The order has been received by exchanges, and is evaluated for pricing. This state only occurs on rare occasions."
-    },
-    {
-      "description": "The order has been stopped, and a trade is guaranteed for the order, usually at a stated price or better, but has not yet occurred. This state only occurs on rare occasions."
-    },
-    {
-      "description": "The order has been rejected, and no further updates will occur for the order. This state occurs on rare occasions and may occur based on various conditions decided by the exchanges."
-    },
-    {
-      "description": "The order has been suspended, and is not eligible for trading. This state only occurs on rare occasions."
-    },
-    {
-      "description": "The order has been completed for the day (either filled or done for day), but remaining settlement calculations are still pending. This state only occurs on rare occasions."
-    },
-    {
-      "description": "The order is part of a bracket leg that is waiting for the initial order to be filled."
-    }
-  ]
-}
diff --git a/schema_json/endpoint/orders/enums/order_time_in_force.json b/schema_json/endpoint/orders/enums/order_time_in_force.json
deleted file mode 100644
index fe468cd2..00000000
--- a/schema_json/endpoint/orders/enums/order_time_in_force.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "enum": [
-    "day",
-    "gtc",
-    "opg",
-    "cls",
-    "ioc",
-    "fok"
-  ],
-  "javaEnums": [
-    {
-      "name": "DAY",
-      "description": "A day order is eligible for execution only on the day it is live. By default, the order is only valid during Regular Trading Hours (9:30am - 4:00pm ET). If unfilled after the closing auction, it is automatically canceled. If submitted after the close, it is queued and submitted the following trading day. However, if marked as eligible for extended hours, the order can also execute during supported extended hours."
-    },
-    {
-      "name": "GOOD_UNTIL_CANCELLED",
-      "description": "The order is good until canceled. Non-marketable GTC limit orders are subject to price adjustments to offset corporate actions affecting the issue. We do not currently support Do Not Reduce(DNR) orders to opt out of such price adjustments."
-    },
-    {
-      "name": "OPG",
-      "description": "Use this TIF with a market/limit order type to submit \"market on open\" (MOO) and \"limit on open\" (LOO) orders. This order is eligible to execute only in the market opening auction. Any unfilled orders after the open will be cancelled. OPG orders submitted after 9:28am but before 7:00pm ET will be rejected. OPG orders submitted after 7:00pm will be queued and routed to the following day’s opening auction. On open/on close orders are routed to the primary exchange. Such orders do not necessarily execute exactly at 9:30am / 4:00pm ET but execute per the exchange’s auction rules."
-    },
-    {
-      "name": "CLS",
-      "description": "Use this TIF with a market/limit order type to submit \"market on close\" (MOC) and \"limit on close\" (LOC) orders. This order is eligible to execute only in the market closing auction. Any unfilled orders after the close will be cancelled. CLS orders submitted after 3:50pm but before 7:00pm ET will be rejected. CLS orders submitted after 7:00pm will be queued and routed to the following day’s closing auction. Only available with API v2."
-    },
-    {
-      "name": "IMMEDIATE_OR_CANCEL",
-      "description": "An Immediate Or Cancel (IOC) order requires all or part of the order to be executed immediately. Any unfilled portion of the order is canceled. Only available with API v2. Most market makers who receive IOC orders will attempt to fill the order on a principal basis only, and cancel any unfilled balance. On occasion, this can result in the entire order being cancelled if the market maker does not have any existing inventory of the security in question."
-    },
-    {
-      "name": "FILL_OR_KILL",
-      "description": "A Fill or Kill (FOK) order is only executed if the entire order quantity can be filled, otherwise the order is canceled. Only available with API v2."
-    }
-  ]
-}
diff --git a/schema_json/endpoint/orders/enums/order_type.json b/schema_json/endpoint/orders/enums/order_type.json
deleted file mode 100644
index 32d93d2b..00000000
--- a/schema_json/endpoint/orders/enums/order_type.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "enum": [
-    "market",
-    "limit",
-    "stop",
-    "stop_limit",
-    "trailing_stop"
-  ],
-  "javaEnums": [
-    {
-      "description": "A market order is a request to buy or sell a security at the currently available market price. It provides the most likely method of filling an order. Market orders fill nearly instantaneously."
-    },
-    {
-      "description": "A limit order is an order to buy or sell at a specified price or better. A buy limit order (a limit order to buy) is executed at the specified limit price or lower (i.e., better). Conversely, a sell limit order (a limit order to sell) is executed at the specified limit price or higher (better). Unlike a market order, you have to specify the limit price parameter when submitting your order."
-    },
-    {
-      "description": "A stop (market) order is an order to buy or sell a security when its price moves past a particular point, ensuring a higher probability of achieving a predetermined entry or exit price. Once the market price crosses the specified stop price, the stop order becomes a market order. Alpaca converts buy stop orders into stop limit orders with a limit price that is 4% higher than a stop price &lt; $50 (or 2.5% higher than a stop price &gt;= $50). Sell stop orders are not converted into stop limit orders."
-    },
-    {
-      "description": "A stop-limit order is a conditional trade over a set time frame that combines the features of a stop order with those of a limit order and is used to mitigate risk. The stop-limit order will be executed at a specified limit price, or better, after a given stop price has been reached. Once the stop price is reached, the stop-limit order becomes a limit order to buy or sell at the limit price or better"
-    },
-    {
-      "description": "Trailing stop orders allow you to continuously and automatically keep updating the stop price threshold based on the stock price movement. You request a single order with a dollar offset value or percentage value as the trail and the actual stop price for this order changes as the stock price moves in your favorable way, or stay at the last level otherwise. This way, you don’t need to monitor the price movement and keep sending replace requests to update the stop price close to the latest market movement."
-    }
-  ]
-}
diff --git a/schema_json/endpoint/orders/order.json b/schema_json/endpoint/orders/order.json
deleted file mode 100644
index 6a6f85fd..00000000
--- a/schema_json/endpoint/orders/order.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
-  "properties": {
-    "id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Order ID."
-    },
-    "client_order_id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Client unique order ID."
-    },
-    "created_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Created at."
-    },
-    "updated_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Updated at."
-    },
-    "submitted_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Submitted at."
-    },
-    "filled_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Filled at."
-    },
-    "expired_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Expired at."
-    },
-    "canceled_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Canceled at."
-    },
-    "failed_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Failed at."
-    },
-    "replaced_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Replaced at."
-    },
-    "replaced_by": {
-      "existingJavaType": "java.lang.String",
-      "title": "The order ID that this order was replaced by."
-    },
-    "replaces": {
-      "existingJavaType": "java.lang.String",
-      "title": "The order ID that this order replaces."
-    },
-    "asset_id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Asset ID."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Asset symbol."
-    },
-    "asset_class": {
-      "existingJavaType": "java.lang.String",
-      "title": "Asset class."
-    },
-    "notional": {
-      "existingJavaType": "java.lang.String",
-      "title": "Ordered notional amount. If entered, qty will be null. Can take up to 9 decimal points."
-    },
-    "qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "quantity",
-      "title": "Ordered quantity. If entered, notional will be null. Can take up to 9 decimal points."
-    },
-    "filled_qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "filledQuantity",
-      "title": "Filled quantity."
-    },
-    "filled_avg_price": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "averageFillPrice",
-      "title": "Filled average price."
-    },
-    "order_class": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderClass",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderClass}. For details of non-simple order classes, please see Bracket Order Overview."
-    },
-    "type": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderType}."
-    },
-    "side": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderSide",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderSide}."
-    },
-    "time_in_force": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderTimeInForce",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderTimeInForce}."
-    },
-    "limit_price": {
-      "existingJavaType": "java.lang.String",
-      "title": "Limit price."
-    },
-    "stop_price": {
-      "existingJavaType": "java.lang.String",
-      "title": "Stop price."
-    },
-    "status": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderStatus",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderStatus}."
-    },
-    "extended_hours": {
-      "existingJavaType": "java.lang.Boolean",
-      "title": "If true, eligible for execution outside regular trading hours."
-    },
-    "legs": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.orders.Order>",
-      "title": "When querying non-simple order_class orders in a nested style, an {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order} entities associated with this order. Otherwise, null."
-    },
-    "trail_percent": {
-      "existingJavaType": "java.lang.String",
-      "title": "The percent value away from the high water mark for trailing stop orders."
-    },
-    "trail_price": {
-      "existingJavaType": "java.lang.String",
-      "title": "The dollar value away from the high water mark for trailing stop orders."
-    },
-    "hwm": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "highWaterMark",
-      "title": "High Water Mark - The highest (lowest) market price seen since the trailing stop order was submitted."
-    }
-  }
-}
diff --git a/schema_json/endpoint/portfolio_history/enums/portfolio_period_unit.json b/schema_json/endpoint/portfolio_history/enums/portfolio_period_unit.json
deleted file mode 100644
index b55c083d..00000000
--- a/schema_json/endpoint/portfolio_history/enums/portfolio_period_unit.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/portfolio-history/\">Portfolio History</a>.",
-  "enum": [
-    "D",
-    "W",
-    "M",
-    "A"
-  ],
-  "javaEnums": [
-    {
-      "name": "DAY"
-    },
-    {
-      "name": "WEEK"
-    },
-    {
-      "name": "MONTH"
-    },
-    {
-      "name": "YEAR"
-    }
-  ]
-}
diff --git a/schema_json/endpoint/portfolio_history/enums/portfolio_time_frame.json b/schema_json/endpoint/portfolio_history/enums/portfolio_time_frame.json
deleted file mode 100644
index 0fb614c7..00000000
--- a/schema_json/endpoint/portfolio_history/enums/portfolio_time_frame.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/portfolio-history/\">Portfolio History</a>.",
-  "enum": [
-    "1Min",
-    "5Min",
-    "15Min",
-    "1H",
-    "1D"
-  ],
-  "javaEnums": [
-    {
-      "name": "ONE_MIN"
-    },
-    {
-      "name": "FIVE_MINUTE"
-    },
-    {
-      "name": "FIFTEEN_MINUTE"
-    },
-    {
-      "name": "ONE_HOUR"
-    },
-    {
-      "name": "ONE_DAY"
-    }
-  ]
-}
diff --git a/schema_json/endpoint/portfolio_history/portfolio_history.json b/schema_json/endpoint/portfolio_history/portfolio_history.json
deleted file mode 100644
index f9f833b4..00000000
--- a/schema_json/endpoint/portfolio_history/portfolio_history.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/portfolio-history/\">Portfolio History</a>.",
-  "properties": {
-    "data_points": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistoryDataPoint>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistoryDataPoint}s."
-    },
-    "base_value": {
-      "existingJavaType": "java.lang.Double",
-      "title": "Basis in dollar of the profit loss calculation."
-    },
-    "timeframe": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.enums.PortfolioTimeFrame",
-      "title": "Time window size of each data element."
-    }
-  }
-}
diff --git a/schema_json/endpoint/portfolio_history/portfolio_history_data_point.json b/schema_json/endpoint/portfolio_history/portfolio_history_data_point.json
deleted file mode 100644
index 8cafa239..00000000
--- a/schema_json/endpoint/portfolio_history/portfolio_history_data_point.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/portfolio-history/\">Portfolio History</a>.",
-  "properties": {
-    "timestamp": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "{@link java.time.ZonedDateTime} of this data element."
-    },
-    "equity": {
-      "existingJavaType": "java.lang.Double",
-      "title": "Equity value of the account in dollar amount as of the end of this time window."
-    },
-    "profit_loss": {
-      "existingJavaType": "java.lang.Double",
-      "title": "Profit/loss in dollar from the base value."
-    },
-    "profit_loss_percent": {
-      "existingJavaType": "java.lang.Double",
-      "title": "Profit/loss in percentage from the base value."
-    }
-  }
-}
diff --git a/schema_json/endpoint/portfolio_history/portfolio_history_response.json b/schema_json/endpoint/portfolio_history/portfolio_history_response.json
deleted file mode 100644
index d3ca8177..00000000
--- a/schema_json/endpoint/portfolio_history/portfolio_history_response.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/portfolio-history/\">Portfolio History</a>.",
-  "properties": {
-    "timestamp": {
-      "existingJavaType": "java.util.ArrayList<java.lang.Long>",
-      "title": "Time of each data element, left-labeled (the beginning of time window)."
-    },
-    "equity": {
-      "existingJavaType": "java.util.ArrayList<java.lang.Double>",
-      "title": "Equity value of the account in dollar amount as of the end of each time window."
-    },
-    "profit_loss": {
-      "existingJavaType": "java.util.ArrayList<java.lang.Double>",
-      "title": "Profit/loss in dollar from the base value."
-    },
-    "profit_loss_pct": {
-      "existingJavaType": "java.util.ArrayList<java.lang.Double>",
-      "javaName": "profitLossPercent",
-      "title": "Profit/loss in percentage from the base value."
-    },
-    "base_value": {
-      "existingJavaType": "java.lang.Double",
-      "title": "Basis in dollar of the profit loss calculation."
-    },
-    "timeframe": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.enums.PortfolioTimeFrame",
-      "title": "Time window size of each data element."
-    }
-  }
-}
diff --git a/schema_json/endpoint/positions/close_position_order.json b/schema_json/endpoint/positions/close_position_order.json
deleted file mode 100644
index 3ffba883..00000000
--- a/schema_json/endpoint/positions/close_position_order.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/positions/\">https://alpaca.markets/docs/api-documentation/api-v2/positions/</a>",
-  "properties": {
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "The symbol."
-    },
-    "status": {
-      "existingJavaType": "java.lang.Integer",
-      "title": "The HTTP status code of the position-closing order."
-    },
-    "body": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.Order",
-      "javaName": "order",
-      "title": "The position-closing {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order}."
-    }
-  }
-}
diff --git a/schema_json/endpoint/positions/position.json b/schema_json/endpoint/positions/position.json
deleted file mode 100644
index 8ae71b33..00000000
--- a/schema_json/endpoint/positions/position.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/positions/\">Positions</a>.",
-  "properties": {
-    "asset_id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Asset ID."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol name of the asset."
-    },
-    "exchange": {
-      "existingJavaType": "java.lang.String",
-      "title": "Exchange name of the asset (\"ErisX\" for crypto)."
-    },
-    "asset_class": {
-      "existingJavaType": "java.lang.String",
-      "title": "Asset class name."
-    },
-    "avg_entry_price": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "averageEntryPrice",
-      "title": "Average entry price of the position."
-    },
-    "qty": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "quantity",
-      "title": "The number of shares."
-    },
-    "qty_available": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "quantityAvailable",
-      "title": "Total number of shares available minus open orders."
-    },
-    "side": {
-      "existingJavaType": "java.lang.String",
-      "title": "\"long\"."
-    },
-    "market_value": {
-      "existingJavaType": "java.lang.String",
-      "title": "Total dollar amount of the position."
-    },
-    "cost_basis": {
-      "existingJavaType": "java.lang.String",
-      "title": "Total cost basis in dollar."
-    },
-    "unrealized_pl": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "unrealizedProfitLoss",
-      "title": "Unrealized profit/loss in dollars."
-    },
-    "unrealized_plpc": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "unrealizedProfitLossPercent",
-      "title": "Unrealized profit/loss percent (by a factor of 1)."
-    },
-    "unrealized_intraday_pl": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "unrealizedIntradayProfitLoss",
-      "title": "Unrealized profit/loss in dollars for the day."
-    },
-    "unrealized_intraday_plpc": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "unrealizedIntradayProfitLossPercent",
-      "title": "Unrealized profit/loss percent (by a factor of 1)."
-    },
-    "current_price": {
-      "existingJavaType": "java.lang.String",
-      "title": "Current asset price per share."
-    },
-    "lastday_price": {
-      "existingJavaType": "java.lang.String",
-      "title": "Last day’s asset price per share based on the closing value of the last trading day."
-    },
-    "change_today": {
-      "existingJavaType": "java.lang.String",
-      "title": "Percent change from last day price (by a factor of 1)."
-    }
-  }
-}
diff --git a/schema_json/endpoint/watchlist/watchlist.json b/schema_json/endpoint/watchlist/watchlist.json
deleted file mode 100644
index 7c7e8602..00000000
--- a/schema_json/endpoint/watchlist/watchlist.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/watchlist/\">Watchlist</a>.",
-  "properties": {
-    "id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Watchlist ID."
-    },
-    "created_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Created at."
-    },
-    "updated_at": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "Updated at."
-    },
-    "name": {
-      "existingJavaType": "java.lang.String",
-      "title": "User-defined watchlist name (up to 64 characters)."
-    },
-    "account_id": {
-      "existingJavaType": "java.lang.String",
-      "title": "Account ID."
-    },
-    "assets": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.assets.Asset>",
-      "title": "The content of this watchlist, in the order as registered by the client."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 4292e85d..bcaaec25 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -1,33 +1,16 @@
 package net.jacobpeterson.alpaca;
 
-import devcsrj.okhttp3.logging.HttpLoggingInterceptor;
 import net.jacobpeterson.alpaca.model.properties.DataAPIType;
 import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
 import net.jacobpeterson.alpaca.properties.AlpacaProperties;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.accountactivities.AccountActivitiesEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.assets.AssetsEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.calendar.CalendarEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.corporateactions.CorporateActionsEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto.CryptoMarketDataEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock.StockMarketDataEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.portfoliohistory.PortfolioHistoryEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.positions.PositionsEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.watchlist.WatchlistEndpoint;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
 import net.jacobpeterson.alpaca.websocket.marketdata.crypto.CryptoMarketDataWebsocket;
-import net.jacobpeterson.alpaca.websocket.marketdata.stock.StockMarketDataWebsocket;
-import net.jacobpeterson.alpaca.rest.endpoint.marketdata.news.NewsEndpoint;
 import net.jacobpeterson.alpaca.websocket.marketdata.news.NewsMarketDataWebsocket;
+import net.jacobpeterson.alpaca.websocket.marketdata.stock.StockMarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocket;
 import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocketInterface;
 import okhttp3.OkHttpClient;
+import okhttp3.logging.HttpLoggingInterceptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,45 +18,20 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * The {@link AlpacaAPI} class contains several instances of various {@link AlpacaEndpoint}s and
- * {@link AlpacaWebsocket}s to interface with Alpaca. You will generally only need one instance of this class in your
- * application. Note that many methods inside the various {@link AlpacaEndpoint}s allow <code>null<code/> to be passed
- * in as a parameter if it is optional.
+ * {@link AlpacaAPI} is the main class used to interface with the various Alpaca API endpoints. You will generally only
+ * need one instance of this class in your application.
  *
- * @see <a href="https://docs.alpaca.markets/api-documentation/api-v2/">Alpaca API Documentation</a>
+ * @see <a href="https://docs.alpaca.markets">Alpaca Docs</a>
  */
 public class AlpacaAPI {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPI.class);
 
-    private static final String VERSION_2_PATH_SEGMENT = "v2";
-    private static final String VERSION_1_BETA_3_PATH_SEGMENT = "v1beta3";
-    private static final String VERSION_1_BETA_1_PATH_SEGMENT = "v1beta1";
-
     private final OkHttpClient okHttpClient;
-    private final AlpacaClient brokerClient;
-    private final AlpacaClient cryptoDataClient;
-    private final AlpacaClient stockDataClient;
-    private final AlpacaClient newsDataClient;
-    // Ordering of fields/methods below are analogous to the ordering in the Alpaca documentation
-    private final AccountEndpoint accountEndpoint;
-    private final CryptoMarketDataEndpoint cryptoMarketDataEndpoint;
-    private final StockMarketDataEndpoint stockMarketDataEndpoint;
-    private final OrdersEndpoint ordersEndpoint;
-    private final PositionsEndpoint positionsEndpoint;
-    private final AssetsEndpoint assetsEndpoint;
-    private final WatchlistEndpoint watchlistEndpoint;
-    private final CalendarEndpoint calendarEndpoint;
-    private final ClockEndpoint clockEndpoint;
-    private final AccountConfigurationEndpoint accountConfigurationEndpoint;
-    private final AccountActivitiesEndpoint accountActivitiesEndpoint;
-    private final PortfolioHistoryEndpoint portfolioHistoryEndpoint;
     private final StreamingWebsocket streamingWebsocket;
     private final CryptoMarketDataWebsocket cryptoMarketDataWebsocket;
     private final StockMarketDataWebsocket stockMarketDataWebsocket;
-    private final CorporateActionsEndpoint corporateActionsEndpoint;
     private final NewsMarketDataWebsocket newsMarketDataWebsocket;
-    private final NewsEndpoint newsEndpoint;
 
     /**
      * Instantiates a new {@link AlpacaAPI} using properties specified in <code>alpaca.properties</code> file (or their
@@ -154,146 +112,17 @@ public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, Stri
         if (okHttpClient == null) {
             OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
                     .cache(null); // Ensure response caching is disabled
-
             if (LOGGER.isDebugEnabled()) {
-                clientBuilder.addInterceptor(new HttpLoggingInterceptor(LOGGER));
+                clientBuilder.addInterceptor(new HttpLoggingInterceptor(LOGGER::debug));
             }
-
             okHttpClient = clientBuilder.build();
         }
-
         this.okHttpClient = okHttpClient;
 
-        String brokerHostSubdomain;
-        switch (endpointAPIType) {
-            case LIVE:
-                brokerHostSubdomain = "api";
-                break;
-            case PAPER:
-                brokerHostSubdomain = "paper-api";
-                break;
-            default:
-                throw new UnsupportedOperationException();
-        }
-
-        if (oAuthToken == null) {
-            brokerClient = new AlpacaClient(okHttpClient, keyID, secretKey,
-                    brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
-            cryptoDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_3_PATH_SEGMENT);
-            stockDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_2_PATH_SEGMENT);
-            newsDataClient = new AlpacaClient(okHttpClient, keyID, secretKey, "data", VERSION_1_BETA_1_PATH_SEGMENT);
-        } else {
-            brokerClient = new AlpacaClient(okHttpClient, oAuthToken, brokerHostSubdomain, VERSION_2_PATH_SEGMENT);
-            cryptoDataClient = null;
-            stockDataClient = null;
-            newsDataClient = null;
-        }
-
-        accountEndpoint = new AccountEndpoint(brokerClient);
-        cryptoMarketDataEndpoint = cryptoDataClient == null ? null : new CryptoMarketDataEndpoint(cryptoDataClient);
-        stockMarketDataEndpoint = stockDataClient == null ? null : new StockMarketDataEndpoint(stockDataClient);
-        ordersEndpoint = new OrdersEndpoint(brokerClient);
-        positionsEndpoint = new PositionsEndpoint(brokerClient);
-        assetsEndpoint = new AssetsEndpoint(brokerClient);
-        watchlistEndpoint = new WatchlistEndpoint(brokerClient);
-        calendarEndpoint = new CalendarEndpoint(brokerClient);
-        clockEndpoint = new ClockEndpoint(brokerClient);
-        accountConfigurationEndpoint = new AccountConfigurationEndpoint(brokerClient);
-        accountActivitiesEndpoint = new AccountActivitiesEndpoint(brokerClient);
-        portfolioHistoryEndpoint = new PortfolioHistoryEndpoint(brokerClient);
-
-        streamingWebsocket = new StreamingWebsocket(okHttpClient, brokerHostSubdomain, keyID, secretKey, oAuthToken);
-        cryptoMarketDataWebsocket = cryptoDataClient == null ? null :
-                new CryptoMarketDataWebsocket(okHttpClient, keyID, secretKey);
-        stockMarketDataWebsocket = stockDataClient == null ? null :
-                new StockMarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
-        corporateActionsEndpoint = new CorporateActionsEndpoint(brokerClient);
-        newsEndpoint = newsDataClient == null ? null :  new NewsEndpoint(newsDataClient);
-        newsMarketDataWebsocket = newsDataClient == null ? null : new NewsMarketDataWebsocket(okHttpClient,  keyID, secretKey);
-    }
-
-    /**
-     * @return the {@link AccountEndpoint}
-     */
-    public AccountEndpoint account() {
-        return accountEndpoint;
-    }
-
-    /**
-     * @return the {@link CryptoMarketDataEndpoint}
-     */
-    public CryptoMarketDataEndpoint cryptoMarketData() {
-        return cryptoMarketDataEndpoint;
-    }
-
-    /**
-     * @return the {@link StockMarketDataEndpoint}
-     */
-    public StockMarketDataEndpoint stockMarketData() {
-        return stockMarketDataEndpoint;
-    }
-
-    /**
-     * @return the {@link OrdersEndpoint}
-     */
-    public OrdersEndpoint orders() {
-        return ordersEndpoint;
-    }
-
-    /**
-     * @return the {@link PositionsEndpoint}
-     */
-    public PositionsEndpoint positions() {
-        return positionsEndpoint;
-    }
-
-    /**
-     * @return the {@link AssetsEndpoint}
-     */
-    public AssetsEndpoint assets() {
-        return assetsEndpoint;
-    }
-
-    /**
-     * @return the {@link WatchlistEndpoint}
-     */
-    public WatchlistEndpoint watchlist() {
-        return watchlistEndpoint;
-    }
-
-    /**
-     * @return the {@link CalendarEndpoint}
-     */
-    public CalendarEndpoint calendar() {
-        return calendarEndpoint;
-    }
-
-    /**
-     * @return the {@link ClockEndpoint}
-     */
-    public ClockEndpoint clock() {
-        return clockEndpoint;
-    }
-
-    /**
-     * @return the {@link AccountConfigurationEndpoint}
-     */
-    public AccountConfigurationEndpoint accountConfiguration() {
-        return accountConfigurationEndpoint;
-    }
-
-    /**
-     * @return the {@link AccountActivitiesEndpoint}
-     */
-    public AccountActivitiesEndpoint accountActivities() {
-        return accountActivitiesEndpoint;
-    }
-
-    /**
-     * @return the {@link PortfolioHistoryEndpoint}
-     */
-    public PortfolioHistoryEndpoint portfolioHistory() {
-        return portfolioHistoryEndpoint;
+        streamingWebsocket = new StreamingWebsocket(okHttpClient, endpointAPIType, keyID, secretKey, oAuthToken);
+        cryptoMarketDataWebsocket = new CryptoMarketDataWebsocket(okHttpClient, keyID, secretKey);
+        stockMarketDataWebsocket = new StockMarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
+        newsMarketDataWebsocket = new NewsMarketDataWebsocket(okHttpClient, keyID, secretKey);
     }
 
     /**
@@ -317,40 +146,6 @@ public MarketDataWebsocketInterface stockMarketDataStreaming() {
         return stockMarketDataWebsocket;
     }
 
-    public OkHttpClient getOkHttpClient() {
-        return okHttpClient;
-    }
-
-    public AlpacaClient getBrokerClient() {
-        return brokerClient;
-    }
-
-    public AlpacaClient getCryptoDataClient() {
-        return cryptoDataClient;
-    }
-
-    public AlpacaClient getStockDataClient() {
-        return stockDataClient;
-    }
-
-    public AlpacaClient getNewsDataClient() {
-        return newsDataClient;
-    }
-
-    /**
-     * @return the {@link CorporateActionsEndpoint}
-     */
-    public CorporateActionsEndpoint corporateActions() {
-        return corporateActionsEndpoint;
-    }
-
-    /**
-     * @return the {@link NewsEndpoint}
-     */
-    public NewsEndpoint newsEndpoint() {
-        return newsEndpoint;
-    }
-
     /**
      * @return the News {@link MarketDataWebsocketInterface}
      */
@@ -358,11 +153,14 @@ public MarketDataWebsocketInterface newsMarketDataStreaming() {
         return newsMarketDataWebsocket;
     }
 
+    public OkHttpClient getOkHttpClient() {
+        return okHttpClient;
+    }
 
     /**
      * Creates a {@link Builder} for {@link AlpacaAPI}.
      *
-     * @return a {@link Builder}
+     * @return the {@link Builder}
      */
     public static Builder builder() {
         return new Builder();
@@ -385,23 +183,23 @@ private Builder() {
             this.dataAPIType = AlpacaProperties.DATA_API_TYPE;
         }
 
-        public Builder withKeyID(String val) {
-            keyID = val;
+        public Builder withKeyID(String keyID) {
+            this.keyID = keyID;
             return this;
         }
 
-        public Builder withSecretKey(String val) {
-            secretKey = val;
+        public Builder withSecretKey(String secretKey) {
+            this.secretKey = secretKey;
             return this;
         }
 
-        public Builder withEndpointAPIType(EndpointAPIType val) {
-            endpointAPIType = val;
+        public Builder withEndpointAPIType(EndpointAPIType endpointAPIType) {
+            this.endpointAPIType = endpointAPIType;
             return this;
         }
 
-        public Builder withDataAPIType(DataAPIType val) {
-            dataAPIType = val;
+        public Builder withDataAPIType(DataAPIType dataAPIType) {
+            this.dataAPIType = dataAPIType;
             return this;
         }
 
diff --git a/schema_json/endpoint/market_data/common/historical/bar/bar.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/bar.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/historical/bar/bar.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/bar.json
diff --git a/schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
diff --git a/schema_json/endpoint/market_data/common/historical/quote/quote.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/quote/quote.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/historical/quote/quote.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/quote/quote.json
diff --git a/schema_json/endpoint/market_data/common/historical/trade/trade.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/trade/trade.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/historical/trade/trade.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/trade/trade.json
diff --git a/schema_json/endpoint/market_data/common/realtime/bar/bar_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/bar/bar_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/bar/bar_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/bar/bar_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/control/error_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/error_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/control/error_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/error_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/subscriptions_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/control/subscriptions_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/subscriptions_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/control/success_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/success_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/control/success_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/success_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/enums/market_data_message_type.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/enums/market_data_message_type.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/enums/market_data_message_type.json
diff --git a/schema_json/endpoint/market_data/common/realtime/market_data_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/market_data_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/market_data_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/market_data_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/quote/quote_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/quote/quote_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/quote/quote_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/quote/quote_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/symbol_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/symbol_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/symbol_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/symbol_message.json
diff --git a/schema_json/endpoint/market_data/common/realtime/trade/trade_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/trade/trade_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/common/realtime/trade/trade_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/trade/trade_message.json
diff --git a/schema_json/endpoint/market_data/crypto/common/enums/taker_side.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/common/enums/taker_side.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/common/enums/taker_side.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/common/enums/taker_side.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bar.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/bar/crypto_bar.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bar.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/crypto_quote.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/quote/crypto_quote.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/crypto_quote.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trade.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/trade/crypto_trade.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trade.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
diff --git a/schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
diff --git a/schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
diff --git a/schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
diff --git a/schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
diff --git a/schema_json/endpoint/market_data/news/common/image.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/image.json
similarity index 100%
rename from schema_json/endpoint/market_data/news/common/image.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/image.json
diff --git a/schema_json/endpoint/market_data/news/common/news_article.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/news_article.json
similarity index 100%
rename from schema_json/endpoint/market_data/news/common/news_article.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/news_article.json
diff --git a/schema_json/endpoint/market_data/news/news_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/news_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/news/news_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/news_response.json
diff --git a/schema_json/endpoint/market_data/news/realtime/news_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/realtime/news_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/news/realtime/news_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/realtime/news_message.json
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/enums/bar_feed.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/stock_bar.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bar.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/bar/stock_bar.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bar.json
diff --git a/schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bars_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/bar/stock_bars_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bars_response.json
diff --git a/schema_json/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
diff --git a/schema_json/endpoint/market_data/stock/historical/quote/stock_quote.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quote.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/quote/stock_quote.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quote.json
diff --git a/schema_json/endpoint/market_data/stock/historical/quote/stock_quotes_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
diff --git a/schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/snapshot/snapshot.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/snapshot/snapshot.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/snapshot/snapshot.json
diff --git a/schema_json/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
diff --git a/schema_json/endpoint/market_data/stock/historical/trade/stock_trade.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trade.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/trade/stock_trade.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trade.json
diff --git a/schema_json/endpoint/market_data/stock/historical/trade/stock_trades_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trades_response.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/historical/trade/stock_trades_response.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trades_response.json
diff --git a/schema_json/endpoint/market_data/stock/realtime/bar/stock_bar_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
diff --git a/schema_json/endpoint/market_data/stock/realtime/quote/stock_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
diff --git a/schema_json/endpoint/market_data/stock/realtime/trade/stock_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
similarity index 100%
rename from schema_json/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
diff --git a/schema_json/endpoint/streaming/authorization/authorization_data.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_data.json
similarity index 100%
rename from schema_json/endpoint/streaming/authorization/authorization_data.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_data.json
diff --git a/schema_json/endpoint/streaming/authorization/authorization_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_message.json
similarity index 100%
rename from schema_json/endpoint/streaming/authorization/authorization_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_message.json
diff --git a/schema_json/endpoint/streaming/enums/streaming_message_type.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/enums/streaming_message_type.json
similarity index 100%
rename from schema_json/endpoint/streaming/enums/streaming_message_type.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/enums/streaming_message_type.json
diff --git a/schema_json/endpoint/streaming/listening/listening_data.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_data.json
similarity index 100%
rename from schema_json/endpoint/streaming/listening/listening_data.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_data.json
diff --git a/schema_json/endpoint/streaming/listening/listening_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_message.json
similarity index 100%
rename from schema_json/endpoint/streaming/listening/listening_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_message.json
diff --git a/schema_json/endpoint/streaming/streaming_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/streaming_message.json
similarity index 100%
rename from schema_json/endpoint/streaming/streaming_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/streaming_message.json
diff --git a/schema_json/endpoint/streaming/trade/enums/trade_update_event.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/enums/trade_update_event.json
similarity index 100%
rename from schema_json/endpoint/streaming/trade/enums/trade_update_event.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/enums/trade_update_event.json
diff --git a/schema_json/endpoint/streaming/trade/trade_update.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update.json
similarity index 82%
rename from schema_json/endpoint/streaming/trade/trade_update.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update.json
index 32f6c324..c0072275 100644
--- a/schema_json/endpoint/streaming/trade/trade_update.json
+++ b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update.json
@@ -20,8 +20,8 @@
       "title": "The position quantity."
     },
     "order": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.orders.Order",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.orders.Order}."
+      "existingJavaType": "net.jacobpeterson.alpaca.openapi.trader.model.Order",
+      "title": "The {@link net.jacobpeterson.alpaca.openapi.trader.model.Order}."
     }
   }
 }
diff --git a/schema_json/endpoint/streaming/trade/trade_update_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update_message.json
similarity index 100%
rename from schema_json/endpoint/streaming/trade/trade_update_message.json
rename to src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update_message.json
diff --git a/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java b/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
index 0c121157..175a83c8 100644
--- a/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
+++ b/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
@@ -14,43 +14,16 @@ public class AlpacaProperties {
     private static final String ALPACA_PROPERTIES_FILE = "alpaca.properties";
     private static final String ALPACA_DEFAULT_PROPERTIES_FILE = "alpaca.default.properties";
 
-    private static final String KEY_ID_KEY = "key_id";
-    /**
-     * The value of {@link #KEY_ID_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
-     */
     public static final String KEY_ID = getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            KEY_ID_KEY);
-
-    private static final String SECRET_KEY_KEY = "secret_key";
-    /**
-     * The value of {@link #SECRET_KEY_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
-     */
+            "key_id");
     public static final String SECRET_KEY = getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            SECRET_KEY_KEY);
-
-    private static final String ENDPOINT_API_TYPE_KEY = "endpoint_api_type";
-    /**
-     * The value of {@link #ENDPOINT_API_TYPE_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
-     */
+            "secret_key");
     public static final EndpointAPIType ENDPOINT_API_TYPE = EndpointAPIType.fromValue(getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            ENDPOINT_API_TYPE_KEY));
-
-    private static final String DATA_API_TYPE_KEY = "data_api_type";
-    /**
-     * The value of {@link #DATA_API_TYPE_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
-     */
+            "endpoint_api_type"));
     public static final DataAPIType DATA_API_TYPE = DataAPIType.fromValue(getProperty(
             ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            DATA_API_TYPE_KEY));
-
-    private static final String USER_AGENT_KEY = "user_agent";
-    /**
-     * The value of {@link #USER_AGENT_KEY} in {@link #ALPACA_PROPERTIES_FILE}.
-     */
-    public static final String USER_AGENT = getProperty(
-            ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            USER_AGENT_KEY);
+            "data_api_type"));
 }
diff --git a/schema_json/properties/data_api_type.json b/src/main/java/net/jacobpeterson/alpaca/properties/data_api_type.json
similarity index 100%
rename from schema_json/properties/data_api_type.json
rename to src/main/java/net/jacobpeterson/alpaca/properties/data_api_type.json
diff --git a/schema_json/properties/endpoint_api_type.json b/src/main/java/net/jacobpeterson/alpaca/properties/endpoint_api_type.json
similarity index 100%
rename from schema_json/properties/endpoint_api_type.json
rename to src/main/java/net/jacobpeterson/alpaca/properties/endpoint_api_type.json
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
deleted file mode 100644
index dc11df3f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClient.java
+++ /dev/null
@@ -1,235 +0,0 @@
-package net.jacobpeterson.alpaca.rest;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
-import net.jacobpeterson.alpaca.properties.AlpacaProperties;
-import okhttp3.Headers;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.lang.reflect.Type;
-import java.util.function.Predicate;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
-
-/**
- * {@link AlpacaClient} represents an HTTP RestAPI client for Alpaca.
- */
-public class AlpacaClient {
-
-    public static final Predicate<Integer> STATUS_CODE_200_OR_207 = (code) -> code == 200 || code == 207;
-    public static final Predicate<Integer> STATUS_CODE_200_OR_204 = (code) -> code == 200 || code == 204;
-
-    private final OkHttpClient okHttpClient;
-    private final HttpUrl baseURL;
-    private final Headers requestHeaders;
-
-    /**
-     * Instantiates a new {@link AlpacaClient}.
-     *
-     * @param okHttpClient       the {@link OkHttpClient}
-     * @param keyID              the key ID
-     * @param secretKey          the secret key
-     * @param alpacaSubdomain    the Alpaca subdomain
-     * @param versionPathSegment the version path segment e.g. "v2"
-     */
-    public AlpacaClient(OkHttpClient okHttpClient, String keyID, String secretKey, String alpacaSubdomain,
-            String versionPathSegment) {
-        this(okHttpClient, keyID, secretKey, null, alpacaSubdomain, versionPathSegment);
-    }
-
-    /**
-     * Instantiates a new {@link AlpacaClient}.
-     *
-     * @param okHttpClient       the {@link OkHttpClient}
-     * @param oAuthToken         the OAuth token
-     * @param alpacaSubdomain    the Alpaca subdomain
-     * @param versionPathSegment the version path segment e.g. "v2"
-     */
-    public AlpacaClient(OkHttpClient okHttpClient, String oAuthToken, String alpacaSubdomain,
-            String versionPathSegment) {
-        this(okHttpClient, null, null, oAuthToken, alpacaSubdomain, versionPathSegment);
-    }
-
-    /**
-     * Instantiates a new {@link AlpacaClient}.
-     *
-     * @param okHttpClient       the {@link OkHttpClient}
-     * @param keyID              the key ID
-     * @param secretKey          the secret key
-     * @param oAuthToken         the OAuth token
-     * @param alpacaSubdomain    the Alpaca subdomain
-     * @param versionPathSegment the version path segment e.g. "v2"
-     */
-    protected AlpacaClient(OkHttpClient okHttpClient, String keyID, String secretKey, String oAuthToken,
-            String alpacaSubdomain, String versionPathSegment) {
-        checkNotNull(okHttpClient);
-        checkNotNull(alpacaSubdomain);
-        checkNotNull(versionPathSegment);
-
-        this.okHttpClient = okHttpClient;
-
-        baseURL = new HttpUrl.Builder()
-                .scheme("https")
-                .host(alpacaSubdomain + ".alpaca.markets")
-                .addPathSegment(versionPathSegment)
-                .build();
-
-        Headers.Builder requestHeadersBuilder = new Headers.Builder();
-        requestHeadersBuilder.add("User-Agent", AlpacaProperties.USER_AGENT);
-        if (oAuthToken != null) {
-            requestHeadersBuilder.add("Authorization", "Bearer " + oAuthToken);
-        } else {
-            requestHeadersBuilder
-                    .add("APCA-API-KEY-ID", keyID)
-                    .add("APCA-API-SECRET-KEY", secretKey);
-        }
-        requestHeaders = requestHeadersBuilder.build();
-    }
-
-    /**
-     * Gets a new {@link okhttp3.HttpUrl.Builder}.
-     *
-     * @return a {@link okhttp3.HttpUrl.Builder}
-     */
-    public HttpUrl.Builder urlBuilder() {
-        return baseURL.newBuilder();
-    }
-
-    /**
-     * Gets a new {@link okhttp3.Request.Builder} with {@link #requestHeaders}.
-     *
-     * @param httpUrl the {@link okhttp3.HttpUrl}
-     *
-     * @return a {@link okhttp3.Request.Builder}
-     */
-    public Request.Builder requestBuilder(HttpUrl httpUrl) {
-        return new Request.Builder().headers(requestHeaders).url(httpUrl);
-    }
-
-    /**
-     * Calls {@link #requestObject(Request, Predicate, Type)} with a success code of <code>200</code>.
-     */
-    public <T> T requestObject(Request request, Type type) throws AlpacaClientException {
-        return requestObject(request, (code) -> code == 200, type);
-    }
-
-    /**
-     * Requests an object given a {@link Request} that is deserialized via {@link Gson#fromJson(Reader, Type)}.
-     *
-     * @param <T>           the type of object
-     * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
-     *                      {@link Predicate#test(Object)} returns false
-     * @param type          the object {@link Type}
-     *
-     * @return the requested object
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public <T> T requestObject(Request request, Predicate<Integer> isSuccessCode, Type type)
-            throws AlpacaClientException {
-        return GSON.fromJson(requestJSON(request, isSuccessCode), type);
-    }
-
-    /**
-     * Calls {@link #requestJSON(Request, Predicate)} with a success code of <code>200</code>.
-     */
-    public JsonElement requestJSON(Request request) throws AlpacaClientException {
-        return requestJSON(request, (code) -> code == 200);
-    }
-
-    /**
-     * Requests a {@link JsonElement} given a {@link Request}.
-     *
-     * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
-     *                      {@link Predicate#test(Object)} returns false
-     *
-     * @return the {@link JsonElement}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public JsonElement requestJSON(Request request, Predicate<Integer> isSuccessCode) throws AlpacaClientException {
-        try (Response response = request(request, isSuccessCode)) {
-            try (ResponseBody responseBody = response.body()) {
-                // Throw exception if 'responseBody' is ever null since we never want to use cachedResponse or
-                // other types of responses.
-                checkState(responseBody != null);
-
-                try (Reader charStream = responseBody.charStream()) {
-                    return JsonParser.parseReader(charStream);
-                } catch (IOException ioException) {
-                    throw new AlpacaClientException(ioException);
-                }
-            }
-        }
-    }
-
-    /**
-     * Sends a {@link Request} and ignores any {@link ResponseBody}.
-     *
-     * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
-     *                      {@link Predicate#test(Object)} returns false
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public void requestVoid(Request request, Predicate<Integer> isSuccessCode) throws AlpacaClientException {
-        // close() will throw an exception if cachedResponse or others were used which is fine
-        // since we never want to use those types of responses.
-        request(request, isSuccessCode).close();
-    }
-
-    /**
-     * Sends a {@link Request} and returns a {@link Response}.
-     *
-     * @param request       the {@link Request}
-     * @param isSuccessCode throws {@link AlpacaClientException} if passed in {@link Response#code()} to the
-     *                      {@link Predicate#test(Object)} returns false
-     *
-     * @return the {@link Response}. <strong>Be sure to call {@link Response#close()} after use.</strong>
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Response request(Request request, Predicate<Integer> isSuccessCode) throws AlpacaClientException {
-        try {
-            Response response = executeRequest(request);
-            if (!isSuccessCode.test(response.code())) {
-                throw new AlpacaClientException(response);
-            }
-            return response;
-        } catch (IOException ioException) {
-            throw new AlpacaClientException(ioException);
-        }
-    }
-
-    /**
-     * Executes a {@link Request}.
-     *
-     * @param request the {@link Request}
-     *
-     * @return the {@link Response}. <strong>Be sure to call {@link Response#close()} after use.</strong>
-     *
-     * @throws IOException thrown for {@link IOException}s
-     */
-    public Response executeRequest(Request request) throws IOException {
-        return okHttpClient.newCall(request).execute();
-    }
-
-    public HttpUrl getBaseURL() {
-        return baseURL;
-    }
-
-    public Headers getRequestHeaders() {
-        return requestHeaders;
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java b/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
deleted file mode 100644
index 4c902b05..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package net.jacobpeterson.alpaca.rest;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-
-import javax.annotation.Nullable;
-
-public class AlpacaClientException extends Exception {
-
-    private static final String CODE_KEY = "code";
-    private static final String MESSAGE_KEY = "message";
-
-    private String responseBody;
-    private Integer responseStatusCode;
-    private String responseStatusMessage;
-    private Integer apiResponseCode;
-    private String apiResponseMessage;
-
-    /**
-     * Instantiates a new {@link AlpacaClientException}.
-     *
-     * @param message the message
-     */
-    public AlpacaClientException(String message) {
-        super(message);
-    }
-
-    /**
-     * Instantiates a new {@link AlpacaClientException}.
-     *
-     * @param cause the cause {@link Throwable}
-     */
-    public AlpacaClientException(Throwable cause) {
-        super(cause);
-    }
-
-    /**
-     * Instantiates a new {@link AlpacaClientException}.
-     *
-     * @param message the message
-     * @param cause   the cause {@link Throwable}
-     */
-    public AlpacaClientException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    /**
-     * Instantiates a new {@link AlpacaClientException}.
-     *
-     * @param response the {@link Response} containing an API response code and message JSON
-     */
-    protected AlpacaClientException(Response response) {
-        responseStatusCode = response.code();
-        responseStatusMessage = response.message();
-
-        try (ResponseBody clientResponseBody = response.body()) {
-            if (clientResponseBody != null) {
-                this.responseBody = clientResponseBody.string();
-            }
-        } catch (Exception ignored) {}
-    }
-
-    @Override
-    public String getMessage() {
-        if (responseBody == null) {
-            return super.getMessage();
-        } else {
-            return parseAPIErrorResponse();
-        }
-    }
-
-    /**
-     * Parses an API error response of the following format:
-     * <pre>
-     *  {
-     *     "code": 505,
-     *     "message": "Error message"
-     *  }
-     * </pre>
-     * and sets {@link #apiResponseCode} and {@link #apiResponseMessage} accordingly.
-     *
-     * @return a formatted message of the error response
-     */
-    private String parseAPIErrorResponse() {
-        try {
-            JsonElement responseJsonElement = JsonParser.parseString(responseBody);
-
-            if (responseJsonElement instanceof JsonObject) {
-                JsonObject responseJsonObject = responseJsonElement.getAsJsonObject();
-
-                if (responseJsonObject.has(CODE_KEY)) {
-                    apiResponseCode = responseJsonObject.get(CODE_KEY).getAsInt();
-                }
-
-                if (responseJsonObject.has(MESSAGE_KEY)) {
-                    apiResponseMessage = responseJsonObject.get(MESSAGE_KEY).getAsString();
-                }
-            }
-
-            // Just use the response JSON if the message could not be parsed.
-            if (apiResponseMessage == null) {
-                apiResponseMessage = responseJsonElement.toString();
-            }
-        } catch (Exception ignored) {}
-
-        // Build message
-        StringBuilder messageBuilder = new StringBuilder();
-        messageBuilder.append("Status Code: ").append(responseStatusCode);
-        messageBuilder.append(", Status Message: \"").append(responseStatusMessage).append("\"");
-
-        if (apiResponseCode != null) {
-            messageBuilder.append(", API Response Code: ").append(apiResponseCode);
-        }
-
-        if (apiResponseMessage != null) {
-            messageBuilder.append(", API Response Message: \"").append(apiResponseMessage).append("\"");
-        }
-
-        return messageBuilder.toString();
-    }
-
-    @Nullable
-    public Integer getResponseStatusCode() {
-        return responseStatusCode;
-    }
-
-    @Nullable
-    public String getResponseStatusMessage() {
-        return responseStatusMessage;
-    }
-
-    @Nullable
-    public Integer getAPIResponseCode() {
-        return apiResponseCode;
-    }
-
-    @Nullable
-    public String getAPIResponseMessage() {
-        return apiResponseMessage;
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java
deleted file mode 100644
index dc4e1616..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint;
-
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-
-/**
- * {@link AlpacaEndpoint} is an abstract class representing a RestAPI endpoint.
- */
-public abstract class AlpacaEndpoint {
-
-    protected final AlpacaClient alpacaClient;
-    protected final String endpointPathSegment;
-
-    /**
-     * Instantiates a new {@link AlpacaEndpoint}.
-     *
-     * @param alpacaClient        the {@link AlpacaClient}
-     * @param endpointPathSegment the endpoint path segment relative to {@link AlpacaClient#getBaseURL()}
-     */
-    public AlpacaEndpoint(AlpacaClient alpacaClient, String endpointPathSegment) {
-        this.alpacaClient = alpacaClient;
-        this.endpointPathSegment = endpointPathSegment;
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java
deleted file mode 100644
index 8054ab4f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.account;
-
-import net.jacobpeterson.alpaca.model.endpoint.account.Account;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://docs.alpaca.markets/api-documentation/api-v2/account/">Account</a>.
- */
-public class AccountEndpoint extends AlpacaEndpoint {
-
-    /**
-     * Instantiates a new {@link AccountEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public AccountEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "account");
-    }
-
-    /**
-     * Returns the {@link Account}.
-     *
-     * @return the {@link Account}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Account get() throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Account.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java
deleted file mode 100644
index c56375bf..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.accountactivities;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.AccountActivity;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.NonTradeActivity;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.TradeActivity;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.ActivityType;
-import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkState;
-import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://docs.alpaca.markets/api-documentation/api-v2/account-activities/">Account
- * Activities</a>.
- */
-public class AccountActivitiesEndpoint extends AlpacaEndpoint {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(AccountActivitiesEndpoint.class);
-
-    private static final String ACTIVITY_TYPE_FIELD = "activity_type";
-
-    /**
-     * Instantiates a new {@link AccountActivitiesEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public AccountActivitiesEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "account/activities");
-    }
-
-    /**
-     * Returns {@link AccountActivity} entries for many {@link ActivityType}s or for a specific {@link ActivityType}.
-     *
-     * @param date          the date for which you want to see activities.
-     * @param until         the response will contain only activities submitted before this date. (Cannot be used with
-     *                      <code>date</code>.)
-     * @param after         the response will contain only activities submitted after this date. (Cannot be used with
-     *                      <code>date</code>.)
-     * @param sortDirection the {@link SortDirection} (defaults to {@link SortDirection#DESCENDING} if unspecified.)
-     * @param pageSize      the maximum number of entries to return in the response. (See the section on paging.)
-     * @param pageToken     the ID of the end of your current page of results. (See the section on paging.)
-     * @param activityTypes the {@link ActivityType}s (<code>null</code> for all {@link ActivityType}s)
-     *
-     * @return a {@link List} of {@link AccountActivity}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<AccountActivity> get(ZonedDateTime date, ZonedDateTime until, ZonedDateTime after,
-            SortDirection sortDirection, Integer pageSize, String pageToken, ActivityType... activityTypes)
-            throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegments(endpointPathSegment);
-
-        if (activityTypes != null) { // Check if we don't want all activity types
-            if (activityTypes.length == 1) { // Get one activity
-                urlBuilder.addPathSegment(activityTypes[0].toString());
-            } else { // Get list of activities
-                urlBuilder.addQueryParameter("activity_types",
-                        // Makes comma-separated list
-                        Arrays.stream(activityTypes).map(ActivityType::toString).collect(Collectors.joining(",")));
-            }
-        }
-
-        if (date != null) {
-            urlBuilder.addQueryParameter("date", date.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
-        }
-
-        if (until != null) {
-            urlBuilder.addQueryParameter("until", until.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
-        }
-
-        if (after != null) {
-            urlBuilder.addQueryParameter("after", after.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
-        }
-
-        if (sortDirection != null) {
-            urlBuilder.addQueryParameter("direction", sortDirection.toString());
-        }
-
-        if (pageSize != null) {
-            urlBuilder.addQueryParameter("page_size", pageSize.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-
-        JsonElement responseJSON = alpacaClient.requestJSON(request);
-        checkState(responseJSON instanceof JsonArray, "The response must be an array! Received: %s", responseJSON);
-
-        ArrayList<AccountActivity> accountActivities = new ArrayList<>();
-        for (JsonElement responseArrayElement : responseJSON.getAsJsonArray()) {
-            checkState(responseArrayElement instanceof JsonObject,
-                    "All array elements must be objects! Received: %s", responseArrayElement);
-
-            JsonObject responseArrayObject = responseArrayElement.getAsJsonObject();
-            JsonElement activityTypeElement = responseArrayObject.get(ACTIVITY_TYPE_FIELD);
-            checkState(activityTypeElement != null, "Activity type elements must have %s field! Received: %s",
-                    ACTIVITY_TYPE_FIELD, responseArrayElement);
-
-            String activityType = activityTypeElement.getAsString();
-            AccountActivity accountActivity;
-
-            // A 'TradeActivity' always has 'activity_type' field as 'FILL'
-            if (activityType.equals(ActivityType.FILL.toString())) {
-                accountActivity = GSON.fromJson(responseArrayObject, TradeActivity.class);
-            } else {
-                accountActivity = GSON.fromJson(responseArrayObject, NonTradeActivity.class);
-            }
-
-            if (accountActivity.getActivityType() == null) {
-                LOGGER.error("No ActivityType enum exists for {}. Report this!", activityType);
-            }
-
-            accountActivities.add(accountActivity);
-        }
-
-        return accountActivities;
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java
deleted file mode 100644
index a319e650..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration;
-
-import net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.AccountConfiguration;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.util.okhttp.JSONBodyBuilder;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/account-configuration/">Account Configuration</a>.
- */
-public class AccountConfigurationEndpoint extends AlpacaEndpoint {
-
-    /**
-     * Instantiates a new {@link AccountConfigurationEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public AccountConfigurationEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "account/configurations");
-    }
-
-    /**
-     * Returns the {@link AccountConfiguration}.
-     *
-     * @return the {@link AccountConfiguration}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public AccountConfiguration get() throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegments(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, AccountConfiguration.class);
-    }
-
-    /**
-     * Sets the {@link AccountConfiguration}.
-     *
-     * @param accountConfiguration the {@link AccountConfiguration}
-     *
-     * @return the updated {@link AccountConfiguration}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public AccountConfiguration set(AccountConfiguration accountConfiguration) throws AlpacaClientException {
-        checkNotNull(accountConfiguration);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegments(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .patch(new JSONBodyBuilder(GSON.toJson(accountConfiguration)).build())
-                .build();
-        return alpacaClient.requestObject(request, AccountConfiguration.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
deleted file mode 100644
index 82763589..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.assets;
-
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.assets.Asset;
-import net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetClass;
-import net.jacobpeterson.alpaca.model.endpoint.assets.enums.AssetStatus;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://docs.alpaca.markets/api-documentation/api-v2/assets/">Assets</a>.
- */
-public class AssetsEndpoint extends AlpacaEndpoint {
-
-    private static final Type ASSET_ARRAYLIST_TYPE = new TypeToken<ArrayList<Asset>>() {}.getType();
-
-    /**
-     * Instantiates a new {@link AssetsEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public AssetsEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "assets");
-    }
-
-    /**
-     * Get a list of {@link Asset}s.
-     *
-     * @param assetStatus the {@link AssetStatus}. By default, all {@link AssetStatus}es are included.
-     * @param assetClass  the {@link AssetClass} (<code>null</code> for {@link AssetClass#US_EQUITY})
-     *
-     * @return a {@link List} of {@link Asset}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Asset> get(AssetStatus assetStatus, AssetClass assetClass) throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        if (assetStatus != null) {
-            urlBuilder.addQueryParameter("status", assetStatus.toString());
-        }
-
-        if (assetClass != null) {
-            urlBuilder.addQueryParameter("asset_class", assetClass.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, ASSET_ARRAYLIST_TYPE);
-    }
-
-    /**
-     * Gets an {@link Asset} by a symbol or {@link Asset#getId()}.
-     *
-     * @param symbolOrAssetID the symbol or {@link Asset#getId()}
-     *
-     * @return the {@link Asset}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Asset getBySymbol(String symbolOrAssetID) throws AlpacaClientException {
-        checkNotNull(symbolOrAssetID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbolOrAssetID);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Asset.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
deleted file mode 100644
index cc1cb5d2..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.calendar;
-
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.calendar.Calendar;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://docs.alpaca.markets/api-documentation/api-v2/calendar/">Calendar</a>.
- */
-public class CalendarEndpoint extends AlpacaEndpoint {
-
-    private static final Type CALENDAR_ARRAYLIST_TYPE = new TypeToken<ArrayList<Calendar>>() {}.getType();
-
-    /**
-     * Instantiates a new {@link CalendarEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public CalendarEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "calendar");
-    }
-
-    /**
-     * Returns the market {@link Calendar} from 1970 to 2029.
-     *
-     * @return a {@link List} of {@link Calendar}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Calendar> get() throws AlpacaClientException {
-        return get(null, null);
-    }
-
-    /**
-     * Returns the market {@link Calendar}.
-     *
-     * @param start the first date to retrieve data for (inclusive)
-     * @param end   the last date to retrieve data for (inclusive)
-     *
-     * @return a {@link List} of {@link Calendar}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Calendar> get(LocalDate start, LocalDate end) throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        if (start != null) {
-            urlBuilder.addQueryParameter("start", start.format(DateTimeFormatter.ISO_DATE));
-        }
-
-        if (end != null) {
-            urlBuilder.addQueryParameter("end", end.format(DateTimeFormatter.ISO_DATE));
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, CALENDAR_ARRAYLIST_TYPE);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java
deleted file mode 100644
index 368edc13..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.clock;
-
-import net.jacobpeterson.alpaca.model.endpoint.clock.Clock;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://docs.alpaca.markets/api-documentation/api-v2/clock/">Clock</a>.
- */
-public class ClockEndpoint extends AlpacaEndpoint {
-
-    /**
-     * Instantiates a new {@link ClockEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public ClockEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "clock");
-    }
-
-    /**
-     * Returns the market {@link Clock}.
-     *
-     * @return the market {@link Clock}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Clock get() throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Clock.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java
deleted file mode 100644
index f467c4f0..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/corporateactions/CorporateActionsEndpoint.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.corporateactions;
-
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.corporateactions.common.Announcement;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://docs.alpaca.markets/reference/get-v2-corporate_actions-announcements-id">Corporate actions</a>
- * Endpoint.
- */
-public class CorporateActionsEndpoint extends AlpacaEndpoint {
-
-    private static final Type ANNOUNCEMENT_ARRAYLIST_TYPE = new TypeToken<ArrayList<Announcement>>() {}.getType();
-
-    private static final List<String> ALL_CA_TYPES = Arrays.asList("dividend", "merger", "spinoff", "split");
-
-    /**
-     * Instantiates a new {@link AlpacaEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public CorporateActionsEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "corporate_actions/announcements");
-    }
-
-    /**
-     * Gets a {@link Announcement} of the requested id.
-     * @param id - announcement id to query for
-     * @return the {@link Announcement}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Announcement getAnnouncement(String id) throws AlpacaClientException {
-        checkNotNull(id);
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(id);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Announcement.class);
-
-    }
-
-    /**
-     * Gets list of all {@link Announcement} within last 90 days
-     * @return a  {@link List} of {@link Announcement}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Announcement> getAnnouncements() throws AlpacaClientException {
-        return this.getAnnouncements(ALL_CA_TYPES, LocalDate.now().minusDays(90), LocalDate.now(), null, null, null);
-    }
-
-    /**
-     *  Gets list of all {@link Announcement} within last 90 days for specific symbol
-     * @param symbol the symbol to query for
-     * @return a  {@link List} of {@link Announcement}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Announcement> getAnnouncements(String symbol) throws AlpacaClientException {
-        return this.getAnnouncements(ALL_CA_TYPES, LocalDate.now().minusDays(90), LocalDate.now(), symbol, null, null);
-    }
-
-    /**
-     *  Gets list of all {@link Announcement} for specific query
-     * @param caTypes - a list of Dividend, Merger, Spinoff, or Split
-     * @param since - the start (inclusive) of the date range when searching corporate action announcements. This should follow the YYYY-MM-DD format. The date range is limited to 90 days
-     * @param until - the end (inclusive) of the date range when searching corporate action announcements. This should follow the YYYY-MM-DD format. The date range is limited to 90 days
-     * @param symbol - the symbol of the company initiating the announcement
-     * @param cusip - the CUSIP of the company initiating the announcement
-     * @param dateType - one of declaration_date, ex_date, record_date, or payable_date
-     * @return a  {@link List} of {@link Announcement}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Announcement> getAnnouncements(
-            List<String> caTypes, LocalDate since, LocalDate until,
-            String symbol,
-            String cusip,
-            String dateType
-            )
-            throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-        checkNotNull(caTypes);
-        checkNotNull(since);
-        checkNotNull(until);
-
-
-        urlBuilder.addQueryParameter("ca_types", String.join(",", caTypes));
-
-        urlBuilder.addQueryParameter("since", since.toString());
-        urlBuilder.addQueryParameter("until", until.toString());
-
-        if (symbol != null) {
-            urlBuilder.addQueryParameter("symbol", symbol);
-        }
-        if (cusip != null) {
-            urlBuilder.addQueryParameter("cusip", cusip);
-        }
-        if (dateType !=null) {
-            urlBuilder.addQueryParameter("date_type", dateType);
-        }
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, ANNOUNCEMENT_ARRAYLIST_TYPE);
-
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
deleted file mode 100644
index f429a32c..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java
+++ /dev/null
@@ -1,302 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.marketdata.crypto;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.LatestCryptoBarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.LatestCryptoOrderbooksResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.LatestCryptoQuotesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshotsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTradesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.LatestCryptoTradesResponse;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.util.format.FormatUtil;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.time.ZonedDateTime;
-import java.util.Collection;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://docs.alpaca.markets/docs/crypto-pricing-data">Historical
- * Crypto Market Data API</a>.
- */
-public class CryptoMarketDataEndpoint extends AlpacaEndpoint {
-
-    public static final String LOCALE = "us";
-
-    /**
-     * Instantiates a new {@link CryptoMarketDataEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public CryptoMarketDataEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "crypto");
-    }
-
-    /**
-     * Gets {@link CryptoTrade} historical data for the requested crypto symbols.
-     *
-     * @param symbols   a {@link Collection} of symbols to query for
-     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted. <code>null</code> for the current day in Central Time.
-     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted. <code>null</code> for now.
-     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
-     *                  is given
-     * @param pageToken pagination token to continue from
-     *
-     * @return the {@link CryptoTradesResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public CryptoTradesResponse getTrades(Collection<String> symbols, ZonedDateTime start,
-            ZonedDateTime end, Integer limit, String pageToken) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("trades");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        if (start != null) {
-            urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        }
-
-        if (end != null) {
-            urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-        }
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, CryptoTradesResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link CryptoTrade}s for the requested securities.
-     *
-     * @param symbols a {@link Collection} of symbols to query for
-     *
-     * @return the {@link LatestCryptoTradesResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public LatestCryptoTradesResponse getLatestTrades(Collection<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("latest")
-                .addPathSegment("trades");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, LatestCryptoTradesResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link CryptoBar}s for the requested securities.
-     *
-     * @param symbols a {@link Collection} of symbols to query for
-     *
-     * @return the {@link LatestCryptoBarsResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public LatestCryptoBarsResponse getLatestBars(Collection<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("latest")
-                .addPathSegment("bars");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, LatestCryptoBarsResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link CryptoQuote}s for the requested securities.
-     *
-     * @param symbols a {@link Collection} of symbols to query for
-     *
-     * @return the {@link LatestCryptoQuotesResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public LatestCryptoQuotesResponse getLatestQuotes(Collection<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("latest")
-                .addPathSegment("quotes");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, LatestCryptoQuotesResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link CryptoSnapshot} for the requested securities.
-     *
-     * @param symbols a {@link Collection} of symbols to query for
-     *
-     * @return the {@link CryptoSnapshotsResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public CryptoSnapshotsResponse getSnapshots(Collection<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("snapshots");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, CryptoSnapshotsResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link CryptoOrderbook}s for the requested securities.
-     *
-     * @param symbols a {@link Collection} of symbols to query for
-     *
-     * @return the {@link LatestCryptoOrderbooksResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public LatestCryptoOrderbooksResponse getLatestOrderbooks(Collection<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("latest")
-                .addPathSegment("orderbooks");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, LatestCryptoOrderbooksResponse.class);
-    }
-
-    /**
-     * Gets {@link CryptoBar} aggregate historical data for the requested crypto.
-     *
-     * @param symbols               a {@link Collection} of symbols to query for
-     * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
-     *                              not accepted. <code>null</code> for now.
-     * @param end                   filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are
-     *                              not accepted. <code>null</code> for now.
-     * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
-     *                              <code>null</code> is given
-     * @param pageToken             pagination token to continue from
-     * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
-     *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
-     *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod} e.g. for <code>15Min</code> bars, you would supply
-     *                              {@link BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
-     *                              <code>barTimePeriodDuration</code> parameter.
-     *
-     * @return the {@link CryptoBarsResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public CryptoBarsResponse getBars(Collection<String> symbols, ZonedDateTime start, ZonedDateTime end, Integer limit,
-            String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod) throws AlpacaClientException {
-        checkNotNull(symbols);
-        if (symbols.isEmpty()) {
-            throw new AlpacaClientException("Collection symbols must not be empty.");
-        }
-        checkNotNull(barTimePeriod);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(LOCALE)
-                .addPathSegment("bars");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        if (start != null) {
-            urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        }
-
-        if (end != null) {
-            urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-        }
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, CryptoBarsResponse.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java
deleted file mode 100644
index 88ee1b7c..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/news/NewsEndpoint.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.marketdata.news;
-
-import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.NewsResponse;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.time.LocalDate;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://docs.alpaca.markets/reference/news-1">News</a>
- * Endpoint.
- */
-public class NewsEndpoint extends AlpacaEndpoint {
-
-    /**
-     * Instantiates a new {@link AlpacaEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public NewsEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "news");
-    }
-
-    /**
-     * Gets latest list of news {@link NewsResponse}.
-     * @return the {@link NewsResponse}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}
-     */
-    public NewsResponse getLatestNews() throws AlpacaClientException {
-        return this.getLatestNews(null, null, null, null, null, null, null, null);
-    }
-
-    /**
-     * Gets latest list of news {@link NewsResponse} for specific symbols.
-     * @param symbols list of symbols to query news for
-     * @return the {@link NewsResponse}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public NewsResponse getLatestNews(List<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        return this.getLatestNews(null, null, null, symbols, null, null, null, null);
-    }
-
-    /**
-     * Gets latest list of news {@link NewsResponse} for specified token.
-     * @param pageToken the ID of the end of your current page of results. (See the section on paging.)
-     * @return the {@link NewsResponse}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public NewsResponse getLatestNewsPaged(String pageToken) throws AlpacaClientException {
-        checkNotNull(pageToken);
-        return this.getLatestNews(null, null, null, null, null, null, null, pageToken);
-    }
-
-    /**
-     * Gets latest list of news {@link NewsResponse}
-     * @param start The inclusive start of the interval
-     * @param end the inclusive end of the interval
-     * @param sortDirection sort articles by updated date
-     * @param symbols list of symbols to query news for
-     * @param limit Limit of news items to be returned for given page
-     * @param includeContent Boolean indicator to include content for news articles (if available)
-     * @param excludeContentless Boolean indicator to exclude news articles that do not contain content
-     * @param pageToken the ID of the end of your current page of results. (See the section on paging.)
-     * @return the {@link NewsResponse}
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public NewsResponse getLatestNews(
-            LocalDate start, LocalDate end,
-            SortDirection sortDirection,
-            List<String> symbols,
-            Integer limit,
-            Boolean includeContent,
-            Boolean excludeContentless,
-            String pageToken) throws AlpacaClientException {
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        if (start != null) {
-            urlBuilder.addQueryParameter("start", start.toString());
-        }
-        if (end != null) {
-            urlBuilder.addQueryParameter("end", end.toString());
-        }
-        if (sortDirection != null) {
-            urlBuilder.addQueryParameter("sort", sortDirection.value());
-        }
-        if (symbols != null && !symbols.isEmpty()) {
-            urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-        }
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", Integer.toString(limit));
-        }
-        if (includeContent != null) {
-            urlBuilder.addQueryParameter("include_content", Boolean.toString(includeContent));
-        }
-        if (excludeContentless != null) {
-            urlBuilder.addQueryParameter("exclude_contentless", Boolean.toString(excludeContentless));
-        }
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-
-        return alpacaClient.requestObject(request, NewsResponse.class);
-
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
deleted file mode 100644
index 3111dde7..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java
+++ /dev/null
@@ -1,368 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.marketdata.stock;
-
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.enums.BarTimePeriod;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.MultiStockBarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBarsResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.enums.BarAdjustment;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.enums.BarFeed;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.LatestStockQuoteResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuotesResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.snapshot.Snapshot;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.LatestStockTradeResponse;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTradesResponse;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.util.format.FormatUtil;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.time.ZonedDateTime;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/">Historical
- * Market Data API v2</a>.
- */
-public class StockMarketDataEndpoint extends AlpacaEndpoint {
-
-    private static final Type SNAPSHOTS_OF_STRINGS_HASHMAP_TYPE =
-            new TypeToken<HashMap<String, Snapshot>>() {}.getType();
-
-    /**
-     * Instantiates a new {@link StockMarketDataEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public StockMarketDataEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "stocks");
-    }
-
-    /**
-     * Gets {@link StockTrade} historical data for the requested security.
-     *
-     * @param symbol    the symbol to query for
-     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted.
-     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted.
-     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
-     *                  is given
-     * @param pageToken pagination token to continue from
-     *
-     * @return the {@link StockTradesResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public StockTradesResponse getTrades(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
-            String pageToken) throws AlpacaClientException {
-        checkNotNull(symbol);
-        checkNotNull(start);
-        checkNotNull(end);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("trades");
-
-        urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, StockTradesResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link StockTrade} for the requested security.
-     *
-     * @param symbol the symbol to query for
-     *
-     * @return the {@link LatestStockTradeResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public LatestStockTradeResponse getLatestTrade(String symbol) throws AlpacaClientException {
-        checkNotNull(symbol);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("trades")
-                .addPathSegment("latest");
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, LatestStockTradeResponse.class);
-    }
-
-    /**
-     * Gets {@link StockQuote} (NBBO or National Best Bid and Offer) historical data for the requested security.
-     *
-     * @param symbol    the symbol to query for
-     * @param start     filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted.
-     * @param end       filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are not
-     *                  accepted.
-     * @param limit     number of data points to return. Must be in range 1-10000, defaults to 1000 if <code>null</code>
-     *                  is given
-     * @param pageToken pagination token to continue from
-     *
-     * @return the {@link StockQuotesResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public StockQuotesResponse getQuotes(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
-            String pageToken) throws AlpacaClientException {
-        checkNotNull(symbol);
-        checkNotNull(start);
-        checkNotNull(end);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("quotes");
-
-        urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, StockQuotesResponse.class);
-    }
-
-    /**
-     * Gets the latest {@link StockQuote} for the requested security.
-     *
-     * @param symbol the symbol to query for
-     *
-     * @return the {@link LatestStockQuoteResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public LatestStockQuoteResponse getLatestQuote(String symbol) throws AlpacaClientException {
-        checkNotNull(symbol);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("quotes")
-                .addPathSegment("latest");
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, LatestStockQuoteResponse.class);
-    }
-
-    /**
-     * Gets {@link StockBar} aggregate historical data for the requested security.
-     *
-     * @param symbol                the symbol to query for
-     * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
-     *                              not accepted.
-     * @param end                   filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are
-     *                              not accepted.
-     * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
-     *                              <code>null</code> is given
-     * @param pageToken             pagination token to continue from
-     * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
-     *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
-     *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply
-     *                              {@link BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
-     *                              <code>barTimePeriodDuration</code> parameter.
-     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is
-     *                              {@link BarAdjustment#RAW}
-     * @param barFeed               defaults to {@link BarFeed#IEX} for Free users and {@link BarFeed#SIP} for users
-     *                              with an Unlimited subscription
-     *
-     * @return the {@link StockBarsResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public StockBarsResponse getBars(String symbol, ZonedDateTime start, ZonedDateTime end, Integer limit,
-            String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod, BarAdjustment barAdjustment,
-            BarFeed barFeed) throws AlpacaClientException {
-        checkNotNull(symbol);
-        checkNotNull(start);
-        checkNotNull(end);
-        checkNotNull(barTimePeriod);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("bars");
-
-        urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
-
-        if (barAdjustment != null) {
-            urlBuilder.addQueryParameter("adjustment", barAdjustment.toString());
-        }
-
-        if (barFeed != null) {
-            urlBuilder.addQueryParameter("feed", barFeed.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, StockBarsResponse.class);
-    }
-
-    /**
-     * Gets {@link StockBar} aggregate historical data for the requested securities.
-     *
-     * @param symbols               a {@link Collection} of symbols to query for
-     * @param start                 filter data equal to or after this {@link ZonedDateTime}. Fractions of a second are
-     *                              not accepted.
-     * @param end                   filter data equal to or before this {@link ZonedDateTime}. Fractions of a second are
-     *                              not accepted.
-     * @param limit                 number of data points to return. Must be in range 1-10000, defaults to 1000 if
-     *                              <code>null</code> is given
-     * @param pageToken             pagination token to continue from
-     * @param barTimePeriodDuration the duration for the given <code>barTimePeriod</code> parameter. e.g. for
-     *                              <code>15Min</code> bars, you would supply <code>15</code> for this parameter and
-     *                              {@link BarTimePeriod#MINUTE} for the <code>barTimePeriod</code> parameter.
-     * @param barTimePeriod         the {@link BarTimePeriod}. e.g. for <code>15Min</code> bars, you would supply
-     *                              {@link BarTimePeriod#MINUTE} for this parameter and <code>15</code> for the
-     *                              <code>barTimePeriodDuration</code> parameter.
-     * @param barAdjustment         specifies the corporate action adjustment for the stocks. Default value is
-     *                              {@link BarAdjustment#RAW}
-     * @param barFeed               defaults to {@link BarFeed#IEX} for Free users and {@link BarFeed#SIP} for users
-     *                              with an Unlimited subscription
-     *
-     * @return the {@link MultiStockBarsResponse}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public MultiStockBarsResponse getBars(Collection<String> symbols, ZonedDateTime start, ZonedDateTime end,
-            Integer limit, String pageToken, int barTimePeriodDuration, BarTimePeriod barTimePeriod,
-            BarAdjustment barAdjustment, BarFeed barFeed) throws AlpacaClientException {
-        checkNotNull(symbols);
-        checkArgument(!symbols.isEmpty(), "'symbols' cannot be empty!");
-        checkNotNull(start);
-        checkNotNull(end);
-        checkNotNull(barTimePeriod);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment("bars");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-        urlBuilder.addQueryParameter("start", FormatUtil.toRFC3339Format(start));
-        urlBuilder.addQueryParameter("end", FormatUtil.toRFC3339Format(end));
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (pageToken != null) {
-            urlBuilder.addQueryParameter("page_token", pageToken);
-        }
-
-        urlBuilder.addQueryParameter("timeframe", barTimePeriodDuration + barTimePeriod.toString());
-
-        if (barAdjustment != null) {
-            urlBuilder.addQueryParameter("adjustment", barAdjustment.toString());
-        }
-
-        if (barFeed != null) {
-            urlBuilder.addQueryParameter("feed", barFeed.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, MultiStockBarsResponse.class);
-    }
-
-    /**
-     * Gets {@link Snapshot}s of the requested securities.
-     *
-     * @param symbols a {@link Collection} of symbols to query for
-     *
-     * @return a {@link Map} with they keys being the symbol and their values being the {@link Snapshot}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Map<String, Snapshot> getSnapshots(Collection<String> symbols) throws AlpacaClientException {
-        checkNotNull(symbols);
-        checkArgument(!symbols.isEmpty(), "'symbols' cannot be empty!");
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment("snapshots");
-
-        urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, SNAPSHOTS_OF_STRINGS_HASHMAP_TYPE);
-    }
-
-    /**
-     * Gets a {@link Snapshot} of the requested security.
-     *
-     * @param symbol the symbol to query for
-     *
-     * @return the {@link Snapshot}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Snapshot getSnapshot(String symbol) throws AlpacaClientException {
-        checkNotNull(symbol);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbol)
-                .addPathSegment("snapshot");
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Snapshot.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
deleted file mode 100644
index b2a0563d..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java
+++ /dev/null
@@ -1,651 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.orders;
-
-import com.google.gson.JsonObject;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
-import net.jacobpeterson.alpaca.model.endpoint.orders.CancelledOrder;
-import net.jacobpeterson.alpaca.model.endpoint.orders.Order;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.CurrentOrderStatus;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderClass;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderSide;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderStatus;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderTimeInForce;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.OrderType;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.util.format.FormatUtil;
-import net.jacobpeterson.alpaca.util.okhttp.JSONBodyBuilder;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_204;
-import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_207;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://alpaca.markets/docs/api-documentation/api-v2/orders/">Orders</a>.
- */
-public class OrdersEndpoint extends AlpacaEndpoint {
-
-    private static final Type ORDER_ARRAYLIST_TYPE = new TypeToken<ArrayList<Order>>() {}.getType();
-    private static final Type CANCELLED_ORDER_ARRAYLIST_TYPE = new TypeToken<ArrayList<CancelledOrder>>() {}.getType();
-
-    /**
-     * Instantiates a new {@link OrdersEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public OrdersEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "orders");
-    }
-
-    /**
-     * Retrieves a list of {@link Order}s for the account, filtered by the supplied query parameters.
-     *
-     * @param status    {@link CurrentOrderStatus} to be queried. Defaults to {@link CurrentOrderStatus#OPEN}.
-     * @param limit     the maximum number of orders in response. Defaults to 50 and max is 500.
-     * @param after     the response will include only ones submitted after this timestamp (exclusive)
-     * @param until     the response will include only ones submitted until this timestamp (exclusive)
-     * @param direction the chronological order of response based on the submission time. Defaults to
-     *                  {@link SortDirection#DESCENDING}.
-     * @param nested    if true, the result will roll up multi-leg orders under the legs field of primary order.
-     * @param symbols   a {@link Collection} of symbols to filter by (e.g. "AAPL,TSLA,MSFT"). A currency pair is
-     *                  required for crypto orders (e.g. "BTCUSD,BCHUSD,LTCUSD,ETCUSD"). <code>null</code> for no
-     *                  filter.
-     *
-     * @return a {@link List} of {@link Order}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Order> get(CurrentOrderStatus status, Integer limit, ZonedDateTime after, ZonedDateTime until,
-            SortDirection direction, Boolean nested, Collection<String> symbols) throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        if (status != null) {
-            urlBuilder.addQueryParameter("status", status.toString());
-        }
-
-        if (limit != null) {
-            urlBuilder.addQueryParameter("limit", limit.toString());
-        }
-
-        if (after != null) {
-            urlBuilder.addQueryParameter("after", after.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
-        }
-
-        if (until != null) {
-            urlBuilder.addQueryParameter("until", until.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
-        }
-
-        if (direction != null) {
-            urlBuilder.addQueryParameter("direction", direction.toString());
-        }
-
-        if (nested != null) {
-            urlBuilder.addQueryParameter("nested", nested.toString());
-        }
-
-        if (symbols != null && !symbols.isEmpty()) {
-            urlBuilder.addQueryParameter("symbols", String.join(",", symbols));
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, ORDER_ARRAYLIST_TYPE);
-    }
-
-    /**
-     * Places a new order for the given account. An order request may be rejected if the account is not authorized for
-     * trading, or if the tradable balance is insufficient to fill the order. Note: many of the parameters for this
-     * method can be set to <code>null</code> if they aren't required for the requested {@link OrderType}.
-     *
-     * @param symbol               symbol or asset ID to identify the asset to trade
-     * @param quantity             number of shares to trade. Can be fractional.
-     * @param notional             dollar amount to trade. Cannot work with <code>quantity</code>. Can only work for
-     *                             {@link OrderType#MARKET} and {@link OrderTimeInForce#DAY}.
-     * @param side                 the {@link OrderSide}
-     * @param type                 the {@link OrderType}
-     * @param timeInForce          the {@link OrderTimeInForce}
-     * @param limitPrice           required if type is {@link OrderType#LIMIT} or {@link OrderType#STOP_LIMIT}
-     * @param stopPrice            required if type is {@link OrderType#STOP} or {@link OrderType#STOP_LIMIT}
-     * @param trailPrice           this or <code>trail_percent</code> is required if type is
-     *                             {@link OrderType#TRAILING_STOP}
-     * @param trailPercent         this or <code>trail_price</code> is required if type is
-     *                             {@link OrderType#TRAILING_STOP}
-     * @param extendedHours        (default) false. If true, order will be eligible to execute in premarket/afterhours.
-     *                             Only works with type {@link OrderType#LIMIT} and {@link OrderTimeInForce#DAY}.
-     * @param clientOrderId        a unique identifier for the order. Automatically generated if <code>null</code>.
-     * @param orderClass           the {@link OrderClass}. For details of non-simple order classes, please see "Bracket
-     *                             Order Overview" on the Alpaca Docs.
-     * @param takeProfitLimitPrice additional parameter for take-profit leg of advanced orders. Required for
-     *                             {@link OrderClass#BRACKET}.
-     * @param stopLossStopPrice    additional parameters for stop-loss leg of advanced orders. Required for
-     *                             {@link OrderClass#BRACKET}.
-     * @param stopLossLimitPrice   additional parameters for stop-loss leg of advanced orders. The stop-loss order
-     *                             becomes a stop-limit order if specified.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestOrder(String symbol, Double quantity, Double notional, OrderSide side, OrderType type,
-            OrderTimeInForce timeInForce, Double limitPrice, Double stopPrice, Double trailPrice, Double trailPercent,
-            Boolean extendedHours, String clientOrderId, OrderClass orderClass, Double takeProfitLimitPrice,
-            Double stopLossStopPrice, Double stopLossLimitPrice)
-            throws AlpacaClientException {
-        checkNotNull(symbol);
-        checkState(quantity != null ^ notional != null, "Either 'quantity' or 'notional' are required.");
-        checkNotNull(side);
-        checkNotNull(type);
-        checkNotNull(timeInForce);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        JSONBodyBuilder jsonBodyBuilder = new JSONBodyBuilder();
-        jsonBodyBuilder.appendJSONBodyProperty("symbol", symbol);
-        jsonBodyBuilder.appendJSONBodyProperty("side", side.toString());
-        jsonBodyBuilder.appendJSONBodyProperty("type", type.toString());
-        jsonBodyBuilder.appendJSONBodyProperty("time_in_force", timeInForce.toString());
-
-        if (quantity != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("qty", quantity.toString());
-        } else {
-            jsonBodyBuilder.appendJSONBodyProperty("notional", notional.toString());
-        }
-
-        if (limitPrice != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("limit_price", FormatUtil.toCurrencyFormat(limitPrice));
-        }
-
-        if (stopPrice != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("stop_price", FormatUtil.toCurrencyFormat(stopPrice));
-        }
-
-        if (trailPrice != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("trail_price", FormatUtil.toCurrencyFormat(trailPrice));
-        }
-
-        if (trailPercent != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("trail_percent", FormatUtil.toCurrencyFormat(trailPercent));
-        }
-
-        if (extendedHours != null) {
-            jsonBodyBuilder.appendJSONBodyJSONProperty("extended_hours", new JsonPrimitive(extendedHours));
-        }
-
-        if (clientOrderId != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("client_order_id", clientOrderId);
-        }
-
-        if (orderClass != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("order_class", orderClass.toString());
-        }
-
-        if (takeProfitLimitPrice != null) {
-            JsonObject takeProfitObject = new JsonObject();
-            takeProfitObject.addProperty("limit_price", takeProfitLimitPrice);
-            jsonBodyBuilder.appendJSONBodyJSONProperty("take_profit", takeProfitObject);
-        }
-
-        if (stopLossStopPrice != null || stopLossLimitPrice != null) {
-            JsonObject stopLossObject = new JsonObject();
-
-            if (stopLossStopPrice != null) {
-                stopLossObject.addProperty("stop_price", stopLossStopPrice);
-            }
-
-            if (stopLossLimitPrice != null) {
-                stopLossObject.addProperty("limit_price", stopLossLimitPrice);
-            }
-
-            jsonBodyBuilder.appendJSONBodyJSONProperty("stop_loss", stopLossObject);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .post(jsonBodyBuilder.build())
-                .build();
-        return alpacaClient.requestObject(request, Order.class);
-    }
-
-    /**
-     * A market order is a request to buy or sell a security at the currently available market price. It provides the
-     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} so check the Javadoc
-     * for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestMarketOrder(String symbol, Integer quantity, OrderSide side, OrderTimeInForce timeInForce)
-            throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.MARKET, timeInForce, null, null,
-                null, null, null, null, OrderClass.SIMPLE, null, null, null);
-    }
-
-    /**
-     * A market order is a request to buy or sell a security at the currently available market price. It provides the
-     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and
-     * {@link OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a fractional quantity so check the Javadoc for that method for
-     * parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestFractionalMarketOrder(String symbol, Double quantity, OrderSide side)
-            throws AlpacaClientException {
-        return requestOrder(symbol, quantity, null, side, OrderType.MARKET, OrderTimeInForce.DAY, null,
-                null, null,
-                null, null, null, null, null, null, null);
-    }
-
-    /**
-     * A market order is a request to buy or sell a security at the currently available market price. It provides the
-     * most likely method of filling an order. Market orders fill nearly instantaneously. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and
-     * {@link OrderTimeInForce#GOOD_UNTIL_CANCELLED} with a notional dollar amount so check the Javadoc for that method
-     * for the parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestNotionalMarketOrder(String symbol, Double notional, OrderSide side)
-            throws AlpacaClientException {
-        return requestOrder(symbol, null, notional, side, OrderType.MARKET, OrderTimeInForce.GOOD_UNTIL_CANCELLED, null,
-                null, null,
-                null, null, null, null, null, null, null);
-    }
-
-    /**
-     * A limit order is an order to buy or sell at a specified price or better. A buy limit order (a limit order to buy)
-     * is executed at the specified limit price or lower (i.e., better). Conversely, a sell limit order (a limit order
-     * to sell) is executed at the specified limit price or higher (better). Unlike a market order, you have to specify
-     * the limit price parameter when submitting your order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} so check the Javadoc
-     * for that method for the parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestLimitOrder(String symbol, Double quantity, OrderSide side, OrderTimeInForce timeInForce,
-            Double limitPrice, Boolean extendedHours) throws AlpacaClientException {
-        return requestOrder(symbol, quantity, null, side, OrderType.LIMIT, timeInForce, limitPrice,
-                null, null, null, extendedHours, null, OrderClass.SIMPLE, null, null, null);
-    }
-
-    /**
-     * A stop (market) order is an order to buy or sell a security when its price moves past a particular point,
-     * ensuring a higher probability of achieving a predetermined entry or exit price. Once the market price crosses the
-     * specified stop price, the stop order becomes a market order. Alpaca converts buy stop orders into stop limit
-     * orders with a limit price that is 4% higher than a stop price &lt; $50 (or 2.5% higher than a stop price &gt;=
-     * $50). Sell stop orders are not converted into stop limit orders. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP} so check the Javadoc
-     * for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestStopOrder(String symbol, Integer quantity, OrderSide side, OrderTimeInForce timeInForce,
-            Double stopPrice, Boolean extendedHours) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.STOP, timeInForce, null, stopPrice,
-                null, null, extendedHours, null, OrderClass.SIMPLE, null, null, null);
-    }
-
-    /**
-     * A stop-limit order is a conditional trade over a set time frame that combines the features of a stop order with
-     * those of a limit order and is used to mitigate risk. The stop-limit order will be executed at a specified limit
-     * price, or better, after a given stop price has been reached. Once the stop price is reached, the stop-limit order
-     * becomes a limit order to buy or sell at the limit price or better. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP_LIMIT} so check the
-     * Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestStopLimitOrder(String symbol, Integer quantity, OrderSide side, OrderTimeInForce timeInForce,
-            Double limitPrice, Double stopPrice, Boolean extendedHours) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.STOP_LIMIT, timeInForce,
-                limitPrice, stopPrice, null, null, extendedHours, null, OrderClass.SIMPLE, null, null, null);
-    }
-
-    /**
-     * A bracket order is a chain of three orders that can be used to manage your position entry and exit. It is a
-     * common use case of an OTOCO (One Triggers OCO {One Cancels Other}) order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#MARKET} and with parameters
-     * for a bracket order so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestMarketBracketOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double takeProfitLimitPrice, Double stopLossStopPrice,
-            Double stopLossLimitPrice) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.MARKET, timeInForce, null, null,
-                null, null, null, null, OrderClass.BRACKET, takeProfitLimitPrice, stopLossStopPrice,
-                stopLossLimitPrice);
-    }
-
-    /**
-     * A bracket order is a chain of three orders that can be used to manage your position entry and exit. It is a
-     * common use case of an OTOCO (One Triggers OCO {One Cancels Other}) order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters
-     * for a bracket order so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestLimitBracketOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double limitPrice, Boolean extendedHours, Double takeProfitLimitPrice,
-            Double stopLossStopPrice, Double stopLossLimitPrice) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.LIMIT, timeInForce, limitPrice,
-                null, null, null, extendedHours, null, OrderClass.BRACKET, takeProfitLimitPrice, stopLossStopPrice,
-                stopLossLimitPrice);
-    }
-
-    /**
-     * OCO (One-Cancels-Other) is another type of advanced order type. This is a set of two orders with the same side
-     * (buy/buy or sell/sell) and currently only exit order is supported. In other words, this is the second part of the
-     * bracket orders where the entry order is already filled, and you can submit the take-profit and stop-loss in one
-     * order submission. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with parameters for a
-     * {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for the parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestOCOOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Boolean extendedHours, Double takeProfitLimitPrice,
-            Double stopLossStopPrice, Double stopLossLimitPrice) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.LIMIT, timeInForce, null, null,
-                null, null, extendedHours, null, OrderClass.ONE_CANCELS_OTHER, takeProfitLimitPrice, stopLossStopPrice,
-                stopLossLimitPrice);
-    }
-
-    /**
-     * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters
-     * for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestOTOMarketOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double takeProfitLimitPrice, Double stopLossStopPrice,
-            Double stopLossLimitPrice) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.MARKET, timeInForce, null, null,
-                null, null, null, null, OrderClass.ONE_CANCELS_OTHER, takeProfitLimitPrice, stopLossStopPrice,
-                stopLossLimitPrice);
-    }
-
-    /**
-     * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#LIMIT} and with parameters
-     * for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestOTOLimitOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double limitPrice, Boolean extendedHours, Double takeProfitLimitPrice,
-            Double stopLossStopPrice, Double stopLossLimitPrice) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.LIMIT, timeInForce, limitPrice,
-                null, null, null, extendedHours, null, OrderClass.ONE_CANCELS_OTHER, takeProfitLimitPrice,
-                stopLossStopPrice, stopLossLimitPrice);
-    }
-
-    /**
-     * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP} and with parameters for
-     * a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestOTOStopOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double stopPrice, Boolean extendedHours, Double takeProfitLimitPrice,
-            Double stopLossStopPrice, Double stopLossLimitPrice) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.STOP, timeInForce, null, stopPrice,
-                null, null, extendedHours, null, OrderClass.ONE_CANCELS_OTHER, takeProfitLimitPrice, stopLossStopPrice,
-                stopLossLimitPrice);
-    }
-
-    /**
-     * OTO (One-Triggers-Other) is a variant of bracket order. It takes one of the take-profit or stop-loss order in
-     * addition to the entry order. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#STOP_LIMIT} and with
-     * parameters for a {@link OrderClass#ONE_CANCELS_OTHER} so check the Javadoc for that method for parameter
-     * details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestOTOStopLimitOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double limitPrice, Double stopPrice, Boolean extendedHours,
-            Double takeProfitLimitPrice, Double stopLossStopPrice, Double stopLossLimitPrice)
-            throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.STOP_LIMIT, timeInForce,
-                limitPrice, stopPrice, null, null, extendedHours, null, OrderClass.ONE_CANCELS_OTHER,
-                takeProfitLimitPrice, stopLossStopPrice, stopLossLimitPrice);
-    }
-
-    /**
-     * Trailing stop orders allow you to continuously and automatically keep updating the stop price threshold based on
-     * the stock price movement. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#TRAILING_STOP} and with
-     * parameters for a {@link OrderType#TRAILING_STOP} so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestTrailingStopPriceOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double trailPrice, Boolean extendedHours) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.TRAILING_STOP, timeInForce, null,
-                null, trailPrice, null, extendedHours, null, null, null, null, null);
-    }
-
-    /**
-     * Trailing stop orders allow you to continuously and automatically keep updating the stop price threshold based on
-     * the stock price movement. This method calls
-     * {@link #requestOrder(String, Double, Double, OrderSide, OrderType, OrderTimeInForce, Double, Double, Double,
-     * Double, Boolean, String, OrderClass, Double, Double, Double)} with {@link OrderType#TRAILING_STOP} and with
-     * parameters for a {@link OrderType#TRAILING_STOP} so check the Javadoc for that method for parameter details.
-     *
-     * @return the requested {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order requestTrailingStopPercentOrder(String symbol, Integer quantity, OrderSide side,
-            OrderTimeInForce timeInForce, Double trailPercent, Boolean extendedHours) throws AlpacaClientException {
-        return requestOrder(symbol, quantity.doubleValue(), null, side, OrderType.TRAILING_STOP, timeInForce, null,
-                null, null, trailPercent, extendedHours, null, null, null, null, null);
-    }
-
-    /**
-     * Retrieves a single {@link Order} for the given <code>orderID</code>.
-     *
-     * @param orderID the {@link Order#getId()}
-     * @param nested  if true, the result will roll up multi-leg orders under the legs field of primary order.
-     *
-     * @return the {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order get(String orderID, Boolean nested) throws AlpacaClientException {
-        checkNotNull(orderID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(orderID);
-
-        if (nested != null) {
-            urlBuilder.addQueryParameter("nested", nested.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Order.class);
-    }
-
-    /**
-     * Retrieves a single {@link Order} for the given <code>clientOrderId</code>.
-     *
-     * @param clientOrderID the {@link Order#getClientOrderId()}
-     *
-     * @return the {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order getByClientID(String clientOrderID) throws AlpacaClientException {
-        checkNotNull(clientOrderID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment("orders:by_client_order_id");
-        urlBuilder.addQueryParameter("client_order_id", clientOrderID);
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Order.class);
-    }
-
-    /**
-     * Replaces a single {@link Order} with updated parameters. Each parameter overrides the corresponding attribute of
-     * the existing {@link Order}. The other attributes remain the same as the existing {@link Order}.
-     *
-     * @param orderID       the {@link Order#getId()}
-     * @param quantity      number of shares to trade. Can be fractional.
-     * @param timeInForce   the {@link OrderTimeInForce}
-     * @param limitPrice    required if type is {@link OrderType#LIMIT} or {@link OrderType#STOP_LIMIT}
-     * @param stopPrice     required if type is {@link OrderType#STOP} or {@link OrderType#STOP_LIMIT}
-     * @param trail         the new value of the <code>trail_price</code> or <code>trail_percent</code> value (works
-     *                      only for {@link Order#getType()} <code>==</code> {@link OrderType#TRAILING_STOP})
-     * @param clientOrderId a unique identifier for the order. Automatically generated if <code>null</code>.
-     *
-     * @return a new {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order replace(String orderID, Integer quantity, OrderTimeInForce timeInForce, Double limitPrice,
-            Double stopPrice, Double trail, String clientOrderId) throws AlpacaClientException {
-        checkNotNull(orderID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(orderID);
-
-        JSONBodyBuilder jsonBodyBuilder = new JSONBodyBuilder();
-
-        if (quantity != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("qty", quantity.toString());
-        }
-
-        if (timeInForce != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("time_in_force", timeInForce.toString());
-        }
-
-        if (limitPrice != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("limit_price", FormatUtil.toCurrencyFormat(limitPrice));
-        }
-
-        if (stopPrice != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("stop_price", FormatUtil.toCurrencyFormat(stopPrice));
-        }
-
-        if (trail != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("trail", FormatUtil.toCurrencyFormat(trail));
-        }
-
-        if (clientOrderId != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("client_order_id", clientOrderId);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .patch(jsonBodyBuilder.build())
-                .build();
-        return alpacaClient.requestObject(request, Order.class);
-    }
-
-    /**
-     * Attempts to cancel all open {@link Order}s. A response will be provided for each {@link Order} that is attempted
-     * to be cancelled. If an {@link Order} is no longer cancelable, the server will respond with status
-     * <code>500</code> and reject the request.
-     *
-     * @return a {@link List} of {@link CancelledOrder}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<CancelledOrder> cancelAll() throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .delete()
-                .build();
-        return alpacaClient.requestObject(request, STATUS_CODE_200_OR_207, CANCELLED_ORDER_ARRAYLIST_TYPE);
-    }
-
-    /**
-     * Attempts to cancel an open {@link Order}. If the {@link Order} is no longer cancelable (example:
-     * {@link Order#getStatus()} <code>==</code> {@link OrderStatus#FILLED}), the server will respond with status
-     * <code>422</code>, and reject the request.
-     *
-     * @param orderID the {@link Order#getId()}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public void cancel(String orderID) throws AlpacaClientException {
-        checkNotNull(orderID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(orderID);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .delete()
-                .build();
-        alpacaClient.requestVoid(request, STATUS_CODE_200_OR_204);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java
deleted file mode 100644
index 722299f0..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.portfoliohistory;
-
-import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistory;
-import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistoryDataPoint;
-import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.PortfolioHistoryResponse;
-import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.enums.PortfolioPeriodUnit;
-import net.jacobpeterson.alpaca.model.endpoint.portfoliohistory.enums.PortfolioTimeFrame;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.util.format.FormatUtil;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-
-import static com.google.common.base.Preconditions.checkState;
-
-/**
- * {@link AlpacaEndpoint} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/portfolio-history/">Portfolio
- * History</a>.
- */
-public class PortfolioHistoryEndpoint extends AlpacaEndpoint {
-
-    /**
-     * Instantiates a new {@link PortfolioHistoryEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public PortfolioHistoryEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "account/portfolio/history");
-    }
-
-    /**
-     * Returns timeseries data about equity and profit/loss (P/L) of the account in requested timespan.
-     *
-     * @param periodLength  the duration of the <code>periodUnit</code>. Defaults to <code>1</code>.
-     * @param periodUnit    the {@link PortfolioPeriodUnit}. Defaults to {@link PortfolioPeriodUnit#MONTH}.
-     * @param timeFrame     the resolution of time window. If omitted, {@link PortfolioTimeFrame#ONE_MIN} for less than
-     *                      7 days period, {@link PortfolioTimeFrame#FIFTEEN_MINUTE} for less than 30 days, or otherwise
-     *                      {@link PortfolioTimeFrame#ONE_DAY}.
-     * @param dateEnd       the date the data is returned up to. Defaults to the current market date (rolls over at the
-     *                      market open if <code>extended_hours</code> is false, otherwise at 7 AM ET)
-     * @param extendedHours if true, include extended hours in the result. This is effective only for timeframe less
-     *                      than {@link PortfolioTimeFrame#ONE_DAY}.
-     *
-     * @return the {@link PortfolioHistory}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public PortfolioHistory get(Integer periodLength, PortfolioPeriodUnit periodUnit, PortfolioTimeFrame timeFrame,
-            LocalDate dateEnd, Boolean extendedHours) throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegments(endpointPathSegment);
-
-        if (periodLength != null && periodUnit != null) {
-            urlBuilder.addQueryParameter("period", periodLength + periodUnit.toString());
-        }
-
-        if (timeFrame != null) {
-            urlBuilder.addQueryParameter("timeframe", timeFrame.toString());
-        }
-
-        if (dateEnd != null) {
-            urlBuilder.addQueryParameter("date_end", dateEnd.format(DateTimeFormatter.ISO_DATE));
-        }
-
-        if (extendedHours != null) {
-            urlBuilder.addQueryParameter("extended_hours", extendedHours.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        PortfolioHistoryResponse response = alpacaClient.requestObject(request,
-                PortfolioHistoryResponse.class);
-
-        // Check if any response arrays differ in size
-        checkState(response.getTimestamp().size() == response.getEquity().size() &&
-                        response.getEquity().size() == response.getProfitLoss().size() &&
-                        response.getProfitLoss().size() == response.getProfitLossPercent().size(),
-                "Response arrays should not differ in size!");
-
-        // Add all data points into one POJO
-        ArrayList<PortfolioHistoryDataPoint> dataPoints = new ArrayList<>();
-        for (int index = 0; index < response.getTimestamp().size(); index++) {
-            dataPoints.add(new PortfolioHistoryDataPoint(
-                    Instant.ofEpochSecond(response.getTimestamp().get(index)).atZone(FormatUtil.NEW_YORK_ZONED_ID),
-                    response.getEquity().get(index),
-                    response.getProfitLoss().get(index),
-                    response.getProfitLossPercent().get(index)));
-        }
-
-        return new PortfolioHistory(
-                dataPoints,
-                response.getBaseValue(),
-                response.getTimeframe());
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
deleted file mode 100644
index fc8eda81..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.positions;
-
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.assets.Asset;
-import net.jacobpeterson.alpaca.model.endpoint.orders.Order;
-import net.jacobpeterson.alpaca.model.endpoint.positions.ClosePositionOrder;
-import net.jacobpeterson.alpaca.model.endpoint.positions.Position;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_207;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://alpaca.markets/docs/api-documentation/api-v2/positions/">Positions</a>.
- */
-public class PositionsEndpoint extends AlpacaEndpoint {
-
-    private static final Type POSITION_ARRAYLIST_TYPE = new TypeToken<ArrayList<Position>>() {}.getType();
-    private static final Type CLOSED_POSITION_ORDER_ARRAYLIST_TYPE =
-            new TypeToken<ArrayList<ClosePositionOrder>>() {}.getType();
-
-    /**
-     * Instantiates a new {@link PositionsEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public PositionsEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "positions");
-    }
-
-    /**
-     * Retrieves a list of the account's open {@link Position}s.
-     *
-     * @return a {@link List} of open {@link Position}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Position> get() throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, POSITION_ARRAYLIST_TYPE);
-    }
-
-    /**
-     * Retrieves the account's open {@link Position}s for the given symbol or {@link Asset#getId()}.
-     *
-     * @param symbolOrAssetID the symbol or {@link Asset#getId()} (required)
-     *
-     * @return the open {@link Position}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Position getBySymbol(String symbolOrAssetID) throws AlpacaClientException {
-        checkNotNull(symbolOrAssetID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbolOrAssetID);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Position.class);
-    }
-
-    /**
-     * Closes (liquidates) all of the account’s open long and short {@link Position}s. A response will be provided for
-     * each order that is attempted to be cancelled. If an order is no longer cancelable, the server will respond with
-     * status 500 and reject the request.
-     *
-     * @param cancelOrders if true is specified, cancel all open orders before liquidating all positions
-     *
-     * @return a {@link List} of open {@link ClosePositionOrder}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<ClosePositionOrder> closeAll(Boolean cancelOrders) throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        if (cancelOrders != null) {
-            urlBuilder.addQueryParameter("cancel_orders", cancelOrders.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .delete()
-                .build();
-        return alpacaClient.requestObject(request, STATUS_CODE_200_OR_207, CLOSED_POSITION_ORDER_ARRAYLIST_TYPE);
-    }
-
-    /**
-     * Closes (liquidates) the account’s open position for the given <code>symbol</code>. Works for both long and short
-     * positions.
-     *
-     * @param symbolOrAssetID the symbol or {@link Asset#getId()}
-     * @param quantity        the number of shares to liquidate. Can accept up to <code>9</code> decimal points. Cannot
-     *                        work with <code>percentage</code>.
-     * @param percentage      the percentage of the position to liquidate. Must be between <code>0</code> and
-     *                        <code>100</code>. Would only sell fractional if position is originally fractional. Can
-     *                        accept up to <code>9</code> decimal points. Cannot work with <code>quantity</code>.
-     *
-     * @return a closing {@link Position} {@link Order}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Order close(String symbolOrAssetID, Double quantity, Double percentage) throws AlpacaClientException {
-        checkNotNull(symbolOrAssetID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(symbolOrAssetID);
-
-        if (quantity != null) {
-            urlBuilder.addQueryParameter("qty", quantity.toString());
-        }
-
-        if (percentage != null) {
-            urlBuilder.addQueryParameter("percentage", percentage.toString());
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .delete()
-                .build();
-        return alpacaClient.requestObject(request, Order.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java b/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
deleted file mode 100644
index 605d651b..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java
+++ /dev/null
@@ -1,216 +0,0 @@
-package net.jacobpeterson.alpaca.rest.endpoint.watchlist;
-
-import com.google.gson.JsonArray;
-import com.google.gson.reflect.TypeToken;
-import net.jacobpeterson.alpaca.model.endpoint.assets.Asset;
-import net.jacobpeterson.alpaca.model.endpoint.watchlist.Watchlist;
-import net.jacobpeterson.alpaca.rest.AlpacaClient;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.AlpacaEndpoint;
-import net.jacobpeterson.alpaca.util.okhttp.JSONBodyBuilder;
-import okhttp3.HttpUrl;
-import okhttp3.Request;
-
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static net.jacobpeterson.alpaca.rest.AlpacaClient.STATUS_CODE_200_OR_204;
-
-/**
- * {@link AlpacaEndpoint} for <a href="https://docs.alpaca.markets/api-documentation/api-v2/watchlist/">Watchlists</a>
- * .
- */
-public class WatchlistEndpoint extends AlpacaEndpoint {
-
-    private static final Type WATCHLIST_ARRAYLIST_TYPE = new TypeToken<ArrayList<Watchlist>>() {}.getType();
-
-    /**
-     * Instantiates a new {@link WatchlistEndpoint}.
-     *
-     * @param alpacaClient the {@link AlpacaClient}
-     */
-    public WatchlistEndpoint(AlpacaClient alpacaClient) {
-        super(alpacaClient, "watchlists");
-    }
-
-    /**
-     * Returns the list of {@link Watchlist}s registered under the account.
-     *
-     * @return a {@link List} of {@link Watchlist}s
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public List<Watchlist> get() throws AlpacaClientException {
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, WATCHLIST_ARRAYLIST_TYPE);
-    }
-
-    /**
-     * Creates a new {@link Watchlist} with initial set of {@link Asset}s.
-     *
-     * @param name    arbitrary name string, up to 64 characters
-     * @param symbols set of symbols {@link String}s
-     *
-     * @return the created {@link Watchlist}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Watchlist create(String name, String... symbols) throws AlpacaClientException {
-        checkNotNull(name);
-        checkArgument(name.length() <= 64, "'name' cannot be longer than 64 characters!");
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment);
-
-        JSONBodyBuilder jsonBodyBuilder = new JSONBodyBuilder();
-        jsonBodyBuilder.appendJSONBodyProperty("name", name);
-
-        if (symbols != null && symbols.length != 0) {
-            JsonArray symbolsArray = new JsonArray();
-            Arrays.stream(symbols).forEach(symbolsArray::add);
-            jsonBodyBuilder.appendJSONBodyJSONProperty("symbols", symbolsArray);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .post(jsonBodyBuilder.build())
-                .build();
-        return alpacaClient.requestObject(request, Watchlist.class);
-    }
-
-    /**
-     * Returns a {@link Watchlist} identified by the ID.
-     *
-     * @param watchlistID the {@link Watchlist#getId()}
-     *
-     * @return the {@link Watchlist}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Watchlist get(String watchlistID) throws AlpacaClientException {
-        checkNotNull(watchlistID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(watchlistID);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .get()
-                .build();
-        return alpacaClient.requestObject(request, Watchlist.class);
-    }
-
-    /**
-     * Updates the name and/or content of a {@link Watchlist}.
-     *
-     * @param watchlistID the {@link Watchlist#getId()}
-     * @param name        the new {@link Watchlist} name
-     * @param symbols     the new list of symbol names to replace the {@link Watchlist} content
-     *
-     * @return the updated {@link Watchlist}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Watchlist update(String watchlistID, String name, String... symbols)
-            throws AlpacaClientException {
-        checkNotNull(watchlistID);
-        checkArgument(name == null || name.length() <= 64, "'name' cannot be longer than 64 characters!");
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(watchlistID);
-
-        JSONBodyBuilder jsonBodyBuilder = new JSONBodyBuilder();
-
-        if (name != null) {
-            jsonBodyBuilder.appendJSONBodyProperty("name", name);
-        }
-
-        if (symbols != null && symbols.length != 0) {
-            JsonArray symbolsArray = new JsonArray();
-            Arrays.stream(symbols).forEach(symbolsArray::add);
-            jsonBodyBuilder.appendJSONBodyJSONProperty("symbols", symbolsArray);
-        }
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .put(jsonBodyBuilder.build())
-                .build();
-        return alpacaClient.requestObject(request, Watchlist.class);
-    }
-
-    /**
-     * Append an asset for the symbol to the end of {@link Watchlist} asset list.
-     *
-     * @param watchlistID the {@link Watchlist#getId()}
-     * @param symbol      the symbol name to add to the {@link Watchlist}
-     *
-     * @return the updated {@link Watchlist}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Watchlist addAsset(String watchlistID, String symbol) throws AlpacaClientException {
-        checkNotNull(watchlistID);
-        checkNotNull(symbol);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(watchlistID);
-
-        JSONBodyBuilder jsonBodyBuilder = new JSONBodyBuilder();
-        jsonBodyBuilder.appendJSONBodyProperty("symbol", symbol);
-
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .post(jsonBodyBuilder.build())
-                .build();
-        return alpacaClient.requestObject(request, Watchlist.class);
-    }
-
-    /**
-     * Deletes a {@link Watchlist}. This is a permanent deletion.
-     *
-     * @param watchlistID the {@link Watchlist#getId()}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public void delete(String watchlistID) throws AlpacaClientException {
-        checkNotNull(watchlistID);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(watchlistID);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .delete()
-                .build();
-        alpacaClient.requestVoid(request, STATUS_CODE_200_OR_204);
-    }
-
-    /**
-     * Delete one entry for an asset by symbol name.
-     *
-     * @param watchlistID the {@link Watchlist#getId()}
-     * @param symbol      symbol name to remove from the {@link Watchlist} content
-     *
-     * @return the updated {@link Watchlist}
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    public Watchlist removeSymbol(String watchlistID, String symbol) throws AlpacaClientException {
-        checkNotNull(watchlistID);
-        checkNotNull(symbol);
-
-        HttpUrl.Builder urlBuilder = alpacaClient.urlBuilder()
-                .addPathSegment(endpointPathSegment)
-                .addPathSegment(watchlistID)
-                .addPathSegment(symbol);
-        Request request = alpacaClient.requestBuilder(urlBuilder.build())
-                .delete()
-                .build();
-        return alpacaClient.requestObject(request, STATUS_CODE_200_OR_204, Watchlist.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java b/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java
deleted file mode 100644
index ba0e2208..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/doc/Documentation2JSONSchema.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package net.jacobpeterson.alpaca.util.doc;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathFactory;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-/**
- * The {@link Documentation2JSONSchema} is used strictly to expedite the process of creating JSON schemas for POJO
- * generation in this library using Alpaca's website documentation.
- */
-public class Documentation2JSONSchema {
-
-    /**
-     * This is a standalone helper method that converts properties HTML copied from the Alpaca docs page into a json
-     * schema property list that includes the type and the description. It's kinda hacky, but it helps in the generation
-     * of JSON POJOs with Javadocs. Must be in the format of a list of:
-     * <pre>
-     * &lt;div class="spec-table"&gt;
-     *     &lt;div class="spec-row"&gt;
-     *     &lt;div class="spec-left"&gt;&lt;span class="spec-name"&gt;id&lt;/span&gt;&lt;/div&gt;
-     *     &lt;div class="spec-right"&gt;
-     *         &lt;div class="spec-type"&gt;string&lt;uuid&gt;&lt;/div&gt;
-     *         &lt;div class="spec-desc"&gt;Account ID.&lt;/div&gt;
-     *     &lt;/div&gt;
-     *     &lt;/div&gt;
-     *     ...
-     * &lt;/div&gt;
-     * </pre>
-     *
-     * @param alpacaDocXML the alpaca doc xml
-     */
-    public static void printAlpacaHTMLWebDocAsJSONSchema(String alpacaDocXML)
-            throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
-        String nameXPath = "//span[" + xPathClassName("spec-name") + "]";
-        String typeXPath = "//div[" + xPathClassName("spec-type") + "]";
-        String descriptionXPath = "//div[" + xPathClassName("spec-desc") + "]";
-
-        System.out.println(getSchemaFromDoc(alpacaDocXML, nameXPath, typeXPath, descriptionXPath));
-    }
-
-    /**
-     * Converts a generic HTML doc for Alpaca with JSON property fields for "name", "type", and "description" into
-     * schema JSON used for
-     * <a href="https://github.com/joelittlejohn/jsonschema2pojo/wiki/Reference">jsonSchema2POJO</a>.
-     *
-     * @param docHTML          the doc html
-     * @param nameXPath        the name x path
-     * @param typeXPath        the type x path
-     * @param descriptionXPath the description x path
-     *
-     * @return the schema from doc
-     *
-     * @throws ParserConfigurationException the parser configuration exception
-     * @throws SAXException                 the sax exception
-     * @throws IOException                  the io exception
-     * @throws XPathExpressionException     the x path expression exception
-     */
-    private static String getSchemaFromDoc(String docHTML, String nameXPath, String typeXPath, String descriptionXPath)
-            throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
-        String schemaJSON = "";
-        DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        Document document = builder.parse(new ByteArrayInputStream(docHTML.getBytes()));
-        XPathFactory xPathFactory = XPathFactory.newInstance();
-        XPath xPath = xPathFactory.newXPath();
-
-        XPathExpression propertyNamesXPath = xPath.compile(nameXPath);
-        XPathExpression propertyTypesXPath = xPath.compile(typeXPath);
-        XPathExpression propertyDescriptionsXPath = xPath.compile(descriptionXPath);
-
-        NodeList propertyNameNodes = (NodeList) propertyNamesXPath.evaluate(document, XPathConstants.NODESET);
-        NodeList propertyTypesNodes = (NodeList) propertyTypesXPath.evaluate(document, XPathConstants.NODESET);
-        NodeList propertyDescriptionsNodes = (NodeList) propertyDescriptionsXPath.evaluate(document,
-                XPathConstants.NODESET);
-
-        for (int propertyIndex = 0; propertyIndex < propertyNameNodes.getLength(); propertyIndex++) {
-            String propertyName = propertyNameNodes.item(propertyIndex).getTextContent();
-            String propertyType = propertyTypesNodes.item(propertyIndex).getTextContent();
-            String propertyDescription = propertyDescriptionsNodes.item(propertyIndex).getTextContent();
-
-            // Property description may be null
-            if (propertyDescription == null || propertyDescription.isEmpty()) {
-                propertyDescription = propertyName;
-            }
-
-            schemaJSON += "\"" + propertyName + "\": {\n";
-            schemaJSON += "\"existingJavaType\": \"" + parseDocTypeToSchemaJavaType(propertyType) + "\",\n";
-            schemaJSON += "\"title\": \"" + propertyDescription + "\"\n";
-            schemaJSON += "},\n";
-        }
-        return schemaJSON;
-    }
-
-    /**
-     * @see <a href="https://github.com/joelittlejohn/jsonschema2pojo/wiki/Reference#type">jsonSchema2Pojo Ref</a>
-     */
-    private static String parseDocTypeToSchemaJavaType(String docType) {
-        docType = docType.toLowerCase();
-
-        if (docType.contains("string") || docType.contains("timestamp")) {
-            return "java.lang.String";
-        } else if (docType.contains("boolean") || docType.contains("bool")) {
-            return "java.lang.Boolean";
-        } else if (docType.contains("int") || docType.contains("integer")) {
-            return "java.lang.Integer";
-        } else if (docType.contains("double") || docType.contains("float") ||
-                docType.contains("number")) {
-            return "java.lang.Double";
-        } else {
-            return "UNKNOWN DOC DATA TYPE";
-        }
-    }
-
-    /**
-     * @see <a href="https://devhints.io/xpath#class-check">https://devhints.io/xpath#class-check</a>
-     */
-    private static String xPathClassName(String className) {
-        return String.format("contains(concat(' ',normalize-space(@class),' '),' %s ')", className);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
deleted file mode 100644
index e797c294..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/format/FormatUtil.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package net.jacobpeterson.alpaca.util.format;
-
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.TemporalAccessor;
-import java.util.Locale;
-
-/**
- * {@link FormatUtil} is a utility class for various formatting.
- */
-public class FormatUtil {
-
-    /**
-     * The {@link ZoneId} for <code>America/New_York</code>.
-     */
-    public static final ZoneId NEW_YORK_ZONED_ID = ZoneId.of("America/New_York");
-
-    // Alpaca uses the following rounding mechanics with respect to buy orders: (1) rounded down to two decimal
-    // places if the last trade price is over $1.00; otherwise, rounded down to four decimal places, hence the '#' in
-    // the 3rd and 4th least significant decimal digits.
-    private static final NumberFormat CURRENCY_FORMATTER = new DecimalFormat("#0.00##",
-            DecimalFormatSymbols.getInstance(Locale.US));
-
-    /**
-     * Formats an arbitrary number to a currency format. e.g. $123.45
-     *
-     * @param numberToFormat the {@link Number} to format
-     *
-     * @return the formatted string
-     */
-    public static String toCurrencyFormat(Number numberToFormat) {
-        return CURRENCY_FORMATTER.format(numberToFormat);
-    }
-
-    /**
-     * Formats a {@link TemporalAccessor} using {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}.
-     * <br>
-     * Alpaca requires <code>RFC 3339</code> formatted timestamps to be provided which includes the timezone. <code>ISO
-     * 8601</code> is compatible with <code>RFC 3339</code> which is what {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}
-     * uses.
-     *
-     * @param zonedDateTime the {@link ZonedDateTime}
-     *
-     * @return the formatted string
-     */
-    public static String toRFC3339Format(ZonedDateTime zonedDateTime) {
-        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java b/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java
deleted file mode 100644
index 11944345..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/okhttp/JSONBodyBuilder.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package net.jacobpeterson.alpaca.util.okhttp;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import okhttp3.MediaType;
-import okhttp3.RequestBody;
-
-/**
- * {@link JSONBodyBuilder} is builder HTTP bodies.
- */
-public class JSONBodyBuilder {
-
-    /**
-     * The UTF-8 JSON {@link MediaType}.
-     */
-    public static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
-
-    private String bodyJSON;
-    private JsonObject bodyJsonObject;
-
-    /**
-     * Instantiates a new {@link JSONBodyBuilder}.
-     */
-    public JSONBodyBuilder() {
-        bodyJsonObject = new JsonObject();
-    }
-
-    /**
-     * Instantiates a new {@link JSONBodyBuilder}.
-     *
-     * @param bodyJSON a JSON body {@link String} that is used instead of the internal {@link #bodyJsonObject}
-     */
-    public JSONBodyBuilder(String bodyJSON) {
-        this.bodyJSON = bodyJSON;
-    }
-
-    /**
-     * Appends a {@link String} to {@link #bodyJsonObject}.
-     *
-     * @param key   the key
-     * @param value the value
-     */
-    public void appendJSONBodyProperty(String key, String value) {
-        bodyJsonObject.addProperty(key, value);
-    }
-
-    /**
-     * Appends a {@link JsonElement} property to {@link #bodyJsonObject}.
-     *
-     * @param key   the key
-     * @param value the {@link JsonElement} value
-     */
-    public void appendJSONBodyJSONProperty(String key, JsonElement value) {
-        bodyJsonObject.add(key, value);
-    }
-
-    /**
-     * Builds a {@link RequestBody}.
-     *
-     * @return a {@link RequestBody}
-     */
-    public RequestBody build() {
-        return RequestBody.create(bodyJSON != null ? bodyJSON : bodyJsonObject.toString(), JSON_MEDIA_TYPE);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java
index 50882e47..718d02a3 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java
@@ -11,9 +11,9 @@
 import java.util.Properties;
 
 /**
- * {@link PropertyUtil} is a util class for all things {@link Properties}.
+ * {@link PropertyUtil} is a utility class for all {@link Properties}-related handling.
  */
-public class PropertyUtil {
+public final class PropertyUtil {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);
 
@@ -60,15 +60,13 @@ public static String getProperty(String propertyFile, String defaultPropertyFile
      * @return the string
      */
     public static String getProperty(String propertyFile, String defaultPropertyFile, String key, String defaultValue) {
-        Properties properties;
-
+        final Properties properties;
         if (!CACHED_PROPERTIES.containsKey(propertyFile)) {
             properties = loadPropertyFile(propertyFile, defaultPropertyFile);
             CACHED_PROPERTIES.put(propertyFile, properties);
         } else {
             properties = CACHED_PROPERTIES.get(propertyFile);
         }
-
         return properties == null ? defaultValue : properties.getProperty(key, defaultValue);
     }
 
@@ -88,53 +86,53 @@ public synchronized static Properties loadPropertyFile(String propertyFile, Stri
 
         // Load the default property file if exists
         Properties defaultProperties = null;
-        InputStream defaultPropertyStream = classLoader.getResourceAsStream(defaultPropertyFile);
-
-        if (defaultPropertyStream != null) {
-            defaultProperties = new Properties();
-
-            // Load the properties
-            try {
-                defaultProperties.load(defaultPropertyStream);
-                LOGGER.debug("Loaded default properties file: {}", defaultPropertyFile);
-            } catch (IOException exception) {
-                LOGGER.error("Could not load default property file: {}\n{}", defaultPropertyFile, exception);
-            }
-
-            // Close the InputStream
-            try {
-                defaultPropertyStream.close();
-            } catch (IOException exception) {
-                LOGGER.error("Could not close default property file stream: {}\n{}", defaultPropertyFile, exception);
+        if (defaultPropertyFile != null) {
+            try (InputStream defaultPropertyStream = classLoader.getResourceAsStream(defaultPropertyFile)) {
+                if (defaultPropertyStream == null) {
+                    LOGGER.warn("No default property file \"{}\" exists on the classpath.", defaultPropertyFile);
+                } else {
+
+                    defaultProperties = new Properties();
+                    try {
+                        defaultProperties.load(defaultPropertyStream);
+                        LOGGER.debug("Loaded default properties file: {}", defaultPropertyFile);
+                    } catch (IOException exception) {
+                        LOGGER.error("Could not load default property file: {}", defaultPropertyFile, exception);
+                    }
+                }
+            } catch (Exception exception) {
+                LOGGER.error("Could not read default property file stream: {}", defaultPropertyFile, exception);
             }
-
         } else {
-            LOGGER.warn("No default property file found for: {}", propertyFile);
+            LOGGER.warn("No default property file given for: {}", propertyFile);
         }
 
-        // Load the property file
+        // Load the property file if exists
         Properties properties = null;
-        InputStream propertyStream = classLoader.getResourceAsStream(propertyFile);
-
-        if (propertyStream != null) {
-            // Add default properties if they were found
-            properties = defaultProperties == null ? new Properties() : new Properties(defaultProperties);
-
-            // Load the properties
-            try {
-                properties.load(propertyStream);
-                LOGGER.info("Loaded properties file: {}", propertyFile);
-            } catch (IOException exception) {
-                LOGGER.error("Could not load property file: {}\n{}", propertyFile, exception);
+        if (propertyFile != null) {
+            try (InputStream propertyStream = classLoader.getResourceAsStream(propertyFile)) {
+                if (propertyStream == null) {
+                    LOGGER.warn("No property file \"{}\" exists on the classpath.", propertyFile);
+                } else {
+                    // Add default properties if they were found
+                    properties = defaultProperties == null ? new Properties() : new Properties(defaultProperties);
+
+                    // Load the properties
+                    try {
+                        properties.load(propertyStream);
+                        LOGGER.info("Loaded properties file: {}", propertyFile);
+                    } catch (IOException exception) {
+                        LOGGER.error("Could not load property file: {}", propertyFile, exception);
+                    }
+                }
+            } catch (Exception exception) {
+                LOGGER.error("Could not read property file stream: {}", propertyFile, exception);
             }
+        } else if (defaultProperties == null) {
+            throw new IllegalStateException("No property files were found!");
+        }
 
-            // Close the InputStream
-            try {
-                propertyStream.close();
-            } catch (IOException exception) {
-                LOGGER.error("Could not close property file stream: {}\n{}", propertyFile, exception);
-            }
-        } else {
+        if (properties == null) {
             LOGGER.debug("Could not find property file: {}", propertyFile);
 
             if (defaultProperties != null) {
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index 18387bcd..59208ccc 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -1,6 +1,5 @@
 package net.jacobpeterson.alpaca.websocket;
 
-import net.jacobpeterson.alpaca.util.okhttp.WebsocketStateListener;
 import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
@@ -216,7 +215,7 @@ private void handleReconnectionAttempt() {
     }
 
     /**
-     * Cleans up this instances state variables.
+     * Cleans up this instance's state variables.
      */
     protected void cleanupState() {
         websocket = null;
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/okhttp/WebsocketStateListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java
similarity index 94%
rename from src/main/java/net/jacobpeterson/alpaca/util/okhttp/WebsocketStateListener.java
rename to src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java
index 38f91d70..4f3713b6 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/okhttp/WebsocketStateListener.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.util.okhttp;
+package net.jacobpeterson.alpaca.websocket;
 
 import okhttp3.Response;
 import okhttp3.WebSocket;
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
index 92a15c0c..2fccb63e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
@@ -12,6 +12,7 @@
 import net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType;
 import net.jacobpeterson.alpaca.model.endpoint.streaming.listening.ListeningMessage;
 import net.jacobpeterson.alpaca.model.endpoint.streaming.trade.TradeUpdateMessage;
+import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
 import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
 import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
@@ -50,14 +51,19 @@ public class StreamingWebsocket extends AlpacaWebsocket<StreamingMessageType, St
     /**
      * Creates a {@link HttpUrl} for {@link StreamingWebsocket} with the given <code>alpacaSubdomain</code>.
      *
-     * @param alpacaSubdomain the Alpaca subdomain
+     * @param endpointAPIType the {@link EndpointAPIType}
      *
      * @return a {@link HttpUrl}
      */
-    private static HttpUrl createWebsocketURL(String alpacaSubdomain) {
+    @SuppressWarnings("UnnecessaryDefault")
+    private static HttpUrl createWebsocketURL(EndpointAPIType endpointAPIType) {
         return new HttpUrl.Builder()
                 .scheme("https") // HttpUrl.Builder doesn't recognize "wss" scheme, but "https" works fine
-                .host(alpacaSubdomain + ".alpaca.markets")
+                .host((switch (endpointAPIType) {
+                    case LIVE -> "api";
+                    case PAPER -> "paper-api";
+                    default -> throw new UnsupportedOperationException();
+                }) + ".alpaca.markets")
                 .addPathSegment("stream")
                 .build();
     }
@@ -68,14 +74,14 @@ private static HttpUrl createWebsocketURL(String alpacaSubdomain) {
      * Instantiates a new {@link StreamingWebsocket}.
      *
      * @param okHttpClient    the {@link OkHttpClient}
-     * @param alpacaSubdomain the Alpaca subdomain
+     * @param endpointAPIType the {@link EndpointAPIType}
      * @param keyID           the key ID
      * @param secretKey       the secret key
      * @param oAuthToken      the OAuth token
      */
-    public StreamingWebsocket(OkHttpClient okHttpClient, String alpacaSubdomain,
+    public StreamingWebsocket(OkHttpClient okHttpClient, EndpointAPIType endpointAPIType,
             String keyID, String secretKey, String oAuthToken) {
-        super(okHttpClient, createWebsocketURL(alpacaSubdomain), "Streaming", keyID, secretKey, oAuthToken);
+        super(okHttpClient, createWebsocketURL(endpointAPIType), "Streaming", keyID, secretKey, oAuthToken);
 
         listenedStreamMessageTypes = new HashSet<>();
     }
diff --git a/src/main/resources/alpaca.default.properties b/src/main/resources/alpaca.default.properties
index 7da46f1c..e3629268 100644
--- a/src/main/resources/alpaca.default.properties
+++ b/src/main/resources/alpaca.default.properties
@@ -1,10 +1,2 @@
-#Example:
-#key_id = <your Key ID>
-#secret_key = <your Secret Key>
-#endpoint_api_type = <must be either "paper" or "live">
-#data_api_type = <must be either "iex" or "sip">
-#user_agent = <some User Agent string>
-#Defaults:
-endpoint_api_type=paper
-data_api_type=iex
-user_agent=JavaRuntimeEnvironment/ OkHttp/4.9.1
+endpoint_api_type:paper
+data_api_type:iex
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
deleted file mode 100644
index 9e6d6136..00000000
--- a/src/test/java/net/jacobpeterson/alpaca/test/live/AlpacaAPITest.java
+++ /dev/null
@@ -1,289 +0,0 @@
-package net.jacobpeterson.alpaca.test.live;
-
-import net.jacobpeterson.alpaca.AlpacaAPI;
-import net.jacobpeterson.alpaca.model.endpoint.account.Account;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.AccountActivity;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.NonTradeActivity;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.TradeActivity;
-import net.jacobpeterson.alpaca.model.endpoint.accountactivities.enums.ActivityType;
-import net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.AccountConfiguration;
-import net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.DTBPCheck;
-import net.jacobpeterson.alpaca.model.endpoint.accountconfiguration.enums.TradeConfirmEmail;
-import net.jacobpeterson.alpaca.model.endpoint.clock.Clock;
-import net.jacobpeterson.alpaca.model.endpoint.common.enums.SortDirection;
-import net.jacobpeterson.alpaca.model.endpoint.orders.Order;
-import net.jacobpeterson.alpaca.model.endpoint.orders.enums.CurrentOrderStatus;
-import net.jacobpeterson.alpaca.rest.AlpacaClientException;
-import net.jacobpeterson.alpaca.rest.endpoint.account.AccountEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.accountconfiguration.AccountConfigurationEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.clock.ClockEndpoint;
-import net.jacobpeterson.alpaca.rest.endpoint.orders.OrdersEndpoint;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.MethodOrderer;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.time.ZonedDateTime;
-import java.util.Collection;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.fail;
-
-/**
- * {@link AlpacaAPITest} tests live endpoints using Alpaca Paper credentials given in the
- * <code>alpaca.properties</code> file on the classpath.
- */
-@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-public class AlpacaAPITest {
-
-    static {
-        // Log trace-level
-        System.setProperty("org.slf4j.simpleLogger.log.net.jacobpeterson", "trace");
-    }
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPITest.class);
-    private static final int RATE_LIMIT_MILLIS = 200; // Wait 200ms between every test to prevent rate-limiting
-
-    private static AlpacaAPI alpacaAPI;
-    private static boolean marketOpen;
-    private static AccountConfiguration accountConfiguration;
-
-    /**
-     * Executed before all tests in this class.
-     */
-    @BeforeAll
-    public static void beforeAll() {
-        alpacaAPI = new AlpacaAPI();
-        marketOpen = false;
-    }
-
-    /**
-     * Executed before each test.
-     */
-    @BeforeEach
-    public void beforeEach() {
-    }
-
-    /**
-     * Executed after each test. Note that this will {@link Thread#sleep(long)} for {@link #RATE_LIMIT_MILLIS} to
-     * protect against rate limiting.
-     */
-    @AfterEach
-    public void afterEach() {
-        try {
-            Thread.sleep(RATE_LIMIT_MILLIS);
-        } catch (InterruptedException exception) {
-            fail("Interrupted when tearing down!");
-        }
-    }
-
-    /**
-     * Tests {@link ClockEndpoint#get()}.
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    @Test
-    @org.junit.jupiter.api.Order(1)
-    public void testClockEndpointGet() throws AlpacaClientException {
-        Clock clock = alpacaAPI.clock().get();
-        assertNotNull(clock);
-
-        LOGGER.debug("{}", clock);
-
-        assertNotNull(clock.getTimestamp());
-        assertNotNull(clock.getIsOpen());
-        assertNotNull(clock.getNextOpen());
-        assertNotNull(clock.getNextClose());
-
-        marketOpen = clock.getIsOpen();
-        if (marketOpen) {
-            LOGGER.info("Market is currently open! All live endpoints will be tested.");
-        } else {
-            LOGGER.info("Market is currently closed! Only some live endpoints will be tested.");
-        }
-    }
-
-    /**
-     * Tests {@link AccountEndpoint#get()}.
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     * @throws NumberFormatException thrown for {@link NumberFormatException}s
-     */
-    @Test
-    @SuppressWarnings("ResultOfMethodCallIgnored")
-    public void testAccountEndpointGet() throws AlpacaClientException, NumberFormatException {
-        Account account = alpacaAPI.account().get();
-        assertNotNull(account);
-
-        LOGGER.debug("{}", account);
-
-        // Assert basic data integrity and not null
-        Double.parseDouble(account.getCash());
-        Double.parseDouble(account.getPortfolioValue());
-        Double.parseDouble(account.getLongMarketValue());
-        Double.parseDouble(account.getShortMarketValue());
-        Double.parseDouble(account.getEquity());
-        Double.parseDouble(account.getLastEquity());
-        Double.parseDouble(account.getBuyingPower());
-        Double.parseDouble(account.getInitialMargin());
-        Double.parseDouble(account.getMaintenanceMargin());
-        Double.parseDouble(account.getLastMaintenanceMargin());
-        Double.parseDouble(account.getDaytradingBuyingPower());
-        Double.parseDouble(account.getRegtBuyingPower());
-
-        // Assert other data exists
-        assertNotNull(account.getId());
-        assertNotNull(account.getAccountNumber());
-        assertNotNull(account.getStatus());
-        assertNotNull(account.getCurrency());
-        assertNotNull(account.getPatternDayTrader());
-        assertNotNull(account.getTradeSuspendedByUser());
-        assertNotNull(account.getTradingBlocked());
-        assertNotNull(account.getTransfersBlocked());
-        assertNotNull(account.getAccountBlocked());
-        assertNotNull(account.getCreatedAt());
-        assertNotNull(account.getShortingEnabled());
-        assertNotNull(account.getMultiplier());
-        assertNotNull(account.getSma());
-        assertNotNull(account.getDaytradeCount());
-    }
-
-    /**
-     * Tests
-     * @{@link AccountActivitiesEndpoint#get(ZonedDateTime, ZonedDateTime, ZonedDateTime, SortDirection, Integer,
-     *String, ActivityType...)} one {@link AccountActivity} exists until now.
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    @Test
-    public void testAccountActivitiesEndpointGetOneActivityExistsUntilNow() throws AlpacaClientException {
-        List<AccountActivity> accountActivities = alpacaAPI.accountActivities().get(
-                null,
-                ZonedDateTime.now(),
-                null,
-                SortDirection.ASCENDING,
-                1,
-                null,
-                (ActivityType[]) null);
-        assertNotNull(accountActivities);
-        assertFalse(accountActivities.isEmpty());
-
-        accountActivities.forEach(accountActivity -> LOGGER.debug(accountActivity.toString()));
-
-        AccountActivity accountActivity = accountActivities.get(0);
-        if (accountActivity instanceof TradeActivity) {
-            TradeActivity tradeActivity = (TradeActivity) accountActivity;
-            assertNotNull(tradeActivity.getActivityType());
-            assertNotNull(tradeActivity.getId());
-            assertNotNull(tradeActivity.getCumulativeQuantity());
-            assertNotNull(tradeActivity.getRemainingQuantity());
-            assertNotNull(tradeActivity.getPrice());
-            assertNotNull(tradeActivity.getQuantity());
-            assertNotNull(tradeActivity.getSide());
-            assertNotNull(tradeActivity.getSymbol());
-            assertNotNull(tradeActivity.getTransactionTime());
-            assertNotNull(tradeActivity.getOrderId());
-            assertNotNull(tradeActivity.getType());
-        } else if (accountActivity instanceof NonTradeActivity) {
-            NonTradeActivity nonTradeActivity = (NonTradeActivity) accountActivity;
-            assertNotNull(nonTradeActivity.getActivityType());
-            assertNotNull(nonTradeActivity.getId());
-            assertNotNull(nonTradeActivity.getDate());
-            assertNotNull(nonTradeActivity.getNetAmount());
-            assertNotNull(nonTradeActivity.getSymbol());
-            assertNotNull(nonTradeActivity.getQuantity());
-            assertNotNull(nonTradeActivity.getPerShareAmount());
-            assertNotNull(nonTradeActivity.getDescription());
-        }
-    }
-
-    /**
-     * Test {@link AccountConfigurationEndpoint#get()}.
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    @Test
-    @org.junit.jupiter.api.Order(1)
-    public void testAccountConfigurationEndpointGet() throws AlpacaClientException {
-        AccountConfiguration accountConfiguration = alpacaAPI.accountConfiguration().get();
-        assertNotNull(accountConfiguration);
-
-        LOGGER.debug("{}", accountConfiguration);
-
-        assertNotNull(accountConfiguration.getDtbpCheck());
-        assertNotNull(accountConfiguration.getTradeConfirmEmail());
-        assertNotNull(accountConfiguration.getSuspendTrade());
-        assertNotNull(accountConfiguration.getNoShorting());
-
-        AlpacaAPITest.accountConfiguration = accountConfiguration;
-    }
-
-    /**
-     * Test {@link AccountConfigurationEndpoint#set(AccountConfiguration)}.
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    @Test
-    @org.junit.jupiter.api.Order(2)
-    public void testAccountConfigurationEndpointSet() throws AlpacaClientException {
-        if (accountConfiguration == null) {
-            AccountConfiguration newAccountConfiguration = new AccountConfiguration(
-                    DTBPCheck.BOTH,
-                    TradeConfirmEmail.NONE,
-                    false,
-                    false);
-            LOGGER.info("Settings Account Configuration to: {}", newAccountConfiguration);
-            alpacaAPI.accountConfiguration().set(newAccountConfiguration);
-        } else {
-            alpacaAPI.accountConfiguration().set(accountConfiguration);
-        }
-    }
-
-    /**
-     * Test
-     * {@link OrdersEndpoint#get(CurrentOrderStatus, Integer, ZonedDateTime, ZonedDateTime, SortDirection, Boolean,
-     * Collection)} one {@link Order} exists until now.
-     *
-     * @throws AlpacaClientException thrown for {@link AlpacaClientException}s
-     */
-    @Test
-    public void testOrdersEndpointGetOneOrderExistsUntilNow() throws AlpacaClientException {
-        List<Order> orders = alpacaAPI.orders().get(
-                CurrentOrderStatus.ALL,
-                1,
-                null,
-                ZonedDateTime.now(),
-                SortDirection.ASCENDING,
-                true,
-                null);
-
-        assertNotNull(orders);
-        assertFalse(orders.isEmpty());
-
-        orders.forEach(order -> LOGGER.debug("{}", order));
-
-        // Assert required fields are present
-        Order order = orders.get(0);
-        assertNotNull(order.getId());
-        assertNotNull(order.getClientOrderId());
-        assertNotNull(order.getCreatedAt());
-        assertNotNull(order.getUpdatedAt());
-        assertNotNull(order.getSubmittedAt());
-        assertNotNull(order.getAssetId());
-        assertNotNull(order.getSymbol());
-        assertNotNull(order.getAssetClass());
-        assertNotNull(order.getQuantity());
-        assertNotNull(order.getFilledQuantity());
-        assertNotNull(order.getType());
-        assertNotNull(order.getSide());
-        assertNotNull(order.getTimeInForce());
-        assertNotNull(order.getStatus());
-        assertNotNull(order.getExtendedHours());
-    }
-}
diff --git a/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java b/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
deleted file mode 100644
index d5f4f320..00000000
--- a/src/test/java/net/jacobpeterson/alpaca/test/mock/AlpacaAPITest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package net.jacobpeterson.alpaca.test.mock;
-
-import net.jacobpeterson.alpaca.AlpacaAPI;
-import net.jacobpeterson.alpaca.model.properties.DataAPIType;
-import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
-import okhttp3.OkHttpClient;
-import org.junit.jupiter.api.Test;
-
-/**
- * {@link AlpacaAPITest} tests {@link AlpacaAPI} using mocked objects with Mockito.
- */
-public class AlpacaAPITest {
-
-    static {
-        System.setProperty("org.slf4j.simpleLogger.log.net.jacobpeterson.alpaca", "trace");
-    }
-
-    /**
-     * Tests {@link AlpacaAPI#AlpacaAPI()}.
-     */
-    @Test
-    public void testAlpacaAPIConstructor_Default() {
-        new AlpacaAPI();
-    }
-
-    /**
-     * Tests {@link AlpacaAPI#AlpacaAPI(String, String)}.
-     */
-    @Test
-    public void testAlpacaAPIConstructor_keyID_secret() {
-        String keyID = "ABCDEFGHIJKLM";
-        String secret = "NOPQURSTUVWXYZ";
-
-        new AlpacaAPI(keyID, secret);
-    }
-
-    /**
-     * Tests {@link AlpacaAPI#AlpacaAPI(String, String, EndpointAPIType, DataAPIType)}.
-     */
-    @Test
-    public void testAlpacaAPIConstructor_keyID_secret_endpointAPIType_dataAPIType() {
-        String keyID = "ABCDEFGHIJKLM";
-        String secret = "NOPQURSTUVWXYZ";
-
-        new AlpacaAPI(keyID, secret, EndpointAPIType.PAPER, DataAPIType.IEX);
-        new AlpacaAPI(keyID, secret, EndpointAPIType.LIVE, DataAPIType.SIP);
-    }
-
-    /**
-     * Tests {@link AlpacaAPI#AlpacaAPI(String)}.
-     */
-    @Test
-    public void testAlpacaAPIConstructor_oAuthToken() {
-        String oAuthToken = "ABCDEFGHIJKLMNOPQURSTUVWXYZ";
-
-        new AlpacaAPI(oAuthToken);
-    }
-
-    /**
-     * Tests {@link AlpacaAPI#AlpacaAPI(OkHttpClient, String, String, String, EndpointAPIType, DataAPIType)}.
-     */
-    @Test
-    public void testAlpacaAPIConstructor_okHttpClient_keyID_secret_oAuthToken_endpointAPIType_dataAPIType() {
-        OkHttpClient okHttpClient = new OkHttpClient();
-        String keyID = "ABCDEFGHIJKLM";
-        String secret = "NOPQURSTUVWXYZ";
-        String oAuthToken = "ABCDEFGHIJKLMNOPQURSTUVWXYZ";
-
-        new AlpacaAPI(okHttpClient, null, null, oAuthToken, EndpointAPIType.PAPER, DataAPIType.IEX);
-        new AlpacaAPI(okHttpClient, keyID, secret, null, EndpointAPIType.LIVE, DataAPIType.SIP);
-        new AlpacaAPI(okHttpClient, null, null, oAuthToken, EndpointAPIType.PAPER, DataAPIType.IEX);
-        new AlpacaAPI(okHttpClient, keyID, secret, null, EndpointAPIType.LIVE, DataAPIType.SIP);
-    }
-}
diff --git a/src/test/resources/alpaca.default.properties b/src/test/resources/alpaca.default.properties
deleted file mode 100644
index 58a3702f..00000000
--- a/src/test/resources/alpaca.default.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-key_id=ABCDEFGHIJKLM
-secret_key=NOPQURSTUVWXYZ
-endpoint_api_type=paper
-data_api_type=iex
-user_agent=JavaRuntimeEnvironment/ OkHttp/4.9.1

From 0383b926bd61028ad396e510ea319d47fa2fc91b Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sat, 2 Mar 2024 13:51:46 -0800
Subject: [PATCH 56/85] Fix JSONSchema2POJOAdaptedPlugin

---
 .../GeneratePOJOsTask.groovy                  | 113 ++++++++----------
 .../JSONSchema2POJOAdaptedPlugin.groovy       |   4 +-
 2 files changed, 55 insertions(+), 62 deletions(-)

diff --git a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy
index 88ac5a4a..650a7c7b 100644
--- a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy
+++ b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/GeneratePOJOsTask.groovy
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument
 /**
  * {@link GeneratePOJOsTask} is a Gradle task that invokes <code>jsonschema2pojo</code> with special configurations
  * and generation logic.
- * TODO publish this plugin somewhere so the code doesn't have to be copied between 'cli' and 'system' Gradle projects.
  * @see <a href="https://github.com/joelittlejohn/jsonschema2pojo/blob/master/jsonschema2pojo-gradle-plugin/src/main/groovy/org/jsonschema2pojo/gradle/GenerateJsonSchemaJavaTask.groovy">GenerateJsonSchemaJavaTask</a>
  */
 class GeneratePOJOsTask extends DefaultTask {
@@ -39,69 +38,63 @@ class GeneratePOJOsTask extends DefaultTask {
         group = "Build"
         description = "Generates Java POJOs from JSON schemas."
 
-        project.afterEvaluate {
-            // Validate plugin configuration
-            configuration = project.extensions.getByName(EXTENSION_KEY) as GenerationConfig
-            checkArgument(!configuration.source.hasNext(), "'source' should not be set for '${name}'.")
-            checkArgument(configuration.targetDirectory == null, "'targetDirectory' should not be set for '${name}'.")
-
-            // Set custom defaults on plugin configuration
-            configuration.targetDirectory = project.file(
-                    "${project.layout.buildDirectory.getAsFile().get().getPath()}/generated-sources/jsonschema2pojo")
-            configuration.targetPackage = configuration.targetPackage == "" ?
-                    "${project.group}.model" : configuration.targetPackage
-            configuration.propertyWordDelimiters = ["-", "_"] as char[]
-            configuration.annotationStyle = "gson"
-            configuration.sourceType = "jsonschema"
-            configuration.customDateTimePattern = "yyyy-MM-ddTHH:mm:ssZ"
-            configuration.includeConstructors = true
-            configuration.serializable = true
-            configuration.includeGetters = true
-            configuration.includeSetters = true
-            configuration.includeCopyConstructor = true
-            configuration.generateBuilders = true
-
-            // Get project main java source set
-            final def projectMainJavaSourceSet =
-                    project.extensions.getByType(SourceSetContainer).named("main").get().java
-
-            // Set task dependents and dependencies
-            project.tasks.named("compileJava").get().dependsOn(this)
-            dependsOn(project.tasks.named("processResources").get())
-
-            // Walk through source files to create a list of JSON schema files to process
-            jsonSchemaFileConfigs = new ArrayList<>()
-            projectMainJavaSourceSet.srcDirs.each { srcDir ->
-                if (!srcDir.exists()) {
-                    return
-                }
-                final def sourceDirPath = srcDir.getAbsolutePath()
-                srcDir.eachFileRecurse { sourceFile ->
-                    final def sourceFilePath = sourceFile.getAbsolutePath()
-                    if (sourceFilePath.endsWith(".json")) {
-                        def targetPackage = sourceFile.getParentFile().getAbsolutePath()
-                                .substring(sourceDirPath.length())
-                                .replace(File.separator, ".").replace("-", "").replace("_", "")
-                                .toLowerCase()
-                        targetPackage = targetPackage.replace("${project.group}", "")
-                        targetPackage = targetPackage.startsWith(".") ? targetPackage.substring(1) : targetPackage
-                        targetPackage = configuration.targetPackage +
-                                (targetPackage.isEmpty() ? "" : targetPackage)
-                        jsonSchemaFileConfigs.add(new JSONSchemaFileConfig(sourceFile, targetPackage))
-
-                        inputs.file(sourceFile)
-                        outputs.file(project.file(configuration.targetDirectory.getAbsolutePath() + File.separator +
-                                targetPackage.replace(".", File.separator) + File.separator +
-                                sourceFile.getName().replace(".json", ".java")))
-                    }
+        // Validate plugin configuration
+        configuration = project.extensions.getByName(EXTENSION_KEY) as GenerationConfig
+        checkArgument(!configuration.source.hasNext(), "'source' should not be set for '${name}'.")
+        checkArgument(configuration.targetDirectory == null, "'targetDirectory' should not be set for '${name}'.")
+
+        // Set custom defaults on plugin configuration
+        configuration.targetDirectory = project.file(
+                "${project.layout.buildDirectory.getAsFile().get().getPath()}/generated-sources/jsonschema2pojo")
+        configuration.targetPackage = configuration.targetPackage == "" ?
+                "${project.group}.model" : configuration.targetPackage
+        configuration.propertyWordDelimiters = ["-", "_"] as char[]
+        configuration.annotationStyle = "gson"
+        configuration.sourceType = "jsonschema"
+        configuration.customDateTimePattern = "yyyy-MM-ddTHH:mm:ssZ"
+        configuration.includeConstructors = true
+        configuration.serializable = true
+        configuration.includeGetters = true
+        configuration.includeSetters = true
+        configuration.includeCopyConstructor = true
+        configuration.generateBuilders = true
+
+        // Get project main java source set
+        final def projectMainJavaSourceSet =
+                project.extensions.getByType(SourceSetContainer).named("main").get().java
+
+        // Walk through source files to create a list of JSON schema files to process
+        jsonSchemaFileConfigs = new ArrayList<>()
+        projectMainJavaSourceSet.srcDirs.each { srcDir ->
+            if (!srcDir.exists()) {
+                return
+            }
+            final def sourceDirPath = srcDir.getAbsolutePath()
+            srcDir.eachFileRecurse { sourceFile ->
+                final def sourceFilePath = sourceFile.getAbsolutePath()
+                if (sourceFilePath.endsWith(".json")) {
+                    def targetPackage = sourceFile.getParentFile().getAbsolutePath()
+                            .substring(sourceDirPath.length())
+                            .replace(File.separator, ".").replace("-", "").replace("_", "")
+                            .toLowerCase()
+                    targetPackage = targetPackage.replace("${project.group}", "")
+                    targetPackage = targetPackage.startsWith(".") ? targetPackage.substring(1) : targetPackage
+                    targetPackage = configuration.targetPackage +
+                            (targetPackage.isEmpty() ? "" : targetPackage)
+                    jsonSchemaFileConfigs.add(new JSONSchemaFileConfig(sourceFile, targetPackage))
+
+                    inputs.file(sourceFile)
+                    outputs.file(project.file(configuration.targetDirectory.getAbsolutePath() + File.separator +
+                            targetPackage.replace(".", File.separator) + File.separator +
+                            sourceFile.getName().replace(".json", ".java")))
                 }
             }
+        }
 
-            outputs.dir(configuration.targetDirectory)
-            outputs.cacheIf { true }
+        outputs.dir(configuration.targetDirectory)
+        outputs.cacheIf { true }
 
-            projectMainJavaSourceSet.srcDirs(configuration.targetDirectory)
-        }
+        projectMainJavaSourceSet.srcDirs(configuration.targetDirectory)
     }
 
     @TaskAction
diff --git a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy
index 9d53a826..963e8276 100644
--- a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy
+++ b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchema2POJOAdaptedPlugin.groovy
@@ -17,8 +17,8 @@ class JSONSchema2POJOAdaptedPlugin implements Plugin<Project> {
     void apply(Project project) {
         project.extensions.create(EXTENSION_KEY, JsonSchemaExtension)
         if (project.plugins.hasPlugin("java")) {
-            final def generatePOJOsTask = project.tasks.register("generatePOJOs", GeneratePOJOsTask)
-            generatePOJOsTask.get().enabled = true
+            project.tasks.register("generatePOJOs", GeneratePOJOsTask)
+            project.tasks.compileJava.dependsOn "generatePOJOs"
         } else {
             throw new GradleException("'java' Gradle plugin required.")
         }

From 73a9a0bfcaed285fbe1bc6538cb60ab1d5236569 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sat, 2 Mar 2024 13:52:37 -0800
Subject: [PATCH 57/85] WIP update `build.gradle` to modify OpenAPI clients

---
 build.gradle | 49 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 10 deletions(-)

diff --git a/build.gradle b/build.gradle
index 844d41b9..bb8bd333 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,7 @@
 import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
 
+import java.nio.file.Paths
+
 plugins {
     id "java"
     id "java-library"
@@ -62,14 +64,16 @@ java {
     withJavadocJar()
     withSourcesJar()
 }
+javadocJar.dependsOn generatePOJOs
+sourcesJar.dependsOn generatePOJOs
 
 [compileJava, compileTestJava]*.options*.encoding = "UTF-8"
 
 javadoc {
     options.addStringOption("source", "17")
-    options.addStringOption("Xdoclint:none", "-quiet") // Suppress Javadoc linting warnings
     options.addStringOption("charset", "UTF-8")
-    options.addStringOption("link", "https://docs.oracle.com/javase/8/docs/api/")
+    options.addStringOption("Xdoclint:none", "-quiet") // Suppress Javadoc linting warnings
+    options.addStringOption("link", "https://docs.oracle.com/en/java/javase/17/docs/api/")
 }
 
 //
@@ -86,12 +90,15 @@ final File specDownloadPath = new File(project.layout.buildDirectory.get().getAs
 final File generatedClientLibrariesPath = new File(project.layout.buildDirectory.get().getAsFile(),
         "/generated/openapi/")
 
-final def generateAllOpenAPIClientsTasks = tasks.register("generateAllOpenAPIClients") {
+final def generateOpenAPIClientsTasks = tasks.register("generateOpenAPIClients") {
     doLast {
-        fixOpenAPIGeneratedClientIssues(generatedClientLibrariesPath)
+        fixOpenAPIGeneratedClientIssues(specIDsOfFileNames, generatedClientLibrariesPath)
     }
 }
-compileJava.dependsOn generateAllOpenAPIClientsTasks
+compileJava.dependsOn generateOpenAPIClientsTasks
+jar.dependsOn generateOpenAPIClientsTasks
+javadocJar.dependsOn generateOpenAPIClientsTasks
+sourcesJar.dependsOn generateOpenAPIClientsTasks
 
 def downloadOpenAPISpecFilesTask = tasks.register("downloadOpenAPISpecFiles") {
     configure {
@@ -165,7 +172,9 @@ for (def specFileName : specIDsOfFileNames.keySet()) {
                 "apiPackage", destinationPackage + ".api",
                 "modelPackage", destinationPackage + ".model",
                 "library", "okhttp-gson",
-                "documentationProvider", "none"))
+                "documentationProvider", "none",
+                "useBeanValidation", "false",
+                "performBeanValidation", "false"))
         cleanupOutput.set(true)
         validateSpec.set(false)
         skipValidateSpec.set(true)
@@ -174,22 +183,41 @@ for (def specFileName : specIDsOfFileNames.keySet()) {
         generateApiTests.set(false)
         generateApiDocumentation.set(false)
     }
-    generateAllOpenAPIClientsTasks.configure {
+    generateOpenAPIClientsTasks.configure {
         dependsOn generateTask
     }
 }
 
-static void fixOpenAPIGeneratedClientIssues(File generatedClientLibrariesPath) {
+static void fixOpenAPIGeneratedClientIssues(Map<String, String> specIDsOfFileNames, File generatedClientLibrariesPath) {
+    // TODO remove these manual fixes once OpenAPI fixes these issues
+
+    final def sourceFilesPath = "src/main/java/net/jacobpeterson/alpaca/openapi"
+
     // Fix broker spec generated client issues with discriminators
     // on some enums: https://github.com/OpenAPITools/openapi-generator/issues/806
     final def brokerTransferDataJavaFile = new File(generatedClientLibrariesPath,
-            "broker/src/main/java/net/jacobpeterson/alpaca/openapi/broker/model/TransferData.java")
+            "broker/${sourceFilesPath}/broker/model/TransferData.java")
     brokerTransferDataJavaFile.text = brokerTransferDataJavaFile.text
             .replace("this.transferType = this.getClass().getSimpleName();", "")
     final def brokerTransferResourceJavaFile = new File(generatedClientLibrariesPath,
-            "broker/src/main/java/net/jacobpeterson/alpaca/openapi/broker/model/TransferResource.java")
+            "broker/${sourceFilesPath}/broker/model/TransferResource.java")
     brokerTransferResourceJavaFile.text = brokerTransferResourceJavaFile.text
             .replace("this.type = this.getClass().getSimpleName();", "")
+
+    // Remove the JSON validation that the generated client models always run (there isn't an option to
+    // disable it for some reason)
+    specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
+        for (File modelFile : Paths.get(generatedClientLibrariesPath.getPath(),
+                it, sourceFilesPath, it.replace("-", ""), "model").toFile().listFiles()) {
+            modelFile.text = modelFile.text
+                    .replaceAll("/\\*\\*\\n {2}\\* Validates the JSON Element and throws an exception if issues found" +
+                            "[\\s\\S]*?\\*/", "")
+                    .replace("public static void validateJsonElement(JsonElement jsonElement) throws IOException {",
+                            "public static boolean validate = false;\n public static void " +
+                                    "validateJsonElement(JsonElement jsonElement) throws IOException {\n" +
+                                    "  if (!validate) return;")
+        }
+    }
 }
 
 //
@@ -206,6 +234,7 @@ publishing {
             groupId = projectGroup
             artifactId = projectArtifactID
             version = projectVersion
+            from(components["java"])
 
             pom {
                 name = projectArtifactID

From a4a9e650cced0bc44c61f42bd1718f16c79f5918 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sat, 2 Mar 2024 21:27:45 -0800
Subject: [PATCH 58/85] WIP implement wrapper classes for OpenAPI generate
 clients

---
 build.gradle                                  |  17 +-
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 291 ++++++++-----
 .../jacobpeterson/alpaca/AlpacaBrokerAPI.java | 304 ++++++++++++++
 .../alpaca/AlpacaMarketDataAPI.java           | 152 +++++++
 .../jacobpeterson/alpaca/AlpacaTraderAPI.java | 212 ++++++++++
 .../alpaca/properties/AlpacaProperties.java   |  29 --
 .../alpaca/properties/data_api_type.json      |   9 -
 .../alpaca/properties/endpoint_api_type.json  |   9 -
 .../alpaca/util/apikey/APIKeyUtil.java        |  23 ++
 .../apitype/broker_api_endpoint_type.json     |   8 +
 .../apitype/market_data_api_source_type.json  |   8 +
 .../apitype/trader_api_endpoint_type.json     |   8 +
 .../alpaca/util/gson/GsonUtil.java            |  28 --
 .../util/gson/adapters/LocalDateAdapter.java  |  31 --
 .../util/gson/adapters/LocalTimeAdapter.java  |  31 --
 .../gson/adapters/ZonedDateTimeAdapter.java   |  31 --
 .../alpaca/util/properties/PropertyUtil.java  | 146 -------
 .../alpaca/websocket/AlpacaWebsocket.java     | 292 -------------
 .../websocket/AlpacaWebsocketInterface.java   |  81 ----
 .../AlpacaWebsocketMessageListener.java       |  20 -
 .../websocket/WebsocketStateListener.java     |  32 --
 .../marketdata/MarketDataListener.java        |  11 -
 .../marketdata/MarketDataWebsocket.java       | 387 ------------------
 .../MarketDataWebsocketInterface.java         |  86 ----
 .../crypto/CryptoMarketDataWebsocket.java     |  27 --
 .../news/NewsMarketDataWebsocket.java         |  15 -
 .../stock/StockMarketDataWebsocket.java       |  30 --
 .../streaming/StreamingListener.java          |  11 -
 .../streaming/StreamingWebsocket.java         | 257 ------------
 .../StreamingWebsocketInterface.java          |  26 --
 src/main/resources/alpaca.default.properties  |   2 -
 31 files changed, 914 insertions(+), 1700 deletions(-)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/properties/data_api_type.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/properties/endpoint_api_type.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/apitype/broker_api_endpoint_type.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/apitype/trader_api_endpoint_type.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingListener.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
 delete mode 100644 src/main/resources/alpaca.default.properties

diff --git a/build.gradle b/build.gradle
index bb8bd333..b13ea565 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,6 +43,9 @@ dependencies {
     // GSON
     implementation group: "com.google.code.gson", name: "gson", version: "2.10.1"
 
+    // Jetbrains Annotations
+    implementation group: "org.jetbrains", name: "annotations", version: "24.1.0"
+
     // OkHttp
     implementation group: "com.squareup.okhttp3", name: "okhttp", version: "5.0.0-alpha.12"
     implementation group: "com.squareup.okhttp3", name: "logging-interceptor", version: "5.0.0-alpha.12"
@@ -213,11 +216,21 @@ static void fixOpenAPIGeneratedClientIssues(Map<String, String> specIDsOfFileNam
                     .replaceAll("/\\*\\*\\n {2}\\* Validates the JSON Element and throws an exception if issues found" +
                             "[\\s\\S]*?\\*/", "")
                     .replace("public static void validateJsonElement(JsonElement jsonElement) throws IOException {",
-                            "public static boolean validate = false;\n public static void " +
+                            "public static boolean validate = false;\n  public static void " +
                                     "validateJsonElement(JsonElement jsonElement) throws IOException {\n" +
-                                    "  if (!validate) return;")
+                                    "    if (!validate) return;")
         }
     }
+
+    // Remove the necessary authentication header logic from the generate client's ApiClient
+    specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
+        final def apiClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
+                it.replace("-", ""), "ApiClient.java").toFile()
+        apiClientFile.text = apiClientFile.text
+                .replaceAll("// Setup authentications \\(key: authentication name, value: authentication\\)" +
+                        "[\\s\\S]*?authentications\\);", "")
+                .replaceAll("for \\(String authName : authNames\\)[\\s\\S]*?uri\\);\\n. {7}}", "")
+    }
 }
 
 //
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index bcaaec25..577324ec 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -1,25 +1,24 @@
 package net.jacobpeterson.alpaca;
 
-import net.jacobpeterson.alpaca.model.properties.DataAPIType;
-import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
-import net.jacobpeterson.alpaca.properties.AlpacaProperties;
-import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
-import net.jacobpeterson.alpaca.websocket.marketdata.crypto.CryptoMarketDataWebsocket;
-import net.jacobpeterson.alpaca.websocket.marketdata.news.NewsMarketDataWebsocket;
-import net.jacobpeterson.alpaca.websocket.marketdata.stock.StockMarketDataWebsocket;
-import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocket;
-import net.jacobpeterson.alpaca.websocket.streaming.StreamingWebsocketInterface;
+import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
+import net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPISourceType;
+import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
 import okhttp3.OkHttpClient;
 import okhttp3.logging.HttpLoggingInterceptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType.SANDBOX;
+import static net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPISourceType.IEX;
+import static net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType.PAPER;
 
 /**
- * {@link AlpacaAPI} is the main class used to interface with the various Alpaca API endpoints. You will generally only
- * need one instance of this class in your application.
+ * {@link AlpacaAPI} is the main class used to interface with the various Alpaca API endpoints. If you are using the
+ * Trading or Market Data APIs for a single Alpaca account or if you are using the Broker API, you will generally only
+ * need one instance of this class. However, if you are using the Trading API with OAuth to act on behalf of an Alpaca
+ * account, this class is optimized so that it can be instantiated quickly, especially when an existing
+ * {@link OkHttpClient} is given in the constructor. Additionally, all API endpoint instances are instantiated lazily.
+ * This class is thread-safe.
  *
  * @see <a href="https://docs.alpaca.markets">Alpaca Docs</a>
  */
@@ -27,134 +26,186 @@ public class AlpacaAPI {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaAPI.class);
 
+    private final String traderKeyID;
+    private final String traderSecretKey;
+    private final String traderOAuthToken;
+    private final TraderAPIEndpointType traderAPIEndpointType;
+    private final MarketDataAPISourceType marketDataAPISourceType;
+    private final String brokerAPIKey;
+    private final String brokerAPISecret;
+    private final BrokerAPIEndpointType brokerAPIEndpointType;
     private final OkHttpClient okHttpClient;
-    private final StreamingWebsocket streamingWebsocket;
-    private final CryptoMarketDataWebsocket cryptoMarketDataWebsocket;
-    private final StockMarketDataWebsocket stockMarketDataWebsocket;
-    private final NewsMarketDataWebsocket newsMarketDataWebsocket;
+
+    private AlpacaTraderAPI trader;
+    private AlpacaMarketDataAPI marketData;
+    private AlpacaBrokerAPI broker;
 
     /**
-     * Instantiates a new {@link AlpacaAPI} using properties specified in <code>alpaca.properties</code> file (or their
-     * associated defaults).
+     * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading or Market Data APIs for a
+     * single Alpaca account.
+     *
+     * @param traderKeyID             the Trader key ID
+     * @param traderSecretKey         the Trader secret key
+     * @param traderAPIEndpointType   the {@link TraderAPIEndpointType}
+     * @param marketDataAPISourceType the {@link MarketDataAPISourceType}
      */
-    public AlpacaAPI() {
-        this(AlpacaProperties.KEY_ID,
-                AlpacaProperties.SECRET_KEY,
-                AlpacaProperties.ENDPOINT_API_TYPE,
-                AlpacaProperties.DATA_API_TYPE);
+    public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointType traderAPIEndpointType,
+            MarketDataAPISourceType marketDataAPISourceType) {
+        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPISourceType, null, null, null,
+                null);
     }
 
     /**
-     * Instantiates a new {@link AlpacaAPI} using properties specified in the given {@link Builder}, otherwise from
-     * <code>alpaca.properties</code> file (or their associated defaults).
+     * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading or Market Data APIs for a
+     * single Alpaca account and a custom {@link OkHttpClient} instance.
+     *
+     * @param traderKeyID             the Trader key ID
+     * @param traderSecretKey         the Trader secret key
+     * @param traderAPIEndpointType   the {@link TraderAPIEndpointType}
+     * @param marketDataAPISourceType the {@link MarketDataAPISourceType}
+     * @param okHttpClient            an existing {@link OkHttpClient} or <code>null</code> to create a new default
+     *                                instance
      */
-    private AlpacaAPI(Builder builder) {
-        this(builder.keyID,
-                builder.secretKey,
-                builder.endpointAPIType,
-                builder.dataAPIType);
+    public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointType traderAPIEndpointType,
+            MarketDataAPISourceType marketDataAPISourceType, OkHttpClient okHttpClient) {
+        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPISourceType, null, null, null,
+                okHttpClient);
     }
 
     /**
-     * Instantiates a new {@link AlpacaAPI}.
+     * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading API with OAuth to act on
+     * behalf of an Alpaca account.
      *
-     * @param keyID     the key ID
-     * @param secretKey the secret key
+     * @param traderOAuthToken      the Trader OAuth token
+     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
      */
-    public AlpacaAPI(String keyID, String secretKey) {
-        this(null, keyID, secretKey, null,
-                AlpacaProperties.ENDPOINT_API_TYPE,
-                AlpacaProperties.DATA_API_TYPE);
+    public AlpacaAPI(String traderOAuthToken, TraderAPIEndpointType traderAPIEndpointType) {
+        this(null, null, traderOAuthToken, traderAPIEndpointType, null, null, null, null, null);
     }
 
     /**
-     * Instantiates a new {@link AlpacaAPI}.
+     * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading API with OAuth to act on
+     * behalf of an Alpaca account and a custom {@link OkHttpClient} instance.
      *
-     * @param keyID           the key ID
-     * @param secretKey       the secret key
-     * @param endpointAPIType the {@link EndpointAPIType}
-     * @param dataAPIType     the {@link DataAPIType}
+     * @param traderOAuthToken      the Trader OAuth token
+     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
+     * @param okHttpClient          an existing {@link OkHttpClient} or <code>null</code> to create a new default
+     *                              instance
      */
-    public AlpacaAPI(String keyID, String secretKey, EndpointAPIType endpointAPIType, DataAPIType dataAPIType) {
-        this(null, keyID, secretKey, null, endpointAPIType, dataAPIType);
+    public AlpacaAPI(String traderOAuthToken, TraderAPIEndpointType traderAPIEndpointType, OkHttpClient okHttpClient) {
+        this(null, null, traderOAuthToken, traderAPIEndpointType, null, null, null, null, okHttpClient);
     }
 
     /**
-     * Instantiates a new {@link AlpacaAPI}.
+     * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Broker API.
+     *
+     * @param brokerAPIKey          the Broker API key
+     * @param brokerAPISecret       the Broker API secret
+     * @param brokerAPIEndpointType the {@link BrokerAPIEndpointType}
+     */
+    public AlpacaAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType) {
+        this(null, null, null, null, null, brokerAPIKey, brokerAPISecret, brokerAPIEndpointType, null);
+    }
+
+    /**
+     * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Broker API and a custom
+     * {@link OkHttpClient} instance.
      *
-     * @param oAuthToken the OAuth token. Note that the Data API v2 does not work with OAuth tokens.
+     * @param brokerAPIKey          the Broker API key
+     * @param brokerAPISecret       the Broker API secret
+     * @param brokerAPIEndpointType the {@link BrokerAPIEndpointType}
+     * @param okHttpClient          an existing {@link OkHttpClient} or <code>null</code> to create a new default
+     *                              instance
      */
-    public AlpacaAPI(String oAuthToken) {
-        this(null, null, null, oAuthToken,
-                AlpacaProperties.ENDPOINT_API_TYPE,
-                AlpacaProperties.DATA_API_TYPE);
+    public AlpacaAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
+            OkHttpClient okHttpClient) {
+        this(null, null, null, null, null, brokerAPIKey, brokerAPISecret, brokerAPIEndpointType, okHttpClient);
     }
 
     /**
      * Instantiates a new {@link AlpacaAPI}.
      *
-     * @param okHttpClient    the {@link OkHttpClient} or <code>null</code> to create a default instance
-     * @param keyID           the key ID
-     * @param secretKey       the secret key
-     * @param oAuthToken      the OAuth token
-     * @param endpointAPIType the {@link EndpointAPIType}
-     * @param dataAPIType     the {@link DataAPIType}
+     * @param traderKeyID             the Trader key ID
+     * @param traderSecretKey         the Trader secret key
+     * @param traderOAuthToken        the Trader OAuth token
+     * @param traderAPIEndpointType   the {@link TraderAPIEndpointType}
+     * @param marketDataAPISourceType the {@link MarketDataAPISourceType}
+     * @param brokerAPIKey            the Broker API key
+     * @param brokerAPISecret         the Broker API secret
+     * @param brokerAPIEndpointType   the {@link BrokerAPIEndpointType}
+     * @param okHttpClient            an existing {@link OkHttpClient} or <code>null</code> to create a new default
+     *                                instance
      */
-    public AlpacaAPI(OkHttpClient okHttpClient, String keyID, String secretKey, String oAuthToken,
-            EndpointAPIType endpointAPIType, DataAPIType dataAPIType) {
-        checkArgument((keyID != null && secretKey != null) ^ oAuthToken != null,
-                "You must specify a (KeyID (%s) and Secret Key (%s)) or an OAuthToken (%s)!",
-                keyID, secretKey, oAuthToken);
-        checkNotNull(endpointAPIType);
-        checkNotNull(dataAPIType);
-
-        // Create default 'okHttpClient'
+    public AlpacaAPI(String traderKeyID, String traderSecretKey,
+            String traderOAuthToken, TraderAPIEndpointType traderAPIEndpointType,
+            MarketDataAPISourceType marketDataAPISourceType,
+            String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
+            OkHttpClient okHttpClient) {
+        this.traderKeyID = traderKeyID;
+        this.traderSecretKey = traderSecretKey;
+        this.traderOAuthToken = traderOAuthToken;
+        this.traderAPIEndpointType = traderAPIEndpointType != null ? traderAPIEndpointType : PAPER;
+        this.marketDataAPISourceType = marketDataAPISourceType != null ? marketDataAPISourceType : IEX;
+        this.brokerAPIKey = brokerAPIKey;
+        this.brokerAPISecret = brokerAPISecret;
+        this.brokerAPIEndpointType = brokerAPIEndpointType != null ? brokerAPIEndpointType : SANDBOX;
+
+        // Create default OkHttpClient instance
         if (okHttpClient == null) {
-            OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder()
-                    .cache(null); // Ensure response caching is disabled
+            OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
             if (LOGGER.isDebugEnabled()) {
                 clientBuilder.addInterceptor(new HttpLoggingInterceptor(LOGGER::debug));
             }
             okHttpClient = clientBuilder.build();
         }
         this.okHttpClient = okHttpClient;
-
-        streamingWebsocket = new StreamingWebsocket(okHttpClient, endpointAPIType, keyID, secretKey, oAuthToken);
-        cryptoMarketDataWebsocket = new CryptoMarketDataWebsocket(okHttpClient, keyID, secretKey);
-        stockMarketDataWebsocket = new StockMarketDataWebsocket(okHttpClient, dataAPIType, keyID, secretKey);
-        newsMarketDataWebsocket = new NewsMarketDataWebsocket(okHttpClient, keyID, secretKey);
     }
 
     /**
-     * @return the {@link StreamingWebsocketInterface}
+     * Gets the {@link OkHttpClient}.
+     *
+     * @return the {@link OkHttpClient}
      */
-    public StreamingWebsocketInterface streaming() {
-        return streamingWebsocket;
+    public OkHttpClient getOkHttpClient() {
+        return okHttpClient;
     }
 
     /**
-     * @return the Crypto {@link MarketDataWebsocketInterface}
+     * Gets the {@link AlpacaTraderAPI}. Lazily instantiated.
+     *
+     * @return the {@link AlpacaTraderAPI}
      */
-    public MarketDataWebsocketInterface cryptoMarketDataStreaming() {
-        return cryptoMarketDataWebsocket;
+    public synchronized AlpacaTraderAPI trader() {
+        if (trader == null) {
+            trader = new AlpacaTraderAPI(traderKeyID, traderSecretKey, traderOAuthToken, traderAPIEndpointType,
+                    okHttpClient);
+        }
+        return trader;
     }
 
     /**
-     * @return the Stock {@link MarketDataWebsocketInterface}
+     * Gets the {@link AlpacaMarketDataAPI}. Lazily instantiated.
+     *
+     * @return the {@link AlpacaMarketDataAPI}
      */
-    public MarketDataWebsocketInterface stockMarketDataStreaming() {
-        return stockMarketDataWebsocket;
+    public synchronized AlpacaMarketDataAPI marketData() {
+        if (marketData == null) {
+            marketData = new AlpacaMarketDataAPI(traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret,
+                    okHttpClient);
+        }
+        return marketData;
     }
 
     /**
-     * @return the News {@link MarketDataWebsocketInterface}
+     * Gets the {@link AlpacaBrokerAPI}. Lazily instantiated.
+     *
+     * @return the {@link AlpacaBrokerAPI}
      */
-    public MarketDataWebsocketInterface newsMarketDataStreaming() {
-        return newsMarketDataWebsocket;
-    }
-
-    public OkHttpClient getOkHttpClient() {
-        return okHttpClient;
+    public synchronized AlpacaBrokerAPI broker() {
+        if (broker == null) {
+            broker = new AlpacaBrokerAPI(brokerAPIKey, brokerAPISecret, brokerAPIEndpointType, okHttpClient);
+        }
+        return broker;
     }
 
     /**
@@ -171,40 +222,66 @@ public static Builder builder() {
      */
     public static final class Builder {
 
-        private String keyID;
-        private String secretKey;
-        private EndpointAPIType endpointAPIType;
-        private DataAPIType dataAPIType;
+        private String traderKeyID;
+        private String traderSecretKey;
+        private String traderOAuthToken;
+        private TraderAPIEndpointType traderAPIEndpointType;
+        private MarketDataAPISourceType marketDataAPISourceType;
+        private String brokerAPIKey;
+        private String brokerAPISecret;
+        private BrokerAPIEndpointType brokerAPIEndpointType;
+        private OkHttpClient okHttpClient;
+
+        private Builder() {}
+
+        public Builder withTraderKeyID(String traderKeyID) {
+            this.traderKeyID = traderKeyID;
+            return this;
+        }
+
+        public Builder withTraderSecretKey(String traderSecretKey) {
+            this.traderSecretKey = traderSecretKey;
+            return this;
+        }
+
+        public Builder withTraderOAuthToken(String traderOAuthToken) {
+            this.traderOAuthToken = traderOAuthToken;
+            return this;
+        }
+
+        public Builder withTraderAPIEndpointType(TraderAPIEndpointType traderAPIEndpointType) {
+            this.traderAPIEndpointType = traderAPIEndpointType;
+            return this;
+        }
 
-        private Builder() {
-            this.keyID = AlpacaProperties.KEY_ID;
-            this.secretKey = AlpacaProperties.SECRET_KEY;
-            this.endpointAPIType = AlpacaProperties.ENDPOINT_API_TYPE;
-            this.dataAPIType = AlpacaProperties.DATA_API_TYPE;
+        public Builder withMarketDataAPISourceType(MarketDataAPISourceType marketDataAPISourceType) {
+            this.marketDataAPISourceType = marketDataAPISourceType;
+            return this;
         }
 
-        public Builder withKeyID(String keyID) {
-            this.keyID = keyID;
+        public Builder withBrokerAPIKey(String brokerAPIKey) {
+            this.brokerAPIKey = brokerAPIKey;
             return this;
         }
 
-        public Builder withSecretKey(String secretKey) {
-            this.secretKey = secretKey;
+        public Builder withBrokerAPISecret(String brokerAPISecret) {
+            this.brokerAPISecret = brokerAPISecret;
             return this;
         }
 
-        public Builder withEndpointAPIType(EndpointAPIType endpointAPIType) {
-            this.endpointAPIType = endpointAPIType;
+        public Builder withBrokerAPIEndpointType(BrokerAPIEndpointType brokerAPIEndpointType) {
+            this.brokerAPIEndpointType = brokerAPIEndpointType;
             return this;
         }
 
-        public Builder withDataAPIType(DataAPIType dataAPIType) {
-            this.dataAPIType = dataAPIType;
+        public Builder withOkHttpClient(OkHttpClient okHttpClient) {
+            this.okHttpClient = okHttpClient;
             return this;
         }
 
         public AlpacaAPI build() {
-            return new AlpacaAPI(this);
+            return new AlpacaAPI(traderKeyID, traderSecretKey, traderOAuthToken, traderAPIEndpointType,
+                    marketDataAPISourceType, brokerAPIKey, brokerAPISecret, brokerAPIEndpointType, okHttpClient);
         }
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
new file mode 100644
index 00000000..7d9851b5
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
@@ -0,0 +1,304 @@
+package net.jacobpeterson.alpaca;
+
+import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
+import net.jacobpeterson.alpaca.openapi.broker.ApiClient;
+import net.jacobpeterson.alpaca.openapi.broker.api.AccountsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.AssetsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.CalendarApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.ClockApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.CorporateActionsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.CountryInfoApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.DocumentsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.EventsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.FundingApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.FundingWalletsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.JournalsApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.KycApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.LogosApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.OAuthApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.RebalancingApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.ReportingApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.TradingApi;
+import net.jacobpeterson.alpaca.openapi.broker.api.WatchlistApi;
+import okhttp3.OkHttpClient;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static net.jacobpeterson.alpaca.util.apikey.APIKeyUtil.createBrokerAPIAuthKey;
+
+/**
+ * {@link AlpacaBrokerAPI} is the class used to interface with the Alpaca Broker API endpoints. This class is
+ * thread-safe.
+ */
+public class AlpacaBrokerAPI {
+
+    private final ApiClient apiClient;
+    private AccountsApi accounts;
+    private AssetsApi assets;
+    private CalendarApi calendar;
+    private ClockApi clock;
+    private CorporateActionsApi corporateActions;
+    private CountryInfoApi countryInfo;
+    private DocumentsApi documents;
+    private EventsApi events;
+    private FundingApi funding;
+    private FundingWalletsApi fundingWallets;
+    private JournalsApi journals;
+    private KycApi kyc;
+    private LogosApi logos;
+    private OAuthApi oAuth;
+    private RebalancingApi rebalancing;
+    private ReportingApi reporting;
+    private TradingApi trading;
+    private WatchlistApi watchlist;
+
+    /**
+     * Instantiates a new {@link AlpacaBrokerAPI}.
+     *
+     * @param brokerAPIKey          the Broker API key
+     * @param brokerAPISecret       the Broker API secret
+     * @param brokerAPIEndpointType the {@link BrokerAPIEndpointType}
+     * @param okHttpClient          an existing {@link OkHttpClient} or <code>null</code> to create a new default
+     *                              instance
+     */
+    @SuppressWarnings("UnnecessaryDefault")
+    AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
+            OkHttpClient okHttpClient) {
+        checkNotNull(brokerAPIKey);
+        checkNotNull(brokerAPISecret);
+        checkNotNull(brokerAPIEndpointType);
+        checkNotNull(okHttpClient);
+
+        apiClient = new ApiClient(okHttpClient);
+        apiClient.setServerIndex(switch (brokerAPIEndpointType) {
+            case SANDBOX -> 0;
+            case PRODUCTION -> 1;
+            default -> throw new UnsupportedOperationException();
+        });
+        apiClient.addDefaultHeader("Authorization", createBrokerAPIAuthKey(brokerAPIKey, brokerAPISecret));
+    }
+
+    /**
+     * Gets the internal {@link ApiClient}.
+     *
+     * @return the {@link ApiClient}
+     */
+    public ApiClient getInternalAPIClient() {
+        return apiClient;
+    }
+
+    /**
+     * Gets the {@link AccountsApi}. Lazily instantiated.
+     *
+     * @return the {@link AccountsApi}
+     */
+    public synchronized AccountsApi accounts() {
+        if (accounts == null) {
+            accounts = new AccountsApi(apiClient);
+        }
+        return accounts;
+    }
+
+    /**
+     * Gets the {@link AssetsApi}. Lazily instantiated.
+     *
+     * @return the {@link AssetsApi}
+     */
+    public synchronized AssetsApi assets() {
+        if (assets == null) {
+            assets = new AssetsApi(apiClient);
+        }
+        return assets;
+    }
+
+    /**
+     * Gets the {@link CalendarApi}. Lazily instantiated.
+     *
+     * @return the {@link CalendarApi}
+     */
+    public synchronized CalendarApi calendar() {
+        if (calendar == null) {
+            calendar = new CalendarApi(apiClient);
+        }
+        return calendar;
+    }
+
+    /**
+     * Gets the {@link ClockApi}. Lazily instantiated.
+     *
+     * @return the {@link ClockApi}
+     */
+    public synchronized ClockApi clock() {
+        if (clock == null) {
+            clock = new ClockApi(apiClient);
+        }
+        return clock;
+    }
+
+    /**
+     * Gets the {@link CorporateActionsApi}. Lazily instantiated.
+     *
+     * @return the {@link CorporateActionsApi}
+     */
+    public synchronized CorporateActionsApi corporateActions() {
+        if (corporateActions == null) {
+            corporateActions = new CorporateActionsApi(apiClient);
+        }
+        return corporateActions;
+    }
+
+    /**
+     * Gets the {@link CountryInfoApi}. Lazily instantiated.
+     *
+     * @return the {@link CountryInfoApi}
+     */
+    public synchronized CountryInfoApi countryInfo() {
+        if (countryInfo == null) {
+            countryInfo = new CountryInfoApi(apiClient);
+        }
+        return countryInfo;
+    }
+
+    /**
+     * Gets the {@link DocumentsApi}. Lazily instantiated.
+     *
+     * @return the {@link DocumentsApi}
+     */
+    public synchronized DocumentsApi documents() {
+        if (documents == null) {
+            documents = new DocumentsApi(apiClient);
+        }
+        return documents;
+    }
+
+    /**
+     * Gets the {@link EventsApi}. Lazily instantiated.
+     *
+     * @return the {@link EventsApi}
+     */
+    public synchronized EventsApi events() {
+        if (events == null) {
+            events = new EventsApi(apiClient);
+        }
+        return events;
+    }
+
+    /**
+     * Gets the {@link FundingApi}. Lazily instantiated.
+     *
+     * @return the {@link FundingApi}
+     */
+    public synchronized FundingApi funding() {
+        if (funding == null) {
+            funding = new FundingApi(apiClient);
+        }
+        return funding;
+    }
+
+    /**
+     * Gets the {@link FundingWalletsApi}. Lazily instantiated.
+     *
+     * @return the {@link FundingWalletsApi}
+     */
+    public synchronized FundingWalletsApi fundingWallets() {
+        if (fundingWallets == null) {
+            fundingWallets = new FundingWalletsApi(apiClient);
+        }
+        return fundingWallets;
+    }
+
+    /**
+     * Gets the {@link JournalsApi}. Lazily instantiated.
+     *
+     * @return the {@link JournalsApi}
+     */
+    public synchronized JournalsApi journals() {
+        if (journals == null) {
+            journals = new JournalsApi(apiClient);
+        }
+        return journals;
+    }
+
+    /**
+     * Gets the {@link KycApi}. Lazily instantiated.
+     *
+     * @return the {@link KycApi}
+     */
+    public synchronized KycApi kyc() {
+        if (kyc == null) {
+            kyc = new KycApi(apiClient);
+        }
+        return kyc;
+    }
+
+    /**
+     * Gets the {@link LogosApi}. Lazily instantiated.
+     *
+     * @return the {@link LogosApi}
+     */
+    public synchronized LogosApi logos() {
+        if (logos == null) {
+            logos = new LogosApi(apiClient);
+        }
+        return logos;
+    }
+
+    /**
+     * Gets the {@link OAuthApi}. Lazily instantiated.
+     *
+     * @return the {@link OAuthApi}
+     */
+    public synchronized OAuthApi oAuth() {
+        if (oAuth == null) {
+            oAuth = new OAuthApi(apiClient);
+        }
+        return oAuth;
+    }
+
+    /**
+     * Gets the {@link RebalancingApi}. Lazily instantiated.
+     *
+     * @return the {@link RebalancingApi}
+     */
+    public synchronized RebalancingApi rebalancing() {
+        if (rebalancing == null) {
+            rebalancing = new RebalancingApi(apiClient);
+        }
+        return rebalancing;
+    }
+
+    /**
+     * Gets the {@link ReportingApi}. Lazily instantiated.
+     *
+     * @return the {@link ReportingApi}
+     */
+    public synchronized ReportingApi reporting() {
+        if (reporting == null) {
+            reporting = new ReportingApi(apiClient);
+        }
+        return reporting;
+    }
+
+    /**
+     * Gets the {@link TradingApi}. Lazily instantiated.
+     *
+     * @return the {@link TradingApi}
+     */
+    public synchronized TradingApi trading() {
+        if (trading == null) {
+            trading = new TradingApi(apiClient);
+        }
+        return trading;
+    }
+
+    /**
+     * Gets the {@link WatchlistApi}. Lazily instantiated.
+     *
+     * @return the {@link WatchlistApi}
+     */
+    public synchronized WatchlistApi watchlist() {
+        if (watchlist == null) {
+            watchlist = new WatchlistApi(apiClient);
+        }
+        return watchlist;
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
new file mode 100644
index 00000000..1538581a
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
@@ -0,0 +1,152 @@
+package net.jacobpeterson.alpaca;
+
+import net.jacobpeterson.alpaca.openapi.marketdata.ApiClient;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.CorporateActionsApi;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.CryptoApi;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.ForexApi;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.LogosApi;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.NewsApi;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.OptionApi;
+import net.jacobpeterson.alpaca.openapi.marketdata.api.StockApi;
+import okhttp3.OkHttpClient;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static net.jacobpeterson.alpaca.util.apikey.APIKeyUtil.createBrokerAPIAuthKey;
+
+/**
+ * {@link AlpacaMarketDataAPI} is the class used to interface with the Alpaca Market Data API endpoints. This class is
+ * thread-safe.
+ */
+public class AlpacaMarketDataAPI {
+
+    private final ApiClient apiClient;
+    private CorporateActionsApi corporateActions;
+    private CryptoApi crypto;
+    private ForexApi forex;
+    private LogosApi logos;
+    private NewsApi news;
+    private OptionApi option;
+    private StockApi stock;
+
+    /**
+     * Instantiates a new {@link AlpacaMarketDataAPI}.
+     *
+     * @param traderKeyID     the Trader key ID
+     * @param traderSecretKey the Trader secret key
+     * @param brokerAPIKey    the Broker API key
+     * @param brokerAPISecret the Broker API secret
+     * @param okHttpClient    an existing {@link OkHttpClient} or <code>null</code> to create a new default instance
+     */
+    AlpacaMarketDataAPI(String traderKeyID, String traderSecretKey,
+            String brokerAPIKey, String brokerAPISecret, OkHttpClient okHttpClient) {
+        checkArgument((traderKeyID != null && traderSecretKey != null) ^
+                        (brokerAPIKey != null && brokerAPISecret != null),
+                "You must specify a (trader key ID and secret key) or an (broker API key and secret)!");
+        checkNotNull(okHttpClient);
+
+        final boolean traderKeysGiven = traderKeyID != null && traderSecretKey != null;
+
+        apiClient = new ApiClient(okHttpClient);
+        apiClient.setServerIndex(traderKeysGiven ? 0 : 1);
+        if (traderKeysGiven) {
+            apiClient.addDefaultHeader("APCA-API-KEY-ID", traderKeyID);
+            apiClient.addDefaultHeader("APCA-API-SECRET-KEY", traderSecretKey);
+        } else {
+            apiClient.addDefaultHeader("Authorization", createBrokerAPIAuthKey(brokerAPIKey, brokerAPISecret));
+        }
+    }
+
+    /**
+     * Gets the internal {@link ApiClient}.
+     *
+     * @return the {@link ApiClient}
+     */
+    public ApiClient getInternalAPIClient() {
+        return apiClient;
+    }
+
+    /**
+     * Gets the {@link CorporateActionsApi}. Lazily instantiated.
+     *
+     * @return the {@link CorporateActionsApi}
+     */
+    public synchronized CorporateActionsApi corporateActions() {
+        if (corporateActions == null) {
+            corporateActions = new CorporateActionsApi(apiClient);
+        }
+        return corporateActions;
+    }
+
+    /**
+     * Gets the {@link CryptoApi}. Lazily instantiated.
+     *
+     * @return the {@link CryptoApi}
+     */
+    public synchronized CryptoApi crypto() {
+        if (crypto == null) {
+            crypto = new CryptoApi(apiClient);
+        }
+        return crypto;
+    }
+
+    /**
+     * Gets the {@link ForexApi}. Lazily instantiated.
+     *
+     * @return the {@link ForexApi}
+     */
+    public synchronized ForexApi forex() {
+        if (forex == null) {
+            forex = new ForexApi(apiClient);
+        }
+        return forex;
+    }
+
+    /**
+     * Gets the {@link LogosApi}. Lazily instantiated.
+     *
+     * @return the {@link LogosApi}
+     */
+    public synchronized LogosApi logos() {
+        if (logos == null) {
+            logos = new LogosApi(apiClient);
+        }
+        return logos;
+    }
+
+    /**
+     * Gets the {@link NewsApi}. Lazily instantiated.
+     *
+     * @return the {@link NewsApi}
+     */
+    public synchronized NewsApi news() {
+        if (news == null) {
+            news = new NewsApi(apiClient);
+        }
+        return news;
+    }
+
+    /**
+     * Gets the {@link OptionApi}. Lazily instantiated.
+     *
+     * @return the {@link OptionApi}
+     */
+    public synchronized OptionApi option() {
+        if (option == null) {
+            option = new OptionApi(apiClient);
+        }
+        return option;
+    }
+
+    /**
+     * Gets the {@link StockApi}. Lazily instantiated.
+     *
+     * @return the {@link StockApi}
+     */
+    public synchronized StockApi stock() {
+        if (stock == null) {
+            stock = new StockApi(apiClient);
+        }
+        return stock;
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java
new file mode 100644
index 00000000..07485e9c
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java
@@ -0,0 +1,212 @@
+package net.jacobpeterson.alpaca;
+
+import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
+import net.jacobpeterson.alpaca.openapi.trader.ApiClient;
+import net.jacobpeterson.alpaca.openapi.trader.api.AccountActivitiesApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.AccountConfigurationsApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.AccountsApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.AssetsApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.CalendarApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.ClockApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.CorporateActionsApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.OrdersApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.PortfolioHistoryApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.PositionsApi;
+import net.jacobpeterson.alpaca.openapi.trader.api.WatchlistsApi;
+import okhttp3.OkHttpClient;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * {@link AlpacaTraderAPI} is the class used to interface with the Alpaca Trader API endpoints. This class is
+ * thread-safe.
+ */
+public class AlpacaTraderAPI {
+
+    private final ApiClient apiClient;
+    private AccountActivitiesApi accountActivities;
+    private AccountConfigurationsApi accountConfigurations;
+    private AccountsApi accounts;
+    private AssetsApi assets;
+    private CalendarApi calendar;
+    private ClockApi clock;
+    private CorporateActionsApi traderCorporateActions;
+    private OrdersApi orders;
+    private PortfolioHistoryApi portfolioHistory;
+    private PositionsApi positions;
+    private WatchlistsApi watchlists;
+
+    /**
+     * Instantiates a new {@link AlpacaTraderAPI}.
+     *
+     * @param traderKeyID           the Trader key ID
+     * @param traderSecretKey       the Trader secret key
+     * @param traderOAuthToken      the Trader OAuth token
+     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
+     * @param okHttpClient          an existing {@link OkHttpClient} or <code>null</code> to create a new default
+     *                              instance
+     */
+    @SuppressWarnings("UnnecessaryDefault")
+    AlpacaTraderAPI(String traderKeyID, String traderSecretKey, String traderOAuthToken,
+            TraderAPIEndpointType traderAPIEndpointType, OkHttpClient okHttpClient) {
+        checkArgument((traderKeyID != null && traderSecretKey != null) ^ (traderOAuthToken != null),
+                "You must specify a (trader key ID and secret key) or an (OAuth token)!");
+        checkNotNull(traderAPIEndpointType);
+        checkNotNull(okHttpClient);
+
+        apiClient = new ApiClient(okHttpClient);
+        apiClient.setServerIndex(switch (traderAPIEndpointType) {
+            case PAPER -> 0;
+            case LIVE -> 1;
+            default -> throw new UnsupportedOperationException();
+        });
+        if (traderKeyID != null && traderSecretKey != null) {
+            apiClient.addDefaultHeader("APCA-API-KEY-ID", traderKeyID);
+            apiClient.addDefaultHeader("APCA-API-SECRET-KEY", traderSecretKey);
+        } else {
+            apiClient.addDefaultHeader("Authorization", "Bearer " + traderOAuthToken);
+        }
+    }
+
+    /**
+     * Gets the internal {@link ApiClient}.
+     *
+     * @return the {@link ApiClient}
+     */
+    public ApiClient getInternalAPIClient() {
+        return apiClient;
+    }
+
+    /**
+     * Gets the {@link AccountActivitiesApi}. Lazily instantiated.
+     *
+     * @return the {@link AccountActivitiesApi}
+     */
+    public synchronized AccountActivitiesApi accountActivities() {
+        if (accountActivities == null) {
+            accountActivities = new AccountActivitiesApi(apiClient);
+        }
+        return accountActivities;
+    }
+
+    /**
+     * Gets the {@link AccountConfigurationsApi}. Lazily instantiated.
+     *
+     * @return the {@link AccountConfigurationsApi}
+     */
+    public synchronized AccountConfigurationsApi accountConfigurations() {
+        if (accountConfigurations == null) {
+            accountConfigurations = new AccountConfigurationsApi(apiClient);
+        }
+        return accountConfigurations;
+    }
+
+    /**
+     * Gets the {@link AccountsApi}. Lazily instantiated.
+     *
+     * @return {@link AccountsApi}
+     */
+    public synchronized AccountsApi accounts() {
+        if (accounts == null) {
+            accounts = new AccountsApi(apiClient);
+        }
+        return accounts;
+    }
+
+    /**
+     * Gets the {@link AssetsApi}. Lazily instantiated.
+     *
+     * @return {@link AssetsApi}
+     */
+    public synchronized AssetsApi assets() {
+        if (assets == null) {
+            assets = new AssetsApi(apiClient);
+        }
+        return assets;
+    }
+
+    /**
+     * Gets the {@link CalendarApi}. Lazily instantiated.
+     *
+     * @return {@link CalendarApi}
+     */
+    public synchronized CalendarApi calendar() {
+        if (calendar == null) {
+            calendar = new CalendarApi(apiClient);
+        }
+        return calendar;
+    }
+
+    /**
+     * Gets the {@link ClockApi}. Lazily instantiated.
+     *
+     * @return {@link ClockApi}
+     */
+    public synchronized ClockApi clock() {
+        if (clock == null) {
+            clock = new ClockApi(apiClient);
+        }
+        return clock;
+    }
+
+    /**
+     * Gets the {@link CorporateActionsApi}. Lazily instantiated.
+     *
+     * @return the {@link CorporateActionsApi}
+     */
+    public synchronized CorporateActionsApi traderCorporateActions() {
+        if (traderCorporateActions == null) {
+            traderCorporateActions = new CorporateActionsApi(apiClient);
+        }
+        return traderCorporateActions;
+    }
+
+    /**
+     * Gets the {@link OrdersApi}. Lazily instantiated.
+     *
+     * @return {@link OrdersApi}
+     */
+    public synchronized OrdersApi orders() {
+        if (orders == null) {
+            orders = new OrdersApi(apiClient);
+        }
+        return orders;
+    }
+
+    /**
+     * Gets the {@link PortfolioHistoryApi}. Lazily instantiated.
+     *
+     * @return {@link PortfolioHistoryApi}
+     */
+    public synchronized PortfolioHistoryApi portfolioHistory() {
+        if (portfolioHistory == null) {
+            portfolioHistory = new PortfolioHistoryApi(apiClient);
+        }
+        return portfolioHistory;
+    }
+
+    /**
+     * Gets the {@link PositionsApi}. Lazily instantiated.
+     *
+     * @return {@link PositionsApi}
+     */
+    public synchronized PositionsApi positions() {
+        if (positions == null) {
+            positions = new PositionsApi(apiClient);
+        }
+        return positions;
+    }
+
+    /**
+     * Gets the {@link WatchlistsApi}. Lazily instantiated.
+     *
+     * @return {@link WatchlistsApi}
+     */
+    public synchronized WatchlistsApi watchlists() {
+        if (watchlists == null) {
+            watchlists = new WatchlistsApi(apiClient);
+        }
+        return watchlists;
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java b/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
deleted file mode 100644
index 175a83c8..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/properties/AlpacaProperties.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package net.jacobpeterson.alpaca.properties;
-
-import net.jacobpeterson.alpaca.AlpacaAPI;
-import net.jacobpeterson.alpaca.model.properties.DataAPIType;
-import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
-
-import static net.jacobpeterson.alpaca.util.properties.PropertyUtil.getProperty;
-
-/**
- * {@link AlpacaProperties} defines properties for {@link AlpacaAPI}.
- */
-public class AlpacaProperties {
-
-    private static final String ALPACA_PROPERTIES_FILE = "alpaca.properties";
-    private static final String ALPACA_DEFAULT_PROPERTIES_FILE = "alpaca.default.properties";
-
-    public static final String KEY_ID = getProperty(
-            ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            "key_id");
-    public static final String SECRET_KEY = getProperty(
-            ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            "secret_key");
-    public static final EndpointAPIType ENDPOINT_API_TYPE = EndpointAPIType.fromValue(getProperty(
-            ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            "endpoint_api_type"));
-    public static final DataAPIType DATA_API_TYPE = DataAPIType.fromValue(getProperty(
-            ALPACA_PROPERTIES_FILE, ALPACA_DEFAULT_PROPERTIES_FILE,
-            "data_api_type"));
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/properties/data_api_type.json b/src/main/java/net/jacobpeterson/alpaca/properties/data_api_type.json
deleted file mode 100644
index 3a782d77..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/properties/data_api_type.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "type": "string",
-  "javaName": "DataAPIType",
-  "title": "Defines the data APIs to use with Alpaca.",
-  "enum": [
-    "iex",
-    "sip"
-  ]
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/properties/endpoint_api_type.json b/src/main/java/net/jacobpeterson/alpaca/properties/endpoint_api_type.json
deleted file mode 100644
index c0eacd4c..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/properties/endpoint_api_type.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "type": "string",
-  "javaName": "EndpointAPIType",
-  "title": "Defines the API endpoints to use with Alpaca.",
-  "enum": [
-    "live",
-    "paper"
-  ]
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java
new file mode 100644
index 00000000..29de58ec
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java
@@ -0,0 +1,23 @@
+package net.jacobpeterson.alpaca.util.apikey;
+
+import java.util.Base64;
+
+/**
+ * {@link APIKeyUtil} is a utility class for Alpaca API keys.
+ */
+public class APIKeyUtil {
+
+    /**
+     * Creates a broker API auth key.
+     *
+     * @param brokerAPIKey    the broker API key
+     * @param brokerAPISecret the broker API secret
+     *
+     * @return the key {@link String}
+     *
+     * @see <a href="https://docs.alpaca.markets/v1.1/docs/getting-started-with-broker-api#api-keys">Alpaca Docs</a>
+     */
+    public static String createBrokerAPIAuthKey(String brokerAPIKey, String brokerAPISecret) {
+        return Base64.getEncoder().encodeToString((brokerAPIKey + ":" + brokerAPISecret).getBytes());
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apitype/broker_api_endpoint_type.json b/src/main/java/net/jacobpeterson/alpaca/util/apitype/broker_api_endpoint_type.json
new file mode 100644
index 00000000..c4dd1c66
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apitype/broker_api_endpoint_type.json
@@ -0,0 +1,8 @@
+{
+  "type": "string",
+  "javaName": "BrokerAPIEndpointType",
+  "enum": [
+    "sandbox",
+    "production"
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json b/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json
new file mode 100644
index 00000000..e0f74cdc
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json
@@ -0,0 +1,8 @@
+{
+  "type": "string",
+  "javaName": "MarketDataAPISourceType",
+  "enum": [
+    "iex",
+    "sip"
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apitype/trader_api_endpoint_type.json b/src/main/java/net/jacobpeterson/alpaca/util/apitype/trader_api_endpoint_type.json
new file mode 100644
index 00000000..574d4109
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apitype/trader_api_endpoint_type.json
@@ -0,0 +1,8 @@
+{
+  "type": "string",
+  "javaName": "TraderAPIEndpointType",
+  "enum": [
+    "paper",
+    "live"
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java
deleted file mode 100644
index dc86e2c1..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/GsonUtil.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.jacobpeterson.alpaca.util.gson;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import net.jacobpeterson.alpaca.util.gson.adapters.LocalDateAdapter;
-import net.jacobpeterson.alpaca.util.gson.adapters.LocalTimeAdapter;
-import net.jacobpeterson.alpaca.util.gson.adapters.ZonedDateTimeAdapter;
-
-import java.time.LocalDate;
-import java.time.LocalTime;
-import java.time.ZonedDateTime;
-
-/**
- * {@link GsonUtil} contains utility methods relating to {@link Gson}.
- */
-public class GsonUtil {
-
-    /**
-     * A {@link Gson} instance with registered date/time adapters.
-     */
-    public static final Gson GSON = new GsonBuilder()
-            .registerTypeAdapter(ZonedDateTime.class, new ZonedDateTimeAdapter())
-            .registerTypeAdapter(LocalDate.class, new LocalDateAdapter())
-            .registerTypeAdapter(LocalTime.class, new LocalTimeAdapter())
-            .enableComplexMapKeySerialization()
-            .setLenient()
-            .create();
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java
deleted file mode 100644
index a9475c17..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalDateAdapter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package net.jacobpeterson.alpaca.util.gson.adapters;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-import java.lang.reflect.Type;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-
-/**
- * {@link LocalDateAdapter} is a {@link Gson} adapter for {@link LocalDate}s.
- */
-public class LocalDateAdapter implements JsonSerializer<LocalDate>, JsonDeserializer<LocalDate> {
-
-    @Override
-    public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
-        return new JsonPrimitive(src.format(DateTimeFormatter.ISO_LOCAL_DATE));
-    }
-
-    @Override
-    public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-        return LocalDate.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ISO_LOCAL_DATE);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java
deleted file mode 100644
index 9d6bb735..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/LocalTimeAdapter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package net.jacobpeterson.alpaca.util.gson.adapters;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-import java.lang.reflect.Type;
-import java.time.LocalTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * {@link LocalTimeAdapter} is a {@link Gson} adapter for {@link LocalTime}s.
- */
-public class LocalTimeAdapter implements JsonSerializer<LocalTime>, JsonDeserializer<LocalTime> {
-
-    @Override
-    public JsonElement serialize(LocalTime localTime, Type typeOfSrc, JsonSerializationContext context) {
-        return new JsonPrimitive(localTime.format(DateTimeFormatter.ISO_LOCAL_TIME));
-    }
-
-    @Override
-    public LocalTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-        return LocalTime.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ISO_LOCAL_TIME);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java
deleted file mode 100644
index e10f1be1..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/gson/adapters/ZonedDateTimeAdapter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package net.jacobpeterson.alpaca.util.gson.adapters;
-
-import com.google.gson.Gson;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-import java.lang.reflect.Type;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * {@link ZonedDateTimeAdapter} is a {@link Gson} adapter for {@link ZonedDateTime}s.
- */
-public class ZonedDateTimeAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
-
-    @Override
-    public JsonElement serialize(ZonedDateTime src, Type typeOfSrc, JsonSerializationContext context) {
-        return new JsonPrimitive(src.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
-    }
-
-    @Override
-    public ZonedDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
-            throws JsonParseException {
-        return ZonedDateTime.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ISO_DATE_TIME);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java
deleted file mode 100644
index 718d02a3..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/util/properties/PropertyUtil.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package net.jacobpeterson.alpaca.util.properties;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * {@link PropertyUtil} is a utility class for all {@link Properties}-related handling.
- */
-public final class PropertyUtil {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);
-
-    public static final Map<String, Properties> CACHED_PROPERTIES = Collections.synchronizedMap(new HashMap<>());
-
-    /**
-     * Gets a string property from a property file. Will try to IO load the properties in the property file if not
-     * cached already.
-     *
-     * @param propertyFile the property file
-     * @param key          the key
-     *
-     * @return the string
-     */
-    public static String getProperty(String propertyFile, String key) {
-        return getProperty(propertyFile, null, key, null);
-    }
-
-    /**
-     * Gets a string property from a property file. Will try to IO load the properties in the property file if not
-     * cached already. If the desired property is not found in the <code>propertyFile</code>, then the
-     * <code>defaultPropertyFile</code> is searched.
-     *
-     * @param propertyFile        the property file
-     * @param defaultPropertyFile the default property file
-     * @param key                 the key
-     *
-     * @return the string
-     */
-    public static String getProperty(String propertyFile, String defaultPropertyFile, String key) {
-        return getProperty(propertyFile, defaultPropertyFile, key, null);
-    }
-
-    /**
-     * Gets a string property from a property file. Will try to IO load the properties in the property file if not
-     * cached already. If the desired property is not found in the <code>propertyFile</code>, then the
-     * <code>defaultPropertyFile</code> is searched, and if it's not there, then <code>defaultValue</code> is returned.
-     *
-     * @param propertyFile        the property file
-     * @param defaultPropertyFile the default property file
-     * @param key                 the key
-     * @param defaultValue        the default value (if the desired property wasn't found, then this is returned)
-     *
-     * @return the string
-     */
-    public static String getProperty(String propertyFile, String defaultPropertyFile, String key, String defaultValue) {
-        final Properties properties;
-        if (!CACHED_PROPERTIES.containsKey(propertyFile)) {
-            properties = loadPropertyFile(propertyFile, defaultPropertyFile);
-            CACHED_PROPERTIES.put(propertyFile, properties);
-        } else {
-            properties = CACHED_PROPERTIES.get(propertyFile);
-        }
-        return properties == null ? defaultValue : properties.getProperty(key, defaultValue);
-    }
-
-    /**
-     * Loads property file {@link Properties}.
-     *
-     * @param propertyFile        the property file name
-     * @param defaultPropertyFile the default property file name
-     *
-     * @return the properties
-     */
-    public synchronized static Properties loadPropertyFile(String propertyFile, String defaultPropertyFile) {
-        ClassLoader classLoader = PropertyUtil.class.getClassLoader();
-        if (classLoader == null) {
-            classLoader = ClassLoader.getSystemClassLoader();
-        }
-
-        // Load the default property file if exists
-        Properties defaultProperties = null;
-        if (defaultPropertyFile != null) {
-            try (InputStream defaultPropertyStream = classLoader.getResourceAsStream(defaultPropertyFile)) {
-                if (defaultPropertyStream == null) {
-                    LOGGER.warn("No default property file \"{}\" exists on the classpath.", defaultPropertyFile);
-                } else {
-
-                    defaultProperties = new Properties();
-                    try {
-                        defaultProperties.load(defaultPropertyStream);
-                        LOGGER.debug("Loaded default properties file: {}", defaultPropertyFile);
-                    } catch (IOException exception) {
-                        LOGGER.error("Could not load default property file: {}", defaultPropertyFile, exception);
-                    }
-                }
-            } catch (Exception exception) {
-                LOGGER.error("Could not read default property file stream: {}", defaultPropertyFile, exception);
-            }
-        } else {
-            LOGGER.warn("No default property file given for: {}", propertyFile);
-        }
-
-        // Load the property file if exists
-        Properties properties = null;
-        if (propertyFile != null) {
-            try (InputStream propertyStream = classLoader.getResourceAsStream(propertyFile)) {
-                if (propertyStream == null) {
-                    LOGGER.warn("No property file \"{}\" exists on the classpath.", propertyFile);
-                } else {
-                    // Add default properties if they were found
-                    properties = defaultProperties == null ? new Properties() : new Properties(defaultProperties);
-
-                    // Load the properties
-                    try {
-                        properties.load(propertyStream);
-                        LOGGER.info("Loaded properties file: {}", propertyFile);
-                    } catch (IOException exception) {
-                        LOGGER.error("Could not load property file: {}", propertyFile, exception);
-                    }
-                }
-            } catch (Exception exception) {
-                LOGGER.error("Could not read property file stream: {}", propertyFile, exception);
-            }
-        } else if (defaultProperties == null) {
-            throw new IllegalStateException("No property files were found!");
-        }
-
-        if (properties == null) {
-            LOGGER.debug("Could not find property file: {}", propertyFile);
-
-            if (defaultProperties != null) {
-                LOGGER.info("Using default properties for: {}", propertyFile);
-                properties = defaultProperties;
-            }
-        }
-
-        return properties;
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
deleted file mode 100644
index 59208ccc..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ /dev/null
@@ -1,292 +0,0 @@
-package net.jacobpeterson.alpaca.websocket;
-
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
-import okhttp3.WebSocket;
-import okhttp3.WebSocketListener;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.Future;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * {@link AlpacaWebsocket} represents an abstract websocket for Alpaca.
- *
- * @param <L> the {@link AlpacaWebsocketMessageListener} type parameter
- * @param <T> the 'message type' type parameter
- * @param <M> the 'message' type parameter
- */
-public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageListener<T, M>> extends WebSocketListener
-        implements AlpacaWebsocketInterface<L> {
-
-    /**
-     * Defines a websocket normal closure code.
-     *
-     * @see WebSocket#close(int, String)
-     */
-    public static final int WEBSOCKET_NORMAL_CLOSURE_CODE = 1000;
-
-    /**
-     * Defines a websocket normal closure message.
-     *
-     * @see WebSocket#close(int, String)
-     */
-    public static final String WEBSOCKET_NORMAL_CLOSURE_MESSAGE = "Normal closure";
-
-    /**
-     * Defines the maximum number of reconnection attempts to be made by an {@link AlpacaWebsocket}.
-     */
-    public static int MAX_RECONNECT_ATTEMPTS = 5;
-
-    /**
-     * Defines the millisecond sleep interval between reconnection attempts made by an {@link AlpacaWebsocket}.
-     */
-    public static int RECONNECT_SLEEP_INTERVAL = 1000;
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaWebsocket.class);
-
-    protected final OkHttpClient okHttpClient;
-    protected final HttpUrl websocketURL;
-    protected final String websocketName;
-    protected final String keyID;
-    protected final String secretKey;
-    protected final String oAuthToken;
-    protected final boolean useOAuth;
-
-    protected L listener;
-    protected WebsocketStateListener websocketStateListener;
-    protected WebSocket websocket;
-    protected boolean connected;
-    protected boolean authenticated;
-    protected CompletableFuture<Boolean> authenticationMessageFuture;
-    protected boolean intentionalClose;
-    protected int reconnectAttempts;
-    protected boolean automaticallyReconnect;
-
-    /**
-     * Instantiates a {@link AlpacaWebsocket}.
-     *
-     * @param okHttpClient  the {@link OkHttpClient}
-     * @param websocketURL  the websocket {@link HttpUrl}
-     * @param websocketName the websocket name
-     * @param keyID         the key ID
-     * @param secretKey     the secret key
-     * @param oAuthToken    the OAuth token
-     */
-    public AlpacaWebsocket(OkHttpClient okHttpClient, HttpUrl websocketURL, String websocketName,
-            String keyID, String secretKey, String oAuthToken) {
-        checkNotNull(okHttpClient);
-        checkNotNull(websocketURL);
-        checkNotNull(websocketName);
-
-        this.okHttpClient = okHttpClient;
-        this.websocketURL = websocketURL;
-        this.websocketName = websocketName;
-        this.keyID = keyID;
-        this.secretKey = secretKey;
-        this.oAuthToken = oAuthToken;
-        useOAuth = oAuthToken != null;
-
-        automaticallyReconnect = true;
-    }
-
-    @Override
-    public void connect() {
-        if (!isConnected()) {
-            Request websocketRequest = new Request.Builder()
-                    .url(websocketURL)
-                    .get()
-                    .build();
-            websocket = okHttpClient.newWebSocket(websocketRequest, this);
-        }
-    }
-
-    @Override
-    public void disconnect() {
-        if (websocket != null && isConnected()) {
-            intentionalClose = true;
-            websocket.close(WEBSOCKET_NORMAL_CLOSURE_CODE, WEBSOCKET_NORMAL_CLOSURE_MESSAGE);
-        } else {
-            cleanupState();
-        }
-    }
-
-    @Override
-    public boolean isConnected() {
-        return connected;
-    }
-
-    @Override
-    public boolean isAuthenticated() {
-        return authenticated;
-    }
-
-    @Override
-    public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
-        connected = true;
-
-        LOGGER.info("{} websocket opened.", websocketName);
-        LOGGER.debug("{} websocket response: {}", websocketName, response);
-
-        // Call 'onConnection' or 'onReconnection' async to avoid any potential dead-locking since this is called
-        // in sync with 'onMessage' in OkHttp's 'WebSocketListener'
-        ForkJoinPool.commonPool().execute(() -> {
-            if (reconnectAttempts > 0) {
-                reconnectAttempts = 0;
-                onReconnection();
-            } else {
-                onConnection();
-            }
-        });
-
-        if (websocketStateListener != null) {
-            websocketStateListener.onOpen(response);
-        }
-    }
-
-    @Override
-    public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
-        connected = false;
-
-        if (intentionalClose) {
-            LOGGER.info("{} websocket closed.", websocketName);
-            LOGGER.debug("Close code: {}, Reason: {}", code, reason);
-            cleanupState();
-        } else {
-            LOGGER.error("{} websocket closed unintentionally! Code: {}, Reason: {}", websocketName, code, reason);
-            handleReconnectionAttempt();
-        }
-
-        if (websocketStateListener != null) {
-            websocketStateListener.onClosed(code, reason);
-        }
-    }
-
-    @Override
-    public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable cause, @Nullable Response response) {
-        LOGGER.error("{} websocket failure!", websocketName, cause);
-
-        // A websocket failure occurs when either there is a connection failure or when the client throws
-        // an Exception when receiving a message. In either case, OkHttp will silently close the websocket
-        // connection, so try to reopen it.
-        connected = false;
-        handleReconnectionAttempt();
-
-        if (websocketStateListener != null) {
-            websocketStateListener.onFailure(cause);
-        }
-    }
-
-    /**
-     * Attempts to reconnect the disconnected {@link #websocket} asynchronously.
-     */
-    private void handleReconnectionAttempt() {
-        if (!automaticallyReconnect) {
-            return;
-        }
-
-        if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
-            LOGGER.info("Attempting to reconnect {} websocket in {} milliseconds...",
-                    websocketName, RECONNECT_SLEEP_INTERVAL);
-
-            reconnectAttempts++;
-
-            ForkJoinPool.commonPool().execute(() -> {
-                try {
-                    Thread.sleep(RECONNECT_SLEEP_INTERVAL);
-                } catch (InterruptedException ignored) {
-                    return;
-                }
-
-                connect();
-            });
-        } else {
-            LOGGER.error("Exhausted {} reconnection attempts. Not attempting to reconnect.", MAX_RECONNECT_ATTEMPTS);
-            cleanupState();
-        }
-    }
-
-    /**
-     * Cleans up this instance's state variables.
-     */
-    protected void cleanupState() {
-        websocket = null;
-        connected = false;
-        authenticated = false;
-        if (authenticationMessageFuture != null && !authenticationMessageFuture.isDone()) {
-            authenticationMessageFuture.complete(false);
-        }
-        authenticationMessageFuture = null;
-        intentionalClose = false;
-        reconnectAttempts = 0;
-    }
-
-    /**
-     * Called asynchronously when a websocket connection is made.
-     */
-    protected abstract void onConnection();
-
-    /**
-     * Called asynchronously when a websocket reconnection is made after unintentional disconnection.
-     */
-    protected abstract void onReconnection();
-
-    /**
-     * Sends an authentication message to authenticate this websocket stream.
-     */
-    protected abstract void sendAuthenticationMessage();
-
-    @Override
-    public Future<Boolean> getAuthorizationFuture() {
-        if (authenticationMessageFuture == null) {
-            authenticationMessageFuture = new CompletableFuture<>();
-        }
-
-        return authenticationMessageFuture;
-    }
-
-    /**
-     * Calls the {@link AlpacaWebsocketMessageListener}.
-     *
-     * @param messageType the message type
-     * @param message     the message
-     */
-    protected void callListener(T messageType, M message) {
-        if (listener != null) {
-            try {
-                listener.onMessage(messageType, message);
-            } catch (Exception exception) {
-                LOGGER.error("{} listener threw exception!", websocketName, exception);
-            }
-        }
-    }
-
-    @Override
-    public void setListener(L listener) {
-        this.listener = listener;
-    }
-
-    public WebsocketStateListener getWebsocketStateListener() {
-        return websocketStateListener;
-    }
-
-    public void setWebsocketStateListener(WebsocketStateListener websocketStateListener) {
-        this.websocketStateListener = websocketStateListener;
-    }
-
-    public boolean doesAutomaticallyReconnect() {
-        return automaticallyReconnect;
-    }
-
-    public void setAutomaticallyReconnect(boolean automaticallyReconnect) {
-        this.automaticallyReconnect = automaticallyReconnect;
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
deleted file mode 100644
index f524e1df..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package net.jacobpeterson.alpaca.websocket;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * {@link AlpacaWebsocketInterface} defines an interface for Alpaca websockets.
- *
- * @param <L> the {@link AlpacaWebsocketMessageListener} type parameter
- */
-public interface AlpacaWebsocketInterface<L extends AlpacaWebsocketMessageListener<?, ?>> {
-
-    /**
-     * Connects this Websocket.
-     */
-    void connect();
-
-    /**
-     * Disconnects this Websocket.
-     */
-    void disconnect();
-
-    /**
-     * Returns true if this websocket is connected.
-     *
-     * @return a boolean
-     */
-    boolean isConnected();
-
-    /**
-     * Returns true if this websocket is authenticated.
-     *
-     * @return a boolean
-     */
-    boolean isAuthenticated();
-
-    /**
-     * True if {@link #isConnected()} and {@link #isAuthenticated()}.
-     *
-     * @return a boolean
-     */
-    default boolean isValid() {
-        return isConnected() && isAuthenticated();
-    }
-
-    /**
-     * Gets a {@link Boolean} {@link Future} that completes when an authentication message that is received after a new
-     * websocket connection indicates successful authentication.
-     * <br>
-     * Note that if this {@link AlpacaWebsocketInterface} is already authorized, the returned {@link Future} will likely
-     * never complete.
-     *
-     * @return a {@link Boolean} {@link Future}
-     */
-    Future<Boolean> getAuthorizationFuture();
-
-    /**
-     * Waits for {@link #getAuthorizationFuture()} to complete and returns its value, except when <code>timeout</code>
-     * time has elapsed, then this will return <code>false</code>.
-     *
-     * @param timeout the timeout time
-     * @param unit    the timeout {@link TimeUnit}
-     *
-     * @return a boolean
-     */
-    default boolean waitForAuthorization(long timeout, TimeUnit unit) {
-        try {
-            return getAuthorizationFuture().get(timeout, unit);
-        } catch (InterruptedException | ExecutionException | TimeoutException ignored) {}
-        return false;
-    }
-
-    /**
-     * Sets the {@link AlpacaWebsocketMessageListener} for this {@link AlpacaWebsocketInterface}.
-     *
-     * @param listener the {@link AlpacaWebsocketMessageListener}
-     */
-    void setListener(L listener);
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
deleted file mode 100644
index da55535c..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.jacobpeterson.alpaca.websocket;
-
-/**
- * {@link AlpacaWebsocketMessageListener} defines a listener interface for messages from an {@link AlpacaWebsocket}
- * instances.
- *
- * @param <T> the 'message type' type parameter
- * @param <M> the 'message' type parameter
- */
-@FunctionalInterface
-public interface AlpacaWebsocketMessageListener<T, M> {
-
-    /**
-     * Called when a message is received.
-     *
-     * @param messageType the message type
-     * @param message     the message
-     */
-    void onMessage(T messageType, M message);
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java
deleted file mode 100644
index 4f3713b6..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/WebsocketStateListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package net.jacobpeterson.alpaca.websocket;
-
-import okhttp3.Response;
-import okhttp3.WebSocket;
-
-/**
- * {@link WebsocketStateListener} is an interface to listen for various state changes in an {@link WebSocket} instance.
- */
-public interface WebsocketStateListener {
-
-    /**
-     * Called when the {@link WebSocket} is connected.
-     *
-     * @param response the HTTP websocket upgrade {@link Response}
-     */
-    void onOpen(Response response);
-
-    /**
-     * Called when the {@link WebSocket} is disconnected.
-     *
-     * @param code   the code
-     * @param reason the reason
-     */
-    void onClosed(int code, String reason);
-
-    /**
-     * Called when a {@link WebSocket} error has occurred.
-     *
-     * @param cause the cause {@link Throwable}
-     */
-    void onFailure(Throwable cause);
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
deleted file mode 100644
index 0f760d7e..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataListener.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.marketdata;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketMessageListener;
-
-/**
- * {@link MarketDataListener} defines a listener interface for {@link MarketDataWebsocket} messages.
- */
-public interface MarketDataListener extends AlpacaWebsocketMessageListener<MarketDataMessageType, MarketDataMessage> {
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
deleted file mode 100644
index 48c297f9..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
+++ /dev/null
@@ -1,387 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.marketdata;
-
-import com.google.common.collect.Iterables;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.ErrorMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SubscriptionsMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SuccessMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.common.NewsArticle;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.news.realtime.NewsMessage;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.WebSocket;
-import org.jetbrains.annotations.NotNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.reflect.Type;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Predicates.not;
-import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
-
-/**
- * {@link MarketDataWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
- * {@link MarketDataWebsocketInterface} interface for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/">Realtime Market Data</a> for both crypto
- * and stocks.
- */
-public abstract class MarketDataWebsocket
-        extends AlpacaWebsocket<MarketDataMessageType, MarketDataMessage, MarketDataListener>
-        implements MarketDataWebsocketInterface {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(MarketDataWebsocket.class);
-    private static final String MESSAGE_TYPE_ELEMENT_KEY = "T";
-    private static final List<String> AUTH_FAILURE_MESSAGES = Arrays.asList(
-            "auth failed",
-            "auth timeout",
-            "not authenticated");
-    private static final List<MarketDataMessageType> SUBSCRIBABLE_MARKET_DATA_MESSAGE_TYPES = Arrays.asList(
-            MarketDataMessageType.TRADE,
-            MarketDataMessageType.QUOTE,
-            MarketDataMessageType.BAR,
-            MarketDataMessageType.NEWS);
-
-    /**
-     * Creates a {@link HttpUrl} for {@link MarketDataWebsocket} with the given <code>websocketURLPathSegments</code>.
-     *
-     * @param websocketURLPathSegments the websocket URL path segments
-     *
-     * @return a {@link HttpUrl}
-     */
-    private static HttpUrl createWebsocketURL(String websocketURLPathSegments) {
-        return new HttpUrl.Builder()
-                .scheme("https") // HttpUrl.Builder doesn't recognize "wss" scheme, but "https" works fine
-                .host("stream.data.alpaca.markets")
-                .addPathSegments(websocketURLPathSegments)
-                .build();
-    }
-
-    private final Set<MarketDataMessageType> listenedMarketDataMessageTypes;
-    private final Set<String> subscribedTrades;
-    private final Set<String> subscribedQuotes;
-    private final Set<String> subscribedBars;
-    private final Set<String> subscribedNews;
-    private final Type tradeClassType;
-    private final Type quoteClassType;
-    private final Type barClassType;
-    private final Type newsClassType = NewsMessage.class;
-
-    /**
-     * Instantiates a new {@link MarketDataWebsocket}.
-     *
-     * @param okHttpClient                the {@link OkHttpClient}
-     * @param websocketURLPathSegments    the websocket URL path segments
-     * @param websocketMarketDataTypeName the websocket market data type name {@link String}
-     * @param keyID                       the key ID
-     * @param secretKey                   the secret key
-     * @param tradeClass                  the {@link TradeMessage} {@link Class} to deserialize data into
-     * @param quoteClass                  the {@link QuoteMessage} {@link Class} to deserialize data into
-     * @param barClass                    the {@link BarMessage} {@link Class} to deserialize data into
-     */
-    public MarketDataWebsocket(OkHttpClient okHttpClient, String websocketURLPathSegments,
-            String websocketMarketDataTypeName, String keyID, String secretKey,
-            Class<? extends TradeMessage> tradeClass, Class<? extends QuoteMessage> quoteClass,
-            Class<? extends BarMessage> barClass) {
-        super(okHttpClient, createWebsocketURL(websocketURLPathSegments), websocketMarketDataTypeName + " Market Data",
-                keyID, secretKey, null);
-
-        listenedMarketDataMessageTypes = new HashSet<>();
-        subscribedTrades = new HashSet<>();
-        subscribedQuotes = new HashSet<>();
-        subscribedBars = new HashSet<>();
-        subscribedNews = new HashSet<>();
-        this.tradeClassType = tradeClass;
-        this.quoteClassType = quoteClass;
-        this.barClassType = barClass;
-    }
-
-    @Override
-    protected void cleanupState() {
-        super.cleanupState();
-
-        listenedMarketDataMessageTypes.clear();
-    }
-
-    @Override
-    protected void onConnection() {
-        sendAuthenticationMessage();
-    }
-
-    @Override
-    protected void onReconnection() {
-        sendAuthenticationMessage();
-        if (waitForAuthorization(5, TimeUnit.SECONDS)) {
-            subscribeToControl(Iterables.toArray(listenedMarketDataMessageTypes, MarketDataMessageType.class));
-            subscribe(subscribedTrades, subscribedQuotes, subscribedBars, subscribedNews);
-        }
-    }
-
-    @Override
-    protected void sendAuthenticationMessage() {
-        // Ensures that 'authenticationMessageFuture' exists
-        getAuthorizationFuture();
-
-        /* Format of message is:
-         * {
-         *   "action": "auth",
-         *   "key": "{APCA-API-KEY-ID}",
-         *   "secret": "{APCA-API-SECRET-KEY}"
-         * }
-         */
-
-        JsonObject authObject = new JsonObject();
-        authObject.addProperty("action", "auth");
-        authObject.addProperty("key", keyID);
-        authObject.addProperty("secret", secretKey);
-
-        LOGGER.info("{} websocket sending authentication message...", websocketName);
-        websocket.send(authObject.toString());
-    }
-
-    // This websocket uses string frames and not binary frames.
-    @Override
-    public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) {
-        LOGGER.trace("{}", message);
-
-        JsonElement messageElement = JsonParser.parseString(message);
-        checkState(messageElement instanceof JsonArray, "Message must be a JsonArray! Received: %s", messageElement);
-
-        JsonArray messageArray = messageElement.getAsJsonArray();
-        for (JsonElement arrayElement : messageArray) {
-            checkState(arrayElement instanceof JsonObject,
-                    "'arrayElement' must be a JsonObject! Received: %s", arrayElement);
-
-            JsonObject messageObject = arrayElement.getAsJsonObject();
-            MarketDataMessageType marketDataMessageType = GSON.fromJson(
-                    messageObject.get(MESSAGE_TYPE_ELEMENT_KEY), MarketDataMessageType.class);
-            checkNotNull(marketDataMessageType, "MarketDataMessageType not found in message: %s", messageObject);
-
-            MarketDataMessage marketDataMessage;
-            switch (marketDataMessageType) {
-                case SUCCESS:
-                    marketDataMessage = GSON.fromJson(messageObject, SuccessMessage.class);
-
-                    if (isSuccessMessageAuthenticated((SuccessMessage) marketDataMessage)) {
-                        LOGGER.info("{} websocket authenticated.", websocketName);
-
-                        authenticated = true;
-
-                        if (authenticationMessageFuture != null) {
-                            authenticationMessageFuture.complete(true);
-                        }
-                    }
-                    break;
-                case ERROR:
-                    marketDataMessage = GSON.fromJson(messageObject, ErrorMessage.class);
-
-                    if (isErrorMessageAuthFailure((ErrorMessage) marketDataMessage) &&
-                            authenticationMessageFuture != null) {
-                        LOGGER.error("{} websocket not authenticated! Received: {}.", websocketName, marketDataMessage);
-
-                        authenticated = false;
-
-                        if (authenticationMessageFuture != null) {
-                            authenticationMessageFuture.complete(false);
-                        }
-                    } else {
-                        LOGGER.error("{} websocket error message: {}", websocketName, marketDataMessage);
-                    }
-                    break;
-                case SUBSCRIPTION:
-                    marketDataMessage = GSON.fromJson(messageObject, SubscriptionsMessage.class);
-
-                    // Update 'listenedMarketDataMessageTypes' and the associated subscribed symbols lists
-                    SubscriptionsMessage subscriptionsMessage = (SubscriptionsMessage) marketDataMessage;
-                    handleSubscriptionMessageList(MarketDataMessageType.TRADE, subscriptionsMessage.getTrades(),
-                            subscribedTrades);
-                    handleSubscriptionMessageList(MarketDataMessageType.QUOTE, subscriptionsMessage.getQuotes(),
-                            subscribedQuotes);
-                    handleSubscriptionMessageList(MarketDataMessageType.BAR, subscriptionsMessage.getBars(),
-                            subscribedBars);
-                    handleSubscriptionMessageList(MarketDataMessageType.NEWS, subscriptionsMessage.getNews(),
-                            subscribedNews);
-                    break;
-                case TRADE:
-                    marketDataMessage = GSON.fromJson(messageObject, tradeClassType);
-                    break;
-                case QUOTE:
-                    marketDataMessage = GSON.fromJson(messageObject, quoteClassType);
-                    break;
-                case BAR:
-                    marketDataMessage = GSON.fromJson(messageObject, barClassType);
-                    break;
-                case NEWS:
-                    marketDataMessage = GSON.fromJson(messageObject, newsClassType);
-                    break;
-                default:
-                    LOGGER.error("Message type {} not implemented!", marketDataMessageType);
-                    continue;
-            }
-
-            if (listenedMarketDataMessageTypes.contains(marketDataMessageType)) {
-                callListener(marketDataMessageType, marketDataMessage);
-            }
-        }
-    }
-
-    /**
-     * Returns true if {@link SuccessMessage#getMessage()} equals "authenticated".
-     *
-     * @param successMessage the {@link SuccessMessage}
-     *
-     * @return a boolean
-     */
-    private boolean isSuccessMessageAuthenticated(SuccessMessage successMessage) {
-        return successMessage.getMessage().equalsIgnoreCase("authenticated");
-    }
-
-    /**
-     * Handles a {@link SubscriptionsMessage} for updating {@link #listenedMarketDataMessageTypes} and returns a
-     * {@link List} of currently subscribe symbols or <code>null</code>.
-     *
-     * @param marketDataMessageType   the {@link MarketDataMessageType}
-     * @param newSubscribedSymbols    the new subscribed symbols from {@link SubscriptionsMessage}
-     * @param currentSubscribeSymbols the currently subscribe symbols {@link Set}
-     */
-    private void handleSubscriptionMessageList(MarketDataMessageType marketDataMessageType,
-            Collection<String> newSubscribedSymbols, Set<String> currentSubscribeSymbols) {
-        if (newSubscribedSymbols != null && !newSubscribedSymbols.isEmpty()) {
-            listenedMarketDataMessageTypes.add(marketDataMessageType);
-            currentSubscribeSymbols.clear();
-            currentSubscribeSymbols.addAll(newSubscribedSymbols);
-        } else {
-            listenedMarketDataMessageTypes.remove(marketDataMessageType);
-            currentSubscribeSymbols.clear();
-        }
-    }
-
-    /**
-     * Returns true if {@link ErrorMessage#getMessage()} is any of {@link #AUTH_FAILURE_MESSAGES}.
-     *
-     * @param errorMessage the {@link ErrorMessage}
-     *
-     * @return a boolean
-     */
-    private boolean isErrorMessageAuthFailure(ErrorMessage errorMessage) {
-        return AUTH_FAILURE_MESSAGES.contains(errorMessage.getMessage().toLowerCase());
-    }
-
-    @Override
-    public void subscribeToControl(MarketDataMessageType... marketDataMessageTypes) {
-        if (marketDataMessageTypes == null) {
-            return;
-        }
-
-        Arrays.stream(marketDataMessageTypes)
-                .filter(not(SUBSCRIBABLE_MARKET_DATA_MESSAGE_TYPES::contains))
-                .forEach(listenedMarketDataMessageTypes::add);
-    }
-
-    @Override
-    public void subscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
-            Collection<String> barSymbols, Collection<String> newsSymbols) {
-        sendSubscriptionUpdate(tradeSymbols, quoteSymbols, barSymbols, newsSymbols, true);
-    }
-
-    @Override
-    public void unsubscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
-            Collection<String> barSymbols, Collection<String> newsSymbols) {
-        sendSubscriptionUpdate(tradeSymbols, quoteSymbols, barSymbols, newsSymbols, false);
-    }
-
-    /**
-     * Sends a subscription update request.
-     *
-     * @param tradeSymbols a {@link Collection} of symbols to update for trades or <code>null</code> for no change
-     * @param quoteSymbols a {@link Collection} of symbols to update for quotes or <code>null</code> for no change
-     * @param barSymbols   a {@link Collection} of symbols to update for bars or <code>null</code> for no change
-     * @param subscribe    true to subscribe, false to unsubscribe
-     */
-    private void sendSubscriptionUpdate(Collection<String> tradeSymbols, Collection<String> quoteSymbols,
-            Collection<String> barSymbols, Collection<String> newsSymbols, boolean subscribe) {
-        if (!isConnected()) {
-            throw new IllegalStateException("This websocket must be connected before sending subscription updates!");
-        }
-
-        /* Format of message is:
-         * {
-         *   "action": "subscribe",
-         *   "trades": ["AAPL"],
-         *   "quotes": ["AMD", "CLDR"],
-         *   "bars": ["*"]
-         * }
-         */
-
-        JsonObject subscriptionUpdateObject = new JsonObject();
-        subscriptionUpdateObject.addProperty("action", subscribe ? "subscribe" : "unsubscribe");
-
-        addSubscriptionUpdateList(subscriptionUpdateObject, "trades", tradeSymbols);
-        addSubscriptionUpdateList(subscriptionUpdateObject, "quotes", quoteSymbols);
-        addSubscriptionUpdateList(subscriptionUpdateObject, "bars", barSymbols);
-        addSubscriptionUpdateList(subscriptionUpdateObject, "news", newsSymbols);
-
-        boolean updateExists = subscriptionUpdateObject.size() > 1;
-        if (updateExists) {
-            websocket.send(subscriptionUpdateObject.toString());
-            LOGGER.info("Requested subscriptions update: {}.", subscriptionUpdateObject);
-        }
-    }
-
-    /**
-     * Adds <code>symbols</code> to <code>subscriptionUpdateObject</code> with <code>elementKey</code> if not empty.
-     *
-     * @param subscriptionUpdateObject the subscription update {@link JsonObject}
-     * @param elementKey               the element key
-     * @param symbols                  the symbols {@link Collection}
-     */
-    private void addSubscriptionUpdateList(JsonObject subscriptionUpdateObject, String elementKey,
-            Collection<String> symbols) {
-        if (symbols != null && !symbols.isEmpty()) {
-            JsonArray symbolArray = new JsonArray();
-            symbols.forEach(symbolArray::add);
-            subscriptionUpdateObject.add(elementKey, symbolArray);
-        }
-    }
-
-    @Override
-    public Collection<MarketDataMessageType> subscribedControls() {
-        return new HashSet<>(listenedMarketDataMessageTypes);
-    }
-
-    @Override
-    public Collection<String> subscribedTrades() {
-        return new HashSet<>(subscribedTrades);
-    }
-
-    @Override
-    public Collection<String> subscribedQuotes() {
-        return new HashSet<>(subscribedQuotes);
-    }
-
-    @Override
-    public Collection<String> subscribedBars() {
-        return new HashSet<>(subscribedBars);
-    }
-
-    @Override
-    public Collection<String> subscribedNews() {
-        return new HashSet<>(subscribedNews);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
deleted file mode 100644
index d4f7700c..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.marketdata;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.ErrorMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SubscriptionsMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.control.SuccessMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
-
-import java.util.Collection;
-
-/**
- * {@link MarketDataWebsocketInterface} is an {@link AlpacaWebsocketInterface} for a {@link MarketDataWebsocket}.
- */
-public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface<MarketDataListener> {
-
-    /**
-     * Subscribe to a specific control {@link MarketDataMessage} which contain information about the stream's current
-     * state. That is, an {@link ErrorMessage}, {@link SubscriptionsMessage}, or {@link SuccessMessage}.
-     *
-     * @param marketDataMessageTypes array containing any of the following: {@link MarketDataMessageType#SUCCESS},
-     *                               {@link MarketDataMessageType#ERROR}, or {@link MarketDataMessageType#SUBSCRIPTION}
-     */
-    void subscribeToControl(MarketDataMessageType... marketDataMessageTypes);
-
-    /**
-     * Subscribes to trades, quotes, or bars according to the given {@link Collection} of symbols.
-     * <br>
-     * Note that any one of the given {@link Collection}s can contain the wildcard character e.g. "*" to subscribe to
-     * ALL available symbols.
-     *
-     * @param tradeSymbols a {@link Collection} of symbols to subscribe to trades or <code>null</code> for no change
-     * @param quoteSymbols a {@link Collection} of symbols to subscribe to quotes or <code>null</code> for no change
-     * @param barSymbols   a {@link Collection} of symbols to subscribe to bars or <code>null</code> for no change
-     *
-     * @see #unsubscribe(Collection, Collection, Collection, Collection)
-     */
-    void subscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols, Collection<String> newsSymbols);
-
-    /**
-     * Unsubscribes from trades, quotes, or bars according to the given {@link Collection} of symbols.
-     * <br>
-     * Note that any one of the given {@link Collection}s can contain the wildcard character (e.g. "*") to unsubscribe
-     * from a previously subscribed wildcard.
-     *
-     * @param tradeSymbols a {@link Collection} of symbols to unsubscribe from trades or <code>null</code> for no
-     *                     change
-     * @param quoteSymbols a {@link Collection} of symbols to unsubscribe from quotes or <code>null</code> for no
-     *                     change
-     * @param barSymbols   a {@link Collection} of symbols to unsubscribe from bars or <code>null</code> for no change
-     *
-     * @see #subscribe(Collection, Collection, Collection, Collection)
-     */
-    void unsubscribe(Collection<String> tradeSymbols, Collection<String> quoteSymbols, Collection<String> barSymbols, Collection<String> newsSymbols);
-
-    /**
-     * Gets all the currently subscribed control {@link MarketDataMessageType}s.
-     *
-     * @return a {@link Collection} of {@link MarketDataMessageType}s
-     */
-    Collection<MarketDataMessageType> subscribedControls();
-
-    /**
-     * Gets all the currently subscribed symbols for trade updates.
-     *
-     * @return a {@link Collection} of {@link String}s
-     */
-    Collection<String> subscribedTrades();
-
-    /**
-     * Gets all the currently subscribed symbols for quote updates.
-     *
-     * @return a {@link Collection} of {@link String}s
-     */
-    Collection<String> subscribedQuotes();
-
-    /**
-     * Gets all the currently subscribed symbols for bar updates.
-     *
-     * @return a {@link Collection} of {@link String}s
-     */
-    Collection<String> subscribedBars();
-
-    Collection<String> subscribedNews();
-
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
deleted file mode 100644
index 27168332..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.marketdata.crypto;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.bar.CryptoBarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.quote.CryptoQuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.trade.CryptoTradeMessage;
-import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
-import okhttp3.OkHttpClient;
-
-/**
- * {@link CryptoMarketDataWebsocket} is a {@link MarketDataWebsocket} for
- * <a href="https://docs.alpaca.markets/docs/real-time-crypto-pricing-data">Realtime
- * Crypto Market Data</a>
- */
-public class CryptoMarketDataWebsocket extends MarketDataWebsocket {
-
-    /**
-     * Instantiates a new {@link CryptoMarketDataWebsocket}.
-     *
-     * @param okHttpClient the {@link OkHttpClient}
-     * @param keyID        the key ID
-     * @param secretKey    the secret key
-     */
-    public CryptoMarketDataWebsocket(OkHttpClient okHttpClient, String keyID, String secretKey) {
-        super(okHttpClient, "v1beta3/crypto/us", "Crypto", keyID, secretKey, CryptoTradeMessage.class,
-                CryptoQuoteMessage.class, CryptoBarMessage.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java
deleted file mode 100644
index 9d2c2620..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.marketdata.news;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
-import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
-import okhttp3.OkHttpClient;
-
-public class NewsMarketDataWebsocket extends MarketDataWebsocket {
-    public NewsMarketDataWebsocket(OkHttpClient okHttpClient,
-            String keyID, String secretKey) {
-        super(okHttpClient, "v1beta1/news", "News", keyID, secretKey, TradeMessage.class,
-                QuoteMessage.class, BarMessage.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java
deleted file mode 100644
index 4b897f0d..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.marketdata.stock;
-
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.bar.StockBarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.quote.StockQuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.trade.StockTradeMessage;
-import net.jacobpeterson.alpaca.model.properties.DataAPIType;
-import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
-import okhttp3.OkHttpClient;
-
-/**
- * {@link StockMarketDataWebsocket} is a {@link MarketDataWebsocket} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/">Realtime
- * Stock Market Data</a>
- */
-public class StockMarketDataWebsocket extends MarketDataWebsocket {
-
-    /**
-     * Instantiates a new {@link StockMarketDataWebsocket}.
-     *
-     * @param okHttpClient the {@link OkHttpClient}
-     * @param dataAPIType  the {@link DataAPIType}
-     * @param keyID        the key ID
-     * @param secretKey    the secret key
-     */
-    public StockMarketDataWebsocket(OkHttpClient okHttpClient, DataAPIType dataAPIType,
-            String keyID, String secretKey) {
-        super(okHttpClient, "v2/" + dataAPIType.toString(), "Stock", keyID, secretKey, StockTradeMessage.class,
-                StockQuoteMessage.class, StockBarMessage.class);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingListener.java
deleted file mode 100644
index 3ebf714d..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingListener.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.streaming;
-
-import net.jacobpeterson.alpaca.model.endpoint.streaming.StreamingMessage;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketMessageListener;
-
-/**
- * {@link StreamingListener} defines a listener interface for {@link StreamingWebsocket} messages.
- */
-public interface StreamingListener extends AlpacaWebsocketMessageListener<StreamingMessageType, StreamingMessage> {
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
deleted file mode 100644
index 2fccb63e..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.streaming;
-
-import com.google.common.collect.Iterables;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.StreamingMessage;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.authorization.AuthorizationData;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.authorization.AuthorizationMessage;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.listening.ListeningMessage;
-import net.jacobpeterson.alpaca.model.endpoint.streaming.trade.TradeUpdateMessage;
-import net.jacobpeterson.alpaca.model.properties.EndpointAPIType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.WebSocket;
-import okio.ByteString;
-import org.jetbrains.annotations.NotNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Predicates.not;
-import static net.jacobpeterson.alpaca.util.gson.GsonUtil.GSON;
-
-/**
- * {@link StreamingWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
- * {@link StreamingWebsocketInterface} interface for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/streaming/">Streaming</a>
- */
-public class StreamingWebsocket extends AlpacaWebsocket<StreamingMessageType, StreamingMessage, StreamingListener>
-        implements StreamingWebsocketInterface {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(StreamingWebsocket.class);
-    private static final String STREAM_ELEMENT_KEY = "stream";
-    private static final List<StreamingMessageType> SUBSCRIBABLE_STREAMING_MESSAGE_TYPES = Collections.singletonList(
-            StreamingMessageType.TRADE_UPDATES);
-
-    /**
-     * Creates a {@link HttpUrl} for {@link StreamingWebsocket} with the given <code>alpacaSubdomain</code>.
-     *
-     * @param endpointAPIType the {@link EndpointAPIType}
-     *
-     * @return a {@link HttpUrl}
-     */
-    @SuppressWarnings("UnnecessaryDefault")
-    private static HttpUrl createWebsocketURL(EndpointAPIType endpointAPIType) {
-        return new HttpUrl.Builder()
-                .scheme("https") // HttpUrl.Builder doesn't recognize "wss" scheme, but "https" works fine
-                .host((switch (endpointAPIType) {
-                    case LIVE -> "api";
-                    case PAPER -> "paper-api";
-                    default -> throw new UnsupportedOperationException();
-                }) + ".alpaca.markets")
-                .addPathSegment("stream")
-                .build();
-    }
-
-    private final Set<StreamingMessageType> listenedStreamMessageTypes;
-
-    /**
-     * Instantiates a new {@link StreamingWebsocket}.
-     *
-     * @param okHttpClient    the {@link OkHttpClient}
-     * @param endpointAPIType the {@link EndpointAPIType}
-     * @param keyID           the key ID
-     * @param secretKey       the secret key
-     * @param oAuthToken      the OAuth token
-     */
-    public StreamingWebsocket(OkHttpClient okHttpClient, EndpointAPIType endpointAPIType,
-            String keyID, String secretKey, String oAuthToken) {
-        super(okHttpClient, createWebsocketURL(endpointAPIType), "Streaming", keyID, secretKey, oAuthToken);
-
-        listenedStreamMessageTypes = new HashSet<>();
-    }
-
-    @Override
-    protected void cleanupState() {
-        super.cleanupState();
-
-        listenedStreamMessageTypes.clear();
-    }
-
-    @Override
-    protected void onConnection() {
-        sendAuthenticationMessage();
-    }
-
-    @Override
-    protected void onReconnection() {
-        sendAuthenticationMessage();
-        if (waitForAuthorization(5, TimeUnit.SECONDS)) {
-            streams(Iterables.toArray(listenedStreamMessageTypes, StreamingMessageType.class));
-        }
-    }
-
-    @Override
-    protected void sendAuthenticationMessage() {
-        // Ensures that 'authenticationMessageFuture' exists
-        getAuthorizationFuture();
-
-        /* Authentication message format:
-         *  {
-         *      "action": "authenticate",
-         *      "data": {
-         *          "key_id": "{YOUR_API_KEY_ID}",
-         *          "secret_key": "{YOUR_API_SECRET_KEY}"
-         *      }
-         *  }
-         */
-
-        JsonObject authObject = new JsonObject();
-        authObject.addProperty("action", "authenticate");
-
-        JsonObject authData = new JsonObject();
-
-        if (useOAuth) {
-            authData.addProperty("oauth_token", oAuthToken);
-        } else {
-            authData.addProperty("key_id", keyID);
-            authData.addProperty("secret_key", secretKey);
-        }
-
-        authObject.add("data", authData);
-
-        LOGGER.info("{} websocket sending authentication message...", websocketName);
-        websocket.send(authObject.toString());
-    }
-
-    // This websocket uses binary frames and not text frames.
-    @Override
-    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) {
-        final String message = byteString.utf8();
-        LOGGER.trace("{}", message);
-
-        JsonElement messageElement = JsonParser.parseString(message);
-        checkState(messageElement instanceof JsonObject, "Message must be a JsonObject! Received: %s", messageElement);
-
-        JsonObject messageObject = messageElement.getAsJsonObject();
-        JsonElement streamElement = messageObject.get(STREAM_ELEMENT_KEY);
-        checkState(streamElement instanceof JsonPrimitive,
-                "Message must contain %s element! Received: %s", STREAM_ELEMENT_KEY, messageElement);
-
-        StreamingMessageType streamingMessageType = GSON.fromJson(streamElement, StreamingMessageType.class);
-        checkNotNull(streamingMessageType, "StreamingMessageType not found in message: %s", messageObject);
-
-        StreamingMessage streamingMessage;
-        switch (streamingMessageType) {
-            case AUTHORIZATION:
-                streamingMessage = GSON.fromJson(messageObject, AuthorizationMessage.class);
-
-                authenticated = isAuthorizationMessageSuccess((AuthorizationMessage) streamingMessage);
-
-                if (!authenticated) {
-                    LOGGER.error("{} websocket not authenticated! Received: {}.", websocketName, streamingMessage);
-                } else {
-                    LOGGER.info("{} websocket authenticated.", websocketName);
-                }
-
-                if (authenticationMessageFuture != null) {
-                    authenticationMessageFuture.complete(authenticated);
-                }
-                break;
-            case LISTENING:
-                streamingMessage = GSON.fromJson(messageObject, ListeningMessage.class);
-
-                // Remove all 'StreamingMessageType's that are no longer listened to and add new ones
-                List<StreamingMessageType> currentTypes = ((ListeningMessage) streamingMessage).getData().getStreams();
-                currentTypes.stream()
-                        .filter(not(listenedStreamMessageTypes::contains))
-                        .forEach(listenedStreamMessageTypes::remove);
-                listenedStreamMessageTypes.addAll(currentTypes);
-                break;
-            case TRADE_UPDATES:
-                streamingMessage = GSON.fromJson(messageObject, TradeUpdateMessage.class);
-                break;
-            default:
-                throw new UnsupportedOperationException();
-        }
-
-        if (listenedStreamMessageTypes.contains(streamingMessageType)) {
-            callListener(streamingMessageType, streamingMessage);
-        }
-    }
-
-    /**
-     * Returns true if {@link AuthorizationMessage} states success.
-     *
-     * @param authorizationMessage the {@link AuthorizationMessage}
-     *
-     * @return a boolean
-     */
-    private boolean isAuthorizationMessageSuccess(AuthorizationMessage authorizationMessage) {
-        AuthorizationData authorizationData = authorizationMessage.getData();
-        return authorizationData.getStatus().equalsIgnoreCase("authorized") &&
-                authorizationData.getAction().equalsIgnoreCase("authenticate");
-    }
-
-    @Override
-    public void streams(StreamingMessageType... streamingMessageTypes) {
-        if (streamingMessageTypes == null || streamingMessageTypes.length == 0) {
-            return;
-        }
-
-        // Add all non-subscribable 'streamingMessageTypes' before connecting or sending websocket message
-        Arrays.stream(streamingMessageTypes)
-                .filter(not(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains))
-                .forEach(listenedStreamMessageTypes::add);
-
-        // Stream request format:
-        // {
-        //     "action": "listen",
-        //     "data": {
-        //         "streams": ["trade_updates"]
-        //     }
-        // }
-
-        JsonObject requestObject = new JsonObject();
-        requestObject.addProperty("action", "listen");
-
-        JsonArray streamsArray = new JsonArray();
-        Arrays.stream(streamingMessageTypes)
-                .filter(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains)
-                .forEach((type) -> streamsArray.add(type.toString()));
-
-        if (streamsArray.isEmpty()) {
-            return;
-        } else if (!isConnected()) {
-            throw new IllegalStateException("This websocket must be connected before subscribing to streams!");
-        }
-
-        JsonObject dataObject = new JsonObject();
-        dataObject.add("streams", streamsArray);
-
-        requestObject.add("data", dataObject);
-
-        websocket.send(requestObject.toString());
-        LOGGER.info("Requested streams: {}.", streamsArray);
-    }
-
-    @Override
-    public Collection<StreamingMessageType> streams() {
-        return new HashSet<>(listenedStreamMessageTypes);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
deleted file mode 100644
index 44f0042e..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocketInterface.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.streaming;
-
-import net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
-
-import java.util.Collection;
-
-/**
- * {@link StreamingWebsocketInterface} is an {@link AlpacaWebsocketInterface} for a {@link StreamingWebsocket}.
- */
-public interface StreamingWebsocketInterface extends AlpacaWebsocketInterface<StreamingListener> {
-
-    /**
-     * Sets the {@link StreamingMessageType}s for this stream to the given <code>streamingMessageTypes</code>.
-     *
-     * @param streamingMessageTypes the {@link StreamingMessageType}s
-     */
-    void streams(StreamingMessageType... streamingMessageTypes);
-
-    /**
-     * Gets all the currently subscribed {@link StreamingMessageType}s.
-     *
-     * @return a {@link Collection} of {@link StreamingMessageType}
-     */
-    Collection<StreamingMessageType> streams();
-}
diff --git a/src/main/resources/alpaca.default.properties b/src/main/resources/alpaca.default.properties
deleted file mode 100644
index e3629268..00000000
--- a/src/main/resources/alpaca.default.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-endpoint_api_type:paper
-data_api_type:iex

From a084b1b5ebfc4bc4557148611c859a62f0284c95 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 3 Mar 2024 14:30:43 -0800
Subject: [PATCH 59/85] WIP implement Broker 'events' API SSE capability

---
 .gitignore                                    |   2 -
 README.md                                     |  27 +--
 build.gradle                                  | 126 ++++++------
 gradle/wrapper/.gitignore                     |   1 -
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |  76 ++++----
 .../jacobpeterson/alpaca/AlpacaBrokerAPI.java |  19 +-
 .../alpaca/AlpacaMarketDataAPI.java           |   3 +-
 .../alpaca/rest/broker/EventsApiSSE.java      | 180 ++++++++++++++++++
 ...> market_data_api_stream_source_type.json} |   2 +-
 .../alpaca/util/sse/SSEListener.java          |  38 ++++
 .../alpaca/util/sse/SSEListenerAdapter.java   |  33 ++++
 .../alpaca/util/sse/SSERequest.java           |  36 ++++
 12 files changed, 416 insertions(+), 127 deletions(-)
 delete mode 100644 gradle/wrapper/.gitignore
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
 rename src/main/java/net/jacobpeterson/alpaca/util/apitype/{market_data_api_source_type.json => market_data_api_stream_source_type.json} (56%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/util/sse/SSERequest.java

diff --git a/.gitignore b/.gitignore
index 985d4c2c..12a5664a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,3 @@ build/
 .settings
 .DS_Store
 .idea/
-*.properties
-!*.default.properties
diff --git a/README.md b/README.md
index 94b7e833..c5ceae7f 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 </p>
 
 # Overview
-This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate REST API clients and implements the websocket streaming interface using [OkHttp](https://square.github.io/okhttp/). This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
+This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
 
 Give this repository a star ⭐ if it helped you build a trading algorithm in Java!
 
@@ -28,17 +28,7 @@ If you are using Maven as your build tool, add the following dependency to your
 </dependency>
 ```
 
-Note that you don't have to use the Maven Central artifacts and instead can just install a clone of this project to your local Maven repository as shown in the [Building](#building) section.
-
-# Configuration
-Creating an `alpaca.properties` file on the classpath (e.g. in `src/main/resources/alpaca.properties`) with the following format allows you to easily load properties using the `AlpacaAPI` default constructor:
-```
-key_id = <your Key ID>
-secret_key = <your Secret Key>
-endpoint_api_type = <must be either "paper" or "live">
-data_api_type = <must be either "iex" or "sip">
-```
-The default values for `alpaca.properties` can be found [here](src/main/resources/alpaca.default.properties).
+Note that you don't have to use the Maven Central artifacts. Instead, you can clone this repository, build this project, and install the artifacts to your local Maven repository as shown in the [Building](#building) section.
 
 # Logger
 For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Logback](http://logback.qos.ch/), [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
@@ -76,11 +66,7 @@ AlpacaAPI alpacaAPI = AlpacaAPI.builder()
     .build();
 ```
 
-Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests. These threads persist even if the main thread exists so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) so your program can exit without calling `System.exit()`, use the following snippet:
-```java
-alpacaAPI.getOkHttpClient().dispatcher().executorService().shutdown();
-alpacaAPI.getOkHttpClient().connectionPool().evictAll();
-```
+Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests via a connection pool. These threads persist even if the main thread exits, so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java), call `alpacaAPI.closeOkHttpClient();`.
 
 See the [OkHttpClient Documentation](https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/#shutdown-isnt-necessary) for more information.
 
@@ -532,16 +518,15 @@ To build this project yourself, clone this repository and run:
 
 To install the built artifacts to your local Maven repository on your machine (the `~/.m2/` directory), run:
 ```
-./gradlew install
+./gradlew publishToMavenLocal
 ```
 
 # TODO
 - Implement Unit Testing for REST API and Websocket streaming (both live and mocked)
-- Implement Broker API websockets
 
 # Contributing
 Contributions are welcome!
 
-When creating a Pull Request, keep the following in mind:
+Do the following before starting your Pull Request:
 1. Create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork.
-2. Use the `dev` branch in this repository as the base branch in your Pull Request.
+2. Use the `dev` branch as the base branch in your Pull Request.
diff --git a/build.gradle b/build.gradle
index b13ea565..1c8e27b1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -48,6 +48,7 @@ dependencies {
 
     // OkHttp
     implementation group: "com.squareup.okhttp3", name: "okhttp", version: "5.0.0-alpha.12"
+    implementation group: "com.squareup.okhttp3", name: "okhttp-sse", version: "5.0.0-alpha.12"
     implementation group: "com.squareup.okhttp3", name: "logging-interceptor", version: "5.0.0-alpha.12"
 
     // Libraries used by the OpenAPI generated client libraries
@@ -69,6 +70,9 @@ java {
 }
 javadocJar.dependsOn generatePOJOs
 sourcesJar.dependsOn generatePOJOs
+jar.dependsOn compileJava
+javadocJar.dependsOn compileJava
+sourcesJar.dependsOn compileJava
 
 [compileJava, compileTestJava]*.options*.encoding = "UTF-8"
 
@@ -93,19 +97,10 @@ final File specDownloadPath = new File(project.layout.buildDirectory.get().getAs
 final File generatedClientLibrariesPath = new File(project.layout.buildDirectory.get().getAsFile(),
         "/generated/openapi/")
 
-final def generateOpenAPIClientsTasks = tasks.register("generateOpenAPIClients") {
-    doLast {
-        fixOpenAPIGeneratedClientIssues(specIDsOfFileNames, generatedClientLibrariesPath)
-    }
-}
-compileJava.dependsOn generateOpenAPIClientsTasks
-jar.dependsOn generateOpenAPIClientsTasks
-javadocJar.dependsOn generateOpenAPIClientsTasks
-sourcesJar.dependsOn generateOpenAPIClientsTasks
-
 def downloadOpenAPISpecFilesTask = tasks.register("downloadOpenAPISpecFiles") {
     configure {
         outputs.dir(specDownloadPath)
+        outputs.cacheIf { true }
     }
 
     doLast {
@@ -131,6 +126,16 @@ def downloadOpenAPISpecFilesTask = tasks.register("downloadOpenAPISpecFiles") {
     }
 }
 
+final def generateOpenAPIClientsTask = tasks.register("generateOpenAPIClients") {
+    it.onlyIf {
+        !downloadOpenAPISpecFilesTask.get().state.upToDate &&
+                downloadOpenAPISpecFilesTask.get().state.failure == null &&
+                !downloadOpenAPISpecFilesTask.get().state.skipped &&
+                downloadOpenAPISpecFilesTask.get().state.executed
+    }
+}
+compileJava.dependsOn generateOpenAPIClientsTask
+
 for (def specFileName : specIDsOfFileNames.keySet()) {
     final def specName = specFileName.replace(".json", "")
     final def inputSpecFile = new File(specDownloadPath, specFileName)
@@ -151,16 +156,10 @@ for (def specFileName : specIDsOfFileNames.keySet()) {
             downloadOpenAPISpecFilesTask
         }
 
-        it.onlyIf {
-            !downloadOpenAPISpecFilesTask.get().state.upToDate &&
-                    downloadOpenAPISpecFilesTask.get().state.failure == null &&
-                    !downloadOpenAPISpecFilesTask.get().state.skipped &&
-                    downloadOpenAPISpecFilesTask.get().state.executed
-        }
-
         configure {
             inputs.file(inputSpecFile)
             outputs.dir(outputDirectory)
+            outputs.cacheIf { true }
         }
 
         generatorName.set("java")
@@ -186,52 +185,64 @@ for (def specFileName : specIDsOfFileNames.keySet()) {
         generateApiTests.set(false)
         generateApiDocumentation.set(false)
     }
-    generateOpenAPIClientsTasks.configure {
+    generateOpenAPIClientsTask.configure {
         dependsOn generateTask
     }
 }
 
-static void fixOpenAPIGeneratedClientIssues(Map<String, String> specIDsOfFileNames, File generatedClientLibrariesPath) {
-    // TODO remove these manual fixes once OpenAPI fixes these issues
-
-    final def sourceFilesPath = "src/main/java/net/jacobpeterson/alpaca/openapi"
-
-    // Fix broker spec generated client issues with discriminators
-    // on some enums: https://github.com/OpenAPITools/openapi-generator/issues/806
-    final def brokerTransferDataJavaFile = new File(generatedClientLibrariesPath,
-            "broker/${sourceFilesPath}/broker/model/TransferData.java")
-    brokerTransferDataJavaFile.text = brokerTransferDataJavaFile.text
-            .replace("this.transferType = this.getClass().getSimpleName();", "")
-    final def brokerTransferResourceJavaFile = new File(generatedClientLibrariesPath,
-            "broker/${sourceFilesPath}/broker/model/TransferResource.java")
-    brokerTransferResourceJavaFile.text = brokerTransferResourceJavaFile.text
-            .replace("this.type = this.getClass().getSimpleName();", "")
-
-    // Remove the JSON validation that the generated client models always run (there isn't an option to
-    // disable it for some reason)
-    specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
-        for (File modelFile : Paths.get(generatedClientLibrariesPath.getPath(),
-                it, sourceFilesPath, it.replace("-", ""), "model").toFile().listFiles()) {
-            modelFile.text = modelFile.text
-                    .replaceAll("/\\*\\*\\n {2}\\* Validates the JSON Element and throws an exception if issues found" +
-                            "[\\s\\S]*?\\*/", "")
-                    .replace("public static void validateJsonElement(JsonElement jsonElement) throws IOException {",
-                            "public static boolean validate = false;\n  public static void " +
-                                    "validateJsonElement(JsonElement jsonElement) throws IOException {\n" +
-                                    "    if (!validate) return;")
-        }
+final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGeneratedClientIssues") {
+    it.onlyIf {
+        !generateOpenAPIClientsTask.get().state.upToDate &&
+                generateOpenAPIClientsTask.get().state.failure == null &&
+                !generateOpenAPIClientsTask.get().state.skipped &&
+                generateOpenAPIClientsTask.get().state.executed
     }
 
-    // Remove the necessary authentication header logic from the generate client's ApiClient
-    specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
-        final def apiClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
-                it.replace("-", ""), "ApiClient.java").toFile()
-        apiClientFile.text = apiClientFile.text
-                .replaceAll("// Setup authentications \\(key: authentication name, value: authentication\\)" +
-                        "[\\s\\S]*?authentications\\);", "")
-                .replaceAll("for \\(String authName : authNames\\)[\\s\\S]*?uri\\);\\n. {7}}", "")
+    doLast {
+        // TODO remove these manual fixes once OpenAPI fixes these issues
+
+        final def sourceFilesPath = "src/main/java/net/jacobpeterson/alpaca/openapi"
+
+        // Fix broker spec generated client issues with discriminators
+        // on some enums: https://github.com/OpenAPITools/openapi-generator/issues/806
+        final def brokerTransferDataJavaFile = new File(generatedClientLibrariesPath,
+                "broker/${sourceFilesPath}/broker/model/TransferData.java")
+        brokerTransferDataJavaFile.text = brokerTransferDataJavaFile.text
+                .replace("this.transferType = this.getClass().getSimpleName();", "")
+        final def brokerTransferResourceJavaFile = new File(generatedClientLibrariesPath,
+                "broker/${sourceFilesPath}/broker/model/TransferResource.java")
+        brokerTransferResourceJavaFile.text = brokerTransferResourceJavaFile.text
+                .replace("this.type = this.getClass().getSimpleName();", "")
+
+        // Remove the JSON validation that the generated client models always run (there isn't an option to
+        // disable it for some reason)
+        specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
+            for (File modelFile : Paths.get(generatedClientLibrariesPath.getPath(),
+                    it, sourceFilesPath, it.replace("-", ""), "model").toFile().listFiles()) {
+                modelFile.text = modelFile.text
+                        .replaceAll("/\\*\\*\\n {2}\\* Validates the JSON Element and throws an exception if issues " +
+                                "found[\\s\\S]*?\\*/", "")
+                        .replace("public static void validateJsonElement(JsonElement jsonElement) throws IOException {",
+                                "public static boolean validate = false;\n  public static void " +
+                                        "validateJsonElement(JsonElement jsonElement) throws IOException {\n" +
+                                        "    if (!validate) return;")
+            }
+        }
+
+        // Remove the necessary authentication header logic from the generate client's ApiClient
+        specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
+            final def apiClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
+                    it.replace("-", ""), "ApiClient.java").toFile()
+            apiClientFile.text = apiClientFile.text
+                    .replaceAll("// Setup authentications \\(key: authentication name, value: authentication\\)" +
+                            "[\\s\\S]*?authentications\\);", "")
+                    .replaceAll("for \\(String authName : authNames\\)[\\s\\S]*?uri\\);\\n. {7}}", "")
+        }
     }
 }
+generateOpenAPIClientsTask.configure {
+    finalizedBy fixOpenAPIGeneratedClientIssuesTask
+}
 
 //
 // END Alpaca OpenAPI Specification (OAS) client generation
@@ -299,11 +310,6 @@ signing {
     sign publishing.publications.mavenJava
 }
 
-// Used to prevent accidental publication of Alpaca keys
-processResources {
-    exclude("alpaca.properties")
-}
-
 //
 // END Publishing
 //
diff --git a/gradle/wrapper/.gitignore b/gradle/wrapper/.gitignore
deleted file mode 100644
index 351625b1..00000000
--- a/gradle/wrapper/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-!gradle-wrapper.properties
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 577324ec..6e6f2da3 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -1,7 +1,7 @@
 package net.jacobpeterson.alpaca;
 
 import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
-import net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPISourceType;
+import net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPIStreamSourceType;
 import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
 import okhttp3.OkHttpClient;
 import okhttp3.logging.HttpLoggingInterceptor;
@@ -9,8 +9,9 @@
 import org.slf4j.LoggerFactory;
 
 import static net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType.SANDBOX;
-import static net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPISourceType.IEX;
+import static net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPIStreamSourceType.IEX;
 import static net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType.PAPER;
+import static okhttp3.logging.HttpLoggingInterceptor.Level.BODY;
 
 /**
  * {@link AlpacaAPI} is the main class used to interface with the various Alpaca API endpoints. If you are using the
@@ -30,7 +31,7 @@ public class AlpacaAPI {
     private final String traderSecretKey;
     private final String traderOAuthToken;
     private final TraderAPIEndpointType traderAPIEndpointType;
-    private final MarketDataAPISourceType marketDataAPISourceType;
+    private final MarketDataAPIStreamSourceType marketDataAPIStreamSourceType;
     private final String brokerAPIKey;
     private final String brokerAPISecret;
     private final BrokerAPIEndpointType brokerAPIEndpointType;
@@ -44,14 +45,14 @@ public class AlpacaAPI {
      * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading or Market Data APIs for a
      * single Alpaca account.
      *
-     * @param traderKeyID             the Trader key ID
-     * @param traderSecretKey         the Trader secret key
-     * @param traderAPIEndpointType   the {@link TraderAPIEndpointType}
-     * @param marketDataAPISourceType the {@link MarketDataAPISourceType}
+     * @param traderKeyID                   the Trader key ID
+     * @param traderSecretKey               the Trader secret key
+     * @param traderAPIEndpointType         the {@link TraderAPIEndpointType}
+     * @param marketDataAPIStreamSourceType the {@link MarketDataAPIStreamSourceType}
      */
     public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointType traderAPIEndpointType,
-            MarketDataAPISourceType marketDataAPISourceType) {
-        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPISourceType, null, null, null,
+            MarketDataAPIStreamSourceType marketDataAPIStreamSourceType) {
+        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPIStreamSourceType, null, null, null,
                 null);
     }
 
@@ -59,16 +60,16 @@ public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointTy
      * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading or Market Data APIs for a
      * single Alpaca account and a custom {@link OkHttpClient} instance.
      *
-     * @param traderKeyID             the Trader key ID
-     * @param traderSecretKey         the Trader secret key
-     * @param traderAPIEndpointType   the {@link TraderAPIEndpointType}
-     * @param marketDataAPISourceType the {@link MarketDataAPISourceType}
-     * @param okHttpClient            an existing {@link OkHttpClient} or <code>null</code> to create a new default
-     *                                instance
+     * @param traderKeyID                   the Trader key ID
+     * @param traderSecretKey               the Trader secret key
+     * @param traderAPIEndpointType         the {@link TraderAPIEndpointType}
+     * @param marketDataAPIStreamSourceType the {@link MarketDataAPIStreamSourceType}
+     * @param okHttpClient                  an existing {@link OkHttpClient} or <code>null</code> to create a new
+     *                                      default instance
      */
     public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointType traderAPIEndpointType,
-            MarketDataAPISourceType marketDataAPISourceType, OkHttpClient okHttpClient) {
-        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPISourceType, null, null, null,
+            MarketDataAPIStreamSourceType marketDataAPIStreamSourceType, OkHttpClient okHttpClient) {
+        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPIStreamSourceType, null, null, null,
                 okHttpClient);
     }
 
@@ -125,27 +126,28 @@ public AlpacaAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointT
     /**
      * Instantiates a new {@link AlpacaAPI}.
      *
-     * @param traderKeyID             the Trader key ID
-     * @param traderSecretKey         the Trader secret key
-     * @param traderOAuthToken        the Trader OAuth token
-     * @param traderAPIEndpointType   the {@link TraderAPIEndpointType}
-     * @param marketDataAPISourceType the {@link MarketDataAPISourceType}
-     * @param brokerAPIKey            the Broker API key
-     * @param brokerAPISecret         the Broker API secret
-     * @param brokerAPIEndpointType   the {@link BrokerAPIEndpointType}
-     * @param okHttpClient            an existing {@link OkHttpClient} or <code>null</code> to create a new default
-     *                                instance
+     * @param traderKeyID                   the Trader key ID
+     * @param traderSecretKey               the Trader secret key
+     * @param traderOAuthToken              the Trader OAuth token
+     * @param traderAPIEndpointType         the {@link TraderAPIEndpointType}
+     * @param marketDataAPIStreamSourceType the {@link MarketDataAPIStreamSourceType}
+     * @param brokerAPIKey                  the Broker API key
+     * @param brokerAPISecret               the Broker API secret
+     * @param brokerAPIEndpointType         the {@link BrokerAPIEndpointType}
+     * @param okHttpClient                  an existing {@link OkHttpClient} or <code>null</code> to create a new
+     *                                      default instance
      */
     public AlpacaAPI(String traderKeyID, String traderSecretKey,
             String traderOAuthToken, TraderAPIEndpointType traderAPIEndpointType,
-            MarketDataAPISourceType marketDataAPISourceType,
+            MarketDataAPIStreamSourceType marketDataAPIStreamSourceType,
             String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
             OkHttpClient okHttpClient) {
         this.traderKeyID = traderKeyID;
         this.traderSecretKey = traderSecretKey;
         this.traderOAuthToken = traderOAuthToken;
         this.traderAPIEndpointType = traderAPIEndpointType != null ? traderAPIEndpointType : PAPER;
-        this.marketDataAPISourceType = marketDataAPISourceType != null ? marketDataAPISourceType : IEX;
+        this.marketDataAPIStreamSourceType = marketDataAPIStreamSourceType != null ?
+                marketDataAPIStreamSourceType : IEX;
         this.brokerAPIKey = brokerAPIKey;
         this.brokerAPISecret = brokerAPISecret;
         this.brokerAPIEndpointType = brokerAPIEndpointType != null ? brokerAPIEndpointType : SANDBOX;
@@ -154,13 +156,23 @@ public AlpacaAPI(String traderKeyID, String traderSecretKey,
         if (okHttpClient == null) {
             OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
             if (LOGGER.isDebugEnabled()) {
-                clientBuilder.addInterceptor(new HttpLoggingInterceptor(LOGGER::debug));
+                final HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(LOGGER::debug);
+                loggingInterceptor.setLevel(BODY);
+                clientBuilder.addInterceptor(loggingInterceptor);
             }
             okHttpClient = clientBuilder.build();
         }
         this.okHttpClient = okHttpClient;
     }
 
+    /**
+     * Closes the {@link OkHttpClient}.
+     */
+    public void closeOkHttpClient() {
+        okHttpClient.dispatcher().executorService().shutdown();
+        okHttpClient.connectionPool().evictAll();
+    }
+
     /**
      * Gets the {@link OkHttpClient}.
      *
@@ -226,7 +238,7 @@ public static final class Builder {
         private String traderSecretKey;
         private String traderOAuthToken;
         private TraderAPIEndpointType traderAPIEndpointType;
-        private MarketDataAPISourceType marketDataAPISourceType;
+        private MarketDataAPIStreamSourceType marketDataAPISourceType;
         private String brokerAPIKey;
         private String brokerAPISecret;
         private BrokerAPIEndpointType brokerAPIEndpointType;
@@ -254,7 +266,7 @@ public Builder withTraderAPIEndpointType(TraderAPIEndpointType traderAPIEndpoint
             return this;
         }
 
-        public Builder withMarketDataAPISourceType(MarketDataAPISourceType marketDataAPISourceType) {
+        public Builder withMarketDataAPIStreamSourceType(MarketDataAPIStreamSourceType marketDataAPISourceType) {
             this.marketDataAPISourceType = marketDataAPISourceType;
             return this;
         }
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
index 7d9851b5..a4aa0c56 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
@@ -9,7 +9,6 @@
 import net.jacobpeterson.alpaca.openapi.broker.api.CorporateActionsApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.CountryInfoApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.DocumentsApi;
-import net.jacobpeterson.alpaca.openapi.broker.api.EventsApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.FundingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.FundingWalletsApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.JournalsApi;
@@ -20,6 +19,7 @@
 import net.jacobpeterson.alpaca.openapi.broker.api.ReportingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.TradingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.WatchlistApi;
+import net.jacobpeterson.alpaca.rest.broker.EventsApiSSE;
 import okhttp3.OkHttpClient;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -39,7 +39,7 @@ public class AlpacaBrokerAPI {
     private CorporateActionsApi corporateActions;
     private CountryInfoApi countryInfo;
     private DocumentsApi documents;
-    private EventsApi events;
+    private EventsApiSSE events;
     private FundingApi funding;
     private FundingWalletsApi fundingWallets;
     private JournalsApi journals;
@@ -60,8 +60,8 @@ public class AlpacaBrokerAPI {
      * @param okHttpClient          an existing {@link OkHttpClient} or <code>null</code> to create a new default
      *                              instance
      */
-    @SuppressWarnings("UnnecessaryDefault")
-    AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
+    @SuppressWarnings("UnnecessaryDefault") AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret,
+            BrokerAPIEndpointType brokerAPIEndpointType,
             OkHttpClient okHttpClient) {
         checkNotNull(brokerAPIKey);
         checkNotNull(brokerAPISecret);
@@ -74,7 +74,8 @@ public class AlpacaBrokerAPI {
             case PRODUCTION -> 1;
             default -> throw new UnsupportedOperationException();
         });
-        apiClient.addDefaultHeader("Authorization", createBrokerAPIAuthKey(brokerAPIKey, brokerAPISecret));
+        apiClient.addDefaultHeader("Authorization", "Basic " +
+                createBrokerAPIAuthKey(brokerAPIKey, brokerAPISecret));
     }
 
     /**
@@ -171,13 +172,13 @@ public synchronized DocumentsApi documents() {
     }
 
     /**
-     * Gets the {@link EventsApi}. Lazily instantiated.
+     * Gets the {@link EventsApiSSE}. Lazily instantiated.
      *
-     * @return the {@link EventsApi}
+     * @return the {@link EventsApiSSE}
      */
-    public synchronized EventsApi events() {
+    public synchronized EventsApiSSE events() {
         if (events == null) {
-            events = new EventsApi(apiClient);
+            events = new EventsApiSSE(apiClient);
         }
         return events;
     }
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
index 1538581a..65682a4e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
@@ -53,7 +53,8 @@ public class AlpacaMarketDataAPI {
             apiClient.addDefaultHeader("APCA-API-KEY-ID", traderKeyID);
             apiClient.addDefaultHeader("APCA-API-SECRET-KEY", traderSecretKey);
         } else {
-            apiClient.addDefaultHeader("Authorization", createBrokerAPIAuthKey(brokerAPIKey, brokerAPISecret));
+            apiClient.addDefaultHeader("Authorization", "Basic " +
+                    createBrokerAPIAuthKey(brokerAPIKey, brokerAPISecret));
         }
     }
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
new file mode 100644
index 00000000..da0ad98d
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
@@ -0,0 +1,180 @@
+package net.jacobpeterson.alpaca.rest.broker;
+
+import com.google.gson.reflect.TypeToken;
+import net.jacobpeterson.alpaca.openapi.broker.ApiClient;
+import net.jacobpeterson.alpaca.openapi.broker.ApiException;
+import net.jacobpeterson.alpaca.openapi.broker.api.EventsApi;
+import net.jacobpeterson.alpaca.openapi.broker.model.AccountStatusEvent;
+import net.jacobpeterson.alpaca.openapi.broker.model.JournalStatusEvent;
+import net.jacobpeterson.alpaca.openapi.broker.model.SubscribeToAdminActionSSE200ResponseInner;
+import net.jacobpeterson.alpaca.openapi.broker.model.TradeUpdateEvent;
+import net.jacobpeterson.alpaca.openapi.broker.model.TradeUpdateEventV2;
+import net.jacobpeterson.alpaca.openapi.broker.model.TransferStatusEvent;
+import net.jacobpeterson.alpaca.util.sse.SSEListener;
+import net.jacobpeterson.alpaca.util.sse.SSERequest;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.sse.EventSource;
+import okhttp3.sse.EventSourceListener;
+import okhttp3.sse.EventSources;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Type;
+import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.util.List;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static net.jacobpeterson.alpaca.openapi.broker.JSON.getGson;
+
+/**
+ * {@link EventsApiSSE} add SSE support to {@link EventsApi}.
+ */
+public class EventsApiSSE {
+
+    private final EventsApi eventsAPI;
+    private final EventSource.Factory eventSourceFactory;
+
+    /**
+     * Instantiates a new {@link EventsApiSSE}.
+     *
+     * @param apiClient the api client
+     */
+    public EventsApiSSE(ApiClient apiClient) {
+        eventsAPI = new EventsApi(apiClient);
+        eventSourceFactory = EventSources.createFactory(apiClient.getHttpClient().newBuilder() // Shallow clone
+                .readTimeout(0, SECONDS)
+                .writeTimeout(0, SECONDS)
+                .build());
+    }
+
+    /**
+     * See {@link EventsApi#getV1EventsNta(String, String, String, Integer, Integer, String, String, Boolean)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToV1EventsNta(String id, String since, String until, Integer sinceId, Integer untilId,
+            String sinceUlid, String untilUlid, Boolean includePreprocessing,
+            SSEListener<List<Object>> sseListener) throws ApiException { // TODO OpenAPI response type is broken
+        final Request request = eventsAPI.getV1EventsNtaCall(id, since, until, sinceId, untilId, sinceUlid, untilUlid,
+                includePreprocessing, null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<Object>>() {}.getType()))); // TODO OpenAPI response type is broken
+    }
+
+    /**
+     * See {@link EventsApi#subscribeToAdminActionSSE(OffsetDateTime, OffsetDateTime, String, String)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToAdminAction(OffsetDateTime since, OffsetDateTime until, String sinceId,
+            String untilId, SSEListener<List<AccountStatusEvent>> sseListener) throws ApiException {
+        final Request request = eventsAPI.subscribeToAdminActionSSECall(since, until, sinceId, untilId, null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<SubscribeToAdminActionSSE200ResponseInner>>() {}.getType())));
+    }
+
+    /**
+     * See
+     * {@link EventsApi#subscribeToJournalStatusSSE(OffsetDateTime, OffsetDateTime, Integer, Integer, String, String,
+     * String)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToJournalStatus(OffsetDateTime since, OffsetDateTime until, Integer sinceId,
+            Integer untilId, String sinceUlid, String untilUlid, String id,
+            SSEListener<List<AccountStatusEvent>> sseListener) throws ApiException {
+        final Request request = eventsAPI.subscribeToJournalStatusSSECall(since, until, sinceId, untilId, sinceUlid,
+                untilUlid, id, null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<JournalStatusEvent>>() {}.getType())));
+    }
+
+    /**
+     * See {@link EventsApi#subscribeToTradeSSE(OffsetDateTime, OffsetDateTime, Integer, Integer, String, String)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToTrade(OffsetDateTime since, OffsetDateTime until, Integer sinceId,
+            Integer untilId, String sinceUlid, String untilUlid, SSEListener<List<AccountStatusEvent>> sseListener)
+            throws ApiException {
+        final Request request = eventsAPI.subscribeToTradeSSECall(since, until, sinceId, untilId, sinceUlid, untilUlid,
+                null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<TradeUpdateEvent>>() {}.getType())));
+    }
+
+    /**
+     * See {@link EventsApi#subscribeToTradeV2SSE(OffsetDateTime, OffsetDateTime, String, String)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToTradeV2(OffsetDateTime since, OffsetDateTime until, String sinceId,
+            String untilId, SSEListener<List<AccountStatusEvent>> sseListener) throws ApiException {
+        final Request request = eventsAPI.subscribeToTradeV2SSECall(since, until, sinceId, untilId, null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<TradeUpdateEventV2>>() {}.getType())));
+    }
+
+    /**
+     * See
+     * {@link EventsApi#subscribeToTransferStatusSSE(OffsetDateTime, OffsetDateTime, Integer, Integer, String,
+     * String)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToTransferStatus(OffsetDateTime since, OffsetDateTime until, Integer sinceId,
+            Integer untilId, String sinceUlid, String untilUlid, SSEListener<List<AccountStatusEvent>> sseListener)
+            throws ApiException {
+        final Request request = eventsAPI.subscribeToTransferStatusSSECall(since, until, sinceId, untilId, sinceUlid,
+                untilUlid, null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<TransferStatusEvent>>() {}.getType())));
+    }
+
+    /**
+     * See
+     * {@link EventsApi#suscribeToAccountStatusSSE(LocalDate, LocalDate, Integer, Integer, String, String, String)}.
+     *
+     * @return a {@link SSERequest}
+     */
+    public SSERequest subscribeToAccountStatus(LocalDate since, LocalDate until, Integer sinceId, Integer untilId,
+            String sinceUlid, String untilUlid, String id, SSEListener<List<AccountStatusEvent>> sseListener)
+            throws ApiException {
+        final Request request = eventsAPI.suscribeToAccountStatusSSECall(since, until, sinceId, untilId, sinceUlid,
+                untilUlid, id, null).request();
+        return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
+                new TypeToken<List<AccountStatusEvent>>() {}.getType())));
+    }
+
+    private <T> EventSourceListener createEventSourceListener(SSEListener<T> sseListener, Type responseTypeToken) {
+        return new EventSourceListener() {
+            @Override
+            public void onClosed(@NotNull EventSource eventSource) {
+                sseListener.onClose();
+            }
+
+            @Override
+            public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type,
+                    @NotNull String data) {
+                sseListener.onEvent(getGson().fromJson(data, responseTypeToken));
+            }
+
+            @Override
+            public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable throwable,
+                    @Nullable Response response) {
+                if (throwable != null && throwable.getMessage().equals("canceled")) {
+                    sseListener.onClose();
+                    return;
+                }
+                sseListener.onFailure(throwable, response);
+            }
+
+            @Override
+            public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
+                sseListener.onOpen();
+            }
+        };
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json b/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_stream_source_type.json
similarity index 56%
rename from src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json
rename to src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_stream_source_type.json
index e0f74cdc..97b87531 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_source_type.json
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_stream_source_type.json
@@ -1,6 +1,6 @@
 {
   "type": "string",
-  "javaName": "MarketDataAPISourceType",
+  "javaName": "MarketDataAPIStreamSourceType",
   "enum": [
     "iex",
     "sip"
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java
new file mode 100644
index 00000000..9ca01253
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java
@@ -0,0 +1,38 @@
+package net.jacobpeterson.alpaca.util.sse;
+
+import okhttp3.Response;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * {@link SSEListener} is a listener interface for server-sent events (SSE).
+ *
+ * @param <T> the data type
+ */
+public interface SSEListener<T> {
+
+    /**
+     * Called on SSE request open.
+     */
+    void onOpen();
+
+    /**
+     * Called on SSE request close.
+     */
+    void onClose();
+
+    /**
+     * Called on SSE request failure.
+     *
+     * @param throwable the {@link Throwable}
+     * @param response  the {@link Response}
+     */
+    void onFailure(@Nullable Throwable throwable, @Nullable Response response);
+
+    /**
+     * Called on SSE request data receive event.
+     *
+     * @param data the {@link T} data
+     */
+    void onEvent(@NotNull T data);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
new file mode 100644
index 00000000..492e9e0f
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
@@ -0,0 +1,33 @@
+package net.jacobpeterson.alpaca.util.sse;
+
+import okhttp3.Response;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link SSEListenerAdapter} and adapter for {@link SSEListener}.
+ *
+ * @param <T> the data type
+ */
+public class SSEListenerAdapter<T> implements SSEListener<T> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(SSEListenerAdapter.class);
+
+    public void onOpen() {
+        LOGGER.info("SSE connection opened.");
+    }
+
+    public void onClose() {
+        LOGGER.info("SSE connection closed.");
+    }
+
+    public void onFailure(@Nullable Throwable throwable, @Nullable Response response) {
+        LOGGER.error("SSE connection failed! {}", response, throwable);
+    }
+
+    public void onEvent(@NotNull T data) {
+        LOGGER.info("SSE event received: {}", data);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSERequest.java b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSERequest.java
new file mode 100644
index 00000000..65e5b96e
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSERequest.java
@@ -0,0 +1,36 @@
+package net.jacobpeterson.alpaca.util.sse;
+
+import okhttp3.sse.EventSource;
+
+/**
+ * {@link SSERequest} is a {@link EventSource} wrapper class.
+ */
+public class SSERequest {
+
+    private final EventSource eventSource;
+
+    /**
+     * Instantiates a new {@link SSERequest}.
+     *
+     * @param eventSource the {@link EventSource}
+     */
+    public SSERequest(EventSource eventSource) {
+        this.eventSource = eventSource;
+    }
+
+    /**
+     * Closes the internal {@link EventSource}.
+     */
+    public void close() {
+        eventSource.cancel();
+    }
+
+    /**
+     * Gets the internal {@link EventSource}.
+     *
+     * @return the {@link EventSource}
+     */
+    public EventSource getInternalEventSource() {
+        return eventSource;
+    }
+}

From c37268aa6d86390335c54967d6425a475f0368ed Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 4 Mar 2024 10:13:20 -0800
Subject: [PATCH 60/85] WIP add updated trades streaming functionality

---
 README.md                                     | 473 +-----------------
 build.gradle                                  |   8 +
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   |  16 +
 .../jacobpeterson/alpaca/AlpacaBrokerAPI.java |   4 +-
 .../authorization/authorization_message.json  |  13 -
 .../enums/streaming_message_type.json         |   9 -
 .../streaming/listening/listening_data.json   |  10 -
 .../listening/listening_message.json          |  13 -
 .../endpoint/streaming/streaming_message.json |  10 -
 .../streaming/trade/trade_update_message.json |  13 -
 .../alpaca/rest/broker/EventsApiSSE.java      |  37 +-
 .../alpaca/util/sse/SSEListener.java          |  14 +-
 .../alpaca/util/sse/SSEListenerAdapter.java   |   8 +-
 .../alpaca/websocket/AlpacaWebsocket.java     | 287 +++++++++++
 .../websocket/AlpacaWebsocketInterface.java   |  82 +++
 .../AlpacaWebsocketMessageListener.java       |  20 +
 .../AlpacaWebsocketStateListener.java         |  33 ++
 .../websocket/trades/TradesListener.java      |  11 +
 .../trades/TradesListenerAdapter.java         |  19 +
 .../websocket/trades/TradesWebsocket.java     | 219 ++++++++
 .../trades/TradesWebsocketInterface.java      |  26 +
 .../authorization/authorization_data.json     |   1 -
 .../authorization/authorization_message.json  |  12 +
 .../model/listening/listening_data.json       |   9 +
 .../model/listening/listening_message.json    |  12 +
 .../trades/model/trades_stream_message.json   |   9 +
 .../model/trades_stream_message_type.json     |   8 +
 .../model/tradeupdate}/trade_update.json      |  22 +-
 .../tradeupdate}/trade_update_event.json      |   7 +-
 .../tradeupdate/trade_update_message.json     |  12 +
 30 files changed, 845 insertions(+), 572 deletions(-)
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/enums/streaming_message_type.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_data.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/streaming_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketStateListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/streaming => websocket/trades/model}/authorization/authorization_data.json (69%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message_type.json
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/streaming/trade => websocket/trades/model/tradeupdate}/trade_update.json (52%)
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/streaming/trade/enums => websocket/trades/model/tradeupdate}/trade_update_event.json (83%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json

diff --git a/README.md b/README.md
index c5ceae7f..afb356b9 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
 <p align="center"><a href="https://petersoj.github.io/alpaca-java/" target="_blank"><img src=".github/image/logo.png" alt="Logo"></a></p>
 <p align="center">
     <a href="https://github.com/Petersoj/alpaca-java" target="_blank"><img alt="GitHub Repository" src="https://img.shields.io/badge/GitHub-000000?logo=github"></a>
-    <a href="https://search.maven.org/artifact/net.jacobpeterson/alpaca-java" target="_blank"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/net.jacobpeterson/alpaca-java"></a>
-    <a href="https://javadoc.io/doc/net.jacobpeterson/alpaca-java" target="_blank"><img src="https://javadoc.io/badge/net.jacobpeterson/alpaca-java.svg" alt="Javadocs"></a>
+    <a href="https://search.maven.org/artifact/net.jacobpeterson.alpaca/alpaca-java" target="_blank"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/net.jacobpeterson.alpaca/alpaca-java"></a>
+    <a href="https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java" target="_blank"><img src="https://javadoc.io/badge/net.jacobpeterson.alpaca/alpaca-java.svg" alt="Javadocs"></a>
     <a href="https://opensource.org/licenses/MIT" target="_blank"><img alt="GitHub" src="https://img.shields.io/github/license/petersoj/alpaca-java"></a>
 </p>
 
 # Overview
-This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
+This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
 
 Give this repository a star ⭐ if it helped you build a trading algorithm in Java!
 
@@ -34,480 +34,33 @@ Note that you don't have to use the Maven Central artifacts. Instead, you can cl
 For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Logback](http://logback.qos.ch/), [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
 
 # Examples
-Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson/alpaca-java) for all classes and method signatures.
+Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java) for all classes and method signatures.
 
 ## [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java)
-The [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) class contains several instances of various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java) and [`AlpacaWebsockets`](src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java) to interface with Alpaca. You will generally only need one instance of this class in your application. Note that many methods inside the various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java) allow `null` to be passed in as a parameter if it is optional.
+[`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) is the main class used to interface with the various Alpaca API endpoints. If you are using the Trading or Market Data APIs for a single Alpaca account or if you are using the Broker API, you will generally only need one instance of this class. However, if you are using the Trading API with OAuth to act on behalf of an Alpaca account, this class is optimized so that it can be instantiated quickly, especially when an existing `OkHttpClient` is given in the constructor. Additionally, all API endpoint instances are instantiated lazily. This class is thread-safe.
 
-The Alpaca API specification is located [here](https://docs.alpaca.markets/api-documentation/api-v2/) and the [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) Javadoc is located [here](https://javadoc.io/doc/net.jacobpeterson/alpaca-java/latest/net/jacobpeterson/alpaca/AlpacaAPI.html).
+The Alpaca API documentation is located [here](https://docs.alpaca.markets/) and the [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) Javadoc is located [here](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/AlpacaAPI.html).
 
 Example usage:
 ```java
-// This constructor uses the 'alpaca.properties' file on the classpath for configuration
-AlpacaAPI alpacaAPI = new AlpacaAPI();
-
-// This constructor passes in a 'keyID' and 'secretKey' and uses the endpoint API type and data API
-// type defined in the 'alpaca.properties' file (which default to 'paper' and 'iex' respectively)
-String keyID = "<some key ID>";
-String secretKey = "<some secret>";
-AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey);
-
-// This constructor passes in a 'keyID' and 'secretKey' and uses the passed in endpoint API type
-// and data API type (which are 'LIVE' and 'SIP' respectively in this example)
-AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey, EndpointAPIType.LIVE, DataAPIType.SIP);
-
-// This constructor is for OAuth tokens
-String oAuthToken = "<some OAuth token>";
-AlpacaAPI alpacaAPI = new AlpacaAPI(oAuthToken);
-
-// This approach uses a builder pattern. Attributes not provided explicitly are retrieved from 'alpaca.properties' file
-AlpacaAPI alpacaAPI = AlpacaAPI.builder()
-    .withEndpointAPIType(EndpointAPIType.PAPER)
-    .build();
+// TODO
 ```
 
 Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests via a connection pool. These threads persist even if the main thread exits, so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java), call `alpacaAPI.closeOkHttpClient();`.
 
-See the [OkHttpClient Documentation](https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/#shutdown-isnt-necessary) for more information.
-
-## [`AlpacaClientException`](src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java)
-[`AlpacaClientException`](src/main/java/net/jacobpeterson/alpaca/rest/AlpacaClientException.java) is thrown anytime an exception occurs when using various [`AlpacaEndpoints`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/AlpacaEndpoint.java). It should be caught and handled within your trading algorithm application accordingly.
-
-## [`AccountEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/account/AccountEndpoint.java)
-The Account API serves important information related to an account, including account status, funds available for trade, funds available for withdrawal, and various flags relevant to an account's ability to trade.
-
-Example usage:
+## Trading API
 ```java
-try {
-    // Get 'Account' information and print it out
-    Account account = alpacaAPI.account().get();
-    System.out.println(account);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`CryptoMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/crypto/CryptoMarketDataEndpoint.java)
-Alpaca provides cryptocurrency data from multiple venues/exchanges, namely: Coinbase, ErisX, and FTX.
-
-Example usage:
-```java
-try {
-    // Get BTCUSD 50 one-hour bars starting on 12/18/2021 from Coinbase and print them out
-    CryptoBarsResponse btcBarsResponse = alpacaAPI.cryptoMarketData().getBars(
-            "BTCUSD",
-            Arrays.asList(Exchange.COINBASE),
-            ZonedDateTime.of(2021, 12, 18, 0, 0, 0, 0, ZoneId.of("America/New_York")),
-            50,
-            null,
-            1,
-            BarTimePeriod.HOUR);
-    btcBarsResponse.getBars().forEach(System.out::println);
-
-    // Get the Best Bid and Offer across multiple exchanges (XBBO) and print it out
-    XbboResponse etcXBBO = alpacaAPI.cryptoMarketData().getXBBO(
-            "ETHUSD",
-            null);
-    System.out.println(etcXBBO);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
+// TODO
 ```
 
-## [`StockMarketDataEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/marketdata/stock/StockMarketDataEndpoint.java)
-The Data API v2 provides market data through an easy-to-use API for historical stock market data.
-
-Example usage:
+## Market Data API
 ```java
-try {
-    // Get AAPL one hour, split-adjusted bars from 7/6/2021 market open
-    // to 7/8/2021 market close from the SIP feed and print them out
-    StockBarsResponse aaplBarsResponse = alpacaAPI.stockMarketData().getBars(
-            "AAPL",
-            ZonedDateTime.of(2021, 7, 6, 9, 30, 0, 0, ZoneId.of("America/New_York")),
-            ZonedDateTime.of(2021, 7, 8, 12 + 4, 0, 0, 0, ZoneId.of("America/New_York")),
-            null,
-            null,
-            1,
-            BarTimePeriod.HOUR,
-            BarAdjustment.SPLIT,
-            BarFeed.SIP);
-    aaplBarsResponse.getBars().forEach(System.out::println);
-
-    // Get AAPL first 10 trades on 7/8/2021 at market open and print them out
-    StockTradesResponse aaplTradesResponse = alpacaAPI.stockMarketData().getTrades(
-            "AAPL",
-            ZonedDateTime.of(2021, 7, 8, 9, 30, 0, 0, ZoneId.of("America/New_York")),
-            ZonedDateTime.of(2021, 7, 8, 9, 31, 0, 0, ZoneId.of("America/New_York")),
-            10,
-            null);
-    aaplTradesResponse.getTrades().forEach(System.out::println);
-
-    // Print out latest AAPL trade
-    Trade latestAAPLTrade = alpacaAPI.stockMarketData().getLatestTrade("AAPL").getTrade();
-    System.out.printf("Latest AAPL Trade: %s\n", latestAAPLTrade);
-
-    // Print out snapshot of AAPL, GME, and TSLA
-    Map<String, Snapshot> snapshots = alpacaAPI.stockMarketData()
-            .getSnapshots(Arrays.asList("AAPL", "GME", "TSLA"));
-    snapshots.forEach((symbol, snapshot) ->
-            System.out.printf("Symbol: %s\nSnapshot: %s\n\n", symbol, snapshot));
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
+// TODO
 ```
 
-## [`OrdersEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/orders/OrdersEndpoint.java)
-The Orders API allows a user to monitor, place, and cancel their orders with Alpaca.
-
-Example usage:
+## Broker API
 ```java
-try {
-    // Print all the 'Order's of AAPL and TSLA until 7/6/2021 in
-    // ascending (oldest to newest) order
-    List<Order> orders = alpacaAPI.orders().get(
-            CurrentOrderStatus.ALL,
-            null,
-            null,
-            ZonedDateTime.of(2021, 7, 6, 0, 0, 0, 0, ZoneId.of("America/New_York")),
-            SortDirection.ASCENDING,
-            true,
-            Arrays.asList("AAPL", "TSLA"));
-    orders.forEach(System.out::println);
-
-    // Request a new limit order for TSLA for 100 shares at a limit price
-    // of $653.00 and TIF of DAY.
-    Order tslaLimitOrder = alpacaAPI.orders().requestLimitOrder(
-            "TSLA",
-            100,
-            OrderSide.BUY,
-            OrderTimeInForce.DAY,
-            653.00,
-            false);
-    System.out.printf("Requested %s %s order at %s\n",
-            tslaLimitOrder.getSymbol(),
-            tslaLimitOrder.getType(),
-            tslaLimitOrder.getSubmittedAt());
-
-    // Check if TSLA limit order has filled 5 seconds later and if it hasn't
-    // replace it with a higher limit order so it fills!
-    Thread.sleep(5000);
-    tslaLimitOrder = alpacaAPI.orders().get(tslaLimitOrder.getId(), false);
-    if (tslaLimitOrder.getFilledAt() == null && tslaLimitOrder.getStatus().equals(OrderStatus.NEW)) {
-        Order replacedOrder = alpacaAPI.orders().replace(
-                tslaLimitOrder.getId(),
-                100,
-                OrderTimeInForce.DAY,
-                655.00,
-                null, null, null);
-        System.out.printf("Replaced TSLA order: %s\n", replacedOrder);
-    }
-
-    // Cancel all open orders after 2 seconds
-    Thread.sleep(2000);
-    List<CancelledOrder> cancelledOrders = alpacaAPI.orders().cancelAll();
-    cancelledOrders.forEach((cancelledOrder) -> System.out.printf("Cancelled: %s\n", cancelledOrder));
-
-    // Request a new fractional market order for 0.5 shares of GME
-    alpacaAPI.orders().requestFractionalMarketOrder("GME", 0.5, OrderSide.BUY);
-    // Request a new notional market order for $25 worth of GME shares
-    alpacaAPI.orders().requestNotionalMarketOrder("GME", 25d, OrderSide.BUY);
-} catch (AlpacaClientException | InterruptedException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`PositionsEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/positions/PositionsEndpoint.java)
-The Positions API provides information about an account's current open positions.
-
-Example usage:
-```java
-try {
-    // Close 50% of all open TSLA and AAPL positions
-    List<Position> openPositions = alpacaAPI.positions().get();
-    for (Position openPosition : openPositions) {
-        if (openPosition.getSymbol().equals("TSLA") || openPosition.getSymbol().equals("AAPL")) {
-            Order closePositionOrder = alpacaAPI.positions()
-                    .close(openPosition.getSymbol(), null, 50d);
-            System.out.printf("Closing 50%% of %s position: %s\n",
-                    openPosition.getSymbol(), closePositionOrder);
-        }
-    }
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`AssetsEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/assets/AssetsEndpoint.java)
-The Assets API serves as the master list of assets available for trade and data consumption from Alpaca.
-
-Example usage:
-```java
-try {
-    // Print out a CSV of all active US Equity 'Asset's
-    List<Asset> activeUSEquities = alpacaAPI.assets().get(AssetStatus.ACTIVE, AssetClass.US_EQUITY);
-    System.out.println(activeUSEquities
-            .stream().map(Asset::getSymbol)
-            .collect(Collectors.joining(", ")));
-
-    // Print out TSLA 'Asset' information
-    Asset tslaAsset = alpacaAPI.assets().getBySymbol("TSLA");
-    System.out.println(tslaAsset);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`WatchlistEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/watchlist/WatchlistEndpoint.java)
-The Watchlist API provides CRUD operation for the account's watchlist.
-
-Example usage:
-```java
-try {
-    // Print out CSV all 'Watchlist' names
-    List<Watchlist> watchlists = alpacaAPI.watchlist().get();
-    System.out.println(watchlists.stream()
-            .map(Watchlist::getName)
-            .collect(Collectors.joining(", ")));
-
-    // Create a watchlist for day trading with TSLA and AAPL initially
-    Watchlist dayTradeWatchlist = alpacaAPI.watchlist().create("Day Trade", "TSLA", "AAPL");
-
-    // Remove TSLA and add MSFT to 'dayTradeWatchlist'
-    alpacaAPI.watchlist().removeSymbol(dayTradeWatchlist.getId(), "TSLA");
-    alpacaAPI.watchlist().addAsset(dayTradeWatchlist.getId(), "MSFT");
-
-    // Set the updated watchlist variable
-    dayTradeWatchlist = alpacaAPI.watchlist().get(dayTradeWatchlist.getId());
-
-    // Print CSV of 'dayTradeWatchlist' 'Asset's
-    System.out.println(dayTradeWatchlist.getAssets()
-            .stream().map(Asset::getSymbol)
-            .collect(Collectors.joining(", ")));
-
-    // Delete the 'dayTradeWatchlist'
-    alpacaAPI.watchlist().delete(dayTradeWatchlist.getId());
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`CalendarEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/calendar/CalendarEndpoint.java)
-The calendar API serves the full list of market days from 1970 to 2029.
-
-Example usage:
-```java
-try {
-    // Get the 'Calendar's of the week of Christmas 2020 and print them out
-    List<Calendar> calendar = alpacaAPI.calendar().get(
-            LocalDate.of(2020, 12, 20),
-            LocalDate.of(2020, 12, 27));
-    calendar.forEach(System.out::println);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`ClockEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/clock/ClockEndpoint.java)
-The clock API serves the current market timestamp, whether the market is currently open, as well as the times of the next market open and close.
-
-Example usage:
-```java
-try {
-    // Get the market 'Clock' and print it out
-    Clock clock = alpacaAPI.clock().get();
-    System.out.println(clock);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`AccountConfigurationEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountconfiguration/AccountConfigurationEndpoint.java)
-The Account Configuration API provides custom configurations about your trading account settings. These configurations control various allow you to modify settings to suit your trading needs.
-
-Example usage:
-```java
-try {
-    // Update the 'AccountConfiguration' to block new orders
-    AccountConfiguration accountConfiguration = alpacaAPI.accountConfiguration().get();
-    accountConfiguration.setSuspendTrade(true);
-    alpacaAPI.accountConfiguration().set(accountConfiguration);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`AccountActivitiesEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/accountactivities/AccountActivitiesEndpoint.java)
-The Account Activities API provides access to a historical record of transaction activities that have impacted your account.
-
-Example usage:
-```java
-try {
-    // Print all order fill and cash deposit 'AccountActivity's on 7/8/2021
-    List<AccountActivity> accountActivities = alpacaAPI.accountActivities().get(
-            ZonedDateTime.of(2021, 7, 8, 0, 0, 0, 0, ZoneId.of("America/New_York")),
-            null,
-            null,
-            SortDirection.ASCENDING,
-            null,
-            null,
-            ActivityType.FILL, ActivityType.CSD);
-    for (AccountActivity accountActivity : accountActivities) {
-        if (accountActivity instanceof TradeActivity) {
-            System.out.println("TradeActivity: " + (TradeActivity) accountActivity);
-        } else if (accountActivity instanceof NonTradeActivity) {
-            System.out.println("NonTradeActivity: " + (NonTradeActivity) accountActivity);
-        }
-    }
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`PortfolioHistoryEndpoint`](src/main/java/net/jacobpeterson/alpaca/rest/endpoint/portfoliohistory/PortfolioHistoryEndpoint.java)
-The Portfolio History API returns the timeseries data for equity and profit loss information of the account.
-
-Example usage:
-```java
-try {
-    // Get 3 days of one-hour 'PortfolioHistory' on 7/8/2021 and print out its data points
-    PortfolioHistory portfolioHistory = alpacaAPI.portfolioHistory().get(
-            3,
-            PortfolioPeriodUnit.DAY,
-            PortfolioTimeFrame.ONE_HOUR,
-            LocalDate.of(2021, 7, 8),
-            false);
-    System.out.printf("Timeframe: %s, Base value: %s \n",
-            portfolioHistory.getTimeframe(),
-            portfolioHistory.getBaseValue());
-    portfolioHistory.getDataPoints().forEach(System.out::println);
-} catch (AlpacaClientException exception) {
-    exception.printStackTrace();
-}
-```
-
-## [`StreamingWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/streaming/StreamingWebsocket.java)
-Alpaca offers websocket streaming of order updates.
-
-Example usage:
-```java
-// Add a 'StreamingListener' that simply prints streaming information
-StreamingListener streamingListener = (messageType, message) ->
-        System.out.printf("%s: %s\n", messageType.name(), message);
-alpacaAPI.streaming().setListener(streamingListener);
-
-// Listen 'AuthorizationMessage' and 'ListeningMessage' messages that contain
-// information about the stream's current state. Note that these are subscribed
-// to before the websocket is connected since these messages usually are sent
-// upon websocket connection.
-alpacaAPI.streaming().streams(StreamingMessageType.AUTHORIZATION,
-        StreamingMessageType.LISTENING);
-
-// Connect the websocket and confirm authentication
-alpacaAPI.streaming().connect();
-alpacaAPI.streaming().waitForAuthorization(5, TimeUnit.SECONDS);
-if (!alpacaAPI.streaming().isValid()) {
-    System.out.println("Websocket not valid!");
-    return;
-}
-
-// Listen to the 'trade update' streams.
-alpacaAPI.streaming().streams(StreamingMessageType.TRADE_UPDATES);
-
-// Wait a few seconds
-Thread.sleep(5000);
-
-// Manually disconnect the websocket
-alpacaAPI.streaming().disconnect();
-```
-
-## [`StockMarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/stock/StockMarketDataWebsocket.java)
-Alpaca's Data API v2 provides websocket streaming for trades, quotes, and minute bars. This helps receive the most up-to-date market information that could help your trading strategy to act upon certain market movement.
-
-Example usage:
-```java
-// Add a 'MarketDataListener' that simply prints market data information
-MarketDataListener marketDataListener = (messageType, message) ->
-        System.out.printf("%s: %s\n", messageType.name(), message);
-alpacaAPI.stockMarketDataStreaming().setListener(marketDataListener);
-
-// Listen to 'SubscriptionsMessage', 'SuccessMessage', and 'ErrorMessage' control messages
-// that contain information about the stream's current state. Note that these are subscribed
-// to before the websocket is connected since these messages usually are sent
-// upon websocket connection.
-alpacaAPI.stockMarketDataStreaming().subscribeToControl(
-        MarketDataMessageType.SUCCESS,
-        MarketDataMessageType.SUBSCRIPTION,
-        MarketDataMessageType.ERROR);
-
-// Connect the websocket and confirm authentication
-alpacaAPI.stockMarketDataStreaming().connect();
-alpacaAPI.stockMarketDataStreaming().waitForAuthorization(5, TimeUnit.SECONDS);
-if (!alpacaAPI.stockMarketDataStreaming().isValid()) {
-    System.out.println("Websocket not valid!");
-    return;
-}
-
-// Listen to AAPL and TSLA trades and all bars via the wildcard operator ('*').
-alpacaAPI.stockMarketDataStreaming().subscribe(
-        Arrays.asList("AAPL", "TSLA"),
-        null,
-        Arrays.asList("*"));
-
-// Wait a few seconds
-Thread.sleep(5000);
-
-// Manually disconnect the websocket
-alpacaAPI.stockMarketDataStreaming().disconnect();
-```
-
-## [`CryptoMarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/crypto/CryptoMarketDataWebsocket.java)
-Alpaca also offers cryptocurrency websocket streaming for trades, quotes, and minute bars.
-
-The usage is identical to the [`StockMarketDataWebsocket`](https://github.com/Petersoj/alpaca-java/#stockmarketdatawebsocket) usage except that accessing the websocket instance via the `AlpacaAPI` instance is done using: `alpacaAPI.cryptoMarketDataStreaming()` instead of `alpacaAPI.stockMarketDataStreaming()`.
-
-Example usage:
-```java
-// The 'CryptoMarketDataWebsocket' setup is identical to the 'StockMarketDataWebsocket' setup
-
-// Listen to BTCUSD and ETHUSD trades and all bars via the wildcard operator ('*').
-alpacaAPI.cryptoMarketDataStreaming().subscribe(
-        Arrays.asList("BTCUSD", "ETHUSD"),
-        null,
-        Arrays.asList("*"));
-```
-
-## [`NewsMarketDataWebsocket`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/news/NewsMarketDataWebsocket.java)
-Alpaca also offers news websocket streaming for symbols.
-
-The usage is identical to the [`StockMarketDataWebsocket`](https://github.com/Petersoj/alpaca-java/#stockmarketdatawebsocket) usage except that accessing the websocket instance via the `AlpacaAPI` instance is done using: `alpacaAPI.newsMarketDataStreaming()` instead of `alpacaAPI.stockMarketDataStreaming()`.
-
-Example usage:
-```java
-MarketDataListener marketDataListener = (messageType, message) ->
-                System.out.printf("%s: %s\n", messageType.name(), message);
-
-MarketDataWebsocketInterface newsWebsocket = alpacaAPI.newsMarketDataStreaming();
-
-newsWebsocket.setListener(marketDataListener);
-newsWebsocket.subscribeToControl(
-                MarketDataMessageType.SUCCESS,
-                MarketDataMessageType.SUBSCRIPTION,
-                MarketDataMessageType.ERROR);
-
-newsWebsocket.connect();
-newsWebsocket.waitForAuthorization(5, TimeUnit.SECONDS);
-if (!newsWebsocket.isValid()) {
-    System.out.println("Websocket not valid!");
-    return;
-}
-newsWebsocket.subscribe(null, null, null, Arrays.asList("*"));
-
-// Wait a few seconds
-Thread.sleep(5000);
-
-// Manually disconnect the websocket
-newsWebsocket.disconnect();
+// TODO
 ```
 
 # Building
diff --git a/build.gradle b/build.gradle
index 1c8e27b1..a0729cd0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -238,6 +238,14 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
                             "[\\s\\S]*?authentications\\);", "")
                     .replaceAll("for \\(String authName : authNames\\)[\\s\\S]*?uri\\);\\n. {7}}", "")
         }
+
+        // Make the 'JSON' classes of the generate clients have a static Gson initializer
+        specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
+            final def jsonClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
+                    it.replace("-", ""), "JSON.java").toFile()
+            jsonClientFile.text = jsonClientFile.text
+                    .replaceAll("\\{\\n {8}GsonBuilder", "static {\n        GsonBuilder")
+        }
     }
 }
 generateOpenAPIClientsTask.configure {
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 6e6f2da3..001aa617 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -3,6 +3,8 @@
 import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
 import net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPIStreamSourceType;
 import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
+import net.jacobpeterson.alpaca.websocket.trades.TradesWebsocket;
+import net.jacobpeterson.alpaca.websocket.trades.TradesWebsocketInterface;
 import okhttp3.OkHttpClient;
 import okhttp3.logging.HttpLoggingInterceptor;
 import org.slf4j.Logger;
@@ -40,6 +42,7 @@ public class AlpacaAPI {
     private AlpacaTraderAPI trader;
     private AlpacaMarketDataAPI marketData;
     private AlpacaBrokerAPI broker;
+    private TradesWebsocket tradesWebsocket;
 
     /**
      * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading or Market Data APIs for a
@@ -220,6 +223,19 @@ public synchronized AlpacaBrokerAPI broker() {
         return broker;
     }
 
+    /**
+     * Gets the {@link TradesWebsocketInterface}. Lazily instantiated.
+     *
+     * @return the {@link TradesWebsocketInterface}
+     */
+    public synchronized TradesWebsocketInterface tradesStream() {
+        if (tradesWebsocket == null) {
+            tradesWebsocket = new TradesWebsocket(okHttpClient, traderAPIEndpointType,
+                    traderKeyID, traderSecretKey, traderOAuthToken);
+        }
+        return tradesWebsocket;
+    }
+
     /**
      * Creates a {@link Builder} for {@link AlpacaAPI}.
      *
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
index a4aa0c56..942d71b3 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
@@ -60,8 +60,8 @@ public class AlpacaBrokerAPI {
      * @param okHttpClient          an existing {@link OkHttpClient} or <code>null</code> to create a new default
      *                              instance
      */
-    @SuppressWarnings("UnnecessaryDefault") AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret,
-            BrokerAPIEndpointType brokerAPIEndpointType,
+    @SuppressWarnings("UnnecessaryDefault")
+    AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
             OkHttpClient okHttpClient) {
         checkNotNull(brokerAPIKey);
         checkNotNull(brokerAPISecret);
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_message.json
deleted file mode 100644
index 5e266609..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_message.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.StreamingMessage"
-  },
-  "properties": {
-    "data": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.authorization.AuthorizationData",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.streaming.authorization.AuthorizationData}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/enums/streaming_message_type.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/enums/streaming_message_type.json
deleted file mode 100644
index 454ee643..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/enums/streaming_message_type.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
-  "enum": [
-    "listening",
-    "authorization",
-    "trade_updates"
-  ]
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_data.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_data.json
deleted file mode 100644
index eb9d6eb9..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_data.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
-  "properties": {
-    "streams": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_message.json
deleted file mode 100644
index 24413cb8..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/listening/listening_message.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.StreamingMessage"
-  },
-  "properties": {
-    "data": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.listening.ListeningData",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.streaming.listening.ListeningData}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/streaming_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/streaming_message.json
deleted file mode 100644
index c7799c71..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/streaming_message.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
-  "properties": {
-    "stream": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.streaming.enums.StreamingMessageType}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update_message.json
deleted file mode 100644
index 014d3d6f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update_message.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.StreamingMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
-  "properties": {
-    "data": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.trade.TradeUpdate",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.streaming.trade.TradeUpdate}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
index da0ad98d..cff5a03c 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
@@ -23,7 +23,6 @@
 import java.lang.reflect.Type;
 import java.time.LocalDate;
 import java.time.OffsetDateTime;
-import java.util.List;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static net.jacobpeterson.alpaca.openapi.broker.JSON.getGson;
@@ -54,13 +53,13 @@ public EventsApiSSE(ApiClient apiClient) {
      *
      * @return a {@link SSERequest}
      */
-    public SSERequest subscribeToV1EventsNta(String id, String since, String until, Integer sinceId, Integer untilId,
-            String sinceUlid, String untilUlid, Boolean includePreprocessing,
-            SSEListener<List<Object>> sseListener) throws ApiException { // TODO OpenAPI response type is broken
+    public SSERequest subscribeToNonTradingActivitiesEvents(String id, String since, String until, Integer sinceId,
+            Integer untilId, String sinceUlid, String untilUlid, Boolean includePreprocessing,
+            SSEListener<Object> sseListener) throws ApiException { // TODO OpenAPI response type is broken
         final Request request = eventsAPI.getV1EventsNtaCall(id, since, until, sinceId, untilId, sinceUlid, untilUlid,
                 includePreprocessing, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<Object>>() {}.getType()))); // TODO OpenAPI response type is broken
+                new TypeToken<Object>() {}.getType()))); // TODO OpenAPI response type is broken
     }
 
     /**
@@ -69,10 +68,10 @@ public SSERequest subscribeToV1EventsNta(String id, String since, String until,
      * @return a {@link SSERequest}
      */
     public SSERequest subscribeToAdminAction(OffsetDateTime since, OffsetDateTime until, String sinceId,
-            String untilId, SSEListener<List<AccountStatusEvent>> sseListener) throws ApiException {
+            String untilId, SSEListener<AccountStatusEvent> sseListener) throws ApiException {
         final Request request = eventsAPI.subscribeToAdminActionSSECall(since, until, sinceId, untilId, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<SubscribeToAdminActionSSE200ResponseInner>>() {}.getType())));
+                new TypeToken<SubscribeToAdminActionSSE200ResponseInner>() {}.getType())));
     }
 
     /**
@@ -84,11 +83,11 @@ public SSERequest subscribeToAdminAction(OffsetDateTime since, OffsetDateTime un
      */
     public SSERequest subscribeToJournalStatus(OffsetDateTime since, OffsetDateTime until, Integer sinceId,
             Integer untilId, String sinceUlid, String untilUlid, String id,
-            SSEListener<List<AccountStatusEvent>> sseListener) throws ApiException {
+            SSEListener<AccountStatusEvent> sseListener) throws ApiException {
         final Request request = eventsAPI.subscribeToJournalStatusSSECall(since, until, sinceId, untilId, sinceUlid,
                 untilUlid, id, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<JournalStatusEvent>>() {}.getType())));
+                new TypeToken<JournalStatusEvent>() {}.getType())));
     }
 
     /**
@@ -97,12 +96,12 @@ public SSERequest subscribeToJournalStatus(OffsetDateTime since, OffsetDateTime
      * @return a {@link SSERequest}
      */
     public SSERequest subscribeToTrade(OffsetDateTime since, OffsetDateTime until, Integer sinceId,
-            Integer untilId, String sinceUlid, String untilUlid, SSEListener<List<AccountStatusEvent>> sseListener)
+            Integer untilId, String sinceUlid, String untilUlid, SSEListener<AccountStatusEvent> sseListener)
             throws ApiException {
         final Request request = eventsAPI.subscribeToTradeSSECall(since, until, sinceId, untilId, sinceUlid, untilUlid,
                 null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<TradeUpdateEvent>>() {}.getType())));
+                new TypeToken<TradeUpdateEvent>() {}.getType())));
     }
 
     /**
@@ -111,10 +110,10 @@ public SSERequest subscribeToTrade(OffsetDateTime since, OffsetDateTime until, I
      * @return a {@link SSERequest}
      */
     public SSERequest subscribeToTradeV2(OffsetDateTime since, OffsetDateTime until, String sinceId,
-            String untilId, SSEListener<List<AccountStatusEvent>> sseListener) throws ApiException {
+            String untilId, SSEListener<AccountStatusEvent> sseListener) throws ApiException {
         final Request request = eventsAPI.subscribeToTradeV2SSECall(since, until, sinceId, untilId, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<TradeUpdateEventV2>>() {}.getType())));
+                new TypeToken<TradeUpdateEventV2>() {}.getType())));
     }
 
     /**
@@ -125,12 +124,12 @@ public SSERequest subscribeToTradeV2(OffsetDateTime since, OffsetDateTime until,
      * @return a {@link SSERequest}
      */
     public SSERequest subscribeToTransferStatus(OffsetDateTime since, OffsetDateTime until, Integer sinceId,
-            Integer untilId, String sinceUlid, String untilUlid, SSEListener<List<AccountStatusEvent>> sseListener)
+            Integer untilId, String sinceUlid, String untilUlid, SSEListener<AccountStatusEvent> sseListener)
             throws ApiException {
         final Request request = eventsAPI.subscribeToTransferStatusSSECall(since, until, sinceId, untilId, sinceUlid,
                 untilUlid, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<TransferStatusEvent>>() {}.getType())));
+                new TypeToken<TransferStatusEvent>() {}.getType())));
     }
 
     /**
@@ -140,12 +139,12 @@ public SSERequest subscribeToTransferStatus(OffsetDateTime since, OffsetDateTime
      * @return a {@link SSERequest}
      */
     public SSERequest subscribeToAccountStatus(LocalDate since, LocalDate until, Integer sinceId, Integer untilId,
-            String sinceUlid, String untilUlid, String id, SSEListener<List<AccountStatusEvent>> sseListener)
+            String sinceUlid, String untilUlid, String id, SSEListener<AccountStatusEvent> sseListener)
             throws ApiException {
         final Request request = eventsAPI.suscribeToAccountStatusSSECall(since, until, sinceId, untilId, sinceUlid,
                 untilUlid, id, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<List<AccountStatusEvent>>() {}.getType())));
+                new TypeToken<AccountStatusEvent>() {}.getType())));
     }
 
     private <T> EventSourceListener createEventSourceListener(SSEListener<T> sseListener, Type responseTypeToken) {
@@ -158,7 +157,7 @@ public void onClosed(@NotNull EventSource eventSource) {
             @Override
             public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type,
                     @NotNull String data) {
-                sseListener.onEvent(getGson().fromJson(data, responseTypeToken));
+                sseListener.onMessage(getGson().fromJson(data, responseTypeToken));
             }
 
             @Override
@@ -168,7 +167,7 @@ public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable thro
                     sseListener.onClose();
                     return;
                 }
-                sseListener.onFailure(throwable, response);
+                sseListener.onError(throwable, response);
             }
 
             @Override
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java
index 9ca01253..5b28942b 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListener.java
@@ -12,27 +12,27 @@
 public interface SSEListener<T> {
 
     /**
-     * Called on SSE request open.
+     * Called on SSE open.
      */
     void onOpen();
 
     /**
-     * Called on SSE request close.
+     * Called on SSE close.
      */
     void onClose();
 
     /**
-     * Called on SSE request failure.
+     * Called on SSE error.
      *
      * @param throwable the {@link Throwable}
      * @param response  the {@link Response}
      */
-    void onFailure(@Nullable Throwable throwable, @Nullable Response response);
+    void onError(@Nullable Throwable throwable, @Nullable Response response);
 
     /**
-     * Called on SSE request data receive event.
+     * Called on SSE message received.
      *
-     * @param data the {@link T} data
+     * @param message the {@link T} message
      */
-    void onEvent(@NotNull T data);
+    void onMessage(@NotNull T message);
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
index 492e9e0f..57e33dfb 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
@@ -23,11 +23,11 @@ public void onClose() {
         LOGGER.info("SSE connection closed.");
     }
 
-    public void onFailure(@Nullable Throwable throwable, @Nullable Response response) {
-        LOGGER.error("SSE connection failed! {}", response, throwable);
+    public void onError(@Nullable Throwable throwable, @Nullable Response response) {
+        LOGGER.error("SSE connection error! {}", response, throwable);
     }
 
-    public void onEvent(@NotNull T data) {
-        LOGGER.info("SSE event received: {}", data);
+    public void onMessage(@NotNull T message) {
+        LOGGER.info("SSE message received: {}", message);
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
new file mode 100644
index 00000000..d475cc8a
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -0,0 +1,287 @@
+package net.jacobpeterson.alpaca.websocket;
+
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.WebSocket;
+import okhttp3.WebSocketListener;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Duration;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.ForkJoinPool.commonPool;
+
+/**
+ * {@link AlpacaWebsocket} represents an abstract websocket for Alpaca.
+ *
+ * @param <L> the {@link AlpacaWebsocketMessageListener} type parameter
+ * @param <T> the 'message type' type parameter
+ * @param <M> the 'message' type parameter
+ */
+public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageListener<T, M>> extends WebSocketListener
+        implements AlpacaWebsocketInterface<L> {
+
+    /**
+     * Defines a websocket normal closure code.
+     *
+     * @see WebSocket#close(int, String)
+     */
+    public static final int WEBSOCKET_NORMAL_CLOSURE_CODE = 1000;
+
+    /**
+     * Defines a websocket normal closure message.
+     *
+     * @see WebSocket#close(int, String)
+     */
+    public static final String WEBSOCKET_NORMAL_CLOSURE_MESSAGE = "Normal closure";
+
+    /**
+     * Defines the maximum number of reconnection attempts to be made by an {@link AlpacaWebsocket}.
+     */
+    public static int MAX_RECONNECT_ATTEMPTS = 10;
+
+    /**
+     * Defines the sleep interval {@link Duration} between reconnection attempts made by an {@link AlpacaWebsocket}.
+     */
+    public static Duration RECONNECT_SLEEP_INTERVAL = Duration.ofSeconds(1);
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AlpacaWebsocket.class);
+
+    protected final OkHttpClient okHttpClient;
+    protected final HttpUrl websocketURL;
+    protected final String websocketName;
+    protected final String keyID;
+    protected final String secretKey;
+    protected final String oAuthToken;
+    protected final boolean useOAuth;
+    protected L listener;
+    protected AlpacaWebsocketStateListener alpacaWebsocketStateListener;
+    protected WebSocket websocket;
+    protected boolean connected;
+    protected boolean authenticated;
+    protected CompletableFuture<Boolean> authenticationMessageFuture;
+    protected boolean intentionalClose;
+    protected int reconnectAttempts;
+
+    protected boolean automaticallyReconnect;
+
+    /**
+     * Instantiates a {@link AlpacaWebsocket}.
+     *
+     * @param okHttpClient  the {@link OkHttpClient}
+     * @param websocketURL  the websocket {@link HttpUrl}
+     * @param websocketName the websocket name
+     * @param keyID         the key ID
+     * @param secretKey     the secret key
+     * @param oAuthToken    the OAuth token
+     */
+    public AlpacaWebsocket(OkHttpClient okHttpClient, HttpUrl websocketURL, String websocketName,
+            String keyID, String secretKey, String oAuthToken) {
+        checkNotNull(okHttpClient);
+        checkNotNull(websocketURL);
+        checkNotNull(websocketName);
+
+        this.okHttpClient = okHttpClient;
+        this.websocketURL = websocketURL;
+        this.websocketName = websocketName;
+        this.keyID = keyID;
+        this.secretKey = secretKey;
+        this.oAuthToken = oAuthToken;
+        useOAuth = oAuthToken != null;
+
+        automaticallyReconnect = true;
+    }
+
+    @Override
+    public void connect() {
+        if (!isConnected()) {
+            final Request websocketRequest = new Request.Builder()
+                    .url(websocketURL)
+                    .get()
+                    .build();
+            websocket = okHttpClient.newWebSocket(websocketRequest, this);
+        }
+    }
+
+    @Override
+    public void disconnect() {
+        if (websocket != null && isConnected()) {
+            intentionalClose = true;
+            websocket.close(WEBSOCKET_NORMAL_CLOSURE_CODE, WEBSOCKET_NORMAL_CLOSURE_MESSAGE);
+        } else {
+            cleanupState();
+        }
+    }
+
+    @Override
+    public boolean isConnected() {
+        return connected;
+    }
+
+    @Override
+    public boolean isAuthenticated() {
+        return authenticated;
+    }
+
+    @Override
+    public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
+        connected = true;
+
+        LOGGER.info("{} websocket opened.", websocketName);
+        LOGGER.debug("{} websocket response: {}", websocketName, response);
+
+        // Call 'onConnection' or 'onReconnection' async to avoid any potential deadlocking since this is called
+        // in sync with 'onMessage' in OkHttp's 'WebSocketListener'
+        commonPool().execute(() -> {
+            if (reconnectAttempts > 0) {
+                onReconnection();
+            } else {
+                onConnection();
+            }
+        });
+
+        if (alpacaWebsocketStateListener != null) {
+            alpacaWebsocketStateListener.onOpen(response);
+        }
+    }
+
+    @Override
+    public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
+        connected = false;
+
+        if (intentionalClose) {
+            LOGGER.info("{} websocket closed.", websocketName);
+            LOGGER.debug("Close code: {}, Reason: {}", code, reason);
+            cleanupState();
+        } else {
+            LOGGER.error("{} websocket closed unintentionally! Code: {}, Reason: {}", websocketName, code, reason);
+            handleReconnectionAttempt();
+        }
+
+        if (alpacaWebsocketStateListener != null) {
+            alpacaWebsocketStateListener.onClosed(code, reason);
+        }
+    }
+
+    @Override
+    public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable cause, @Nullable Response response) {
+        LOGGER.error("{} websocket failure!", websocketName, cause);
+
+        // A websocket failure occurs when either there is a connection failure or when the client throws
+        // an exception when receiving a message. In either case, OkHttp will close the websocket connection,
+        // so try to reopen it.
+        connected = false;
+        handleReconnectionAttempt();
+
+        if (alpacaWebsocketStateListener != null) {
+            alpacaWebsocketStateListener.onFailure(cause);
+        }
+    }
+
+    /**
+     * Attempts to reconnect the disconnected {@link #websocket} asynchronously.
+     */
+    private void handleReconnectionAttempt() {
+        if (!automaticallyReconnect) {
+            return;
+        }
+        if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
+            LOGGER.info("Attempting to reconnect {} websocket in {} seconds... (attempt {} of {})",
+                    websocketName, RECONNECT_SLEEP_INTERVAL.toSeconds(), reconnectAttempts + 1, MAX_RECONNECT_ATTEMPTS);
+            reconnectAttempts++;
+            commonPool().execute(() -> {
+                try {
+                    Thread.sleep(RECONNECT_SLEEP_INTERVAL.toMillis());
+                } catch (InterruptedException interruptedException) {
+                    return;
+                }
+                connect();
+            });
+        } else {
+            LOGGER.error("Exhausted {} reconnection attempts. Not attempting to reconnect.", MAX_RECONNECT_ATTEMPTS);
+            cleanupState();
+        }
+    }
+
+    /**
+     * Cleans up this instance's state variables.
+     */
+    protected void cleanupState() {
+        websocket = null;
+        connected = false;
+        authenticated = false;
+        if (authenticationMessageFuture != null && !authenticationMessageFuture.isDone()) {
+            authenticationMessageFuture.complete(false);
+        }
+        authenticationMessageFuture = null;
+        intentionalClose = false;
+        reconnectAttempts = 0;
+    }
+
+    /**
+     * Called asynchronously when a websocket connection is made.
+     */
+    protected abstract void onConnection();
+
+    /**
+     * Called asynchronously when a websocket reconnection is made after unintentional disconnection.
+     */
+    protected abstract void onReconnection();
+
+    /**
+     * Sends an authentication message to authenticate this websocket stream.
+     */
+    protected abstract void sendAuthenticationMessage();
+
+    @Override
+    public Future<Boolean> getAuthorizationFuture() {
+        if (authenticationMessageFuture == null) {
+            authenticationMessageFuture = new CompletableFuture<>();
+        }
+        return authenticationMessageFuture;
+    }
+
+    /**
+     * Calls the {@link AlpacaWebsocketMessageListener}.
+     *
+     * @param messageType the message type
+     * @param message     the message
+     */
+    protected void callListener(T messageType, M message) {
+        if (listener != null) {
+            try {
+                listener.onMessage(messageType, message);
+            } catch (Exception exception) {
+                LOGGER.error("{} listener threw exception!", websocketName, exception);
+            }
+        }
+    }
+
+    @Override
+    public void setListener(L listener) {
+        this.listener = listener;
+    }
+
+    public AlpacaWebsocketStateListener getWebsocketStateListener() {
+        return alpacaWebsocketStateListener;
+    }
+
+    public void setAlpacaWebsocketStateListener(AlpacaWebsocketStateListener alpacaWebsocketStateListener) {
+        this.alpacaWebsocketStateListener = alpacaWebsocketStateListener;
+    }
+
+    public boolean doesAutomaticallyReconnect() {
+        return automaticallyReconnect;
+    }
+
+    public void setAutomaticallyReconnect(boolean automaticallyReconnect) {
+        this.automaticallyReconnect = automaticallyReconnect;
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
new file mode 100644
index 00000000..033475a2
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
@@ -0,0 +1,82 @@
+package net.jacobpeterson.alpaca.websocket;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * {@link AlpacaWebsocketInterface} defines an interface for Alpaca websockets.
+ *
+ * @param <L> the {@link AlpacaWebsocketMessageListener} type parameter
+ */
+public interface AlpacaWebsocketInterface<L extends AlpacaWebsocketMessageListener<?, ?>> {
+
+    /**
+     * Connects this Websocket.
+     */
+    void connect();
+
+    /**
+     * Disconnects this Websocket.
+     */
+    void disconnect();
+
+    /**
+     * Returns <code>true</code> if this websocket is connected, <code>false</code> otherwise.
+     *
+     * @return a boolean
+     */
+    boolean isConnected();
+
+    /**
+     * Returns <code>true</code> if this websocket is authenticated, <code>false</code> otherwise.
+     *
+     * @return a boolean
+     */
+    boolean isAuthenticated();
+
+    /**
+     * Returns <code>true</code> if {@link #isConnected()} and {@link #isAuthenticated()}, <code>false</code>
+     * otherwise.
+     *
+     * @return a boolean
+     */
+    default boolean isValid() {
+        return isConnected() && isAuthenticated();
+    }
+
+    /**
+     * Gets a {@link Boolean} {@link Future} that completes when an authentication message that is received after a new
+     * websocket connection indicates successful authentication.
+     * <br>
+     * Note that if this {@link AlpacaWebsocketInterface} is already authorized, the returned {@link Future} will likely
+     * never complete.
+     *
+     * @return a {@link Boolean} {@link Future}
+     */
+    Future<Boolean> getAuthorizationFuture();
+
+    /**
+     * Waits for {@link #getAuthorizationFuture()} to complete and returns its value, except when <code>timeout</code>
+     * time has elapsed, then this will return <code>false</code>.
+     *
+     * @param timeout the timeout time
+     * @param unit    the timeout {@link TimeUnit}
+     *
+     * @return a boolean
+     */
+    default boolean waitForAuthorization(long timeout, TimeUnit unit) {
+        try {
+            return getAuthorizationFuture().get(timeout, unit);
+        } catch (InterruptedException | ExecutionException | TimeoutException ignored) {}
+        return false;
+    }
+
+    /**
+     * Sets the {@link AlpacaWebsocketMessageListener} for this {@link AlpacaWebsocketInterface}.
+     *
+     * @param listener the {@link AlpacaWebsocketMessageListener}
+     */
+    void setListener(L listener);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
new file mode 100644
index 00000000..da55535c
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
@@ -0,0 +1,20 @@
+package net.jacobpeterson.alpaca.websocket;
+
+/**
+ * {@link AlpacaWebsocketMessageListener} defines a listener interface for messages from an {@link AlpacaWebsocket}
+ * instances.
+ *
+ * @param <T> the 'message type' type parameter
+ * @param <M> the 'message' type parameter
+ */
+@FunctionalInterface
+public interface AlpacaWebsocketMessageListener<T, M> {
+
+    /**
+     * Called when a message is received.
+     *
+     * @param messageType the message type
+     * @param message     the message
+     */
+    void onMessage(T messageType, M message);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketStateListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketStateListener.java
new file mode 100644
index 00000000..2667c2ca
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketStateListener.java
@@ -0,0 +1,33 @@
+package net.jacobpeterson.alpaca.websocket;
+
+import okhttp3.Response;
+import okhttp3.WebSocket;
+
+/**
+ * {@link AlpacaWebsocketStateListener} is an interface to listen for various state changes in an {@link WebSocket}
+ * instance.
+ */
+public interface AlpacaWebsocketStateListener {
+
+    /**
+     * Called when the {@link WebSocket} is connected.
+     *
+     * @param response the HTTP websocket upgrade {@link Response}
+     */
+    void onOpen(Response response);
+
+    /**
+     * Called when the {@link WebSocket} is disconnected.
+     *
+     * @param code   the code
+     * @param reason the reason
+     */
+    void onClosed(int code, String reason);
+
+    /**
+     * Called when a {@link WebSocket} error has occurred.
+     *
+     * @param cause the cause {@link Throwable}
+     */
+    void onFailure(Throwable cause);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java
new file mode 100644
index 00000000..c96baee5
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java
@@ -0,0 +1,11 @@
+package net.jacobpeterson.alpaca.websocket.trades;
+
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketMessageListener;
+
+/**
+ * {@link TradesListener} defines a listener interface for {@link TradesWebsocket} messages.
+ */
+public interface TradesListener extends AlpacaWebsocketMessageListener<TradesStreamMessageType, TradesStreamMessage> {
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java
new file mode 100644
index 00000000..a6dd93ec
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java
@@ -0,0 +1,19 @@
+package net.jacobpeterson.alpaca.websocket.trades;
+
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link TradesListenerAdapter} is an adapter class for {@link TradesListener}.
+ */
+public class TradesListenerAdapter implements TradesListener {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(TradesListenerAdapter.class);
+
+    @Override
+    public void onMessage(TradesStreamMessageType messageType, TradesStreamMessage message) {
+        LOGGER.info("{} message received: {}", messageType, message);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
new file mode 100644
index 00000000..a6fc8896
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
@@ -0,0 +1,219 @@
+package net.jacobpeterson.alpaca.websocket.trades;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationData;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationMessage;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.listening.ListeningMessage;
+import net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdateMessage;
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.WebSocket;
+import okio.ByteString;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.Iterables.toArray;
+import static com.google.gson.JsonParser.parseString;
+import static java.lang.String.format;
+import static java.util.Collections.singletonList;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType.TRADE_UPDATES;
+import static net.jacobpeterson.alpaca.openapi.trader.JSON.getGson;
+
+/**
+ * {@link TradesWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
+ * {@link TradesWebsocketInterface} interface for
+ * <a href="https://docs.alpaca.markets/docs/websocket-streaming">Trades Streaming</a>.
+ */
+public class TradesWebsocket extends AlpacaWebsocket<TradesStreamMessageType, TradesStreamMessage, TradesListener>
+        implements TradesWebsocketInterface {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(TradesWebsocket.class);
+    private static final String STREAM_ELEMENT_KEY = "stream";
+    private static final List<TradesStreamMessageType> SUBSCRIBABLE_STREAMING_MESSAGE_TYPES =
+            singletonList(TRADE_UPDATES);
+
+    /**
+     * Creates a {@link HttpUrl} for {@link TradesWebsocket}.
+     *
+     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
+     *
+     * @return a {@link HttpUrl}
+     */
+    @SuppressWarnings("UnnecessaryDefault")
+    private static HttpUrl createWebsocketURL(TraderAPIEndpointType traderAPIEndpointType) {
+        return new HttpUrl.Builder()
+                .scheme("https")
+                .host((switch (traderAPIEndpointType) {
+                    case LIVE -> "api";
+                    case PAPER -> "paper-api";
+                    default -> throw new UnsupportedOperationException();
+                }) + ".alpaca.markets")
+                .addPathSegment("stream")
+                .build();
+    }
+
+    private final Set<TradesStreamMessageType> listenedMessageTypes;
+
+    /**
+     * Instantiates a new {@link TradesWebsocket}.
+     *
+     * @param okHttpClient          the {@link OkHttpClient}
+     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
+     * @param keyID                 the key ID
+     * @param secretKey             the secret key
+     * @param oAuthToken            the OAuth token
+     */
+    public TradesWebsocket(OkHttpClient okHttpClient, TraderAPIEndpointType traderAPIEndpointType,
+            String keyID, String secretKey, String oAuthToken) {
+        super(okHttpClient, createWebsocketURL(traderAPIEndpointType), "Trades Stream", keyID, secretKey, oAuthToken);
+        listenedMessageTypes = new HashSet<>();
+    }
+
+    @Override
+    protected void cleanupState() {
+        super.cleanupState();
+        listenedMessageTypes.clear();
+    }
+
+    @Override
+    protected void onConnection() {
+        sendAuthenticationMessage();
+    }
+
+    @Override
+    protected void onReconnection() {
+        sendAuthenticationMessage();
+        if (waitForAuthorization(5, SECONDS)) {
+            subscribe(toArray(listenedMessageTypes, TradesStreamMessageType.class));
+        }
+    }
+
+    @Override
+    protected void sendAuthenticationMessage() {
+        // Ensure that the authorization Future exists
+        getAuthorizationFuture();
+
+        final JsonObject authObject = new JsonObject();
+        authObject.addProperty("action", "authenticate");
+        final JsonObject authData = new JsonObject();
+        if (useOAuth) {
+            authData.addProperty("oauth_token", oAuthToken);
+        } else {
+            authData.addProperty("key_id", keyID);
+            authData.addProperty("secret_key", secretKey);
+        }
+        authObject.add("data", authData);
+
+        LOGGER.info("{} websocket sending authentication message...", websocketName);
+        websocket.send(authObject.toString());
+    }
+
+    @Override
+    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) {
+        final String messageString = byteString.utf8();
+        LOGGER.trace("Websocket message received: {}", messageString);
+
+        // Parse JSON string and identify 'messageType'
+        final JsonElement messageElement = parseString(messageString);
+        checkState(messageElement instanceof JsonObject, "Message must be a JsonObject! Received: %s", messageElement);
+        final JsonObject messageObject = messageElement.getAsJsonObject();
+        final JsonElement streamElement = messageObject.get(STREAM_ELEMENT_KEY);
+        checkState(streamElement instanceof JsonPrimitive,
+                "Message must contain %s element! Received: %s", STREAM_ELEMENT_KEY, messageElement);
+        final TradesStreamMessageType messageType = getGson().fromJson(streamElement, TradesStreamMessageType.class);
+        checkNotNull(messageType, "TradesStreamMessageType not found in message: %s", messageObject);
+
+        // Deserialize message based on 'messageType'
+        TradesStreamMessage message;
+        switch (messageType) {
+            case AUTHORIZATION:
+                message = getGson().fromJson(messageObject, AuthorizationMessage.class);
+                final AuthorizationData authorizationData = ((AuthorizationMessage) message).getData();
+                authenticated = authorizationData.getStatus().equalsIgnoreCase("authorized") &&
+                        authorizationData.getAction().equalsIgnoreCase("authenticate");
+                if (!authenticated) {
+                    throw new RuntimeException(format("%s websocket not authenticated! Received: %s.",
+                            websocketName, message));
+                } else {
+                    LOGGER.info("{} websocket authenticated.", websocketName);
+                }
+                if (authenticationMessageFuture != null) {
+                    authenticationMessageFuture.complete(authenticated);
+                }
+                break;
+            case LISTENING:
+                message = getGson().fromJson(messageObject, ListeningMessage.class);
+                // Remove all 'TradeStreamMessageType's that are no longer listened to and add new ones
+                final List<TradesStreamMessageType> currentTypes = ((ListeningMessage) message).getData().getStreams();
+                currentTypes.stream()
+                        .filter(not(listenedMessageTypes::contains))
+                        .forEach(listenedMessageTypes::remove);
+                listenedMessageTypes.addAll(currentTypes);
+                break;
+            case TRADE_UPDATES:
+                message = getGson().fromJson(messageObject, TradeUpdateMessage.class);
+                break;
+            default:
+                throw new UnsupportedOperationException();
+        }
+
+        // Call listener
+        if (listenedMessageTypes.contains(messageType)) {
+            callListener(messageType, message);
+        }
+    }
+
+    @Override
+    public void subscribe(TradesStreamMessageType... messageTypes) {
+        if (messageTypes == null || messageTypes.length == 0) {
+            return;
+        }
+
+        // Add all non-subscribable 'messageTypes' before connecting or sending websocket message
+        Arrays.stream(messageTypes)
+                .filter(not(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains))
+                .forEach(listenedMessageTypes::add);
+
+        // Create streams subscription JSON message
+        final JsonObject requestObject = new JsonObject();
+        requestObject.addProperty("action", "listen");
+        final JsonArray streamsArray = new JsonArray();
+        Arrays.stream(messageTypes)
+                .filter(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains)
+                .forEach((type) -> streamsArray.add(type.toString()));
+        if (streamsArray.isEmpty()) {
+            return;
+        } else if (!isConnected()) {
+            throw new IllegalStateException("This websocket must be connected before subscribing to streams!");
+        }
+        final JsonObject dataObject = new JsonObject();
+        dataObject.add("streams", streamsArray);
+        requestObject.add("data", dataObject);
+
+        websocket.send(requestObject.toString());
+        LOGGER.info("Requested streams: {}.", streamsArray);
+    }
+
+    @Override
+    public Collection<TradesStreamMessageType> subscriptions() {
+        return new HashSet<>(listenedMessageTypes);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java
new file mode 100644
index 00000000..b866e37e
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java
@@ -0,0 +1,26 @@
+package net.jacobpeterson.alpaca.websocket.trades;
+
+import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
+
+import java.util.Collection;
+
+/**
+ * {@link TradesWebsocketInterface} is an {@link AlpacaWebsocketInterface} for a {@link TradesWebsocket}.
+ */
+public interface TradesWebsocketInterface extends AlpacaWebsocketInterface<TradesListener> {
+
+    /**
+     * Sets the {@link TradesStreamMessageType}s for this stream.
+     *
+     * @param messageTypes the {@link TradesStreamMessageType}s
+     */
+    void subscribe(TradesStreamMessageType... messageTypes);
+
+    /**
+     * Gets all the currently subscribed {@link TradesStreamMessageType}s.
+     *
+     * @return a {@link Collection} of {@link TradesStreamMessageType}
+     */
+    Collection<TradesStreamMessageType> subscriptions();
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_data.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_data.json
similarity index 69%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_data.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_data.json
index e88df4d5..f05ae9bd 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/authorization/authorization_data.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_data.json
@@ -1,6 +1,5 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
   "properties": {
     "status": {
       "existingJavaType": "java.lang.String",
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json
new file mode 100644
index 00000000..82edef4d
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json
@@ -0,0 +1,12 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage"
+  },
+  "properties": {
+    "data": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationData",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationData}."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json
new file mode 100644
index 00000000..78a68ba5
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json
@@ -0,0 +1,9 @@
+{
+  "type": "object",
+  "properties": {
+    "streams": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType>",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType}s."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json
new file mode 100644
index 00000000..58b7b54f
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json
@@ -0,0 +1,12 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage"
+  },
+  "properties": {
+    "data": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.listening.ListeningData",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.listening.ListeningData}."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json
new file mode 100644
index 00000000..fa337195
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json
@@ -0,0 +1,9 @@
+{
+  "type": "object",
+  "properties": {
+    "stream": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType}."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message_type.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message_type.json
new file mode 100644
index 00000000..98ab6ea6
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message_type.json
@@ -0,0 +1,8 @@
+{
+  "type": "string",
+  "enum": [
+    "listening",
+    "authorization",
+    "trade_updates"
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update.json
similarity index 52%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update.json
index c0072275..ab5b6875 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/trade_update.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update.json
@@ -1,24 +1,32 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/streaming/\">Streaming</a>.",
   "properties": {
     "event": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.streaming.trade.enums.TradeUpdateEvent",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.streaming.trade.enums.TradeUpdateEvent}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdateEvent",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdateEvent}."
+    },
+    "execution_id": {
+      "existingJavaType": "java.lang.String",
+      "title": "The execution ID."
     },
     "price": {
       "existingJavaType": "java.lang.String",
       "title": "The price."
     },
-    "timestamp": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "title": "The timestamp."
-    },
     "position_qty": {
       "existingJavaType": "java.lang.String",
       "javaName": "positionQuantity",
       "title": "The position quantity."
     },
+    "qty": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "quantity",
+      "title": "The quantity."
+    },
+    "timestamp": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "title": "The timestamp."
+    },
     "order": {
       "existingJavaType": "net.jacobpeterson.alpaca.openapi.trader.model.Order",
       "title": "The {@link net.jacobpeterson.alpaca.openapi.trader.model.Order}."
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/enums/trade_update_event.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_event.json
similarity index 83%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/enums/trade_update_event.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_event.json
index e422fe52..1277728e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/streaming/trade/enums/trade_update_event.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_event.json
@@ -1,6 +1,5 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/orders/\">Orders</a>.",
   "enum": [
     "new",
     "fill",
@@ -24,13 +23,13 @@
       "description": "Sent when an order has been routed to exchanges for execution."
     },
     {
-      "description": "Sent when your order has been completely filled."
+      "description": "Sent when an order has been completely filled."
     },
     {
       "description": "Sent when a number of shares less than the total remaining quantity on your order has been filled."
     },
     {
-      "description": "Sent when your requested cancelation of an order is processed."
+      "description": "Sent when your requested cancellation of an order is processed."
     },
     {
       "description": "Sent when an order has reached the end of its lifespan, as determined by the order’s time in force value."
@@ -51,7 +50,7 @@
       "description": "Sent when your order has been stopped, and a trade is guaranteed for the order, usually at a stated price or better, but has not yet occurred."
     },
     {
-      "description": "Sent when the order is awaiting cancelation. Most cancelations will occur without the order entering this state."
+      "description": "Sent when the order is awaiting cancellation. Most cancellations will occur without the order entering this state."
     },
     {
       "description": "Sent when the order is awaiting replacement."
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json
new file mode 100644
index 00000000..6876c589
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json
@@ -0,0 +1,12 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage"
+  },
+  "properties": {
+    "data": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdate",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdate}."
+    }
+  }
+}

From 27e951b2aa2f3c5934c8b2809afb1a07fbfe0b35 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 4 Mar 2024 21:54:44 -0800
Subject: [PATCH 61/85] WIP update market data websocket POJO JSON schemas

---
 .../common/historical/bar/bar.json            | 41 -----------
 .../historical/bar/enums/bar_time_period.json | 28 --------
 .../common/historical/quote/quote.json        | 21 ------
 .../common/historical/trade/trade.json        | 21 ------
 .../control/subscriptions_message.json        | 25 -------
 .../realtime/control/success_message.json     | 14 ----
 .../common/realtime/market_data_message.json  | 11 ---
 .../common/realtime/quote/quote_message.json  | 24 -------
 .../common/realtime/symbol_message.json       | 14 ----
 .../common/realtime/trade/trade_message.json  | 24 -------
 .../crypto/historical/bar/crypto_bar.json     | 14 ----
 .../historical/bar/crypto_bars_response.json  | 14 ----
 .../bar/latest_crypto_bars_response.json      | 10 ---
 .../orderbook/crypto_orderbook.json           | 21 ------
 .../latest_crypto_orderbooks_response.json    | 10 ---
 .../crypto/historical/quote/crypto_quote.json | 19 ------
 .../quote/latest_crypto_quotes_response.json  | 10 ---
 .../historical/snapshot/crypto_snapshot.json  | 31 ---------
 .../snapshot/crypto_snapshots_response.json   | 10 ---
 .../crypto/historical/trade/crypto_trade.json | 19 ------
 .../trade/crypto_trades_response.json         | 14 ----
 .../trade/latest_crypto_trades_response.json  | 10 ---
 .../realtime/bar/crypto_bar_message.json      | 14 ----
 .../realtime/quote/crypto_quote_message.json  | 19 ------
 .../realtime/trade/crypto_trade_message.json  | 19 ------
 .../market_data/news/common/image.json        | 13 ----
 .../market_data/news/common/news_article.json | 39 -----------
 .../market_data/news/news_response.json       | 12 ----
 .../news/realtime/news_message.json           | 40 -----------
 .../historical/bar/enums/bar_adjustment.json  | 24 -------
 .../stock/historical/bar/enums/bar_feed.json  |  8 ---
 .../bar/multi_stock_bars_response.json        | 14 ----
 .../stock/historical/bar/stock_bar.json       | 14 ----
 .../historical/bar/stock_bars_response.json   | 18 -----
 .../quote/latest_stock_quote_response.json    | 14 ----
 .../stock/historical/quote/stock_quote.json   | 34 ----------
 .../quote/stock_quotes_response.json          | 18 -----
 .../stock/historical/snapshot/snapshot.json   | 26 -------
 .../trade/latest_stock_trade_response.json    | 14 ----
 .../stock/historical/trade/stock_trade.json   | 29 --------
 .../trade/stock_trades_response.json          | 18 -----
 .../stock/realtime/bar/stock_bar_message.json | 14 ----
 .../realtime/quote/stock_quote_message.json   | 39 -----------
 .../realtime/trade/stock_trade_message.json   | 29 --------
 .../model}/control/error_message.json         |  3 +-
 .../model/control/success_message.json        | 13 ++++
 .../model/control/success_message_type.json   |  7 ++
 .../marketdata/model/market_data_message.json |  4 ++
 .../crypto/CryptoMarketDataWebsocket.java     | 27 ++++++++
 .../crypto/model/bar/crypto_bar_message.json  | 53 +++++++++++++++
 .../model/crypto_market_data_message.json     | 13 ++++
 .../crypto_market_data_message_type.json}     | 19 ++++--
 .../orderbook/crypto_orderbook_entry.json     |  5 +-
 .../orderbook/crypto_orderbook_message.json   | 28 ++++++++
 .../model/quote/crypto_quote_message.json     | 38 +++++++++++
 .../crypto_subscriptions_message.json         | 33 +++++++++
 .../model/trade/crypto_trade_message.json     | 38 +++++++++++
 .../model/trade/crypto_trade_taker_side.json} |  1 -
 .../streams/news/NewsMarketDataWebsocket.java | 15 ++++
 .../streams/news/model/news/news_message.json | 38 +++++++++++
 .../news/model/news_market_data_message.json  | 13 ++++
 .../model/news_market_data_message_type.json  | 23 +++++++
 .../stock/StockMarketDataWebsocket.java       | 30 ++++++++
 .../stock/model/bar/stock_bar_message.json}   | 27 +++++---
 ...tock_limit_up_limit_down_band_message.json | 38 +++++++++++
 .../model/quote/stock_quote_message.json      | 58 ++++++++++++++++
 .../model/stock_market_data_message.json      | 13 ++++
 .../model/stock_market_data_message_type.json | 55 +++++++++++++++
 .../stock_subscriptions_message.json          | 49 +++++++++++++
 .../model/trade/stock_trade_message.json      | 48 +++++++++++++
 .../stock_trade_cancel_error_action.json      | 15 ++++
 .../stock_trade_cancel_error_message.json     | 48 +++++++++++++
 .../stock_trade_correction_message.json       | 68 +++++++++++++++++++
 .../stock_trading_status_message.json         | 43 ++++++++++++
 .../websocket/trades/TradesWebsocket.java     | 10 ++-
 75 files changed, 846 insertions(+), 901 deletions(-)
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/bar.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/quote/quote.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/trade/trade.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/subscriptions_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/success_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/market_data_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/quote/quote_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/symbol_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/trade/trade_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bar.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/crypto_quote.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trade.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/image.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/news_article.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/news_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/realtime/news_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bar.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bars_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quote.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/snapshot/snapshot.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trade.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trades_response.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/market_data/common/realtime => websocket/marketdata/model}/control/error_message.json (57%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message_type.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/market_data_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/bar/crypto_bar_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message.json
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/market_data/common/realtime/enums/market_data_message_type.json => websocket/marketdata/streams/crypto/model/crypto_market_data_message_type.json} (58%)
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/market_data/crypto/historical => websocket/marketdata/streams/crypto/model}/orderbook/crypto_orderbook_entry.json (57%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/quote/crypto_quote_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_message.json
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/market_data/crypto/common/enums/taker_side.json => websocket/marketdata/streams/crypto/model/trade/crypto_trade_taker_side.json} (52%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message_type.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java
 rename src/main/java/net/jacobpeterson/alpaca/{endpoint/market_data/common/realtime/bar/bar_message.json => websocket/marketdata/streams/stock/model/bar/stock_bar_message.json} (51%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/limituplimitdownband/stock_limit_up_limit_down_band_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message_type.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_action.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradingstatus/stock_trading_status_message.json

diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/bar.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/bar.json
deleted file mode 100644
index b2f3b42f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/bar.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "o": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "open",
-      "title": "Open price."
-    },
-    "h": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "high",
-      "title": "High price."
-    },
-    "l": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "low",
-      "title": "Low price."
-    },
-    "c": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "close",
-      "title": "Close price."
-    },
-    "n": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "tradeCount",
-      "title": "Trade count."
-    },
-    "vw": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "vwap",
-      "title": "VWAP (Volume Weighted Average Price)."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/enums/bar_time_period.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
deleted file mode 100644
index 0305b24e..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/bar/enums/bar_time_period.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "enum": [
-    "Min",
-    "Hour",
-    "Day",
-    "Week",
-    "Month"
-  ],
-  "javaEnums": [
-    {
-      "name": "MINUTE"
-    },
-    {
-      "name": "HOUR"
-    },
-    {
-      "name": "DAY"
-    },
-    {
-      "name": "WEEK"
-    },
-    {
-      "name": "MONTH"
-    }
-  ]
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/quote/quote.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/quote/quote.json
deleted file mode 100644
index ed75e88f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/quote/quote.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "ap": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askPrice",
-      "title": "Ask price."
-    },
-    "bp": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidPrice",
-      "title": "Bid price."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/trade/trade.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/trade/trade.json
deleted file mode 100644
index 1cacafa5..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/historical/trade/trade.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    },
-    "p": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "price",
-      "title": "Trade price."
-    },
-    "i": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "tradeID",
-      "title": "Trade ID."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/subscriptions_message.json
deleted file mode 100644
index b44cbb85..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/subscriptions_message.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "trades": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of subscribed trades."
-    },
-    "quotes": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of subscribed quotes."
-    },
-    "bars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of subscribed bars."
-    },
-    "news": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of subscribed news."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/success_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/success_message.json
deleted file mode 100644
index 2edd335b..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/success_message.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "msg": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "message",
-      "title": "The success message."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/market_data_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/market_data_message.json
deleted file mode 100644
index d41cd481..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/market_data_message.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "T": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType",
-      "javaName": "messageType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.enums.MarketDataMessageType}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/quote/quote_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/quote/quote_message.json
deleted file mode 100644
index d6bdaabc..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/quote/quote_message.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.SymbolMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "ap": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askPrice",
-      "title": "Ask price."
-    },
-    "bp": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidPrice",
-      "title": "Bid price."
-    },
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/symbol_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/symbol_message.json
deleted file mode 100644
index a6f40f69..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/symbol_message.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "S": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "symbol",
-      "title": "The symbol."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/trade/trade_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/trade/trade_message.json
deleted file mode 100644
index ca01450f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/trade/trade_message.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.SymbolMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
-  "properties": {
-    "i": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "tradeID",
-      "title": "Trade ID."
-    },
-    "p": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "price",
-      "title": "Trade price."
-    },
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp with nanosecond precision."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bar.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bar.json
deleted file mode 100644
index 1fbf8b5d..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bar.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.Bar"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "v": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "volume",
-      "title": "Volume."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
deleted file mode 100644
index 8461d5cc..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/crypto_bars_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "bars": {
-      "existingJavaType": "java.util.Map<String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar>>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar}s."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
deleted file mode 100644
index b502ddc3..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/bar/latest_crypto_bars_response.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "bars": {
-      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar>",
-      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
deleted file mode 100644
index 04409010..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://docs.alpaca.markets/reference/v1beta3cryptolatestorderbooks\">Latest Orderbook</a>.",
-  "properties": {
-    "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
-      "javaName": "timestamp",
-      "title": "Timestamp in RFC-3339 format with nanosecond precision."
-    },
-    "b": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry>",
-      "javaName": "bidSide",
-      "title": "An {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry}s for the buy/bid side."
-    },
-    "a": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry>",
-      "javaName": "askSide",
-      "title": "An {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbookEntry}s for the ask/sell side."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
deleted file mode 100644
index 31c605d3..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/latest_crypto_orderbooks_response.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "orderbooks": {
-      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook>",
-      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.orderbook.CryptoOrderbook}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/crypto_quote.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/crypto_quote.json
deleted file mode 100644
index 97e50e0b..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/crypto_quote.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.quote.Quote"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "as": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askSize",
-      "title": "Ask size."
-    },
-    "bs": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidSize",
-      "title": "Bid size."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
deleted file mode 100644
index 995ccd02..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/quote/latest_crypto_quotes_response.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "quotes": {
-      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote>",
-      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
deleted file mode 100644
index 9ff6ab0d..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshot.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://docs.alpaca.markets/reference/v1beta3cryptosnapshots\">Snapshots</a>.",
-  "properties": {
-    "dailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar",
-      "javaName": "dailyBar",
-      "title": "OHLC aggregate of all the trades in a given interval."
-    },
-    "latestQuote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.quote.CryptoQuote",
-      "javaName": "latestQuote",
-      "title": "The best bid and ask information for a given security."
-    },
-    "latestTrade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade",
-      "javaName": "latestTrade",
-      "title": "A crypto trade"
-    },
-    "minuteBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar",
-      "javaName": "minuteBar",
-      "title": "OHLC aggregate of all the trades in a given interval."
-    },
-    "prevDailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.bar.CryptoBar",
-      "javaName": "prevDailyBar",
-      "title": "OHLC aggregate of all the trades in a given interval."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
deleted file mode 100644
index 3ec3f3b8..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/snapshot/crypto_snapshots_response.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "snapshots": {
-      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot>",
-      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.snapshot.CryptoSnapshot}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trade.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trade.json
deleted file mode 100644
index 8e5bf0da..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trade.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.trade.Trade"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "s": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "size",
-      "title": "Trade size."
-    },
-    "tks": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide",
-      "javaName": "takerSide",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide} of the trade."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
deleted file mode 100644
index 0a007487..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/crypto_trades_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "trades": {
-      "existingJavaType": "java.util.Map<String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade>>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}s."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
deleted file mode 100644
index 4e6749c2..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/trade/latest_crypto_trades_response.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-references/market-data-api/crypto-pricing-data/historical/\">Historical Crypto Data</a>.",
-  "properties": {
-    "trades": {
-      "existingJavaType": "java.util.Map<String, net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade>",
-      "title": "The {@link java.util.Map} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.historical.trade.CryptoTrade}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
deleted file mode 100644
index 82cfe47f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/bar/crypto_bar_message.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
-  "properties": {
-    "v": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "volume",
-      "title": "Volume."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
deleted file mode 100644
index 1eac99c5..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/quote/crypto_quote_message.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
-  "properties": {
-    "as": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "askSize",
-      "title": "Ask size."
-    },
-    "bs": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "bidSize",
-      "title": "Bid size."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
deleted file mode 100644
index 09ee5a87..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/realtime/trade/crypto_trade_message.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/real-time/\">Realtime Crypto Market Data</a>.",
-  "properties": {
-    "s": {
-      "existingJavaType": "java.lang.Double",
-      "javaName": "size",
-      "title": "Trade size."
-    },
-    "tks": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide",
-      "javaName": "takerSide",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.common.enums.TakerSide} of the trade."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/image.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/image.json
deleted file mode 100644
index 2f3bf607..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/image.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-    "type": "object",
-    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
-    "properties": {
-      "size": {
-        "existingJavaType": "java.lang.String"
-      },
-      "url": {
-        "existingJavaType": "java.lang.String"
-      }
-    }
-  }
-  
\ No newline at end of file
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/news_article.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/news_article.json
deleted file mode 100644
index ee80a6d7..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/common/news_article.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-    "type": "object",
-    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
-    "properties": {
-      "author": {
-        "existingJavaType": "java.lang.String"
-      },
-      "content": {
-        "existingJavaType": "java.lang.String"
-      },
-      "created_at": {
-        "existingJavaType": "java.time.ZonedDateTime"
-      },
-      "headline": {
-        "existingJavaType": "java.lang.String"
-      },
-      "id": {
-        "existingJavaType": "java.lang.Long"
-      },
-      "images": {
-        "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.news.common.Image>"
-      },
-      "source": {
-        "existingJavaType": "java.lang.String"
-      },
-      "summary": {
-        "existingJavaType": "java.lang.String"
-      },
-      "symbols": {
-        "existingJavaType": "java.util.ArrayList<java.lang.String>"
-      },
-      "updated_at": {
-        "existingJavaType": "java.time.ZonedDateTime"
-      },
-      "url": {
-        "existingJavaType": "java.lang.String"
-      }
-    }
-  }
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/news_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/news_response.json
deleted file mode 100644
index b25ed7f9..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/news_response.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-    "type": "object",
-    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
-    "properties": {
-      "news": {
-        "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.news.common.NewsArticle>"
-      },
-      "next_page_token": {
-        "existingJavaType": "java.lang.String"
-      }
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/realtime/news_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/realtime/news_message.json
deleted file mode 100644
index bee011fe..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/news/realtime/news_message.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-    "type": "object",
-    "title": "See <a href=\"https://docs.alpaca.markets/reference/news-1\">News</a>.",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
-  },
-  "properties": {
-    "headline": {
-      "existingJavaType": "java.lang.String"
-    },
-    "created_at": {
-      "existingJavaType": "java.time.ZonedDateTime"
-    },
-    "updated_at": {
-      "existingJavaType": "java.time.ZonedDateTime"
-    },
-      "author": {
-        "existingJavaType": "java.lang.String"
-      },
-    "summary": {
-      "existingJavaType": "java.lang.String"
-    },
-      "content": {
-        "existingJavaType": "java.lang.String"
-      },
-
-    "url": {
-      "existingJavaType": "java.lang.String"
-    },
-    "symbols": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>"
-    },
-    "source": {
-      "existingJavaType": "java.lang.String"
-    },
-      "id": {
-        "existingJavaType": "java.lang.Long"
-      }
-    }
-  }
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
deleted file mode 100644
index d42b31b2..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_adjustment.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "enum": [
-    "raw",
-    "split",
-    "dividend",
-    "all"
-  ],
-  "javaEnums": [
-    {
-      "name": "RAW"
-    },
-    {
-      "name": "SPLIT"
-    },
-    {
-      "name": "DIVIDEND"
-    },
-    {
-      "name": "ALL"
-    }
-  ]
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_feed.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
deleted file mode 100644
index 87d8a1a4..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/enums/bar_feed.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "enum": [
-    "IEX",
-    "SIP"
-  ]
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
deleted file mode 100644
index 65028159..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/multi_stock_bars_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "bars": {
-      "existingJavaType": "java.util.HashMap<java.lang.String, java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar>>",
-      "title": "The {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}s of {@link java.lang.String} symbols {@link java.util.HashMap}."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bar.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bar.json
deleted file mode 100644
index ae851453..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bar.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.bar.Bar"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "v": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "volume",
-      "title": "Volume."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bars_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bars_response.json
deleted file mode 100644
index 5add43d2..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/bar/stock_bars_response.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "bars": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}s. This may be <code>null</code> if no bars exist."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
deleted file mode 100644
index 4a9a3992..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/latest_stock_quote_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "quote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quote.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quote.json
deleted file mode 100644
index 164f093f..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quote.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.quote.Quote"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "ax": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "askExchange",
-      "title": "Ask exchange."
-    },
-    "as": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "askSize",
-      "title": "Ask size."
-    },
-    "bx": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "bidExchange",
-      "title": "Bid exchange."
-    },
-    "bs": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "bidSize",
-      "title": "Bid size."
-    },
-    "c": {
-      "existingJavaType": "java.util.ArrayList<String>",
-      "javaName": "conditions",
-      "title": "The {@link java.util.ArrayList} of quote conditions."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quotes_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
deleted file mode 100644
index 418dcf6b..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/quote/stock_quotes_response.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "quotes": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote}s."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/snapshot/snapshot.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/snapshot/snapshot.json
deleted file mode 100644
index 9617b3de..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/snapshot/snapshot.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "latestTrade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade}."
-    },
-    "latestQuote": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.quote.StockQuote}."
-    },
-    "minuteBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar",
-      "title": "The minute {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}."
-    },
-    "dailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar",
-      "title": "The daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}."
-    },
-    "prevDailyBar": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar",
-      "title": "The previous daily {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.bar.StockBar}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
deleted file mode 100644
index b4765b53..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/latest_stock_trade_response.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "trade": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade",
-      "title": "The latest {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade}."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trade.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trade.json
deleted file mode 100644
index ea3e96cf..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trade.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.historical.trade.Trade"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "x": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "exchange",
-      "title": "Exchange where the trade happened."
-    },
-    "s": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "size",
-      "title": "Trade size."
-    },
-    "c": {
-      "existingJavaType": "java.util.ArrayList<String>",
-      "javaName": "conditions",
-      "title": "The {@link java.util.ArrayList} of trade conditions."
-    },
-    "z": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "tape",
-      "title": "Tape."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trades_response.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trades_response.json
deleted file mode 100644
index 9b08b1da..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/historical/trade/stock_trades_response.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "type": "object",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/historical/\">Historical Stock Data</a>.",
-  "properties": {
-    "trades": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.historical.trade.StockTrade}s."
-    },
-    "symbol": {
-      "existingJavaType": "java.lang.String",
-      "title": "Symbol that was queried."
-    },
-    "next_page_token": {
-      "existingJavaType": "java.lang.String",
-      "title": "Token that can be used to query the next page."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/bar/stock_bar_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
deleted file mode 100644
index 6bfbf62a..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/bar/stock_bar_message.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Realtime Stock Market Data</a>.",
-  "properties": {
-    "v": {
-      "existingJavaType": "java.lang.Long",
-      "javaName": "volume",
-      "title": "Volume."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/quote/stock_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
deleted file mode 100644
index 2633ccde..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/quote/stock_quote_message.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Realtime Stock Market Data</a>.",
-  "properties": {
-    "ax": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "askExchange",
-      "title": "Ask exchange code."
-    },
-    "as": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "askSize",
-      "title": "Ask size."
-    },
-    "bx": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "bidExchange",
-      "title": "Bid exchange code."
-    },
-    "bs": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "bidSize",
-      "title": "Bid size."
-    },
-    "c": {
-      "existingJavaType": "java.util.ArrayList<String>",
-      "javaName": "conditions",
-      "title": "The {@link java.util.ArrayList} of quote conditions."
-    },
-    "z": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "tape",
-      "title": "Tape."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/trade/stock_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
deleted file mode 100644
index c3b0bc51..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/stock/realtime/trade/stock_trade_message.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage"
-  },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/\">Realtime Stock Market Data</a>.",
-  "properties": {
-    "x": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "exchange",
-      "title": "Exchange code where the trade occurred."
-    },
-    "s": {
-      "existingJavaType": "java.lang.Integer",
-      "javaName": "size",
-      "title": "Trade size."
-    },
-    "c": {
-      "existingJavaType": "java.util.ArrayList<String>",
-      "javaName": "conditions",
-      "title": "The {@link java.util.ArrayList} of trade conditions."
-    },
-    "z": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "tape",
-      "title": "Tape."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/error_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/error_message.json
similarity index 57%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/error_message.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/error_message.json
index 39b9e7f2..655d6a03 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/control/error_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/error_message.json
@@ -1,9 +1,8 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.MarketDataMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.model.MarketDataMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
     "code": {
       "existingJavaType": "java.lang.Integer",
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message.json
new file mode 100644
index 00000000..e471b400
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message.json
@@ -0,0 +1,13 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.model.MarketDataMessage"
+  },
+  "properties": {
+    "msg": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.model.control.SuccessMessageType",
+      "javaName": "messageType",
+      "title": "The success message type."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message_type.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message_type.json
new file mode 100644
index 00000000..61c6c983
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/control/success_message_type.json
@@ -0,0 +1,7 @@
+{
+  "type": "string",
+  "enum": [
+    "success",
+    "authenticated"
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/market_data_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/market_data_message.json
new file mode 100644
index 00000000..e1a8346a
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/model/market_data_message.json
@@ -0,0 +1,4 @@
+{
+  "type": "object",
+  "properties": {}
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java
new file mode 100644
index 00000000..8eed2ce2
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java
@@ -0,0 +1,27 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.bar.CryptoBarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.quote.CryptoQuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.trade.CryptoTradeMessage;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.OkHttpClient;
+
+/**
+ * {@link CryptoMarketDataWebsocket} is a {@link MarketDataWebsocket} for
+ * <a href="https://docs.alpaca.markets/docs/real-time-crypto-pricing-data">Realtime
+ * Crypto Market Data</a>
+ */
+public class CryptoMarketDataWebsocket extends MarketDataWebsocket {
+
+    /**
+     * Instantiates a new {@link CryptoMarketDataWebsocket}.
+     *
+     * @param okHttpClient the {@link OkHttpClient}
+     * @param keyID        the key ID
+     * @param secretKey    the secret key
+     */
+    public CryptoMarketDataWebsocket(OkHttpClient okHttpClient, String keyID, String secretKey) {
+        super(okHttpClient, "v1beta3/crypto/us", "Crypto", keyID, secretKey, CryptoTradeMessage.class,
+                CryptoQuoteMessage.class, CryptoBarMessage.class);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/bar/crypto_bar_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/bar/crypto_bar_message.json
new file mode 100644
index 00000000..f24e3e99
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/bar/crypto_bar_message.json
@@ -0,0 +1,53 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "o": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "open",
+      "title": "The open price."
+    },
+    "h": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "high",
+      "title": "The high price."
+    },
+    "l": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "low",
+      "title": "The low price."
+    },
+    "c": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "close",
+      "title": "The close price."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp."
+    },
+    "v": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "volume",
+      "title": "The volume."
+    },
+    "n": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeCount",
+      "title": "The trade count."
+    },
+    "vw": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "vwap",
+      "title": "The VWAP (Volume Weighted Average Price)."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message.json
new file mode 100644
index 00000000..0ced4e8f
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message.json
@@ -0,0 +1,13 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.model.MarketDataMessage"
+  },
+  "properties": {
+    "T": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType",
+      "javaName": "messageType",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType}."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/enums/market_data_message_type.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message_type.json
similarity index 58%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/enums/market_data_message_type.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message_type.json
index 6999050d..a84d1728 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/enums/market_data_message_type.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/crypto_market_data_message_type.json
@@ -1,6 +1,5 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "enum": [
     "success",
     "error",
@@ -8,7 +7,9 @@
     "t",
     "q",
     "b",
-    "n"
+    "d",
+    "u",
+    "o"
   ],
   "javaEnums": [
     {
@@ -21,16 +22,22 @@
       "name": "SUBSCRIPTION"
     },
     {
-      "name": "TRADE"
+      "name": "TRADES"
     },
     {
-      "name": "QUOTE"
+      "name": "QUOTES"
     },
     {
-      "name": "BAR"
+      "name": "MINUTE_BARS"
     },
     {
-      "name": "NEWS"
+      "name": "DAILY_BARS"
+    },
+    {
+      "name": "UPDATED_BARS"
+    },
+    {
+      "name": "ORDER_BOOKS"
     }
   ]
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_entry.json
similarity index 57%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_entry.json
index 5387ecc7..51ce64b5 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/historical/orderbook/crypto_orderbook_entry.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_entry.json
@@ -1,16 +1,15 @@
 {
   "type": "object",
-  "title": "See <a href=\"https://docs.alpaca.markets/reference/v1beta3cryptolatestorderbooks\">Latest Orderbook</a>.",
   "properties": {
     "p": {
       "existingJavaType": "java.lang.Double",
       "javaName": "price",
-      "title": "Price."
+      "title": "The price."
     },
     "s": {
       "existingJavaType": "java.lang.Double",
       "javaName": "size",
-      "title": "Size."
+      "title": "The size."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json
new file mode 100644
index 00000000..80aa0c54
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json
@@ -0,0 +1,28 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "a": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry>",
+      "javaName": "asks",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry} asks."
+    },
+    "b": {
+      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry>",
+      "javaName": "bids",
+      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry} bids."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/quote/crypto_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/quote/crypto_quote_message.json
new file mode 100644
index 00000000..d314de7a
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/quote/crypto_quote_message.json
@@ -0,0 +1,38 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "ap": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
+      "title": "The ask price."
+    },
+    "as": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askSize",
+      "title": "The ask size."
+    },
+    "bp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
+      "title": "The bid price."
+    },
+    "bs": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidSize",
+      "title": "The bid size."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json
new file mode 100644
index 00000000..19b1f191
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json
@@ -0,0 +1,33 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "trades": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to trades."
+    },
+    "quotes": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to quotes."
+    },
+    "bars": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "javaName": "minuteBars",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to minute bars."
+    },
+    "dailyBars": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to daily bars."
+    },
+    "updatedBars": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to updated bars."
+    },
+    "orderbooks": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to orderbooks."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_message.json
new file mode 100644
index 00000000..13118dd7
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_message.json
@@ -0,0 +1,38 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "i": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeID",
+      "title": "The trade ID."
+    },
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "The trade price."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "size",
+      "title": "The trade size."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "tks": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.trade.CryptoTradeTakerSide",
+      "javaName": "takerSide",
+      "title": "The taker side."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/common/enums/taker_side.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_taker_side.json
similarity index 52%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/common/enums/taker_side.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_taker_side.json
index 8c8e4adf..d793fbfb 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/crypto/common/enums/taker_side.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/trade/crypto_trade_taker_side.json
@@ -1,6 +1,5 @@
 {
   "type": "string",
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-crypto-data/\">Crypto Data</a>.",
   "enum": [
     "B",
     "S"
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java
new file mode 100644
index 00000000..8e92fa69
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java
@@ -0,0 +1,15 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.news;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.OkHttpClient;
+
+public class NewsMarketDataWebsocket extends MarketDataWebsocket {
+    public NewsMarketDataWebsocket(OkHttpClient okHttpClient,
+            String keyID, String secretKey) {
+        super(okHttpClient, "v1beta1/news", "News", keyID, secretKey, TradeMessage.class,
+                QuoteMessage.class, BarMessage.class);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json
new file mode 100644
index 00000000..047c0790
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json
@@ -0,0 +1,38 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessage"
+  },
+  "properties": {
+    "id": {
+      "existingJavaType": "java.lang.Long"
+    },
+    "source": {
+      "existingJavaType": "java.lang.String"
+    },
+    "headline": {
+      "existingJavaType": "java.lang.String"
+    },
+    "author": {
+      "existingJavaType": "java.lang.String"
+    },
+    "summary": {
+      "existingJavaType": "java.lang.String"
+    },
+    "content": {
+      "existingJavaType": "java.lang.String"
+    },
+    "url": {
+      "existingJavaType": "java.lang.String"
+    },
+    "symbols": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>"
+    },
+    "created_at": {
+      "existingJavaType": "java.time.OffsetDateTime"
+    },
+    "updated_at": {
+      "existingJavaType": "java.time.OffsetDateTime"
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message.json
new file mode 100644
index 00000000..1f967c5b
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message.json
@@ -0,0 +1,13 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.model.MarketDataMessage"
+  },
+  "properties": {
+    "T": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType",
+      "javaName": "messageType",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType}."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message_type.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message_type.json
new file mode 100644
index 00000000..4ddd6017
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news_market_data_message_type.json
@@ -0,0 +1,23 @@
+{
+  "type": "string",
+  "enum": [
+    "success",
+    "error",
+    "subscription",
+    "n"
+  ],
+  "javaEnums": [
+    {
+      "name": "SUCCESS"
+    },
+    {
+      "name": "ERROR"
+    },
+    {
+      "name": "SUBSCRIPTION"
+    },
+    {
+      "name": "NEWS"
+    }
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java
new file mode 100644
index 00000000..87bfe6c9
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java
@@ -0,0 +1,30 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.stock;
+
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.bar.StockBarMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.quote.StockQuoteMessage;
+import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.trade.StockTradeMessage;
+import net.jacobpeterson.alpaca.model.properties.DataAPIType;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.OkHttpClient;
+
+/**
+ * {@link StockMarketDataWebsocket} is a {@link MarketDataWebsocket} for
+ * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/">Realtime
+ * Stock Market Data</a>
+ */
+public class StockMarketDataWebsocket extends MarketDataWebsocket {
+
+    /**
+     * Instantiates a new {@link StockMarketDataWebsocket}.
+     *
+     * @param okHttpClient the {@link OkHttpClient}
+     * @param dataAPIType  the {@link DataAPIType}
+     * @param keyID        the key ID
+     * @param secretKey    the secret key
+     */
+    public StockMarketDataWebsocket(OkHttpClient okHttpClient, DataAPIType dataAPIType,
+            String keyID, String secretKey) {
+        super(okHttpClient, "v2/" + dataAPIType.toString(), "Stock", keyID, secretKey, StockTradeMessage.class,
+                StockQuoteMessage.class, StockBarMessage.class);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/bar/bar_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/bar/stock_bar_message.json
similarity index 51%
rename from src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/bar/bar_message.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/bar/stock_bar_message.json
index 9df2a564..95c6c0d3 100644
--- a/src/main/java/net/jacobpeterson/alpaca/endpoint/market_data/common/realtime/bar/bar_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/bar/stock_bar_message.json
@@ -1,44 +1,53 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.SymbolMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
   },
-  "title": "See <a href=\"https://alpaca.markets/docs/api-documentation/api-v2/market-data/\">Market Data</a>.",
   "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
     "o": {
       "existingJavaType": "java.lang.Double",
       "javaName": "open",
-      "title": "Open price."
+      "title": "The open price."
     },
     "h": {
       "existingJavaType": "java.lang.Double",
       "javaName": "high",
-      "title": "High price."
+      "title": "The high price."
     },
     "l": {
       "existingJavaType": "java.lang.Double",
       "javaName": "low",
-      "title": "Low price."
+      "title": "The low price."
     },
     "c": {
       "existingJavaType": "java.lang.Double",
       "javaName": "close",
-      "title": "Close price."
+      "title": "The close price."
     },
     "t": {
-      "existingJavaType": "java.time.ZonedDateTime",
+      "existingJavaType": "java.time.OffsetDateTime",
       "javaName": "timestamp",
       "title": "The timestamp."
     },
+    "v": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "volume",
+      "title": "The volume."
+    },
     "n": {
       "existingJavaType": "java.lang.Long",
       "javaName": "tradeCount",
-      "title": "Trade count."
+      "title": "The trade count."
     },
     "vw": {
       "existingJavaType": "java.lang.Double",
       "javaName": "vwap",
-      "title": "VWAP (Volume Weighted Average Price)."
+      "title": "The VWAP (Volume Weighted Average Price)."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/limituplimitdownband/stock_limit_up_limit_down_band_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/limituplimitdownband/stock_limit_up_limit_down_band_message.json
new file mode 100644
index 00000000..db9238cf
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/limituplimitdownband/stock_limit_up_limit_down_band_message.json
@@ -0,0 +1,38 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "u": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "limitUpPrice",
+      "title": "The upper limit price band of a security."
+    },
+    "d": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "limitDownPrice",
+      "title": "The lower limit price band of a security."
+    },
+    "i": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "indicator",
+      "title": "The indicator."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "z": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "tape",
+      "title": "The tape."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json
new file mode 100644
index 00000000..27ff6daa
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json
@@ -0,0 +1,58 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "ax": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "askExchange",
+      "title": "The ask exchange code."
+    },
+    "ap": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "askPrice",
+      "title": "The ask price."
+    },
+    "as": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "askSize",
+      "title": "The ask size."
+    },
+    "bx": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "bidExchange",
+      "title": "The bid exchange code."
+    },
+    "bp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "bidPrice",
+      "title": "The bid price."
+    },
+    "bs": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "bidSize",
+      "title": "The bid size."
+    },
+    "c": {
+      "existingJavaType": "java.util.ArrayList<String>",
+      "javaName": "conditions",
+      "title": "The {@link java.util.ArrayList} of quote conditions."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "z": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "tape",
+      "title": "The tape."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message.json
new file mode 100644
index 00000000..8fbfaf6b
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message.json
@@ -0,0 +1,13 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.model.MarketDataMessage"
+  },
+  "properties": {
+    "T": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType",
+      "javaName": "messageType",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType}."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message_type.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message_type.json
new file mode 100644
index 00000000..17495738
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/stock_market_data_message_type.json
@@ -0,0 +1,55 @@
+{
+  "type": "string",
+  "enum": [
+    "success",
+    "error",
+    "subscription",
+    "t",
+    "q",
+    "b",
+    "d",
+    "u",
+    "c",
+    "x",
+    "l",
+    "s"
+  ],
+  "javaEnums": [
+    {
+      "name": "SUCCESS"
+    },
+    {
+      "name": "ERROR"
+    },
+    {
+      "name": "SUBSCRIPTION"
+    },
+    {
+      "name": "TRADES"
+    },
+    {
+      "name": "QUOTES"
+    },
+    {
+      "name": "MINUTE_BARS"
+    },
+    {
+      "name": "DAILY_BARS"
+    },
+    {
+      "name": "UPDATED_BARS"
+    },
+    {
+      "name": "TRADE_CORRECTIONS"
+    },
+    {
+      "name": "TRADE_CANCEL_ERRORS"
+    },
+    {
+      "name": "LIMIT_UP_LIMIT_DOWN_BANDS"
+    },
+    {
+      "name": "TRADING_STATUSES"
+    }
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json
new file mode 100644
index 00000000..986947f6
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json
@@ -0,0 +1,49 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "trades": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to trades."
+    },
+    "quotes": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to quotes."
+    },
+    "bars": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "javaName": "minuteBars",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to minute bars."
+    },
+    "dailyBars": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to daily bars."
+    },
+    "updatedBars": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to updated bars."
+    },
+    "corrections": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "javaName": "tradeCorrections",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to trade corrections."
+    },
+    "cancelErrors": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "javaName": "tradeCancelErrors",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to trade cancels/errors."
+    },
+    "lulds": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "javaName": "limitUpLimitDownBands",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to limit up - limit down bands."
+    },
+    "statuses": {
+      "existingJavaType": "java.util.ArrayList<java.lang.String>",
+      "javaName": "tradingStatuses",
+      "title": "The {@link java.util.ArrayList} of symbols subscribed to trading statuses."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json
new file mode 100644
index 00000000..4294856a
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json
@@ -0,0 +1,48 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "i": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeID",
+      "title": "The trade ID."
+    },
+    "x": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "exchange",
+      "title": "The exchange code where the trade occurred."
+    },
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "The trade price."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "size",
+      "title": "The trade size."
+    },
+    "c": {
+      "existingJavaType": "java.util.ArrayList<String>",
+      "javaName": "conditions",
+      "title": "The {@link java.util.ArrayList} of trade conditions."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "z": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "tape",
+      "title": "The tape."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_action.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_action.json
new file mode 100644
index 00000000..c13371a9
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_action.json
@@ -0,0 +1,15 @@
+{
+  "type": "string",
+  "enum": [
+    "C",
+    "E"
+  ],
+  "javaEnums": [
+    {
+      "name": "CANCEL"
+    },
+    {
+      "name": "ERROR"
+    }
+  ]
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_message.json
new file mode 100644
index 00000000..577586c8
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecancelerror/stock_trade_cancel_error_message.json
@@ -0,0 +1,48 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "i": {
+      "existingJavaType": "java.lang.Long",
+      "javaName": "tradeID",
+      "title": "The trade ID."
+    },
+    "x": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "exchange",
+      "title": "The exchange code where the trade cancel/error occurred."
+    },
+    "p": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "price",
+      "title": "The trade price."
+    },
+    "s": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "size",
+      "title": "The trade size."
+    },
+    "a": {
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecancelerror.StockTradeCancelErrorAction",
+      "javaName": "action",
+      "title": "The trade cancel/error action."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "z": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "tape",
+      "title": "The tape."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json
new file mode 100644
index 00000000..0b6daaca
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json
@@ -0,0 +1,68 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "x": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "exchange",
+      "title": "The exchange code where the trade correction occurred."
+    },
+    "oi": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "originalTradeID",
+      "title": "The original trade ID."
+    },
+    "op": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "originalPrice",
+      "title": "The original trade price."
+    },
+    "os": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "originalSize",
+      "title": "The original trade size."
+    },
+    "oc": {
+      "existingJavaType": "java.util.ArrayList<String>",
+      "javaName": "originalConditions",
+      "title": "The {@link java.util.ArrayList} of original trade conditions."
+    },
+    "ci": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "correctedTradeID",
+      "title": "The corrected trade ID."
+    },
+    "cp": {
+      "existingJavaType": "java.lang.Double",
+      "javaName": "correctedPrice",
+      "title": "The corrected trade price."
+    },
+    "cs": {
+      "existingJavaType": "java.lang.Integer",
+      "javaName": "correctedSize",
+      "title": "The corrected trade size."
+    },
+    "cc": {
+      "existingJavaType": "java.util.ArrayList<String>",
+      "javaName": "correctedConditions",
+      "title": "The {@link java.util.ArrayList} of original trade conditions."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "z": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "tape",
+      "title": "The tape."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradingstatus/stock_trading_status_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradingstatus/stock_trading_status_message.json
new file mode 100644
index 00000000..d1946769
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradingstatus/stock_trading_status_message.json
@@ -0,0 +1,43 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "sc": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "statusCode",
+      "title": "The status code."
+    },
+    "sm": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "statusMessage",
+      "title": "The status message."
+    },
+    "rc": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "reasonCode",
+      "title": "The reason code."
+    },
+    "rm": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "reasonMessage",
+      "title": "The reason message."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    },
+    "z": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "tape",
+      "title": "The tape."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
index a6fc8896..5759d355 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
@@ -31,7 +31,6 @@
 import static com.google.common.base.Predicates.not;
 import static com.google.common.collect.Iterables.toArray;
 import static com.google.gson.JsonParser.parseString;
-import static java.lang.String.format;
 import static java.util.Collections.singletonList;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType.TRADE_UPDATES;
@@ -127,7 +126,7 @@ protected void sendAuthenticationMessage() {
     }
 
     @Override
-    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) {
+    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) { // Binary framing
         final String messageString = byteString.utf8();
         LOGGER.trace("Websocket message received: {}", messageString);
 
@@ -147,11 +146,10 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteStri
             case AUTHORIZATION:
                 message = getGson().fromJson(messageObject, AuthorizationMessage.class);
                 final AuthorizationData authorizationData = ((AuthorizationMessage) message).getData();
-                authenticated = authorizationData.getStatus().equalsIgnoreCase("authorized") &&
-                        authorizationData.getAction().equalsIgnoreCase("authenticate");
+                authenticated = authorizationData.getAction().equalsIgnoreCase("authenticate") &&
+                        authorizationData.getStatus().equalsIgnoreCase("authorized");
                 if (!authenticated) {
-                    throw new RuntimeException(format("%s websocket not authenticated! Received: %s.",
-                            websocketName, message));
+                    throw new RuntimeException(websocketName + " websocket authentication failed!");
                 } else {
                     LOGGER.info("{} websocket authenticated.", websocketName);
                 }

From 1eed19449629f8424dc581d8623e3182458ff02b Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 5 Mar 2024 09:25:23 -0800
Subject: [PATCH 62/85] WIP fix IllegalArgumentExceptions with JSON empty
 string values

---
 build.gradle                                             | 9 ++++++---
 .../jacobpeterson/alpaca/rest/broker/EventsApiSSE.java   | 9 +++++++++
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/build.gradle b/build.gradle
index a0729cd0..b747b084 100644
--- a/build.gradle
+++ b/build.gradle
@@ -215,7 +215,8 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
                 .replace("this.type = this.getClass().getSimpleName();", "")
 
         // Remove the JSON validation that the generated client models always run (there isn't an option to
-        // disable it for some reason)
+        // disable it for some reason). Also, remove throwing an IllegalArgumentException for a null/empty string
+        // given to enum 'fromValue' methods.
         specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
             for (File modelFile : Paths.get(generatedClientLibrariesPath.getPath(),
                     it, sourceFilesPath, it.replace("-", ""), "model").toFile().listFiles()) {
@@ -223,9 +224,11 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
                         .replaceAll("/\\*\\*\\n {2}\\* Validates the JSON Element and throws an exception if issues " +
                                 "found[\\s\\S]*?\\*/", "")
                         .replace("public static void validateJsonElement(JsonElement jsonElement) throws IOException {",
-                                "public static boolean validate = false;\n  public static void " +
+                                "public static boolean validate = false;\n    public static void " +
                                         "validateJsonElement(JsonElement jsonElement) throws IOException {\n" +
-                                        "    if (!validate) return;")
+                                        "      if (!validate) return;")
+                        .replace("throw new IllegalArgumentException(\"Unexpected value '\" + value + \"'\");",
+                                "return null;")
             }
         }
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
index cff5a03c..5df2e615 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
@@ -19,6 +19,8 @@
 import okhttp3.sse.EventSources;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.lang.reflect.Type;
 import java.time.LocalDate;
@@ -32,6 +34,8 @@
  */
 public class EventsApiSSE {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(EventsApiSSE.class);
+
     private final EventsApi eventsAPI;
     private final EventSource.Factory eventSourceFactory;
 
@@ -151,18 +155,22 @@ private <T> EventSourceListener createEventSourceListener(SSEListener<T> sseList
         return new EventSourceListener() {
             @Override
             public void onClosed(@NotNull EventSource eventSource) {
+                LOGGER.info("Event source closed: {}", eventSource);
                 sseListener.onClose();
             }
 
             @Override
             public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type,
                     @NotNull String data) {
+                LOGGER.trace("Event source event: eventSource={} id={}, type={}, data={}", eventSource, id, type, data);
                 sseListener.onMessage(getGson().fromJson(data, responseTypeToken));
             }
 
             @Override
             public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable throwable,
                     @Nullable Response response) {
+                LOGGER.error("Event source failure: eventSource={} throwable={}, response={}",
+                        eventSource, throwable, response);
                 if (throwable != null && throwable.getMessage().equals("canceled")) {
                     sseListener.onClose();
                     return;
@@ -172,6 +180,7 @@ public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable thro
 
             @Override
             public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
+                LOGGER.info("Event source opened: {}", eventSource);
                 sseListener.onOpen();
             }
         };

From df6de0ff3ab841ba13c3d3d0fe8c30d25c987504 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Thu, 7 Mar 2024 11:50:33 -0800
Subject: [PATCH 63/85] WIP fix OpenAPI 'generate client' tasks build logic
 flow

---
 README.md    |  1 +
 build.gradle | 35 +++++++++++++++--------------------
 2 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md
index afb356b9..3aa3cfcc 100644
--- a/README.md
+++ b/README.md
@@ -75,6 +75,7 @@ To install the built artifacts to your local Maven repository on your machine (t
 ```
 
 # TODO
+- Implement better reconnect logic for Websockets and SSE streaming
 - Implement Unit Testing for REST API and Websocket streaming (both live and mocked)
 
 # Contributing
diff --git a/build.gradle b/build.gradle
index b747b084..c6c53daa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -125,17 +125,9 @@ def downloadOpenAPISpecFilesTask = tasks.register("downloadOpenAPISpecFiles") {
                 .replace(",\"\":{\"type\":\"string\",\"x-stoplight\":{\"id\":\"1zg5jnb6rzdoo\"}}", "")
     }
 }
+compileJava.dependsOn downloadOpenAPISpecFilesTask
 
-final def generateOpenAPIClientsTask = tasks.register("generateOpenAPIClients") {
-    it.onlyIf {
-        !downloadOpenAPISpecFilesTask.get().state.upToDate &&
-                downloadOpenAPISpecFilesTask.get().state.failure == null &&
-                !downloadOpenAPISpecFilesTask.get().state.skipped &&
-                downloadOpenAPISpecFilesTask.get().state.executed
-    }
-}
-compileJava.dependsOn generateOpenAPIClientsTask
-
+final def generateTasks = []
 for (def specFileName : specIDsOfFileNames.keySet()) {
     final def specName = specFileName.replace(".json", "")
     final def inputSpecFile = new File(specDownloadPath, specFileName)
@@ -185,17 +177,22 @@ for (def specFileName : specIDsOfFileNames.keySet()) {
         generateApiTests.set(false)
         generateApiDocumentation.set(false)
     }
-    generateOpenAPIClientsTask.configure {
-        dependsOn generateTask
-    }
+    compileJava.dependsOn generateTask
+    generateTasks.add(generateTask)
 }
 
 final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGeneratedClientIssues") {
+    configure {
+        mustRunAfter(generateTasks)
+    }
+
     it.onlyIf {
-        !generateOpenAPIClientsTask.get().state.upToDate &&
-                generateOpenAPIClientsTask.get().state.failure == null &&
-                !generateOpenAPIClientsTask.get().state.skipped &&
-                generateOpenAPIClientsTask.get().state.executed
+        generateTasks.stream().allMatch { generateTask ->
+            !generateTask.get().state.upToDate &&
+                    generateTask.get().state.failure == null &&
+                    !generateTask.get().state.skipped &&
+                    generateTask.get().state.executed
+        }
     }
 
     doLast {
@@ -251,9 +248,7 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
         }
     }
 }
-generateOpenAPIClientsTask.configure {
-    finalizedBy fixOpenAPIGeneratedClientIssuesTask
-}
+compileJava.dependsOn fixOpenAPIGeneratedClientIssuesTask
 
 //
 // END Alpaca OpenAPI Specification (OAS) client generation

From ec35299fe7287c7bd5a3797e6e92f871b5bd3827 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Thu, 7 Mar 2024 18:06:34 -0800
Subject: [PATCH 64/85] Finish updating websocket interface and functionality

---
 .../net/jacobpeterson/alpaca/AlpacaAPI.java   | 106 ++++++---
 .../alpaca/AlpacaMarketDataAPI.java           |   1 -
 .../alpaca/rest/broker/EventsApiSSE.java      |   2 +-
 ...=> market_data_websocket_source_type.json} |   1 -
 .../alpaca/util/sse/SSEListenerAdapter.java   |   4 +-
 .../alpaca/websocket/AlpacaWebsocket.java     |  89 +++----
 .../websocket/AlpacaWebsocketInterface.java   |  24 +-
 .../AlpacaWebsocketMessageListener.java       |  20 --
 .../marketdata/MarketDataWebsocket.java       | 198 ++++++++++++++++
 .../MarketDataWebsocketInterface.java         |   8 +
 .../crypto/CryptoMarketDataListener.java      |  55 +++++
 .../CryptoMarketDataListenerAdapter.java      |  30 +++
 .../crypto/CryptoMarketDataWebsocket.java     | 174 ++++++++++++--
 .../CryptoMarketDataWebsocketInterface.java   | 116 +++++++++
 .../control/crypto_subscriptions_message.json |  34 +++
 ...ntry.json => crypto_order_book_entry.json} |   0
 .../orderbook/crypto_order_book_message.json  |  28 +++
 .../orderbook/crypto_orderbook_message.json   |  28 ---
 .../crypto_subscriptions_message.json         |  33 ---
 .../streams/news/NewsMarketDataListener.java  |  17 ++
 .../news/NewsMarketDataListenerAdapter.java   |  12 +
 .../streams/news/NewsMarketDataWebsocket.java |  86 ++++++-
 .../NewsMarketDataWebsocketInterface.java     |  36 +++
 .../control/news_subscriptions_message.json   |  12 +
 .../streams/news/model/news/news_message.json |   2 +-
 .../stock/StockMarketDataListener.java        |  78 +++++++
 .../stock/StockMarketDataListenerAdapter.java |  42 ++++
 .../stock/StockMarketDataWebsocket.java       | 220 ++++++++++++++++--
 .../StockMarketDataWebsocketInterface.java    | 136 +++++++++++
 .../control/stock_subscriptions_message.json  |  39 ++++
 .../model/quote/stock_quote_message.json      |   4 +-
 .../stock_subscriptions_message.json          |  49 ----
 .../model/trade/stock_trade_message.json      |   4 +-
 .../stock_trade_correction_message.json       |   8 +-
 .../websocket/trades/TradesListener.java      |  11 -
 .../trades/TradesListenerAdapter.java         |  19 --
 .../websocket/trades/TradesWebsocket.java     | 217 -----------------
 .../trades/TradesWebsocketInterface.java      |  26 ---
 .../model/listening/listening_data.json       |   9 -
 .../websocket/updates/UpdatesListener.java    |  17 ++
 .../websocket/updates/UpdatesWebsocket.java   | 172 ++++++++++++++
 .../updates/UpdatesWebsocketInterface.java    |  24 ++
 .../authorization/authorization_data.json     |   0
 .../authorization/authorization_message.json  |   6 +-
 .../model/listening/listening_data.json       |   9 +
 .../model/listening/listening_message.json    |   6 +-
 .../model/tradeupdate/trade_update.json       |   4 +-
 .../model/tradeupdate/trade_update_event.json |   0
 .../tradeupdate/trade_update_message.json     |   6 +-
 .../model/updates_message.json}               |   4 +-
 .../model/updates_message_type.json}          |   0
 51 files changed, 1658 insertions(+), 568 deletions(-)
 rename src/main/java/net/jacobpeterson/alpaca/util/apitype/{market_data_api_stream_source_type.json => market_data_websocket_source_type.json} (56%)
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListenerAdapter.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocketInterface.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/control/crypto_subscriptions_message.json
 rename src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/{crypto_orderbook_entry.json => crypto_order_book_entry.json} (100%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListenerAdapter.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocketInterface.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/control/news_subscriptions_message.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListenerAdapter.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocketInterface.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/control/stock_subscriptions_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java
 delete mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesListener.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocketInterface.java
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades => updates}/model/authorization/authorization_data.json (100%)
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades => updates}/model/authorization/authorization_message.json (66%)
 create mode 100644 src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_data.json
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades => updates}/model/listening/listening_message.json (69%)
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades => updates}/model/tradeupdate/trade_update.json (90%)
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades => updates}/model/tradeupdate/trade_update_event.json (100%)
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades => updates}/model/tradeupdate/trade_update_message.json (69%)
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades/model/trades_stream_message.json => updates/model/updates_message.json} (69%)
 rename src/main/java/net/jacobpeterson/alpaca/websocket/{trades/model/trades_stream_message_type.json => updates/model/updates_message_type.json} (100%)

diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 001aa617..2ae2e22e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -1,17 +1,23 @@
 package net.jacobpeterson.alpaca;
 
 import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
-import net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPIStreamSourceType;
+import net.jacobpeterson.alpaca.model.util.apitype.MarketDataWebsocketSourceType;
 import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
-import net.jacobpeterson.alpaca.websocket.trades.TradesWebsocket;
-import net.jacobpeterson.alpaca.websocket.trades.TradesWebsocketInterface;
+import net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto.CryptoMarketDataWebsocket;
+import net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto.CryptoMarketDataWebsocketInterface;
+import net.jacobpeterson.alpaca.websocket.marketdata.streams.news.NewsMarketDataWebsocket;
+import net.jacobpeterson.alpaca.websocket.marketdata.streams.news.NewsMarketDataWebsocketInterface;
+import net.jacobpeterson.alpaca.websocket.marketdata.streams.stock.StockMarketDataWebsocket;
+import net.jacobpeterson.alpaca.websocket.marketdata.streams.stock.StockMarketDataWebsocketInterface;
+import net.jacobpeterson.alpaca.websocket.updates.UpdatesWebsocket;
+import net.jacobpeterson.alpaca.websocket.updates.UpdatesWebsocketInterface;
 import okhttp3.OkHttpClient;
 import okhttp3.logging.HttpLoggingInterceptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType.SANDBOX;
-import static net.jacobpeterson.alpaca.model.util.apitype.MarketDataAPIStreamSourceType.IEX;
+import static net.jacobpeterson.alpaca.model.util.apitype.MarketDataWebsocketSourceType.IEX;
 import static net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType.PAPER;
 import static okhttp3.logging.HttpLoggingInterceptor.Level.BODY;
 
@@ -33,7 +39,7 @@ public class AlpacaAPI {
     private final String traderSecretKey;
     private final String traderOAuthToken;
     private final TraderAPIEndpointType traderAPIEndpointType;
-    private final MarketDataAPIStreamSourceType marketDataAPIStreamSourceType;
+    private final MarketDataWebsocketSourceType marketDataWebsocketSourceType;
     private final String brokerAPIKey;
     private final String brokerAPISecret;
     private final BrokerAPIEndpointType brokerAPIEndpointType;
@@ -42,7 +48,10 @@ public class AlpacaAPI {
     private AlpacaTraderAPI trader;
     private AlpacaMarketDataAPI marketData;
     private AlpacaBrokerAPI broker;
-    private TradesWebsocket tradesWebsocket;
+    private UpdatesWebsocket updatesWebsocket;
+    private StockMarketDataWebsocket stockMarketDataWebsocket;
+    private CryptoMarketDataWebsocket cryptoMarketDataWebsocket;
+    private NewsMarketDataWebsocket newsMarketDataWebsocket;
 
     /**
      * Instantiates a new {@link AlpacaAPI}. Use this constructor if you are using the Trading or Market Data APIs for a
@@ -51,11 +60,11 @@ public class AlpacaAPI {
      * @param traderKeyID                   the Trader key ID
      * @param traderSecretKey               the Trader secret key
      * @param traderAPIEndpointType         the {@link TraderAPIEndpointType}
-     * @param marketDataAPIStreamSourceType the {@link MarketDataAPIStreamSourceType}
+     * @param marketDataWebsocketSourceType the {@link MarketDataWebsocketSourceType}
      */
     public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointType traderAPIEndpointType,
-            MarketDataAPIStreamSourceType marketDataAPIStreamSourceType) {
-        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPIStreamSourceType, null, null, null,
+            MarketDataWebsocketSourceType marketDataWebsocketSourceType) {
+        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataWebsocketSourceType, null, null, null,
                 null);
     }
 
@@ -66,13 +75,13 @@ public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointTy
      * @param traderKeyID                   the Trader key ID
      * @param traderSecretKey               the Trader secret key
      * @param traderAPIEndpointType         the {@link TraderAPIEndpointType}
-     * @param marketDataAPIStreamSourceType the {@link MarketDataAPIStreamSourceType}
+     * @param marketDataWebsocketSourceType the {@link MarketDataWebsocketSourceType}
      * @param okHttpClient                  an existing {@link OkHttpClient} or <code>null</code> to create a new
      *                                      default instance
      */
     public AlpacaAPI(String traderKeyID, String traderSecretKey, TraderAPIEndpointType traderAPIEndpointType,
-            MarketDataAPIStreamSourceType marketDataAPIStreamSourceType, OkHttpClient okHttpClient) {
-        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataAPIStreamSourceType, null, null, null,
+            MarketDataWebsocketSourceType marketDataWebsocketSourceType, OkHttpClient okHttpClient) {
+        this(traderKeyID, traderSecretKey, null, traderAPIEndpointType, marketDataWebsocketSourceType, null, null, null,
                 okHttpClient);
     }
 
@@ -133,7 +142,7 @@ public AlpacaAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointT
      * @param traderSecretKey               the Trader secret key
      * @param traderOAuthToken              the Trader OAuth token
      * @param traderAPIEndpointType         the {@link TraderAPIEndpointType}
-     * @param marketDataAPIStreamSourceType the {@link MarketDataAPIStreamSourceType}
+     * @param marketDataWebsocketSourceType the {@link MarketDataWebsocketSourceType}
      * @param brokerAPIKey                  the Broker API key
      * @param brokerAPISecret               the Broker API secret
      * @param brokerAPIEndpointType         the {@link BrokerAPIEndpointType}
@@ -142,18 +151,20 @@ public AlpacaAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointT
      */
     public AlpacaAPI(String traderKeyID, String traderSecretKey,
             String traderOAuthToken, TraderAPIEndpointType traderAPIEndpointType,
-            MarketDataAPIStreamSourceType marketDataAPIStreamSourceType,
+            MarketDataWebsocketSourceType marketDataWebsocketSourceType,
             String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
             OkHttpClient okHttpClient) {
         this.traderKeyID = traderKeyID;
         this.traderSecretKey = traderSecretKey;
         this.traderOAuthToken = traderOAuthToken;
-        this.traderAPIEndpointType = traderAPIEndpointType != null ? traderAPIEndpointType : PAPER;
-        this.marketDataAPIStreamSourceType = marketDataAPIStreamSourceType != null ?
-                marketDataAPIStreamSourceType : IEX;
+        this.traderAPIEndpointType = traderAPIEndpointType != null ?
+                traderAPIEndpointType : PAPER;
+        this.marketDataWebsocketSourceType = marketDataWebsocketSourceType != null ?
+                marketDataWebsocketSourceType : IEX;
         this.brokerAPIKey = brokerAPIKey;
         this.brokerAPISecret = brokerAPISecret;
-        this.brokerAPIEndpointType = brokerAPIEndpointType != null ? brokerAPIEndpointType : SANDBOX;
+        this.brokerAPIEndpointType = brokerAPIEndpointType != null ?
+                brokerAPIEndpointType : SANDBOX;
 
         // Create default OkHttpClient instance
         if (okHttpClient == null) {
@@ -224,16 +235,55 @@ public synchronized AlpacaBrokerAPI broker() {
     }
 
     /**
-     * Gets the {@link TradesWebsocketInterface}. Lazily instantiated.
+     * Gets the {@link UpdatesWebsocketInterface}. Lazily instantiated.
      *
-     * @return the {@link TradesWebsocketInterface}
+     * @return the {@link UpdatesWebsocketInterface}
      */
-    public synchronized TradesWebsocketInterface tradesStream() {
-        if (tradesWebsocket == null) {
-            tradesWebsocket = new TradesWebsocket(okHttpClient, traderAPIEndpointType,
+    public synchronized UpdatesWebsocketInterface updatesStream() {
+        if (updatesWebsocket == null) {
+            updatesWebsocket = new UpdatesWebsocket(okHttpClient, traderAPIEndpointType,
                     traderKeyID, traderSecretKey, traderOAuthToken);
         }
-        return tradesWebsocket;
+        return updatesWebsocket;
+    }
+
+    /**
+     * Gets the {@link StockMarketDataWebsocketInterface}. Lazily instantiated.
+     *
+     * @return the {@link StockMarketDataWebsocketInterface}
+     */
+    public synchronized StockMarketDataWebsocketInterface stockMarketDataStream() {
+        if (stockMarketDataWebsocket == null) {
+            stockMarketDataWebsocket = new StockMarketDataWebsocket(okHttpClient,
+                    traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret, marketDataWebsocketSourceType);
+        }
+        return stockMarketDataWebsocket;
+    }
+
+    /**
+     * Gets the {@link CryptoMarketDataWebsocketInterface}. Lazily instantiated.
+     *
+     * @return the {@link CryptoMarketDataWebsocketInterface}
+     */
+    public synchronized CryptoMarketDataWebsocketInterface cryptoMarketDataStream() {
+        if (cryptoMarketDataWebsocket == null) {
+            cryptoMarketDataWebsocket = new CryptoMarketDataWebsocket(okHttpClient,
+                    traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret);
+        }
+        return cryptoMarketDataWebsocket;
+    }
+
+    /**
+     * Gets the {@link NewsMarketDataWebsocketInterface}. Lazily instantiated.
+     *
+     * @return the {@link NewsMarketDataWebsocketInterface}
+     */
+    public synchronized NewsMarketDataWebsocketInterface newsMarketDataStream() {
+        if (newsMarketDataWebsocket == null) {
+            newsMarketDataWebsocket = new NewsMarketDataWebsocket(okHttpClient,
+                    traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret);
+        }
+        return newsMarketDataWebsocket;
     }
 
     /**
@@ -254,7 +304,7 @@ public static final class Builder {
         private String traderSecretKey;
         private String traderOAuthToken;
         private TraderAPIEndpointType traderAPIEndpointType;
-        private MarketDataAPIStreamSourceType marketDataAPISourceType;
+        private MarketDataWebsocketSourceType marketDataWebsocketSourceType;
         private String brokerAPIKey;
         private String brokerAPISecret;
         private BrokerAPIEndpointType brokerAPIEndpointType;
@@ -282,8 +332,8 @@ public Builder withTraderAPIEndpointType(TraderAPIEndpointType traderAPIEndpoint
             return this;
         }
 
-        public Builder withMarketDataAPIStreamSourceType(MarketDataAPIStreamSourceType marketDataAPISourceType) {
-            this.marketDataAPISourceType = marketDataAPISourceType;
+        public Builder withMarketDataWebsocketSourceType(MarketDataWebsocketSourceType marketDataWebsocketSourceType) {
+            this.marketDataWebsocketSourceType = marketDataWebsocketSourceType;
             return this;
         }
 
@@ -309,7 +359,7 @@ public Builder withOkHttpClient(OkHttpClient okHttpClient) {
 
         public AlpacaAPI build() {
             return new AlpacaAPI(traderKeyID, traderSecretKey, traderOAuthToken, traderAPIEndpointType,
-                    marketDataAPISourceType, brokerAPIKey, brokerAPISecret, brokerAPIEndpointType, okHttpClient);
+                    marketDataWebsocketSourceType, brokerAPIKey, brokerAPISecret, brokerAPIEndpointType, okHttpClient);
         }
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
index 65682a4e..1728c09f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
@@ -46,7 +46,6 @@ public class AlpacaMarketDataAPI {
         checkNotNull(okHttpClient);
 
         final boolean traderKeysGiven = traderKeyID != null && traderSecretKey != null;
-
         apiClient = new ApiClient(okHttpClient);
         apiClient.setServerIndex(traderKeysGiven ? 0 : 1);
         if (traderKeysGiven) {
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
index 5df2e615..ea2341e5 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
@@ -155,7 +155,7 @@ private <T> EventSourceListener createEventSourceListener(SSEListener<T> sseList
         return new EventSourceListener() {
             @Override
             public void onClosed(@NotNull EventSource eventSource) {
-                LOGGER.info("Event source closed: {}", eventSource);
+                LOGGER.info("Event source closed: eventSource={}", eventSource);
                 sseListener.onClose();
             }
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_stream_source_type.json b/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_websocket_source_type.json
similarity index 56%
rename from src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_stream_source_type.json
rename to src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_websocket_source_type.json
index 97b87531..5a27c8fb 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_api_stream_source_type.json
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apitype/market_data_websocket_source_type.json
@@ -1,6 +1,5 @@
 {
   "type": "string",
-  "javaName": "MarketDataAPIStreamSourceType",
   "enum": [
     "iex",
     "sip"
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
index 57e33dfb..0bd194d1 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/sse/SSEListenerAdapter.java
@@ -24,10 +24,10 @@ public void onClose() {
     }
 
     public void onError(@Nullable Throwable throwable, @Nullable Response response) {
-        LOGGER.error("SSE connection error! {}", response, throwable);
+        LOGGER.error("SSE connection error! response={}", response, throwable);
     }
 
     public void onMessage(@NotNull T message) {
-        LOGGER.info("SSE message received: {}", message);
+        LOGGER.info("SSE message received: message={}", message);
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index d475cc8a..518435c2 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -20,13 +20,8 @@
 
 /**
  * {@link AlpacaWebsocket} represents an abstract websocket for Alpaca.
- *
- * @param <L> the {@link AlpacaWebsocketMessageListener} type parameter
- * @param <T> the 'message type' type parameter
- * @param <M> the 'message' type parameter
  */
-public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageListener<T, M>> extends WebSocketListener
-        implements AlpacaWebsocketInterface<L> {
+public abstract class AlpacaWebsocket extends WebSocketListener implements AlpacaWebsocketInterface {
 
     /**
      * Defines a websocket normal closure code.
@@ -45,7 +40,7 @@ public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageList
     /**
      * Defines the maximum number of reconnection attempts to be made by an {@link AlpacaWebsocket}.
      */
-    public static int MAX_RECONNECT_ATTEMPTS = 10;
+    public static int MAX_RECONNECT_ATTEMPTS = 5;
 
     /**
      * Defines the sleep interval {@link Duration} between reconnection attempts made by an {@link AlpacaWebsocket}.
@@ -57,13 +52,8 @@ public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageList
     protected final OkHttpClient okHttpClient;
     protected final HttpUrl websocketURL;
     protected final String websocketName;
-    protected final String keyID;
-    protected final String secretKey;
-    protected final String oAuthToken;
-    protected final boolean useOAuth;
-    protected L listener;
     protected AlpacaWebsocketStateListener alpacaWebsocketStateListener;
-    protected WebSocket websocket;
+    private WebSocket websocket;
     protected boolean connected;
     protected boolean authenticated;
     protected CompletableFuture<Boolean> authenticationMessageFuture;
@@ -78,12 +68,8 @@ public abstract class AlpacaWebsocket<T, M, L extends AlpacaWebsocketMessageList
      * @param okHttpClient  the {@link OkHttpClient}
      * @param websocketURL  the websocket {@link HttpUrl}
      * @param websocketName the websocket name
-     * @param keyID         the key ID
-     * @param secretKey     the secret key
-     * @param oAuthToken    the OAuth token
      */
-    public AlpacaWebsocket(OkHttpClient okHttpClient, HttpUrl websocketURL, String websocketName,
-            String keyID, String secretKey, String oAuthToken) {
+    protected AlpacaWebsocket(OkHttpClient okHttpClient, HttpUrl websocketURL, String websocketName) {
         checkNotNull(okHttpClient);
         checkNotNull(websocketURL);
         checkNotNull(websocketName);
@@ -91,10 +77,6 @@ public AlpacaWebsocket(OkHttpClient okHttpClient, HttpUrl websocketURL, String w
         this.okHttpClient = okHttpClient;
         this.websocketURL = websocketURL;
         this.websocketName = websocketName;
-        this.keyID = keyID;
-        this.secretKey = secretKey;
-        this.oAuthToken = oAuthToken;
-        useOAuth = oAuthToken != null;
 
         automaticallyReconnect = true;
     }
@@ -133,10 +115,7 @@ public boolean isAuthenticated() {
     @Override
     public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
         connected = true;
-
-        LOGGER.info("{} websocket opened.", websocketName);
-        LOGGER.debug("{} websocket response: {}", websocketName, response);
-
+        LOGGER.info("{} websocket response: response={}", websocketName, response);
         // Call 'onConnection' or 'onReconnection' async to avoid any potential deadlocking since this is called
         // in sync with 'onMessage' in OkHttp's 'WebSocketListener'
         commonPool().execute(() -> {
@@ -146,7 +125,6 @@ public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
                 onConnection();
             }
         });
-
         if (alpacaWebsocketStateListener != null) {
             alpacaWebsocketStateListener.onOpen(response);
         }
@@ -155,16 +133,13 @@ public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
     @Override
     public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) {
         connected = false;
-
         if (intentionalClose) {
-            LOGGER.info("{} websocket closed.", websocketName);
-            LOGGER.debug("Close code: {}, Reason: {}", code, reason);
+            LOGGER.info("{} websocket closed. code={}, reason={}", websocketName, code, reason);
             cleanupState();
         } else {
-            LOGGER.error("{} websocket closed unintentionally! Code: {}, Reason: {}", websocketName, code, reason);
+            LOGGER.error("{} websocket closed unintentionally! code={}, reason={}", websocketName, code, reason);
             handleReconnectionAttempt();
         }
-
         if (alpacaWebsocketStateListener != null) {
             alpacaWebsocketStateListener.onClosed(code, reason);
         }
@@ -173,13 +148,11 @@ public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String rea
     @Override
     public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable cause, @Nullable Response response) {
         LOGGER.error("{} websocket failure!", websocketName, cause);
-
         // A websocket failure occurs when either there is a connection failure or when the client throws
         // an exception when receiving a message. In either case, OkHttp will close the websocket connection,
         // so try to reopen it.
         connected = false;
         handleReconnectionAttempt();
-
         if (alpacaWebsocketStateListener != null) {
             alpacaWebsocketStateListener.onFailure(cause);
         }
@@ -225,6 +198,16 @@ protected void cleanupState() {
         reconnectAttempts = 0;
     }
 
+    /**
+     * Sends a message to the underlying {@link #websocket}.
+     *
+     * @param message the message
+     */
+    protected void sendWebsocketMessage(String message) {
+        LOGGER.trace("Websocket message sent: {}", message);
+        websocket.send(message);
+    }
+
     /**
      * Called asynchronously when a websocket connection is made.
      */
@@ -248,40 +231,30 @@ public Future<Boolean> getAuthorizationFuture() {
         return authenticationMessageFuture;
     }
 
-    /**
-     * Calls the {@link AlpacaWebsocketMessageListener}.
-     *
-     * @param messageType the message type
-     * @param message     the message
-     */
-    protected void callListener(T messageType, M message) {
-        if (listener != null) {
-            try {
-                listener.onMessage(messageType, message);
-            } catch (Exception exception) {
-                LOGGER.error("{} listener threw exception!", websocketName, exception);
-            }
-        }
-    }
-
     @Override
-    public void setListener(L listener) {
-        this.listener = listener;
-    }
-
-    public AlpacaWebsocketStateListener getWebsocketStateListener() {
-        return alpacaWebsocketStateListener;
-    }
-
     public void setAlpacaWebsocketStateListener(AlpacaWebsocketStateListener alpacaWebsocketStateListener) {
         this.alpacaWebsocketStateListener = alpacaWebsocketStateListener;
     }
 
+    @Override
     public boolean doesAutomaticallyReconnect() {
         return automaticallyReconnect;
     }
 
+    @Override
     public void setAutomaticallyReconnect(boolean automaticallyReconnect) {
         this.automaticallyReconnect = automaticallyReconnect;
     }
+
+    public OkHttpClient getOkHttpClient() {
+        return okHttpClient;
+    }
+
+    public HttpUrl getWebsocketURL() {
+        return websocketURL;
+    }
+
+    public WebSocket getWebsocket() {
+        return websocket;
+    }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
index 033475a2..48adfb4d 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketInterface.java
@@ -7,10 +7,8 @@
 
 /**
  * {@link AlpacaWebsocketInterface} defines an interface for Alpaca websockets.
- *
- * @param <L> the {@link AlpacaWebsocketMessageListener} type parameter
  */
-public interface AlpacaWebsocketInterface<L extends AlpacaWebsocketMessageListener<?, ?>> {
+public interface AlpacaWebsocketInterface {
 
     /**
      * Connects this Websocket.
@@ -74,9 +72,23 @@ default boolean waitForAuthorization(long timeout, TimeUnit unit) {
     }
 
     /**
-     * Sets the {@link AlpacaWebsocketMessageListener} for this {@link AlpacaWebsocketInterface}.
+     * Sets the {@link AlpacaWebsocketStateListener}.
      *
-     * @param listener the {@link AlpacaWebsocketMessageListener}
+     * @param alpacaWebsocketStateListener the {@link AlpacaWebsocketStateListener}
      */
-    void setListener(L listener);
+    void setAlpacaWebsocketStateListener(AlpacaWebsocketStateListener alpacaWebsocketStateListener);
+
+    /**
+     * Returns <code>true</code> if this websocket automatically reconnects, <code>false</code> otherwise.
+     *
+     * @return a boolean
+     */
+    boolean doesAutomaticallyReconnect();
+
+    /**
+     * Sets whether to automatically reconnect and reauthenticate on a websocket failure. <code>true</code> by default.
+     *
+     * @param automaticallyReconnect <code>true</code> to automatically reconnect, <code>false</code> otherwise
+     */
+    void setAutomaticallyReconnect(boolean automaticallyReconnect);
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
deleted file mode 100644
index da55535c..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocketMessageListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.jacobpeterson.alpaca.websocket;
-
-/**
- * {@link AlpacaWebsocketMessageListener} defines a listener interface for messages from an {@link AlpacaWebsocket}
- * instances.
- *
- * @param <T> the 'message type' type parameter
- * @param <M> the 'message' type parameter
- */
-@FunctionalInterface
-public interface AlpacaWebsocketMessageListener<T, M> {
-
-    /**
-     * Called when a message is received.
-     *
-     * @param messageType the message type
-     * @param message     the message
-     */
-    void onMessage(T messageType, M message);
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
new file mode 100644
index 00000000..fbb7dca9
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java
@@ -0,0 +1,198 @@
+package net.jacobpeterson.alpaca.websocket.marketdata;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.model.MarketDataMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.model.control.ErrorMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.model.control.SuccessMessage;
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.WebSocket;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+import java.util.function.Function;
+
+import static com.google.common.collect.Sets.difference;
+import static com.google.gson.JsonParser.parseString;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.model.control.SuccessMessageType.AUTHENTICATED;
+import static net.jacobpeterson.alpaca.openapi.marketdata.JSON.getGson;
+
+/**
+ * {@link MarketDataWebsocket} is an abstract {@link AlpacaWebsocket} implementation for
+ * <a href="https://docs.alpaca.markets/docs/streaming-market-data">Market Data Streaming</a>.
+ *
+ * @param <T> the 'message type' type
+ * @param <S> the 'subscription message' type
+ * @param <L> the 'listener' type
+ */
+public abstract class MarketDataWebsocket<T, S extends MarketDataMessage, L> extends AlpacaWebsocket
+        implements MarketDataWebsocketInterface {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MarketDataWebsocket.class);
+    private static final Set<String> AUTH_FAILURE_MESSAGES = Set.of("auth failed", "auth timeout", "not authenticated");
+
+    protected final String authKey;
+    protected final String authSecret;
+    protected final Class<T> messageTypeClass;
+    protected final Class<S> subscriptionsMessageClass;
+    protected S subscriptionsMessage;
+    protected L listener;
+
+    /**
+     * Instantiates a new {@link MarketDataWebsocket}.
+     *
+     * @param okHttpClient                the {@link OkHttpClient}
+     * @param websocketURL                the websocket {@link HttpUrl}
+     * @param websocketMarketDataTypeName the websocket market data type name {@link String}
+     * @param traderKeyID                 the trader key ID
+     * @param traderSecretKey             the trader secret key
+     * @param brokerAPIKey                the broker API key
+     * @param brokerAPISecret             the broker API secret
+     * @param messageTypeClass            the {@link T} message type {@link Class}
+     * @param subscriptionsMessageClass   the {@link S} subscription message {@link Class}
+     */
+    protected MarketDataWebsocket(OkHttpClient okHttpClient, HttpUrl websocketURL, String websocketMarketDataTypeName,
+            String traderKeyID, String traderSecretKey, String brokerAPIKey, String brokerAPISecret,
+            Class<T> messageTypeClass, Class<S> subscriptionsMessageClass) {
+        super(okHttpClient, websocketURL, websocketMarketDataTypeName + " Market Data");
+        final boolean traderKeysGiven = traderKeyID != null && traderSecretKey != null;
+        this.authKey = traderKeysGiven ? traderKeyID : brokerAPIKey;
+        this.authSecret = traderKeysGiven ? traderSecretKey : brokerAPISecret;
+        this.messageTypeClass = messageTypeClass;
+        this.subscriptionsMessageClass = subscriptionsMessageClass;
+    }
+
+    @Override
+    protected void cleanupState() {
+        super.cleanupState();
+        subscriptionsMessage = null;
+    }
+
+    @Override
+    protected void onConnection() {
+        sendAuthenticationMessage();
+    }
+
+    @Override
+    protected void onReconnection() {
+        sendAuthenticationMessage();
+        if (waitForAuthorization(5, SECONDS) && subscriptionsMessage != null) {
+            sendSubscriptionMessage(subscriptionsMessage, true);
+        }
+    }
+
+    @Override
+    protected void sendAuthenticationMessage() {
+        // Ensure that the authorization Future exists
+        getAuthorizationFuture();
+
+        final JsonObject authObject = new JsonObject();
+        authObject.addProperty("action", "auth");
+        authObject.addProperty("key", authKey);
+        authObject.addProperty("secret", authSecret);
+
+        LOGGER.info("{} websocket sending authentication message...", websocketName);
+        sendWebsocketMessage(authObject.toString());
+    }
+
+    @Override
+    public void onMessage(@NotNull WebSocket webSocket, @NotNull String message) { // Text framing
+        LOGGER.trace("Websocket message received: {}", message);
+
+        // Loop through message array and handle each message according to its type
+        for (JsonElement arrayElement : parseString(message).getAsJsonArray()) {
+            final JsonObject messageObject = arrayElement.getAsJsonObject();
+            final T messageType = getGson().fromJson(messageObject.get("T"), messageTypeClass);
+            if (isSuccessMessageType(messageType)) {
+                final SuccessMessage successMessage = getGson().fromJson(messageObject, SuccessMessage.class);
+                if (successMessage.getMessageType() == AUTHENTICATED) {
+                    LOGGER.info("{} websocket authenticated.", websocketName);
+                    authenticated = true;
+                    if (authenticationMessageFuture != null) {
+                        authenticationMessageFuture.complete(true);
+                    }
+                }
+            } else if (isErrorMessageType(messageType)) {
+                final ErrorMessage errorMessage = getGson().fromJson(messageObject, ErrorMessage.class);
+                if (AUTH_FAILURE_MESSAGES.contains(errorMessage.getMessage()) && authenticationMessageFuture != null) {
+                    authenticated = false;
+                    authenticationMessageFuture.complete(false);
+                    throw new RuntimeException(websocketName + " websocket authentication failed!");
+                } else {
+                    throw new RuntimeException(websocketName + " websocket error! Message: " + errorMessage);
+                }
+            } else if (isSubscriptionMessageType(messageType)) {
+                subscriptionsMessage = getGson().fromJson(messageObject, subscriptionsMessageClass);
+            } else if (listener != null) {
+                callListenerWithMessage(messageType, messageObject);
+            }
+        }
+    }
+
+    /**
+     * Sets the websocket stream's subscriptions for a specific message type.
+     *
+     * @param previousSubscriptions           the previous subscriptions symbol {@link Set}
+     * @param newSubscriptions                the new subscriptions symbol {@link Set}
+     * @param subscriptionUpdateObjectCreator the subscription update object creator {@link Function}
+     */
+    protected void setSubscriptions(@NotNull Set<String> previousSubscriptions,
+            @NotNull Set<String> newSubscriptions, @NotNull Function<Set<String>, S> subscriptionUpdateObjectCreator) {
+        // Unsubscribe from previous subscriptions
+        final Set<String> unsubscribeSet = difference(previousSubscriptions, newSubscriptions);
+        if (!unsubscribeSet.isEmpty()) {
+            sendSubscriptionMessage(subscriptionUpdateObjectCreator.apply(unsubscribeSet), false);
+        }
+        // Subscribe to new subscriptions
+        final Set<String> subscribeSet = difference(newSubscriptions, previousSubscriptions);
+        if (!subscribeSet.isEmpty()) {
+            sendSubscriptionMessage(subscriptionUpdateObjectCreator.apply(subscribeSet), true);
+        }
+    }
+
+    private void sendSubscriptionMessage(S subscriptionsMessage, boolean subscribe) {
+        final JsonObject subscribeObject = getGson().toJsonTree(subscriptionsMessage).getAsJsonObject();
+        subscribeObject.addProperty("action", subscribe ? "subscribe" : "unsubscribe");
+        sendWebsocketMessage(getGson().toJson(subscribeObject));
+    }
+
+    /**
+     * Whether the given <code>messageType</code> is "success".
+     *
+     * @param messageType the message type
+     *
+     * @return a boolean
+     */
+    protected abstract boolean isSuccessMessageType(T messageType);
+
+    /**
+     * Whether the given <code>messageType</code> is "error".
+     *
+     * @param messageType the message type
+     *
+     * @return a boolean
+     */
+    protected abstract boolean isErrorMessageType(T messageType);
+
+    /**
+     * Whether the given <code>messageType</code> is "subscription".
+     *
+     * @param messageType the message type
+     *
+     * @return a boolean
+     */
+    protected abstract boolean isSubscriptionMessageType(T messageType);
+
+    /**
+     * Calls the {@link #listener} with a {@link MarketDataMessage} from a {@link JsonObject}.
+     *
+     * @param messageType   the message type
+     * @param messageObject the message {@link JsonObject}
+     */
+    protected abstract void callListenerWithMessage(T messageType, JsonObject messageObject);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
new file mode 100644
index 00000000..989389eb
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocketInterface.java
@@ -0,0 +1,8 @@
+package net.jacobpeterson.alpaca.websocket.marketdata;
+
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
+
+/**
+ * {@link MarketDataWebsocketInterface} is an {@link AlpacaWebsocketInterface} for {@link MarketDataWebsocket}.
+ */
+public interface MarketDataWebsocketInterface extends AlpacaWebsocketInterface {}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListener.java
new file mode 100644
index 00000000..ae8efe52
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListener.java
@@ -0,0 +1,55 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.bar.CryptoBarMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.quote.CryptoQuoteMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.trade.CryptoTradeMessage;
+
+/**
+ * {@link CryptoMarketDataListener} defines a listener interface for {@link CryptoMarketDataWebsocketInterface}
+ * messages.
+ */
+public interface CryptoMarketDataListener {
+
+    /**
+     * Called when a {@link CryptoTradeMessage} is received.
+     *
+     * @param trade the {@link CryptoTradeMessage}
+     */
+    void onTrade(CryptoTradeMessage trade);
+
+    /**
+     * Called when a {@link CryptoQuoteMessage} is received.
+     *
+     * @param quote the {@link CryptoQuoteMessage}
+     */
+    void onQuote(CryptoQuoteMessage quote);
+
+    /**
+     * Called when a {@link CryptoBarMessage} is received.
+     *
+     * @param bar the {@link CryptoBarMessage}
+     */
+    void onMinuteBar(CryptoBarMessage bar);
+
+    /**
+     * Called when a {@link CryptoBarMessage} is received.
+     *
+     * @param bar the {@link CryptoBarMessage}
+     */
+    void onDailyBar(CryptoBarMessage bar);
+
+    /**
+     * Called when a {@link CryptoBarMessage} is received.
+     *
+     * @param bar the {@link CryptoBarMessage}
+     */
+    void onUpdatedBar(CryptoBarMessage bar);
+
+    /**
+     * Called when a {@link CryptoOrderBookMessage} is received.
+     *
+     * @param orderBook the {@link CryptoOrderBookMessage}
+     */
+    void onOrderBook(CryptoOrderBookMessage orderBook);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListenerAdapter.java
new file mode 100644
index 00000000..cf1cc348
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataListenerAdapter.java
@@ -0,0 +1,30 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.bar.CryptoBarMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.quote.CryptoQuoteMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.trade.CryptoTradeMessage;
+
+/**
+ * {@link CryptoMarketDataListenerAdapter} is an adapter for {@link CryptoMarketDataListener}.
+ */
+public class CryptoMarketDataListenerAdapter implements CryptoMarketDataListener {
+
+    @Override
+    public void onTrade(CryptoTradeMessage trade) {}
+
+    @Override
+    public void onQuote(CryptoQuoteMessage quote) {}
+
+    @Override
+    public void onMinuteBar(CryptoBarMessage bar) {}
+
+    @Override
+    public void onDailyBar(CryptoBarMessage bar) {}
+
+    @Override
+    public void onUpdatedBar(CryptoBarMessage bar) {}
+
+    @Override
+    public void onOrderBook(CryptoOrderBookMessage orderBook) {}
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java
index 8eed2ce2..ed3a88ad 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocket.java
@@ -1,27 +1,175 @@
 package net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.bar.CryptoBarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.quote.CryptoQuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.crypto.realtime.trade.CryptoTradeMessage;
+import com.google.gson.JsonObject;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.bar.CryptoBarMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.control.CryptoSubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.quote.CryptoQuoteMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.trade.CryptoTradeMessage;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
 
+import java.util.Set;
+
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType.ERROR;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType.SUBSCRIPTION;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType.SUCCESS;
+import static net.jacobpeterson.alpaca.openapi.marketdata.JSON.getGson;
+
 /**
- * {@link CryptoMarketDataWebsocket} is a {@link MarketDataWebsocket} for
- * <a href="https://docs.alpaca.markets/docs/real-time-crypto-pricing-data">Realtime
- * Crypto Market Data</a>
+ * {@link CryptoMarketDataWebsocket} is an implementation for {@link CryptoMarketDataWebsocketInterface}.
  */
-public class CryptoMarketDataWebsocket extends MarketDataWebsocket {
+public class CryptoMarketDataWebsocket
+        extends MarketDataWebsocket<CryptoMarketDataMessageType, CryptoSubscriptionsMessage, CryptoMarketDataListener>
+        implements CryptoMarketDataWebsocketInterface {
 
     /**
      * Instantiates a new {@link CryptoMarketDataWebsocket}.
      *
-     * @param okHttpClient the {@link OkHttpClient}
-     * @param keyID        the key ID
-     * @param secretKey    the secret key
+     * @param okHttpClient    the {@link OkHttpClient}
+     * @param traderKeyID     the trader key ID
+     * @param traderSecretKey the trader secret key
+     * @param brokerAPIKey    the broker API key
+     * @param brokerAPISecret the broker API secret
      */
-    public CryptoMarketDataWebsocket(OkHttpClient okHttpClient, String keyID, String secretKey) {
-        super(okHttpClient, "v1beta3/crypto/us", "Crypto", keyID, secretKey, CryptoTradeMessage.class,
-                CryptoQuoteMessage.class, CryptoBarMessage.class);
+    public CryptoMarketDataWebsocket(OkHttpClient okHttpClient, String traderKeyID, String traderSecretKey,
+            String brokerAPIKey, String brokerAPISecret) {
+        super(okHttpClient, new HttpUrl.Builder()
+                        .scheme("https")
+                        .host("stream.data.alpaca.markets")
+                        .addPathSegments("v1beta3/crypto/us")
+                        .build(),
+                "Crypto", traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret,
+                CryptoMarketDataMessageType.class, CryptoSubscriptionsMessage.class);
+    }
+
+    @Override
+    protected boolean isSuccessMessageType(CryptoMarketDataMessageType messageType) {
+        return messageType == SUCCESS;
+    }
+
+    @Override
+    protected boolean isErrorMessageType(CryptoMarketDataMessageType messageType) {
+        return messageType == ERROR;
+    }
+
+    @Override
+    protected boolean isSubscriptionMessageType(CryptoMarketDataMessageType messageType) {
+        return messageType == SUBSCRIPTION;
+    }
+
+    @Override
+    protected void callListenerWithMessage(CryptoMarketDataMessageType messageType, JsonObject messageObject) {
+        switch (messageType) {
+            case TRADES:
+                listener.onTrade(getGson().fromJson(messageObject, CryptoTradeMessage.class));
+                break;
+            case QUOTES:
+                listener.onQuote(getGson().fromJson(messageObject, CryptoQuoteMessage.class));
+                break;
+            case MINUTE_BARS:
+                listener.onMinuteBar(getGson().fromJson(messageObject, CryptoBarMessage.class));
+                break;
+            case DAILY_BARS:
+                listener.onDailyBar(getGson().fromJson(messageObject, CryptoBarMessage.class));
+                break;
+            case UPDATED_BARS:
+                listener.onUpdatedBar(getGson().fromJson(messageObject, CryptoBarMessage.class));
+                break;
+            case ORDER_BOOKS:
+                listener.onOrderBook(getGson().fromJson(messageObject, CryptoOrderBookMessage.class));
+                break;
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public void setListener(CryptoMarketDataListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public void setTradeSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getTradeSubscriptions(), symbols,
+                set -> newEmptyCryptoSubscriptionsMessage().withTrades(set));
+    }
+
+    @Override
+    public Set<String> getTradeSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getTrades();
+    }
+
+    @Override
+    public void setQuoteSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getQuoteSubscriptions(), symbols,
+                set -> newEmptyCryptoSubscriptionsMessage().withQuotes(set));
+    }
+
+    @Override
+    public Set<String> getQuoteSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getQuotes();
+    }
+
+    @Override
+    public void setMinuteBarSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getMinuteBarSubscriptions(), symbols,
+                set -> newEmptyCryptoSubscriptionsMessage().withMinuteBars(set));
+    }
+
+    @Override
+    public Set<String> getMinuteBarSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getMinuteBars();
+    }
+
+    @Override
+    public void setDailyBarSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getDailyBarSubscriptions(), symbols,
+                set -> newEmptyCryptoSubscriptionsMessage().withDailyBars(set));
+    }
+
+    @Override
+    public Set<String> getDailyBarSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getDailyBars();
+    }
+
+    @Override
+    public void setUpdatedBarSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getUpdatedBarSubscriptions(), symbols,
+                set -> newEmptyCryptoSubscriptionsMessage().withUpdatedBars(set));
+    }
+
+    @Override
+    public Set<String> getUpdatedBarSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getUpdatedBars();
+    }
+
+    @Override
+    public void setOrderBookSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getOrderBookSubscriptions(), symbols,
+                set -> newEmptyCryptoSubscriptionsMessage().withOrderBooks(set));
+    }
+
+    @Override
+    public Set<String> getOrderBookSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getOrderBooks();
+    }
+
+    private CryptoSubscriptionsMessage newEmptyCryptoSubscriptionsMessage() {
+        return new CryptoSubscriptionsMessage()
+                .withTrades(null)
+                .withQuotes(null)
+                .withMinuteBars(null)
+                .withDailyBars(null)
+                .withUpdatedBars(null)
+                .withOrderBooks(null);
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocketInterface.java
new file mode 100644
index 00000000..a24e250f
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocketInterface.java
@@ -0,0 +1,116 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessageType;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
+
+import java.util.Set;
+
+/**
+ * {@link CryptoMarketDataWebsocketInterface} is a {@link MarketDataWebsocketInterface} for
+ * {@link CryptoMarketDataWebsocket}.
+ */
+public interface CryptoMarketDataWebsocketInterface extends MarketDataWebsocketInterface {
+
+    /**
+     * Sets the {@link CryptoMarketDataListener}.
+     *
+     * @param listener the {@link CryptoMarketDataListener}
+     */
+    void setListener(CryptoMarketDataListener listener);
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link CryptoMarketDataMessageType#TRADES}. This will remove all
+     * previous {@link CryptoMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setTradeSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link CryptoMarketDataMessageType#TRADES}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getTradeSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link CryptoMarketDataMessageType#QUOTES}. This will remove all
+     * previous {@link CryptoMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setQuoteSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link CryptoMarketDataMessageType#QUOTES}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getQuoteSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link CryptoMarketDataMessageType#MINUTE_BARS}. This will remove
+     * all previous {@link CryptoMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setMinuteBarSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link CryptoMarketDataMessageType#MINUTE_BARS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getMinuteBarSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link CryptoMarketDataMessageType#DAILY_BARS}. This will remove all
+     * previous {@link CryptoMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setDailyBarSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link CryptoMarketDataMessageType#DAILY_BARS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getDailyBarSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link CryptoMarketDataMessageType#UPDATED_BARS}. This will remove
+     * all previous {@link CryptoMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setUpdatedBarSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link CryptoMarketDataMessageType#UPDATED_BARS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getUpdatedBarSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link CryptoMarketDataMessageType#ORDER_BOOKS}. This will remove
+     * all previous {@link CryptoMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setOrderBookSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link CryptoMarketDataMessageType#ORDER_BOOKS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getOrderBookSubscriptions();
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/control/crypto_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/control/crypto_subscriptions_message.json
new file mode 100644
index 00000000..6d8438a8
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/control/crypto_subscriptions_message.json
@@ -0,0 +1,34 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "trades": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to trades."
+    },
+    "quotes": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to quotes."
+    },
+    "bars": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "javaName": "minuteBars",
+      "title": "The {@link java.util.Set} of symbols subscribed to minute bars."
+    },
+    "dailyBars": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to daily bars."
+    },
+    "updatedBars": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to updated bars."
+    },
+    "orderbooks": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "javaName": "orderBooks",
+      "title": "The {@link java.util.Set} of symbols subscribed to order books."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_entry.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_entry.json
similarity index 100%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_entry.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_entry.json
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json
new file mode 100644
index 00000000..76c632fa
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json
@@ -0,0 +1,28 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
+  },
+  "properties": {
+    "S": {
+      "existingJavaType": "java.lang.String",
+      "javaName": "symbol",
+      "title": "The symbol."
+    },
+    "a": {
+      "existingJavaType": "java.util.Set<net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry>",
+      "javaName": "asks",
+      "title": "The {@link java.util.Set} of {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry} asks."
+    },
+    "b": {
+      "existingJavaType": "java.util.Set<net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry>",
+      "javaName": "bids",
+      "title": "The {@link java.util.Set} of {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry} bids."
+    },
+    "t": {
+      "existingJavaType": "java.time.OffsetDateTime",
+      "javaName": "timestamp",
+      "title": "The timestamp with nanosecond precision."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json
deleted file mode 100644
index 80aa0c54..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_orderbook_message.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
-  },
-  "properties": {
-    "S": {
-      "existingJavaType": "java.lang.String",
-      "javaName": "symbol",
-      "title": "The symbol."
-    },
-    "a": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry>",
-      "javaName": "asks",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry} asks."
-    },
-    "b": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry>",
-      "javaName": "bids",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookEntry} bids."
-    },
-    "t": {
-      "existingJavaType": "java.time.OffsetDateTime",
-      "javaName": "timestamp",
-      "title": "The timestamp with nanosecond precision."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json
deleted file mode 100644
index 19b1f191..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/subscriptions/crypto_subscriptions_message.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.CryptoMarketDataMessage"
-  },
-  "properties": {
-    "trades": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to trades."
-    },
-    "quotes": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to quotes."
-    },
-    "bars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "javaName": "minuteBars",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to minute bars."
-    },
-    "dailyBars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to daily bars."
-    },
-    "updatedBars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to updated bars."
-    },
-    "orderbooks": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to orderbooks."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListener.java
new file mode 100644
index 00000000..c47cb6dc
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListener.java
@@ -0,0 +1,17 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.news;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.news.NewsMessage;
+
+/**
+ * {@link NewsMarketDataListener} defines a listener interface for {@link NewsMarketDataWebsocketInterface} messages.
+ */
+@FunctionalInterface
+public interface NewsMarketDataListener {
+
+    /**
+     * Called when a {@link NewsMessage} is received.
+     *
+     * @param news the {@link NewsMessage}
+     */
+    void onNews(NewsMessage news);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListenerAdapter.java
new file mode 100644
index 00000000..2278df82
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataListenerAdapter.java
@@ -0,0 +1,12 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.news;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.news.NewsMessage;
+
+/**
+ * {@link NewsMarketDataListenerAdapter} is an adapter for {@link NewsMarketDataListener}.
+ */
+public class NewsMarketDataListenerAdapter implements NewsMarketDataListener {
+
+    @Override
+    public void onNews(NewsMessage news) {}
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java
index 8e92fa69..495f7932 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocket.java
@@ -1,15 +1,85 @@
 package net.jacobpeterson.alpaca.websocket.marketdata.streams.news;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.bar.BarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.quote.QuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.common.realtime.trade.TradeMessage;
+import com.google.gson.JsonObject;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.control.NewsSubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.news.NewsMessage;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
 
-public class NewsMarketDataWebsocket extends MarketDataWebsocket {
-    public NewsMarketDataWebsocket(OkHttpClient okHttpClient,
-            String keyID, String secretKey) {
-        super(okHttpClient, "v1beta1/news", "News", keyID, secretKey, TradeMessage.class,
-                QuoteMessage.class, BarMessage.class);
+import java.util.Set;
+
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType.ERROR;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType.SUBSCRIPTION;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType.SUCCESS;
+import static net.jacobpeterson.alpaca.openapi.marketdata.JSON.getGson;
+
+/**
+ * {@link NewsMarketDataWebsocket} is an implementation for {@link NewsMarketDataWebsocketInterface}.
+ */
+public class NewsMarketDataWebsocket
+        extends MarketDataWebsocket<NewsMarketDataMessageType, NewsSubscriptionsMessage, NewsMarketDataListener>
+        implements NewsMarketDataWebsocketInterface {
+
+    /**
+     * Instantiates a new {@link NewsMarketDataWebsocket}.
+     *
+     * @param okHttpClient    the {@link OkHttpClient}
+     * @param traderKeyID     the trader key ID
+     * @param traderSecretKey the trader secret key
+     * @param brokerAPIKey    the broker API key
+     * @param brokerAPISecret the broker API secret
+     */
+    public NewsMarketDataWebsocket(OkHttpClient okHttpClient, String traderKeyID, String traderSecretKey,
+            String brokerAPIKey, String brokerAPISecret) {
+        super(okHttpClient, new HttpUrl.Builder()
+                        .scheme("https")
+                        .host("stream.data.alpaca.markets")
+                        .addPathSegments("v1beta1/news")
+                        .build(),
+                "News", traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret,
+                NewsMarketDataMessageType.class, NewsSubscriptionsMessage.class);
+    }
+
+    @Override
+    protected boolean isSuccessMessageType(NewsMarketDataMessageType messageType) {
+        return messageType == SUCCESS;
+    }
+
+    @Override
+    protected boolean isErrorMessageType(NewsMarketDataMessageType messageType) {
+        return messageType == ERROR;
+    }
+
+    @Override
+    protected boolean isSubscriptionMessageType(NewsMarketDataMessageType messageType) {
+        return messageType == SUBSCRIPTION;
+    }
+
+    @Override
+    protected void callListenerWithMessage(NewsMarketDataMessageType messageType, JsonObject messageObject) {
+        if (messageType == NewsMarketDataMessageType.NEWS) {
+            listener.onNews(getGson().fromJson(messageObject, NewsMessage.class));
+        } else {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public void setListener(NewsMarketDataListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public void setNewsSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getNewsSubscriptions(), symbols,
+                set -> new NewsSubscriptionsMessage().withNews(set));
+    }
+
+    @Override
+    public Set<String> getNewsSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getNews();
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocketInterface.java
new file mode 100644
index 00000000..8a89857d
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocketInterface.java
@@ -0,0 +1,36 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.news;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessageType;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
+
+import java.util.Set;
+
+/**
+ * {@link NewsMarketDataWebsocketInterface} is a {@link MarketDataWebsocketInterface} for
+ * {@link NewsMarketDataWebsocket}.
+ */
+public interface NewsMarketDataWebsocketInterface extends MarketDataWebsocketInterface {
+
+    /**
+     * Sets the {@link NewsMarketDataListener}.
+     *
+     * @param listener the {@link NewsMarketDataListener}
+     */
+    void setListener(NewsMarketDataListener listener);
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link NewsMarketDataMessageType#NEWS}. This will remove all
+     * previous {@link NewsMarketDataMessageType#NEWS} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setNewsSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link NewsMarketDataMessageType#NEWS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getNewsSubscriptions();
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/control/news_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/control/news_subscriptions_message.json
new file mode 100644
index 00000000..dbd84da7
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/control/news_subscriptions_message.json
@@ -0,0 +1,12 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.news.model.NewsMarketDataMessage"
+  },
+  "properties": {
+    "news": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to news."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json
index 047c0790..638a3a67 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/model/news/news_message.json
@@ -26,7 +26,7 @@
       "existingJavaType": "java.lang.String"
     },
     "symbols": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>"
+      "existingJavaType": "java.util.Set<java.lang.String>"
     },
     "created_at": {
       "existingJavaType": "java.time.OffsetDateTime"
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListener.java
new file mode 100644
index 00000000..396e6af2
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListener.java
@@ -0,0 +1,78 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.stock;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.bar.StockBarMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.limituplimitdownband.StockLimitUpLimitDownBandMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.quote.StockQuoteMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.trade.StockTradeMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecancelerror.StockTradeCancelErrorMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecorrection.StockTradeCorrectionMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradingstatus.StockTradingStatusMessage;
+
+/**
+ * {@link StockMarketDataListener} defines a listener interface for {@link StockMarketDataWebsocketInterface} messages.
+ */
+public interface StockMarketDataListener {
+
+    /**
+     * Called when a {@link StockTradeMessage} is received.
+     *
+     * @param trade the {@link StockTradeMessage}
+     */
+    void onTrade(StockTradeMessage trade);
+
+    /**
+     * Called when a {@link StockQuoteMessage} is received.
+     *
+     * @param quote the {@link StockQuoteMessage}
+     */
+    void onQuote(StockQuoteMessage quote);
+
+    /**
+     * Called when a {@link StockBarMessage} is received.
+     *
+     * @param bar the {@link StockBarMessage}
+     */
+    void onMinuteBar(StockBarMessage bar);
+
+    /**
+     * Called when a {@link StockBarMessage} is received.
+     *
+     * @param bar the {@link StockBarMessage}
+     */
+    void onDailyBar(StockBarMessage bar);
+
+    /**
+     * Called when a {@link StockBarMessage} is received.
+     *
+     * @param bar the {@link StockBarMessage}
+     */
+    void onUpdatedBar(StockBarMessage bar);
+
+    /**
+     * Called when a {@link StockTradeCorrectionMessage} is received.
+     *
+     * @param tradeCorrection the {@link StockTradeCorrectionMessage}
+     */
+    void onTradeCorrection(StockTradeCorrectionMessage tradeCorrection);
+
+    /**
+     * Called when a {@link StockTradeCancelErrorMessage} is received.
+     *
+     * @param tradeCancelError the {@link StockTradeCancelErrorMessage}
+     */
+    void onTradeCancelError(StockTradeCancelErrorMessage tradeCancelError);
+
+    /**
+     * Called when a {@link StockLimitUpLimitDownBandMessage} is received.
+     *
+     * @param limitUpLimitDownBand the {@link StockLimitUpLimitDownBandMessage}
+     */
+    void onLimitUpLimitDownBand(StockLimitUpLimitDownBandMessage limitUpLimitDownBand);
+
+    /**
+     * Called when a {link StockTradingStatusMessage} is received.
+     *
+     * @param tradingStatus the {@link StockTradingStatusMessage}
+     */
+    void onTradingStatus(StockTradingStatusMessage tradingStatus);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListenerAdapter.java
new file mode 100644
index 00000000..df99badf
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataListenerAdapter.java
@@ -0,0 +1,42 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.stock;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.bar.StockBarMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.limituplimitdownband.StockLimitUpLimitDownBandMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.quote.StockQuoteMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.trade.StockTradeMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecancelerror.StockTradeCancelErrorMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecorrection.StockTradeCorrectionMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradingstatus.StockTradingStatusMessage;
+
+/**
+ * {@link StockMarketDataListenerAdapter} is an adapter for {@link StockMarketDataListener}.
+ */
+public class StockMarketDataListenerAdapter implements StockMarketDataListener {
+
+    @Override
+    public void onTrade(StockTradeMessage trade) {}
+
+    @Override
+    public void onQuote(StockQuoteMessage quote) {}
+
+    @Override
+    public void onMinuteBar(StockBarMessage bar) {}
+
+    @Override
+    public void onDailyBar(StockBarMessage bar) {}
+
+    @Override
+    public void onUpdatedBar(StockBarMessage bar) {}
+
+    @Override
+    public void onTradeCorrection(StockTradeCorrectionMessage tradeCorrection) {}
+
+    @Override
+    public void onTradeCancelError(StockTradeCancelErrorMessage tradeCancelError) {}
+
+    @Override
+    public void onLimitUpLimitDownBand(StockLimitUpLimitDownBandMessage limitUpLimitDownBand) {}
+
+    @Override
+    public void onTradingStatus(StockTradingStatusMessage tradingStatus) {}
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java
index 87bfe6c9..753d0246 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocket.java
@@ -1,30 +1,218 @@
 package net.jacobpeterson.alpaca.websocket.marketdata.streams.stock;
 
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.bar.StockBarMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.quote.StockQuoteMessage;
-import net.jacobpeterson.alpaca.model.endpoint.marketdata.stock.realtime.trade.StockTradeMessage;
-import net.jacobpeterson.alpaca.model.properties.DataAPIType;
+import com.google.gson.JsonObject;
+import net.jacobpeterson.alpaca.model.util.apitype.MarketDataWebsocketSourceType;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.bar.StockBarMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.control.StockSubscriptionsMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.limituplimitdownband.StockLimitUpLimitDownBandMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.quote.StockQuoteMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.trade.StockTradeMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecancelerror.StockTradeCancelErrorMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradecorrection.StockTradeCorrectionMessage;
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.tradingstatus.StockTradingStatusMessage;
 import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocket;
+import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
 
+import java.util.Set;
+
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType.ERROR;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType.SUBSCRIPTION;
+import static net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType.SUCCESS;
+import static net.jacobpeterson.alpaca.openapi.marketdata.JSON.getGson;
+
 /**
- * {@link StockMarketDataWebsocket} is a {@link MarketDataWebsocket} for
- * <a href="https://alpaca.markets/docs/api-documentation/api-v2/market-data/alpaca-data-api-v2/real-time/">Realtime
- * Stock Market Data</a>
+ * {@link StockMarketDataWebsocket} is an implementation for {@link StockMarketDataWebsocketInterface}.
  */
-public class StockMarketDataWebsocket extends MarketDataWebsocket {
+public class StockMarketDataWebsocket
+        extends MarketDataWebsocket<StockMarketDataMessageType, StockSubscriptionsMessage, StockMarketDataListener>
+        implements StockMarketDataWebsocketInterface {
+
+    private static HttpUrl createWebsocketURL(boolean isSandbox,
+            MarketDataWebsocketSourceType marketDataWebsocketSourceType) {
+        return new HttpUrl.Builder()
+                .scheme("https")
+                .host(isSandbox ? "stream.data.sandbox.alpaca.markets" : "stream.data.alpaca.markets")
+                .addPathSegment("v2")
+                .addPathSegment(marketDataWebsocketSourceType.toString())
+                .build();
+    }
 
     /**
      * Instantiates a new {@link StockMarketDataWebsocket}.
      *
-     * @param okHttpClient the {@link OkHttpClient}
-     * @param dataAPIType  the {@link DataAPIType}
-     * @param keyID        the key ID
-     * @param secretKey    the secret key
+     * @param okHttpClient                  the {@link OkHttpClient}
+     * @param traderKeyID                   the trader key ID
+     * @param traderSecretKey               the trader secret key
+     * @param brokerAPIKey                  the broker API key
+     * @param brokerAPISecret               the broker API secret
+     * @param marketDataWebsocketSourceType the {@link MarketDataWebsocketSourceType}
      */
-    public StockMarketDataWebsocket(OkHttpClient okHttpClient, DataAPIType dataAPIType,
-            String keyID, String secretKey) {
-        super(okHttpClient, "v2/" + dataAPIType.toString(), "Stock", keyID, secretKey, StockTradeMessage.class,
-                StockQuoteMessage.class, StockBarMessage.class);
+    public StockMarketDataWebsocket(OkHttpClient okHttpClient, String traderKeyID, String traderSecretKey,
+            String brokerAPIKey, String brokerAPISecret, MarketDataWebsocketSourceType marketDataWebsocketSourceType) {
+        super(okHttpClient,
+                createWebsocketURL(brokerAPIKey != null && brokerAPISecret != null, marketDataWebsocketSourceType),
+                "Stock", traderKeyID, traderSecretKey, brokerAPIKey, brokerAPISecret,
+                StockMarketDataMessageType.class, StockSubscriptionsMessage.class);
+    }
+
+    @Override
+    protected boolean isSuccessMessageType(StockMarketDataMessageType messageType) {
+        return messageType == SUCCESS;
+    }
+
+    @Override
+    protected boolean isErrorMessageType(StockMarketDataMessageType messageType) {
+        return messageType == ERROR;
+    }
+
+    @Override
+    protected boolean isSubscriptionMessageType(StockMarketDataMessageType messageType) {
+        return messageType == SUBSCRIPTION;
+    }
+
+    @Override
+    protected void callListenerWithMessage(StockMarketDataMessageType messageType, JsonObject messageObject) {
+        switch (messageType) {
+            case TRADES:
+                listener.onTrade(getGson().fromJson(messageObject,
+                        StockTradeMessage.class));
+                break;
+            case QUOTES:
+                listener.onQuote(getGson().fromJson(messageObject,
+                        StockQuoteMessage.class));
+                break;
+            case MINUTE_BARS:
+                listener.onMinuteBar(getGson().fromJson(messageObject,
+                        StockBarMessage.class));
+                break;
+            case DAILY_BARS:
+                listener.onDailyBar(getGson().fromJson(messageObject,
+                        StockBarMessage.class));
+                break;
+            case UPDATED_BARS:
+                listener.onUpdatedBar(getGson().fromJson(messageObject,
+                        StockBarMessage.class));
+                break;
+            case TRADE_CORRECTIONS:
+                listener.onTradeCorrection(getGson().fromJson(messageObject,
+                        StockTradeCorrectionMessage.class));
+                break;
+            case TRADE_CANCEL_ERRORS:
+                listener.onTradeCancelError(getGson().fromJson(messageObject,
+                        StockTradeCancelErrorMessage.class));
+                break;
+            case LIMIT_UP_LIMIT_DOWN_BANDS:
+                listener.onLimitUpLimitDownBand(getGson().fromJson(messageObject,
+                        StockLimitUpLimitDownBandMessage.class));
+                break;
+            case TRADING_STATUSES:
+                listener.onTradingStatus(getGson().fromJson(messageObject,
+                        StockTradingStatusMessage.class));
+                break;
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public void setListener(StockMarketDataListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public void setTradeSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getTradeSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withTrades(set));
+    }
+
+    @Override
+    public Set<String> getTradeSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getTrades();
+    }
+
+    @Override
+    public void setQuoteSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getQuoteSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withQuotes(set));
+    }
+
+    @Override
+    public Set<String> getQuoteSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getQuotes();
+    }
+
+    @Override
+    public void setMinuteBarSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getMinuteBarSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withMinuteBars(set));
+    }
+
+    @Override
+    public Set<String> getMinuteBarSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getMinuteBars();
+    }
+
+    @Override
+    public void setDailyBarSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getDailyBarSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withDailyBars(set));
+    }
+
+    @Override
+    public Set<String> getDailyBarSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getDailyBars();
+    }
+
+    @Override
+    public void setUpdatedBarSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getUpdatedBarSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withUpdatedBars(set));
+    }
+
+    @Override
+    public Set<String> getUpdatedBarSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getUpdatedBars();
+    }
+
+    @Override
+    public void setLimitUpLimitDownBandSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getLimitUpLimitDownBandSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withLimitUpLimitDownBands(set));
+    }
+
+    @Override
+    public Set<String> getLimitUpLimitDownBandSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getLimitUpLimitDownBands();
+    }
+
+    @Override
+    public void setTradingStatuseSubscriptions(Set<String> symbols) {
+        symbols = symbols == null ? Set.of() : symbols;
+        setSubscriptions(getTradingStatuseSubscriptions(), symbols,
+                set -> newEmptyStockSubscriptionsMessage().withTradingStatuses(set));
+    }
+
+    @Override
+    public Set<String> getTradingStatuseSubscriptions() {
+        return subscriptionsMessage == null ? Set.of() : subscriptionsMessage.getTradingStatuses();
+    }
+
+    private StockSubscriptionsMessage newEmptyStockSubscriptionsMessage() {
+        return new StockSubscriptionsMessage()
+                .withTrades(null)
+                .withQuotes(null)
+                .withMinuteBars(null)
+                .withDailyBars(null)
+                .withUpdatedBars(null)
+                .withLimitUpLimitDownBands(null)
+                .withTradingStatuses(null);
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocketInterface.java
new file mode 100644
index 00000000..32fc3b8d
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocketInterface.java
@@ -0,0 +1,136 @@
+package net.jacobpeterson.alpaca.websocket.marketdata.streams.stock;
+
+import net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessageType;
+import net.jacobpeterson.alpaca.websocket.marketdata.MarketDataWebsocketInterface;
+
+import java.util.Set;
+
+/**
+ * {@link StockMarketDataWebsocketInterface} is a {@link MarketDataWebsocketInterface} for
+ * {@link StockMarketDataWebsocket}.
+ */
+public interface StockMarketDataWebsocketInterface extends MarketDataWebsocketInterface {
+
+    /**
+     * Sets the {@link StockMarketDataListener}.
+     *
+     * @param listener the {@link StockMarketDataListener}
+     */
+    void setListener(StockMarketDataListener listener);
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#TRADES}. This will remove all
+     * previous {@link StockMarketDataMessageType#TRADES} subscriptions.
+     * <br>
+     * Note that this will automatically apply the same <code>symbols</code> subscription list to the
+     * {@link StockMarketDataMessageType#TRADE_CORRECTIONS} and {@link StockMarketDataMessageType#TRADE_CANCEL_ERRORS}
+     * types.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setTradeSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#TRADES}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getTradeSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#QUOTES}. This will remove all
+     * previous {@link StockMarketDataMessageType#QUOTES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setQuoteSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#QUOTES}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getQuoteSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#MINUTE_BARS}. This will remove all
+     * previous {@link StockMarketDataMessageType#MINUTE_BARS} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setMinuteBarSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#MINUTE_BARS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getMinuteBarSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#DAILY_BARS}. This will remove all
+     * previous {@link StockMarketDataMessageType#DAILY_BARS} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setDailyBarSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#DAILY_BARS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getDailyBarSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#UPDATED_BARS}. This will remove
+     * all previous {@link StockMarketDataMessageType#UPDATED_BARS} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setUpdatedBarSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#UPDATED_BARS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getUpdatedBarSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#LIMIT_UP_LIMIT_DOWN_BANDS}. This
+     * will remove all previous {@link StockMarketDataMessageType#LIMIT_UP_LIMIT_DOWN_BANDS} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setLimitUpLimitDownBandSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#LIMIT_UP_LIMIT_DOWN_BANDS}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getLimitUpLimitDownBandSubscriptions();
+
+    /**
+     * Subscribes the given <code>symbols</code> to {@link StockMarketDataMessageType#TRADING_STATUSES}. This will
+     * remove all previous {@link StockMarketDataMessageType#TRADING_STATUSES} subscriptions.
+     *
+     * @param symbols the {@link Set} of symbols (use an asterisk ("*") as a wildcard to subscribe to all available
+     *                symbols) or <code>null</code> to unsubscribe from all symbols
+     */
+    void setTradingStatuseSubscriptions(Set<String> symbols);
+
+    /**
+     * Gets the current symbols subscribed to {@link StockMarketDataMessageType#TRADING_STATUSES}.
+     *
+     * @return a {@link Set} of {@link String} symbols
+     */
+    Set<String> getTradingStatuseSubscriptions();
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/control/stock_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/control/stock_subscriptions_message.json
new file mode 100644
index 00000000..05a1d449
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/control/stock_subscriptions_message.json
@@ -0,0 +1,39 @@
+{
+  "type": "object",
+  "extends": {
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
+  },
+  "properties": {
+    "trades": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to trades."
+    },
+    "quotes": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to quotes."
+    },
+    "bars": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "javaName": "minuteBars",
+      "title": "The {@link java.util.Set} of symbols subscribed to minute bars."
+    },
+    "dailyBars": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to daily bars."
+    },
+    "updatedBars": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "title": "The {@link java.util.Set} of symbols subscribed to updated bars."
+    },
+    "lulds": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "javaName": "limitUpLimitDownBands",
+      "title": "The {@link java.util.Set} of symbols subscribed to limit up - limit down bands."
+    },
+    "statuses": {
+      "existingJavaType": "java.util.Set<java.lang.String>",
+      "javaName": "tradingStatuses",
+      "title": "The {@link java.util.Set} of symbols subscribed to trading statuses."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json
index 27ff6daa..b0e2d72f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/quote/stock_quote_message.json
@@ -40,9 +40,9 @@
       "title": "The bid size."
     },
     "c": {
-      "existingJavaType": "java.util.ArrayList<String>",
+      "existingJavaType": "java.util.Set<String>",
       "javaName": "conditions",
-      "title": "The {@link java.util.ArrayList} of quote conditions."
+      "title": "The {@link java.util.Set} of quote conditions."
     },
     "t": {
       "existingJavaType": "java.time.OffsetDateTime",
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json
deleted file mode 100644
index 986947f6..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/subscriptions/stock_subscriptions_message.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
-  "type": "object",
-  "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.marketdata.streams.stock.model.StockMarketDataMessage"
-  },
-  "properties": {
-    "trades": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to trades."
-    },
-    "quotes": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to quotes."
-    },
-    "bars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "javaName": "minuteBars",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to minute bars."
-    },
-    "dailyBars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to daily bars."
-    },
-    "updatedBars": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to updated bars."
-    },
-    "corrections": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "javaName": "tradeCorrections",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to trade corrections."
-    },
-    "cancelErrors": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "javaName": "tradeCancelErrors",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to trade cancels/errors."
-    },
-    "lulds": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "javaName": "limitUpLimitDownBands",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to limit up - limit down bands."
-    },
-    "statuses": {
-      "existingJavaType": "java.util.ArrayList<java.lang.String>",
-      "javaName": "tradingStatuses",
-      "title": "The {@link java.util.ArrayList} of symbols subscribed to trading statuses."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json
index 4294856a..239a8164 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/trade/stock_trade_message.json
@@ -30,9 +30,9 @@
       "title": "The trade size."
     },
     "c": {
-      "existingJavaType": "java.util.ArrayList<String>",
+      "existingJavaType": "java.util.Set<String>",
       "javaName": "conditions",
-      "title": "The {@link java.util.ArrayList} of trade conditions."
+      "title": "The {@link java.util.Set} of trade conditions."
     },
     "t": {
       "existingJavaType": "java.time.OffsetDateTime",
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json
index 0b6daaca..acb6a6ab 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/model/tradecorrection/stock_trade_correction_message.json
@@ -30,9 +30,9 @@
       "title": "The original trade size."
     },
     "oc": {
-      "existingJavaType": "java.util.ArrayList<String>",
+      "existingJavaType": "java.util.Set<String>",
       "javaName": "originalConditions",
-      "title": "The {@link java.util.ArrayList} of original trade conditions."
+      "title": "The {@link java.util.Set} of original trade conditions."
     },
     "ci": {
       "existingJavaType": "java.lang.Integer",
@@ -50,9 +50,9 @@
       "title": "The corrected trade size."
     },
     "cc": {
-      "existingJavaType": "java.util.ArrayList<String>",
+      "existingJavaType": "java.util.Set<String>",
       "javaName": "correctedConditions",
-      "title": "The {@link java.util.ArrayList} of original trade conditions."
+      "title": "The {@link java.util.Set} of original trade conditions."
     },
     "t": {
       "existingJavaType": "java.time.OffsetDateTime",
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java
deleted file mode 100644
index c96baee5..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListener.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.trades;
-
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketMessageListener;
-
-/**
- * {@link TradesListener} defines a listener interface for {@link TradesWebsocket} messages.
- */
-public interface TradesListener extends AlpacaWebsocketMessageListener<TradesStreamMessageType, TradesStreamMessage> {
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java
deleted file mode 100644
index a6dd93ec..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesListenerAdapter.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.trades;
-
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * {@link TradesListenerAdapter} is an adapter class for {@link TradesListener}.
- */
-public class TradesListenerAdapter implements TradesListener {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(TradesListenerAdapter.class);
-
-    @Override
-    public void onMessage(TradesStreamMessageType messageType, TradesStreamMessage message) {
-        LOGGER.info("{} message received: {}", messageType, message);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
deleted file mode 100644
index 5759d355..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocket.java
+++ /dev/null
@@ -1,217 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.trades;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonPrimitive;
-import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationData;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationMessage;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.listening.ListeningMessage;
-import net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdateMessage;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.WebSocket;
-import okio.ByteString;
-import org.jetbrains.annotations.NotNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Predicates.not;
-import static com.google.common.collect.Iterables.toArray;
-import static com.google.gson.JsonParser.parseString;
-import static java.util.Collections.singletonList;
-import static java.util.concurrent.TimeUnit.SECONDS;
-import static net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType.TRADE_UPDATES;
-import static net.jacobpeterson.alpaca.openapi.trader.JSON.getGson;
-
-/**
- * {@link TradesWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
- * {@link TradesWebsocketInterface} interface for
- * <a href="https://docs.alpaca.markets/docs/websocket-streaming">Trades Streaming</a>.
- */
-public class TradesWebsocket extends AlpacaWebsocket<TradesStreamMessageType, TradesStreamMessage, TradesListener>
-        implements TradesWebsocketInterface {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(TradesWebsocket.class);
-    private static final String STREAM_ELEMENT_KEY = "stream";
-    private static final List<TradesStreamMessageType> SUBSCRIBABLE_STREAMING_MESSAGE_TYPES =
-            singletonList(TRADE_UPDATES);
-
-    /**
-     * Creates a {@link HttpUrl} for {@link TradesWebsocket}.
-     *
-     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
-     *
-     * @return a {@link HttpUrl}
-     */
-    @SuppressWarnings("UnnecessaryDefault")
-    private static HttpUrl createWebsocketURL(TraderAPIEndpointType traderAPIEndpointType) {
-        return new HttpUrl.Builder()
-                .scheme("https")
-                .host((switch (traderAPIEndpointType) {
-                    case LIVE -> "api";
-                    case PAPER -> "paper-api";
-                    default -> throw new UnsupportedOperationException();
-                }) + ".alpaca.markets")
-                .addPathSegment("stream")
-                .build();
-    }
-
-    private final Set<TradesStreamMessageType> listenedMessageTypes;
-
-    /**
-     * Instantiates a new {@link TradesWebsocket}.
-     *
-     * @param okHttpClient          the {@link OkHttpClient}
-     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
-     * @param keyID                 the key ID
-     * @param secretKey             the secret key
-     * @param oAuthToken            the OAuth token
-     */
-    public TradesWebsocket(OkHttpClient okHttpClient, TraderAPIEndpointType traderAPIEndpointType,
-            String keyID, String secretKey, String oAuthToken) {
-        super(okHttpClient, createWebsocketURL(traderAPIEndpointType), "Trades Stream", keyID, secretKey, oAuthToken);
-        listenedMessageTypes = new HashSet<>();
-    }
-
-    @Override
-    protected void cleanupState() {
-        super.cleanupState();
-        listenedMessageTypes.clear();
-    }
-
-    @Override
-    protected void onConnection() {
-        sendAuthenticationMessage();
-    }
-
-    @Override
-    protected void onReconnection() {
-        sendAuthenticationMessage();
-        if (waitForAuthorization(5, SECONDS)) {
-            subscribe(toArray(listenedMessageTypes, TradesStreamMessageType.class));
-        }
-    }
-
-    @Override
-    protected void sendAuthenticationMessage() {
-        // Ensure that the authorization Future exists
-        getAuthorizationFuture();
-
-        final JsonObject authObject = new JsonObject();
-        authObject.addProperty("action", "authenticate");
-        final JsonObject authData = new JsonObject();
-        if (useOAuth) {
-            authData.addProperty("oauth_token", oAuthToken);
-        } else {
-            authData.addProperty("key_id", keyID);
-            authData.addProperty("secret_key", secretKey);
-        }
-        authObject.add("data", authData);
-
-        LOGGER.info("{} websocket sending authentication message...", websocketName);
-        websocket.send(authObject.toString());
-    }
-
-    @Override
-    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) { // Binary framing
-        final String messageString = byteString.utf8();
-        LOGGER.trace("Websocket message received: {}", messageString);
-
-        // Parse JSON string and identify 'messageType'
-        final JsonElement messageElement = parseString(messageString);
-        checkState(messageElement instanceof JsonObject, "Message must be a JsonObject! Received: %s", messageElement);
-        final JsonObject messageObject = messageElement.getAsJsonObject();
-        final JsonElement streamElement = messageObject.get(STREAM_ELEMENT_KEY);
-        checkState(streamElement instanceof JsonPrimitive,
-                "Message must contain %s element! Received: %s", STREAM_ELEMENT_KEY, messageElement);
-        final TradesStreamMessageType messageType = getGson().fromJson(streamElement, TradesStreamMessageType.class);
-        checkNotNull(messageType, "TradesStreamMessageType not found in message: %s", messageObject);
-
-        // Deserialize message based on 'messageType'
-        TradesStreamMessage message;
-        switch (messageType) {
-            case AUTHORIZATION:
-                message = getGson().fromJson(messageObject, AuthorizationMessage.class);
-                final AuthorizationData authorizationData = ((AuthorizationMessage) message).getData();
-                authenticated = authorizationData.getAction().equalsIgnoreCase("authenticate") &&
-                        authorizationData.getStatus().equalsIgnoreCase("authorized");
-                if (!authenticated) {
-                    throw new RuntimeException(websocketName + " websocket authentication failed!");
-                } else {
-                    LOGGER.info("{} websocket authenticated.", websocketName);
-                }
-                if (authenticationMessageFuture != null) {
-                    authenticationMessageFuture.complete(authenticated);
-                }
-                break;
-            case LISTENING:
-                message = getGson().fromJson(messageObject, ListeningMessage.class);
-                // Remove all 'TradeStreamMessageType's that are no longer listened to and add new ones
-                final List<TradesStreamMessageType> currentTypes = ((ListeningMessage) message).getData().getStreams();
-                currentTypes.stream()
-                        .filter(not(listenedMessageTypes::contains))
-                        .forEach(listenedMessageTypes::remove);
-                listenedMessageTypes.addAll(currentTypes);
-                break;
-            case TRADE_UPDATES:
-                message = getGson().fromJson(messageObject, TradeUpdateMessage.class);
-                break;
-            default:
-                throw new UnsupportedOperationException();
-        }
-
-        // Call listener
-        if (listenedMessageTypes.contains(messageType)) {
-            callListener(messageType, message);
-        }
-    }
-
-    @Override
-    public void subscribe(TradesStreamMessageType... messageTypes) {
-        if (messageTypes == null || messageTypes.length == 0) {
-            return;
-        }
-
-        // Add all non-subscribable 'messageTypes' before connecting or sending websocket message
-        Arrays.stream(messageTypes)
-                .filter(not(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains))
-                .forEach(listenedMessageTypes::add);
-
-        // Create streams subscription JSON message
-        final JsonObject requestObject = new JsonObject();
-        requestObject.addProperty("action", "listen");
-        final JsonArray streamsArray = new JsonArray();
-        Arrays.stream(messageTypes)
-                .filter(SUBSCRIBABLE_STREAMING_MESSAGE_TYPES::contains)
-                .forEach((type) -> streamsArray.add(type.toString()));
-        if (streamsArray.isEmpty()) {
-            return;
-        } else if (!isConnected()) {
-            throw new IllegalStateException("This websocket must be connected before subscribing to streams!");
-        }
-        final JsonObject dataObject = new JsonObject();
-        dataObject.add("streams", streamsArray);
-        requestObject.add("data", dataObject);
-
-        websocket.send(requestObject.toString());
-        LOGGER.info("Requested streams: {}.", streamsArray);
-    }
-
-    @Override
-    public Collection<TradesStreamMessageType> subscriptions() {
-        return new HashSet<>(listenedMessageTypes);
-    }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java
deleted file mode 100644
index b866e37e..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/TradesWebsocketInterface.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package net.jacobpeterson.alpaca.websocket.trades;
-
-import net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType;
-import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
-
-import java.util.Collection;
-
-/**
- * {@link TradesWebsocketInterface} is an {@link AlpacaWebsocketInterface} for a {@link TradesWebsocket}.
- */
-public interface TradesWebsocketInterface extends AlpacaWebsocketInterface<TradesListener> {
-
-    /**
-     * Sets the {@link TradesStreamMessageType}s for this stream.
-     *
-     * @param messageTypes the {@link TradesStreamMessageType}s
-     */
-    void subscribe(TradesStreamMessageType... messageTypes);
-
-    /**
-     * Gets all the currently subscribed {@link TradesStreamMessageType}s.
-     *
-     * @return a {@link Collection} of {@link TradesStreamMessageType}
-     */
-    Collection<TradesStreamMessageType> subscriptions();
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json b/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json
deleted file mode 100644
index 78a68ba5..00000000
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_data.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "type": "object",
-  "properties": {
-    "streams": {
-      "existingJavaType": "java.util.ArrayList<net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType>",
-      "title": "The {@link java.util.ArrayList} of {@link net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType}s."
-    }
-  }
-}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesListener.java b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesListener.java
new file mode 100644
index 00000000..3a752308
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesListener.java
@@ -0,0 +1,17 @@
+package net.jacobpeterson.alpaca.websocket.updates;
+
+import net.jacobpeterson.alpaca.model.websocket.updates.model.tradeupdate.TradeUpdateMessage;
+
+/**
+ * {@link UpdatesListener} defines a listener interface for {@link UpdatesWebsocketInterface} messages.
+ */
+@FunctionalInterface
+public interface UpdatesListener {
+
+    /**
+     * Called when a {@link TradeUpdateMessage} is received.
+     *
+     * @param tradeUpdate the {@link TradeUpdateMessage}
+     */
+    void onTradeUpdate(TradeUpdateMessage tradeUpdate);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java
new file mode 100644
index 00000000..99e1db42
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java
@@ -0,0 +1,172 @@
+package net.jacobpeterson.alpaca.websocket.updates;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
+import net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType;
+import net.jacobpeterson.alpaca.model.websocket.updates.model.authorization.AuthorizationMessage;
+import net.jacobpeterson.alpaca.model.websocket.updates.model.tradeupdate.TradeUpdateMessage;
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocket;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.WebSocket;
+import okio.ByteString;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static com.google.gson.JsonParser.parseString;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType.TRADE_UPDATES;
+import static net.jacobpeterson.alpaca.openapi.trader.JSON.getGson;
+
+/**
+ * {@link UpdatesWebsocket} is an {@link AlpacaWebsocket} implementation and provides the
+ * {@link UpdatesWebsocketInterface} interface for
+ * <a href="https://docs.alpaca.markets/docs/websocket-streaming">Updates Streaming</a>.
+ */
+public class UpdatesWebsocket extends AlpacaWebsocket implements UpdatesWebsocketInterface {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(UpdatesWebsocket.class);
+
+    @SuppressWarnings("UnnecessaryDefault")
+    private static HttpUrl createWebsocketURL(TraderAPIEndpointType traderAPIEndpointType) {
+        return new HttpUrl.Builder()
+                .scheme("https")
+                .host((switch (traderAPIEndpointType) {
+                    case LIVE -> "api";
+                    case PAPER -> "paper-api";
+                    default -> throw new UnsupportedOperationException();
+                }) + ".alpaca.markets")
+                .addPathSegment("stream")
+                .build();
+    }
+
+    protected final String keyID;
+    protected final String secretKey;
+    protected final String oAuthToken;
+    protected UpdatesListener listener;
+    protected boolean listenToTradeUpdates;
+
+    /**
+     * Instantiates a new {@link UpdatesWebsocket}.
+     *
+     * @param okHttpClient          the {@link OkHttpClient}
+     * @param traderAPIEndpointType the {@link TraderAPIEndpointType}
+     * @param keyID                 the key ID
+     * @param secretKey             the secret key
+     * @param oAuthToken            the OAuth token
+     */
+    public UpdatesWebsocket(OkHttpClient okHttpClient, TraderAPIEndpointType traderAPIEndpointType,
+            String keyID, String secretKey, String oAuthToken) {
+        super(okHttpClient, createWebsocketURL(traderAPIEndpointType), "Trades Stream");
+        this.keyID = keyID;
+        this.secretKey = secretKey;
+        this.oAuthToken = oAuthToken;
+    }
+
+    @Override
+    protected void cleanupState() {
+        super.cleanupState();
+    }
+
+    @Override
+    protected void onConnection() {
+        sendAuthenticationMessage();
+    }
+
+    @Override
+    protected void onReconnection() {
+        sendAuthenticationMessage();
+        if (waitForAuthorization(5, SECONDS) && listenToTradeUpdates) {
+            sendTradeUpdatesListenMessage();
+        }
+    }
+
+    @Override
+    protected void sendAuthenticationMessage() {
+        // Ensure that the authorization Future exists
+        getAuthorizationFuture();
+
+        final JsonObject authObject = new JsonObject();
+        authObject.addProperty("action", "authenticate");
+        final JsonObject authData = new JsonObject();
+        if (oAuthToken != null) {
+            authData.addProperty("oauth_token", oAuthToken);
+        } else {
+            authData.addProperty("key_id", keyID);
+            authData.addProperty("secret_key", secretKey);
+        }
+        authObject.add("data", authData);
+
+        LOGGER.info("{} websocket sending authentication message...", websocketName);
+        sendWebsocketMessage(authObject.toString());
+    }
+
+    @Override
+    public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString byteString) { // Binary framing
+        final String messageString = byteString.utf8();
+        LOGGER.trace("Websocket message received: message={}", messageString);
+
+        // Parse JSON string and identify 'messageType'
+        final JsonObject messageObject = parseString(messageString).getAsJsonObject();
+        final UpdatesMessageType messageType =
+                getGson().fromJson(messageObject.get("stream"), UpdatesMessageType.class);
+
+        // Deserialize message based on 'messageType' and call listener
+        switch (messageType) {
+            case AUTHORIZATION:
+                final AuthorizationMessage authorizationMessage =
+                        getGson().fromJson(messageObject, AuthorizationMessage.class);
+                authenticated = authorizationMessage.getData().getAction().equalsIgnoreCase("authenticate") &&
+                        authorizationMessage.getData().getStatus().equalsIgnoreCase("authorized");
+                if (authenticationMessageFuture != null) {
+                    authenticationMessageFuture.complete(authenticated);
+                }
+                if (!authenticated) {
+                    throw new RuntimeException(websocketName + " websocket authentication failed!");
+                } else {
+                    LOGGER.info("{} websocket authenticated.", websocketName);
+                }
+                break;
+            case LISTENING:
+                break;
+            case TRADE_UPDATES:
+                if (listener != null) {
+                    listener.onTradeUpdate(getGson().fromJson(messageObject, TradeUpdateMessage.class));
+                }
+                break;
+            default:
+                throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public void setListener(UpdatesListener listener) {
+        this.listener = listener;
+    }
+
+    @Override
+    public void subscribeToTradeUpdates(boolean subscribe) {
+        listenToTradeUpdates = subscribe;
+        sendTradeUpdatesListenMessage();
+    }
+
+    private void sendTradeUpdatesListenMessage() {
+        if (!isConnected()) {
+            throw new IllegalStateException("This websocket must be connected before subscribing to streams!");
+        }
+        final JsonObject requestObject = new JsonObject();
+        requestObject.addProperty("action", "listen");
+        final JsonArray streamsArray = new JsonArray();
+        if (listenToTradeUpdates) {
+            streamsArray.add(TRADE_UPDATES.toString());
+        }
+        final JsonObject dataObject = new JsonObject();
+        dataObject.add("streams", streamsArray);
+        requestObject.add("data", dataObject);
+
+        sendWebsocketMessage(requestObject.toString());
+        LOGGER.info("Requested streams: streams={}.", streamsArray);
+    }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocketInterface.java b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocketInterface.java
new file mode 100644
index 00000000..0c86554c
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocketInterface.java
@@ -0,0 +1,24 @@
+package net.jacobpeterson.alpaca.websocket.updates;
+
+import net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType;
+import net.jacobpeterson.alpaca.websocket.AlpacaWebsocketInterface;
+
+/**
+ * {@link UpdatesWebsocketInterface} is an {@link AlpacaWebsocketInterface} for {@link UpdatesWebsocket}.
+ */
+public interface UpdatesWebsocketInterface extends AlpacaWebsocketInterface {
+
+    /**
+     * Sets the {@link UpdatesListener}.
+     *
+     * @param listener the {@link UpdatesListener}
+     */
+    void setListener(UpdatesListener listener);
+
+    /**
+     * Subscribes to {@link UpdatesMessageType#TRADE_UPDATES}.
+     *
+     * @param subscribe <code>true</code> to subscribe, <code>false</code> otherwise
+     */
+    void subscribeToTradeUpdates(boolean subscribe);
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_data.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/authorization/authorization_data.json
similarity index 100%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_data.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/authorization/authorization_data.json
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/authorization/authorization_message.json
similarity index 66%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/authorization/authorization_message.json
index 82edef4d..4c991d10 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/authorization/authorization_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/authorization/authorization_message.json
@@ -1,12 +1,12 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessage"
   },
   "properties": {
     "data": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationData",
-      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.authorization.AuthorizationData}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.authorization.AuthorizationData",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.updates.model.authorization.AuthorizationData}."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_data.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_data.json
new file mode 100644
index 00000000..b2798264
--- /dev/null
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_data.json
@@ -0,0 +1,9 @@
+{
+  "type": "object",
+  "properties": {
+    "streams": {
+      "existingJavaType": "java.util.Set<net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType>",
+      "title": "The {@link java.util.Set} of {@link net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType}s."
+    }
+  }
+}
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_message.json
similarity index 69%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_message.json
index 58b7b54f..0590794f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/listening/listening_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/listening/listening_message.json
@@ -1,12 +1,12 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessage"
   },
   "properties": {
     "data": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.listening.ListeningData",
-      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.listening.ListeningData}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.listening.ListeningData",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.updates.model.listening.ListeningData}."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update.json
similarity index 90%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update.json
index ab5b6875..27f86dc2 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update.json
@@ -2,8 +2,8 @@
   "type": "object",
   "properties": {
     "event": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdateEvent",
-      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdateEvent}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.tradeupdate.TradeUpdateEvent",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.updates.model.tradeupdate.TradeUpdateEvent}."
     },
     "execution_id": {
       "existingJavaType": "java.lang.String",
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_event.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update_event.json
similarity index 100%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_event.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update_event.json
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update_message.json
similarity index 69%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update_message.json
index 6876c589..af22fa58 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/tradeupdate/trade_update_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/tradeupdate/trade_update_message.json
@@ -1,12 +1,12 @@
 {
   "type": "object",
   "extends": {
-    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessage"
+    "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessage"
   },
   "properties": {
     "data": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdate",
-      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.tradeupdate.TradeUpdate}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.tradeupdate.TradeUpdate",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.updates.model.tradeupdate.TradeUpdate}."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/updates_message.json
similarity index 69%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/updates_message.json
index fa337195..ad625676 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/updates_message.json
@@ -2,8 +2,8 @@
   "type": "object",
   "properties": {
     "stream": {
-      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType",
-      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.trades.model.TradesStreamMessageType}."
+      "existingJavaType": "net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType",
+      "title": "The {@link net.jacobpeterson.alpaca.model.websocket.updates.model.UpdatesMessageType}."
     }
   }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message_type.json b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/updates_message_type.json
similarity index 100%
rename from src/main/java/net/jacobpeterson/alpaca/websocket/trades/model/trades_stream_message_type.json
rename to src/main/java/net/jacobpeterson/alpaca/websocket/updates/model/updates_message_type.json

From ee5f1ae3fd8d1e95a8271f1d8a349b2b35ae712d Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Thu, 7 Mar 2024 22:06:54 -0800
Subject: [PATCH 65/85] Refactor REST API wrapper classes packages

---
 src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java       | 3 +++
 .../alpaca/{ => rest/broker}/AlpacaBrokerAPI.java           | 6 +++---
 .../alpaca/rest/broker/{ => events}/EventsApiSSE.java       | 2 +-
 .../alpaca/{ => rest/marketdata}/AlpacaMarketDataAPI.java   | 4 ++--
 .../alpaca/{ => rest/trader}/AlpacaTraderAPI.java           | 4 ++--
 5 files changed, 11 insertions(+), 8 deletions(-)
 rename src/main/java/net/jacobpeterson/alpaca/{ => rest/broker}/AlpacaBrokerAPI.java (97%)
 rename src/main/java/net/jacobpeterson/alpaca/rest/broker/{ => events}/EventsApiSSE.java (99%)
 rename src/main/java/net/jacobpeterson/alpaca/{ => rest/marketdata}/AlpacaMarketDataAPI.java (97%)
 rename src/main/java/net/jacobpeterson/alpaca/{ => rest/trader}/AlpacaTraderAPI.java (97%)

diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index 2ae2e22e..c1c73096 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -3,6 +3,9 @@
 import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
 import net.jacobpeterson.alpaca.model.util.apitype.MarketDataWebsocketSourceType;
 import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
+import net.jacobpeterson.alpaca.rest.broker.AlpacaBrokerAPI;
+import net.jacobpeterson.alpaca.rest.marketdata.AlpacaMarketDataAPI;
+import net.jacobpeterson.alpaca.rest.trader.AlpacaTraderAPI;
 import net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto.CryptoMarketDataWebsocket;
 import net.jacobpeterson.alpaca.websocket.marketdata.streams.crypto.CryptoMarketDataWebsocketInterface;
 import net.jacobpeterson.alpaca.websocket.marketdata.streams.news.NewsMarketDataWebsocket;
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java
similarity index 97%
rename from src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java
index 942d71b3..c2d6aa2e 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaBrokerAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca;
+package net.jacobpeterson.alpaca.rest.broker;
 
 import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
 import net.jacobpeterson.alpaca.openapi.broker.ApiClient;
@@ -19,7 +19,7 @@
 import net.jacobpeterson.alpaca.openapi.broker.api.ReportingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.TradingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.WatchlistApi;
-import net.jacobpeterson.alpaca.rest.broker.EventsApiSSE;
+import net.jacobpeterson.alpaca.rest.broker.events.EventsApiSSE;
 import okhttp3.OkHttpClient;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -61,7 +61,7 @@ public class AlpacaBrokerAPI {
      *                              instance
      */
     @SuppressWarnings("UnnecessaryDefault")
-    AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
+    public AlpacaBrokerAPI(String brokerAPIKey, String brokerAPISecret, BrokerAPIEndpointType brokerAPIEndpointType,
             OkHttpClient okHttpClient) {
         checkNotNull(brokerAPIKey);
         checkNotNull(brokerAPISecret);
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
similarity index 99%
rename from src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
index ea2341e5..5bf918de 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/EventsApiSSE.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca.rest.broker;
+package net.jacobpeterson.alpaca.rest.broker.events;
 
 import com.google.gson.reflect.TypeToken;
 import net.jacobpeterson.alpaca.openapi.broker.ApiClient;
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java b/src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java
similarity index 97%
rename from src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java
index 1728c09f..4206b485 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaMarketDataAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca;
+package net.jacobpeterson.alpaca.rest.marketdata;
 
 import net.jacobpeterson.alpaca.openapi.marketdata.ApiClient;
 import net.jacobpeterson.alpaca.openapi.marketdata.api.CorporateActionsApi;
@@ -38,7 +38,7 @@ public class AlpacaMarketDataAPI {
      * @param brokerAPISecret the Broker API secret
      * @param okHttpClient    an existing {@link OkHttpClient} or <code>null</code> to create a new default instance
      */
-    AlpacaMarketDataAPI(String traderKeyID, String traderSecretKey,
+    public AlpacaMarketDataAPI(String traderKeyID, String traderSecretKey,
             String brokerAPIKey, String brokerAPISecret, OkHttpClient okHttpClient) {
         checkArgument((traderKeyID != null && traderSecretKey != null) ^
                         (brokerAPIKey != null && brokerAPISecret != null),
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java b/src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java
similarity index 97%
rename from src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java
rename to src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java
index 07485e9c..515ba377 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaTraderAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java
@@ -1,4 +1,4 @@
-package net.jacobpeterson.alpaca;
+package net.jacobpeterson.alpaca.rest.trader;
 
 import net.jacobpeterson.alpaca.model.util.apitype.TraderAPIEndpointType;
 import net.jacobpeterson.alpaca.openapi.trader.ApiClient;
@@ -48,7 +48,7 @@ public class AlpacaTraderAPI {
      *                              instance
      */
     @SuppressWarnings("UnnecessaryDefault")
-    AlpacaTraderAPI(String traderKeyID, String traderSecretKey, String traderOAuthToken,
+    public AlpacaTraderAPI(String traderKeyID, String traderSecretKey, String traderOAuthToken,
             TraderAPIEndpointType traderAPIEndpointType, OkHttpClient okHttpClient) {
         checkArgument((traderKeyID != null && traderSecretKey != null) ^ (traderOAuthToken != null),
                 "You must specify a (trader key ID and secret key) or an (OAuth token)!");

From 09d7a15c640c10c2139211c1b6bc3220ebfc7660 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 8 Mar 2024 10:53:25 -0800
Subject: [PATCH 66/85] AlpacaWebsocket bug fix, change OpenAPI client methods
 visibility

---
 build.gradle                                     | 16 ++++++++++++++++
 .../alpaca/rest/broker/events/EventsApiSSE.java  |  4 ++--
 .../alpaca/websocket/AlpacaWebsocket.java        |  5 +++++
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/build.gradle b/build.gradle
index c6c53daa..2048c1aa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -229,6 +229,22 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
             }
         }
 
+        // Make certain method signatures in the generated client API classes 'protected' instead of 'public'
+        specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
+            for (File apiFile : Paths.get(generatedClientLibrariesPath.getPath(),
+                    it, sourceFilesPath, it.replace("-", ""), "api").toFile().listFiles()) {
+                // Don't make replacements in the broker 'events' API as that class is wrapped
+                if (apiFile.name == "EventsApi.java") {
+                    continue
+                }
+                // Make replacements
+                apiFile.text = apiFile.text
+                        .replace("public okhttp3.Call", "protected okhttp3.Call")
+                        .replace("public ApiResponse", "protected ApiResponse")
+                        .replaceAll("\\n.*public ApiClient[\\s\\S]*?customBaseUrl;\\n {4}}", "")
+            }
+        }
+
         // Remove the necessary authentication header logic from the generate client's ApiClient
         specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
             final def apiClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
index 5bf918de..65efee8f 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
@@ -169,12 +169,12 @@ public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Null
             @Override
             public void onFailure(@NotNull EventSource eventSource, @Nullable Throwable throwable,
                     @Nullable Response response) {
-                LOGGER.error("Event source failure: eventSource={} throwable={}, response={}",
-                        eventSource, throwable, response);
                 if (throwable != null && throwable.getMessage().equals("canceled")) {
                     sseListener.onClose();
                     return;
                 }
+                LOGGER.error("Event source failure: eventSource={} throwable={}, response={}",
+                        eventSource, throwable, response);
                 sseListener.onError(throwable, response);
             }
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index 518435c2..e4440fbb 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -147,6 +147,11 @@ public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String rea
 
     @Override
     public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable cause, @Nullable Response response) {
+        if (intentionalClose) {
+            onClosed(webSocket, WEBSOCKET_NORMAL_CLOSURE_CODE, WEBSOCKET_NORMAL_CLOSURE_MESSAGE);
+            return;
+        }
+
         LOGGER.error("{} websocket failure!", websocketName, cause);
         // A websocket failure occurs when either there is a connection failure or when the client throws
         // an exception when receiving a message. In either case, OkHttp will close the websocket connection,

From f80a4292f78bb006fc106d0c3c40cc3b6e722013 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 8 Mar 2024 10:56:21 -0800
Subject: [PATCH 67/85] Update README

---
 README.md | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 196 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 3aa3cfcc..25c7d6ff 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 </p>
 
 # Overview
-This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/).
+This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/). This library strives to provide the complete Alpaca API as a Java client implementation, so open a [new issue](https://github.com/Petersoj/alpaca-java/issues) or [new pull request](https://github.com/Petersoj/alpaca-java/pulls) if you find something missing.
 
 Give this repository a star ⭐ if it helped you build a trading algorithm in Java!
 
@@ -33,6 +33,9 @@ Note that you don't have to use the Maven Central artifacts. Instead, you can cl
 # Logger
 For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Logback](http://logback.qos.ch/), [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
 
+# `BigDecimal` vs. `Double`
+It is generally considered bad practice to represent currency values in floating-point data types such as `float` or `double` because it can lead to [rounding errors](https://ta4j.github.io/ta4j-wiki/Num.html) and [precision loss](https://stackoverflow.com/a/3730040/4352701) in calculations. However, using floating-point data types can have significant performance benefits compared to using arbitrary-precision number data types, especially in a quantitative finance and algorithmic trading environment. Because of this, `alpaca-java` uses the `Double` data type (the `double` boxed type) when using the Market Data APIs and the `BigDecimal` type when using the Trading or Broker APIs. The thinking behind this is that exact decimal quantities are important when placing real trades with real money, but less important when performing calculations of financial indicators. The best solution would be to use the [TA4j](https://github.com/ta4j/ta4j) `Num` interface so that you can decide what data type to use based on your use case, but that is on the [TODO list](#todo) for now.
+
 # Examples
 Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java) for all classes and method signatures.
 
@@ -42,25 +45,204 @@ Note that the examples below are not exhaustive. Refer to the [Javadoc](https://
 The Alpaca API documentation is located [here](https://docs.alpaca.markets/) and the [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java) Javadoc is located [here](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/AlpacaAPI.html).
 
 Example usage:
+
+Use this code if you are using the Trading or Market Data APIs for a single Alpaca account:
 ```java
-// TODO
+final String keyID = "<your_key_id>";
+final String secretKey = "<your_secret_key>";
+final TraderAPIEndpointType endpointType = TraderAPIEndpointType.PAPER; // or 'LIVE'
+final MarketDataWebsocketSourceType sourceType = MarketDataWebsocketSourceType.IEX; // or 'SIP'
+final AlpacaAPI alpacaAPI = new AlpacaAPI(keyID, secretKey, endpointType, sourceType);
 ```
 
-Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests via a connection pool. These threads persist even if the main thread exits, so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java), call `alpacaAPI.closeOkHttpClient();`.
+Use this code if you are using the Trading API with OAuth to act on behalf of an Alpaca account:
+```java
+final String oAuthToken = "<an_oauth_token>";
+final TraderAPIEndpointType endpointType = TraderAPIEndpointType.PAPER; // or 'LIVE'
+final AlpacaAPI alpacaAPI = new AlpacaAPI(oAuthToken, endpointType);
+```
 
-## Trading API
+Use this code if you are using the Broker API:
 ```java
-// TODO
+final String brokerAPIKey = "<your_broker_api_key>";
+final String brokerAPISecret = "<your_broker_api_secret>";
+final BrokerAPIEndpointType endpointType = BrokerAPIEndpointType.SANDBOX; // or 'PRODUCTION'
+final AlpacaAPI alpacaAPI = new AlpacaAPI(brokerAPIKey, brokerAPISecret, endpointType);
 ```
 
-## Market Data API
+Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests via a connection pool. These threads persist even if the main thread exits, so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java), use `alpacaAPI.closeOkHttpClient();`.
+
+## [`Trader API`](src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java)
+The [`Trader API`](src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java) is used for placing trades, updating account details, getting open positions, and more. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.html) for a list of all available method signatures.
+
+Example usage:
 ```java
-// TODO
+// Place a market order to buy one share of Apple
+final Order openingOrder = alpacaAPI.trader().orders()
+        .postOrder(new PostOrderRequest()
+                .symbol("AAPL")
+                .qty("1")
+                .side(OrderSide.BUY)
+                .type(OrderType.MARKET)
+                .timeInForce(TimeInForce.GTC));
+System.out.println("Opening Apple order: " + openingOrder);
+
+// Wait for massive gains
+Thread.sleep(10_000);
+
+// Close the Apple position
+Order closingOrder = alpacaAPI.trader().positions()
+        .deleteOpenPosition("AAPL", null, new BigDecimal("100"));
+System.out.println("Closing Apple order: " + openingOrder);
+
+// Wait for closing trade to fill
+Thread.sleep(10_000);
+
+// Print out PnL
+final String openFillPrice = alpacaAPI.trader().orders()
+        .getOrderByOrderID(UUID.fromString(openingOrder.getId()), false)
+        .getFilledAvgPrice();
+final String closeFillPrice = alpacaAPI.trader().orders()
+        .getOrderByOrderID(UUID.fromString(closingOrder.getId()), false)
+        .getFilledAvgPrice();
+System.out.println("PnL from Apple trade: " +
+        new BigDecimal(closeFillPrice).subtract(new BigDecimal(openFillPrice)));
 ```
 
-## Broker API
+## [`Market Data API`](src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java)
+The [`Market Data API`](src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java) is used getting market data for stocks, cryptocurrencies, options, and more. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.html) for a list of all available method signatures.
+
+Example usage:
 ```java
-// TODO
+// Print out the latest Tesla trade
+final StockTrade latestTSLATrade = alpacaAPI.marketData().stock()
+        .stockLatestTradeSingle("TSLA", StockFeed.IEX, null).getTrade();
+System.out.printf("Latest TSLA trade: price=%s, size=%s\n",
+        latestTSLATrade.getP(), latestTSLATrade.getS());
+
+// Print out the highest Bitcoin ask price on the order book
+final CryptoOrderbook latestBitcoinOrderBooks = alpacaAPI.marketData().crypto()
+        .cryptoLatestOrderbooks(CryptoLoc.US, "BTC/USD").getOrderbooks().get("BTC/USD");
+final Double highestBitcoinAskPrice = latestBitcoinOrderBooks.getA().stream()
+        .map(CryptoOrderbookEntry::getP)
+        .max(Double::compare)
+        .orElse(null);
+System.out.println("Bitcoin highest ask price: " + highestBitcoinAskPrice);
+
+// Print out the latest Microsoft option trade
+final String latestMSFTOptionTrade = alpacaAPI.marketData().option()
+        .optionChain("MSFT").getSnapshots().entrySet().stream()
+        .filter(entry -> entry.getValue().getLatestTrade() != null)
+        .map(entry -> Map.entry(entry.getKey(), entry.getValue().getLatestTrade()))
+        .max(Comparator.comparing(entry -> entry.getValue().getT()))
+        .map(entry -> String.format("ticker=%s, time=%s, price=%s",
+                entry.getKey(), entry.getValue().getT(), entry.getValue().getP()))
+        .orElse(null);
+System.out.println("Latest Microsoft option trade: " + latestMSFTOptionTrade);
+```
+
+## [`Broker API`](src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java)
+The [`Broker API`](src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java) is used for creating new Alpaca accounts for your end users, funding their accounts, placing trades, and more. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.html) for a list of all available method signatures.
+
+Example usage:
+```java
+// Listen to the trade events (SSE) and print them out
+final SSERequest sseRequest = alpacaAPI.broker().events()
+        .subscribeToTradeV2(null, null, null, null, new SSEListenerAdapter<>());
+
+// Wait for SSE channel to be ready
+Thread.sleep(2000);
+
+// Buy one share of GME for an account
+alpacaAPI.broker().trading()
+        .createOrderForAccount(UUID.fromString("<some_account_uuid>"),
+                new CreateOrderRequest()
+                        .symbol("GME")
+                        .qty(BigDecimal.ONE)
+                        .side(OrderSide.SELL)
+                        .timeInForce(TimeInForce.GTC)
+                        .type(OrderType.MARKET));
+// Wait to be filled
+Thread.sleep(2000);
+
+// Close the SSE stream and the OkHttpClient to exit cleanly
+sseRequest.close();
+alpacaAPI.closeOkHttpClient();
+```
+
+## [`Updates Stream`](src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java)
+The [`Updates Stream`](src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java) is used for listening to trade updates in realtime. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocketInterface.html) for a list of all available method signatures.
+
+Example usage:
+```java
+// Connect to the 'updates' stream and wait until it's authorized
+alpacaAPI.updatesStream().connect();
+if (!alpacaAPI.updatesStream().waitForAuthorization(5, TimeUnit.SECONDS)) {
+    throw new RuntimeException();
+}
+
+// Print out trade updates
+alpacaAPI.updatesStream().setListener(System.out::println);
+alpacaAPI.updatesStream().subscribeToTradeUpdates(true);
+
+// Place a trade
+alpacaAPI.trader().orders().postOrder(new PostOrderRequest()
+        .symbol("AAPL")
+        .qty("1")
+        .side(OrderSide.BUY)
+        .type(OrderType.MARKET)
+        .timeInForce(TimeInForce.GTC));
+
+// Wait a few seconds
+Thread.sleep(5000);
+
+// Close the trade
+alpacaAPI.trader().positions().deleteAllOpenPositions(true);
+
+// Wait a few seconds
+Thread.sleep(5000);
+
+// Disconnect the 'updates' stream and exit cleanly
+alpacaAPI.updatesStream().disconnect();
+alpacaAPI.closeOkHttpClient();
+```
+
+## [`Market Data Stream`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java)
+The [`Market Data Stream`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java) is used for listening to stock market data, crypto market data, and news data in realtime. Refer to the Javadocs ([stock](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/websocket/marketdata/streams/stock/StockMarketDataWebsocketInterface.html), [crypto](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/CryptoMarketDataWebsocketInterface.html), [news](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/websocket/marketdata/streams/news/NewsMarketDataWebsocketInterface.html)) for a list of all available method signatures.
+
+Example usage:
+```java
+// Connect to the 'stock market data' stream and wait until it's authorized
+alpacaAPI.stockMarketDataStream().connect();
+if (!alpacaAPI.stockMarketDataStream().waitForAuthorization(5, TimeUnit.SECONDS)) {
+    throw new RuntimeException();
+}
+
+// Print out trade messages
+alpacaAPI.stockMarketDataStream().setListener(new StockMarketDataListenerAdapter() {
+    @Override
+    public void onTrade(StockTradeMessage trade) {
+        System.out.println("Received trade: " + trade);
+    }
+});
+
+// Subscribe to AAPL trades
+System.out.println("Subscribed to Apple trades.");
+alpacaAPI.stockMarketDataStream().setTradeSubscriptions(Set.of("AAPL"));
+
+// Wait a few seconds
+Thread.sleep(5000);
+
+// Unsubscribe from AAPL and subscribe to TSLA and MSFT
+System.out.println("Subscribed to Tesla and Microsoft trades.");
+alpacaAPI.stockMarketDataStream().setTradeSubscriptions(Set.of("TSLA", "MSFT"));
+
+// Wait a few seconds
+Thread.sleep(5000);
+
+// Disconnect the 'stock market data' stream and exit cleanly
+alpacaAPI.stockMarketDataStream().disconnect();
+alpacaAPI.closeOkHttpClient();
 ```
 
 # Building
@@ -75,12 +257,13 @@ To install the built artifacts to your local Maven repository on your machine (t
 ```
 
 # TODO
-- Implement better reconnect logic for Websockets and SSE streaming
-- Implement Unit Testing for REST API and Websocket streaming (both live and mocked)
+- Implement better reconnect logic for Websockets and SSE streaming.
+- Implement Unit Testing for REST API and Websocket streaming (both live and mocked).
+- Use [TA4j](https://github.com/ta4j/ta4j) `Num` interface instead of `Double` or `BigDecimal` for number data types so that users can use either `Double` or `BigDecimal` for performance or precision in price data.
 
 # Contributing
 Contributions are welcome!
 
-Do the following before starting your Pull Request:
+Do the following before starting your pull request:
 1. Create a new branch in your forked repository for your feature or bug fix instead of committing directly to the `master` branch in your fork.
-2. Use the `dev` branch as the base branch in your Pull Request.
+2. Use the `dev` branch as the base branch in your pull request.

From a20ba8f8f526e5de954416054907bfa1802f817f Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 8 Mar 2024 11:05:03 -0800
Subject: [PATCH 68/85] Consolidate throwing exception for disconnected
 websockets

---
 README.md                                                      | 1 +
 .../net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java    | 3 +++
 .../alpaca/websocket/updates/UpdatesWebsocket.java             | 3 ---
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 25c7d6ff..d3b6dace 100644
--- a/README.md
+++ b/README.md
@@ -162,6 +162,7 @@ alpacaAPI.broker().trading()
                         .side(OrderSide.SELL)
                         .timeInForce(TimeInForce.GTC)
                         .type(OrderType.MARKET));
+
 // Wait to be filled
 Thread.sleep(2000);
 
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
index e4440fbb..d6f920c2 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/AlpacaWebsocket.java
@@ -209,6 +209,9 @@ protected void cleanupState() {
      * @param message the message
      */
     protected void sendWebsocketMessage(String message) {
+        if (!isConnected()) {
+            throw new IllegalStateException("This websocket must be connected before send a message!");
+        }
         LOGGER.trace("Websocket message sent: {}", message);
         websocket.send(message);
     }
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java
index 99e1db42..ae31a623 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java
@@ -153,9 +153,6 @@ public void subscribeToTradeUpdates(boolean subscribe) {
     }
 
     private void sendTradeUpdatesListenMessage() {
-        if (!isConnected()) {
-            throw new IllegalStateException("This websocket must be connected before subscribing to streams!");
-        }
         final JsonObject requestObject = new JsonObject();
         requestObject.addProperty("action", "listen");
         final JsonArray streamsArray = new JsonArray();

From b34f8dd0e2449eec65401792fb988d79a4ab42b3 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 8 Mar 2024 11:07:52 -0800
Subject: [PATCH 69/85] Undo premature version number change

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index d3b6dace..dade8336 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Give this repository a star ⭐ if it helped you build a trading algorithm in Ja
 If you are using Gradle as your build tool, add the following dependency to your `build.gradle` file:
 
 ```
-implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "10.0.0"
+implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "9.2.0"
 ```
 
 If you are using Maven as your build tool, add the following dependency to your `pom.xml` file:
@@ -24,7 +24,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson.alpaca</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>10.0.0</version>
+    <version>9.2.0</version>
 </dependency>
 ```
 
diff --git a/build.gradle b/build.gradle
index 2048c1aa..08d8e81d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,7 +22,7 @@ plugins {
 
 final def projectGroup = "net.jacobpeterson.alpaca"
 final def projectArtifactID = "alpaca-java"
-final def projectVersion = "10.0.0-SNAPSHOT"
+final def projectVersion = "9.2.0-SNAPSHOT"
 
 group = projectGroup
 version = projectVersion

From 32495432b5ee3e49c48fdf61ee2c7a1ae59ee9a3 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 8 Mar 2024 11:08:33 -0800
Subject: [PATCH 70/85] Update to 10.0.0

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index dade8336..d3b6dace 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Give this repository a star ⭐ if it helped you build a trading algorithm in Ja
 If you are using Gradle as your build tool, add the following dependency to your `build.gradle` file:
 
 ```
-implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "9.2.0"
+implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "10.0.0"
 ```
 
 If you are using Maven as your build tool, add the following dependency to your `pom.xml` file:
@@ -24,7 +24,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson.alpaca</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>9.2.0</version>
+    <version>10.0.0</version>
 </dependency>
 ```
 
diff --git a/build.gradle b/build.gradle
index 08d8e81d..2048c1aa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,7 +22,7 @@ plugins {
 
 final def projectGroup = "net.jacobpeterson.alpaca"
 final def projectArtifactID = "alpaca-java"
-final def projectVersion = "9.2.0-SNAPSHOT"
+final def projectVersion = "10.0.0-SNAPSHOT"
 
 group = projectGroup
 version = projectVersion

From 5b9f9e2b20a931d9c108a68d0d999abe9b95c2fb Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Fri, 8 Mar 2024 12:27:15 -0800
Subject: [PATCH 71/85] Minor update to README

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index d3b6dace..0ad65e93 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Note that you don't have to use the Maven Central artifacts. Instead, you can cl
 For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Logback](http://logback.qos.ch/), [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
 
 # `BigDecimal` vs. `Double`
-It is generally considered bad practice to represent currency values in floating-point data types such as `float` or `double` because it can lead to [rounding errors](https://ta4j.github.io/ta4j-wiki/Num.html) and [precision loss](https://stackoverflow.com/a/3730040/4352701) in calculations. However, using floating-point data types can have significant performance benefits compared to using arbitrary-precision number data types, especially in a quantitative finance and algorithmic trading environment. Because of this, `alpaca-java` uses the `Double` data type (the `double` boxed type) when using the Market Data APIs and the `BigDecimal` type when using the Trading or Broker APIs. The thinking behind this is that exact decimal quantities are important when placing real trades with real money, but less important when performing calculations of financial indicators. The best solution would be to use the [TA4j](https://github.com/ta4j/ta4j) `Num` interface so that you can decide what data type to use based on your use case, but that is on the [TODO list](#todo) for now.
+It is generally considered bad practice to represent currency values in floating-point data types such as `float` or `double` because it can lead to [rounding errors](https://ta4j.github.io/ta4j-wiki/Num.html) and [precision loss](https://stackoverflow.com/a/3730040/4352701) in calculations. However, using floating-point data types can have significant performance benefits compared to using arbitrary-precision number data types, especially in a quantitative finance and algorithmic trading environment. Because of this, `alpaca-java` uses the `Double` data type (the `double` boxed type) when using the Market Data APIs and the `BigDecimal` data type when using the Trading or Broker APIs. The thinking behind this is that exact decimal quantities are important when placing real trades with real money, but less important when performing calculations of financial indicators. The best solution would be to use the [TA4j](https://github.com/ta4j/ta4j) `Num` interface so that you can decide what data type to use based on your use case, but that is on the [TODO list](#todo) for now. By the way, using `BigDecimal` wouldn't matter if Alpaca used floating-point data types in their internal systems or in their REST APIs, but the fact that they use `String` data types in some of the REST API JSON responses and their Trading and Broker OpenAPI specifications don't use the `double` data type, this leads me to believe that using `BigDecimal` does actually matter. 
 
 # Examples
 Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java) for all classes and method signatures.
@@ -228,15 +228,15 @@ alpacaAPI.stockMarketDataStream().setListener(new StockMarketDataListenerAdapter
 });
 
 // Subscribe to AAPL trades
-System.out.println("Subscribed to Apple trades.");
 alpacaAPI.stockMarketDataStream().setTradeSubscriptions(Set.of("AAPL"));
+System.out.println("Subscribed to Apple trades.");
 
 // Wait a few seconds
 Thread.sleep(5000);
 
 // Unsubscribe from AAPL and subscribe to TSLA and MSFT
-System.out.println("Subscribed to Tesla and Microsoft trades.");
 alpacaAPI.stockMarketDataStream().setTradeSubscriptions(Set.of("TSLA", "MSFT"));
+System.out.println("Subscribed to Tesla and Microsoft trades.");
 
 // Wait a few seconds
 Thread.sleep(5000);

From 933c28f521d33c3d1681d738840693501a6bac44 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sat, 9 Mar 2024 08:38:58 -0800
Subject: [PATCH 72/85] Fix OpenAPI generated client 'anyOf' and 'oneOf' issues

---
 build.gradle                                  | 20 ++++++++---
 .../alpaca/rest/broker/AlpacaBrokerAPI.java   | 33 +++++++++++++++++++
 .../alpaca/rest/trader/AlpacaTraderAPI.java   |  9 +++++
 3 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/build.gradle b/build.gradle
index 2048c1aa..7eadeb3c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -220,10 +220,15 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
                 modelFile.text = modelFile.text
                         .replaceAll("/\\*\\*\\n {2}\\* Validates the JSON Element and throws an exception if issues " +
                                 "found[\\s\\S]*?\\*/", "")
+                        .replace("import java.io.IOException;",
+                                "import java.io.IOException;\nimport java.util.function.Predicate;")
                         .replace("public static void validateJsonElement(JsonElement jsonElement) throws IOException {",
-                                "public static boolean validate = false;\n    public static void " +
-                                        "validateJsonElement(JsonElement jsonElement) throws IOException {\n" +
-                                        "      if (!validate) return;")
+                                "public static boolean validate = false;\n" +
+                                        "    public static Predicate<JsonElement> isValid = jsonElement -> true;\n" +
+                                        "    public static void validateJsonElement(JsonElement jsonElement) " +
+                                        "throws IOException {\n" +
+                                        "      if (!isValid.test(jsonElement)) throw new RuntimeException();\n" +
+                                        "      if (!validate) return;\n")
                         .replace("throw new IllegalArgumentException(\"Unexpected value '\" + value + \"'\");",
                                 "return null;")
             }
@@ -255,12 +260,19 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
                     .replaceAll("for \\(String authName : authNames\\)[\\s\\S]*?uri\\);\\n. {7}}", "")
         }
 
-        // Make the 'JSON' classes of the generate clients have a static Gson initializer
+        // Make the 'JSON' classes of the generate clients have a static Gson initializer. Also fix the OffsetDateTime
+        // Gson type adapter to accommodate ISO local dates.
         specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
             final def jsonClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
                     it.replace("-", ""), "JSON.java").toFile()
             jsonClientFile.text = jsonClientFile.text
                     .replaceAll("\\{\\n {8}GsonBuilder", "static {\n        GsonBuilder")
+                    .replaceAll("}[\\s\\S].*return OffsetDateTime\\.parse\\(date, formatter\\);",
+                            "} else if (!date.contains(\"T\")) {\n" +
+                                    "return LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE)" +
+                                    ".atTime(java.time.LocalTime.MIN.atOffset(java.time.ZoneOffset.UTC));\n" +
+                                    "}\n" +
+                                    "return OffsetDateTime.parse(date, formatter);")
         }
     }
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java
index c2d6aa2e..cfbd3e75 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/AlpacaBrokerAPI.java
@@ -1,5 +1,6 @@
 package net.jacobpeterson.alpaca.rest.broker;
 
+import com.google.gson.JsonElement;
 import net.jacobpeterson.alpaca.model.util.apitype.BrokerAPIEndpointType;
 import net.jacobpeterson.alpaca.openapi.broker.ApiClient;
 import net.jacobpeterson.alpaca.openapi.broker.api.AccountsApi;
@@ -19,9 +20,18 @@
 import net.jacobpeterson.alpaca.openapi.broker.api.ReportingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.TradingApi;
 import net.jacobpeterson.alpaca.openapi.broker.api.WatchlistApi;
+import net.jacobpeterson.alpaca.openapi.broker.model.AdminActionLegacyNote;
+import net.jacobpeterson.alpaca.openapi.broker.model.AdminActionLiquidation;
+import net.jacobpeterson.alpaca.openapi.broker.model.AdminActionTransactionCancel;
+import net.jacobpeterson.alpaca.openapi.broker.model.JNLC;
+import net.jacobpeterson.alpaca.openapi.broker.model.JNLS;
+import net.jacobpeterson.alpaca.openapi.broker.model.NonTradeActivity;
+import net.jacobpeterson.alpaca.openapi.broker.model.TradeActivity;
 import net.jacobpeterson.alpaca.rest.broker.events.EventsApiSSE;
 import okhttp3.OkHttpClient;
 
+import java.util.function.BiFunction;
+
 import static com.google.common.base.Preconditions.checkNotNull;
 import static net.jacobpeterson.alpaca.util.apikey.APIKeyUtil.createBrokerAPIAuthKey;
 
@@ -31,6 +41,29 @@
  */
 public class AlpacaBrokerAPI {
 
+    // Set validation predicates for 'anyOf' and 'oneOf' models
+    static {
+        // 'AccountsApi' models
+        TradeActivity.isValid = jsonElement -> jsonElement.getAsJsonObject().has("type");
+        NonTradeActivity.isValid = jsonElement -> !jsonElement.getAsJsonObject().has("type");
+
+        // 'JournalsApi' models
+        final BiFunction<Boolean, JsonElement, Boolean> transferPredicate = (jnlc, jsonElement) -> {
+            final String entryType = jsonElement.getAsJsonObject().get("entry_type").getAsString();
+            return jnlc ? entryType.equals("JNLC") : entryType.equals("JNLS");
+        };
+        JNLS.isValid = jsonElement -> transferPredicate.apply(true, jsonElement);
+        JNLC.isValid = jsonElement -> transferPredicate.apply(false, jsonElement);
+
+        // 'EventsApi' models
+        AdminActionLegacyNote.isValid = jsonElement -> jsonElement.getAsJsonObject()
+                .get("type").getAsString().contains("note_admin_event");
+        AdminActionLiquidation.isValid = jsonElement -> jsonElement.getAsJsonObject()
+                .get("type").getAsString().equals("liquidation_admin_event");
+        AdminActionTransactionCancel.isValid = jsonElement -> jsonElement.getAsJsonObject()
+                .get("type").getAsString().equals("transaction_cancel_admin_event");
+    }
+
     private final ApiClient apiClient;
     private AccountsApi accounts;
     private AssetsApi assets;
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java b/src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java
index 515ba377..7b560a26 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java
@@ -13,6 +13,8 @@
 import net.jacobpeterson.alpaca.openapi.trader.api.PortfolioHistoryApi;
 import net.jacobpeterson.alpaca.openapi.trader.api.PositionsApi;
 import net.jacobpeterson.alpaca.openapi.trader.api.WatchlistsApi;
+import net.jacobpeterson.alpaca.openapi.trader.model.NonTradeActivities;
+import net.jacobpeterson.alpaca.openapi.trader.model.TradingActivities;
 import okhttp3.OkHttpClient;
 
 import static com.google.common.base.Preconditions.checkArgument;
@@ -24,6 +26,13 @@
  */
 public class AlpacaTraderAPI {
 
+    // Set validation predicates for 'anyOf' and 'oneOf' models
+    static {
+        // 'AccountConfigurationsApi' models
+        TradingActivities.isValid = jsonElement -> jsonElement.getAsJsonObject().has("type");
+        NonTradeActivities.isValid = jsonElement -> !jsonElement.getAsJsonObject().has("type");
+    }
+
     private final ApiClient apiClient;
     private AccountActivitiesApi accountActivities;
     private AccountConfigurationsApi accountConfigurations;

From 5d205a7cfad21a11d7b14e28b47b4b70fd5a3364 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 10 Mar 2024 20:10:44 -0700
Subject: [PATCH 73/85] Minor update to README

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 0ad65e93..c7f1cef2 100644
--- a/README.md
+++ b/README.md
@@ -91,7 +91,7 @@ System.out.println("Opening Apple order: " + openingOrder);
 Thread.sleep(10_000);
 
 // Close the Apple position
-Order closingOrder = alpacaAPI.trader().positions()
+final Order closingOrder = alpacaAPI.trader().positions()
         .deleteOpenPosition("AAPL", null, new BigDecimal("100"));
 System.out.println("Closing Apple order: " + openingOrder);
 

From ab2225f88f224cad8a95e39326b97e7f246bddd5 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 12 Mar 2024 12:10:38 -0700
Subject: [PATCH 74/85] Update websocket `CryptoOrderBookMessage` to add `r`
 boolean

---
 README.md                                                    | 2 +-
 .../crypto/model/orderbook/crypto_order_book_message.json    | 5 +++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index c7f1cef2..6f855358 100644
--- a/README.md
+++ b/README.md
@@ -110,7 +110,7 @@ System.out.println("PnL from Apple trade: " +
 ```
 
 ## [`Market Data API`](src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java)
-The [`Market Data API`](src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java) is used getting market data for stocks, cryptocurrencies, options, and more. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.html) for a list of all available method signatures.
+The [`Market Data API`](src/main/java/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.java) is used for getting market data for stocks, cryptocurrencies, options, and more. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/rest/marketdata/AlpacaMarketDataAPI.html) for a list of all available method signatures.
 
 Example usage:
 ```java
diff --git a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json
index 76c632fa..79814a8a 100644
--- a/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json
+++ b/src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/streams/crypto/model/orderbook/crypto_order_book_message.json
@@ -23,6 +23,11 @@
       "existingJavaType": "java.time.OffsetDateTime",
       "javaName": "timestamp",
       "title": "The timestamp with nanosecond precision."
+    },
+    "r": {
+      "existingJavaType": "java.lang.Boolean",
+      "javaName": "reset",
+      "title": "Whether this particular {@link net.jacobpeterson.alpaca.model.websocket.marketdata.streams.crypto.model.orderbook.CryptoOrderBookMessage} contains the whole order book and not just an update. Typically <code>true</code> for the first message and <code>false</code> for subsequent messages."
     }
   }
 }

From c8d41b72161c8827dc331fe246c8d6de198b7b3c Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sat, 23 Mar 2024 18:57:10 -0700
Subject: [PATCH 75/85] Minor fixes/changes

---
 README.md                                                     | 2 +-
 build.gradle                                                  | 4 ++--
 .../java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 6f855358..e91ae098 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 </p>
 
 # Overview
-This library is a Java client implementation of the <a href="https://alpaca.markets/">Alpaca</a> API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/v1.1/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/). This library strives to provide the complete Alpaca API as a Java client implementation, so open a [new issue](https://github.com/Petersoj/alpaca-java/issues) or [new pull request](https://github.com/Petersoj/alpaca-java/pulls) if you find something missing.
+This library is a Java client implementation of the [Alpaca](https://alpaca.markets) API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/). This library strives to provide the complete Alpaca API as a Java client implementation, so open a [new issue](https://github.com/Petersoj/alpaca-java/issues) or [new pull request](https://github.com/Petersoj/alpaca-java/pulls) if you find something missing.
 
 Give this repository a star ⭐ if it helped you build a trading algorithm in Java!
 
diff --git a/build.gradle b/build.gradle
index 7eadeb3c..6009b4a1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -87,7 +87,7 @@ javadoc {
 // BEGIN Alpaca OpenAPI Specification (OAS) client generation
 //
 
-// These IDs come from the URLs on this page: https://docs.alpaca.markets/v1.1/openapi
+// These IDs come from the URLs on this page: https://docs.alpaca.markets/openapi
 final def specIDsOfFileNames = Map.of(
         "trader.json", "657760422129f9005cf4bd58",
         "broker.json", "657771fc2471d20070183049",
@@ -107,7 +107,7 @@ def downloadOpenAPISpecFilesTask = tasks.register("downloadOpenAPISpecFiles") {
         // Download spec files
         specIDsOfFileNames.forEach { fileName, alpacaSpecID ->
             final def outputFile = new File(specDownloadPath, fileName)
-            final def specURL = "https://docs.alpaca.markets/v1.1/openapi/${alpacaSpecID}"
+            final def specURL = "https://docs.alpaca.markets/openapi/${alpacaSpecID}"
             outputFile.getParentFile().mkdirs()
             try {
                 logger.info("Downloading OpenAPI spec file from: {}", specURL)
diff --git a/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java b/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java
index 29de58ec..c9d0ce07 100644
--- a/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java
+++ b/src/main/java/net/jacobpeterson/alpaca/util/apikey/APIKeyUtil.java
@@ -15,7 +15,7 @@ public class APIKeyUtil {
      *
      * @return the key {@link String}
      *
-     * @see <a href="https://docs.alpaca.markets/v1.1/docs/getting-started-with-broker-api#api-keys">Alpaca Docs</a>
+     * @see <a href="https://docs.alpaca.markets/docs/getting-started-with-broker-api#api-keys">Alpaca Docs</a>
      */
     public static String createBrokerAPIAuthKey(String brokerAPIKey, String brokerAPISecret) {
         return Base64.getEncoder().encodeToString((brokerAPIKey + ":" + brokerAPISecret).getBytes());

From ea620b890c7083da0db4c5a40b3abf1ed1ba294e Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sat, 23 Mar 2024 18:57:55 -0700
Subject: [PATCH 76/85] Update to 10.0.1

---
 README.md    | 4 ++--
 build.gradle | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index e91ae098..fdb5fc2d 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Give this repository a star ⭐ if it helped you build a trading algorithm in Ja
 If you are using Gradle as your build tool, add the following dependency to your `build.gradle` file:
 
 ```
-implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "10.0.0"
+implementation group: "net.jacobpeterson.alpaca", name: "alpaca-java", version: "10.0.1"
 ```
 
 If you are using Maven as your build tool, add the following dependency to your `pom.xml` file:
@@ -24,7 +24,7 @@ If you are using Maven as your build tool, add the following dependency to your
 <dependency>
     <groupId>net.jacobpeterson.alpaca</groupId>
     <artifactId>alpaca-java</artifactId>
-    <version>10.0.0</version>
+    <version>10.0.1</version>
 </dependency>
 ```
 
diff --git a/build.gradle b/build.gradle
index 6009b4a1..5dc0a489 100644
--- a/build.gradle
+++ b/build.gradle
@@ -22,7 +22,7 @@ plugins {
 
 final def projectGroup = "net.jacobpeterson.alpaca"
 final def projectArtifactID = "alpaca-java"
-final def projectVersion = "10.0.0-SNAPSHOT"
+final def projectVersion = "10.0.1-SNAPSHOT"
 
 group = projectGroup
 version = projectVersion

From a937af46cf1396a217501f597659be7cc11648ed Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 14 Apr 2024 10:19:52 -0700
Subject: [PATCH 77/85] Fix README logo URL, remove unused variable

---
 README.md                                                       | 2 +-
 .../plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy   | 2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/README.md b/README.md
index fdb5fc2d..449b8c23 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-<p align="center"><a href="https://petersoj.github.io/alpaca-java/" target="_blank"><img src=".github/image/logo.png" alt="Logo"></a></p>
+<p align="center"><a href="https://petersoj.github.io/alpaca-java/" target="_blank"><img src="https://raw.githubusercontent.com/Petersoj/alpaca-java/master/.github/image/logo.png" alt="Logo"></a></p>
 <p align="center">
     <a href="https://github.com/Petersoj/alpaca-java" target="_blank"><img alt="GitHub Repository" src="https://img.shields.io/badge/GitHub-000000?logo=github"></a>
     <a href="https://search.maven.org/artifact/net.jacobpeterson.alpaca/alpaca-java" target="_blank"><img alt="Maven Central" src="https://img.shields.io/maven-central/v/net.jacobpeterson.alpaca/alpaca-java"></a>
diff --git a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy
index 698a30b9..d4dfd8fd 100644
--- a/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy
+++ b/buildSrc/src/main/groovy/net/jacobpeterson/alpaca/buildsrc/plugin/jsonschema2pojoadapted/JSONSchemaFileConfig.groovy
@@ -7,7 +7,6 @@ class JSONSchemaFileConfig {
 
     private File sourceFile
     private String targetPackage
-    private String outputFilePath
 
     /**
      * Instantiates a new {@link JSONSchemaFileConfig}.
@@ -18,7 +17,6 @@ class JSONSchemaFileConfig {
     JSONSchemaFileConfig(File sourceFile, String targetPackage) {
         this.sourceFile = sourceFile
         this.targetPackage = targetPackage
-        this.outputFilePath = outputFilePath
     }
 
     File getSourceFile() {

From 125c78003d17c3a434085838cc4a06985b3fc354 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 2 Jul 2024 19:44:37 -0700
Subject: [PATCH 78/85] Minor updates to README

---
 README.md                                             | 10 +++-------
 src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java | 10 +---------
 2 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index 449b8c23..a7a4efb9 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 </p>
 
 # Overview
-This library is a Java client implementation of the [Alpaca](https://alpaca.markets) API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specifications](https://docs.alpaca.markets/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/). This library strives to provide the complete Alpaca API as a Java client implementation, so open a [new issue](https://github.com/Petersoj/alpaca-java/issues) or [new pull request](https://github.com/Petersoj/alpaca-java/pulls) if you find something missing.
+This library is a Java client implementation of the [Alpaca](https://alpaca.markets) API. Alpaca lets you trade with algorithms, connect with apps, and build services all with a commission-free trading API for stocks, crypto, and options. This library uses the [Alpaca OpenAPI Specification](https://docs.alpaca.markets/openapi) to generate clients for the REST API with the [OkHttp](https://square.github.io/okhttp/) library, but implements the websocket and SSE streaming interface using a custom implementation with the [OkHttp](https://square.github.io/okhttp/) library. This library is community developed and if you have any questions, please ask them on [Github Discussions](https://github.com/Petersoj/alpaca-java/discussions), the [Alpaca Slack #dev-alpaca-java channel](https://alpaca.markets/slack), or on the [Alpaca Forums](https://forum.alpaca.markets/). This library strives to provide the complete Alpaca API as a Java client implementation, so open a [new issue](https://github.com/Petersoj/alpaca-java/issues) or [new pull request](https://github.com/Petersoj/alpaca-java/pulls) if you find something missing.
 
 Give this repository a star ⭐ if it helped you build a trading algorithm in Java!
 
@@ -34,7 +34,7 @@ Note that you don't have to use the Maven Central artifacts. Instead, you can cl
 For logging, this library uses [SLF4j](http://www.slf4j.org/) which serves as an interface for various logging frameworks. This enables you to use whatever logging framework you would like. However, if you do not add a logging framework as a dependency in your project, the console will output a message stating that SLF4j is defaulting to a no-operation (NOP) logger implementation. To enable logging, add a logging framework of your choice as a dependency to your project such as [Logback](http://logback.qos.ch/), [Log4j 2](http://logging.apache.org/log4j/2.x/index.html), [SLF4j-simple](http://www.slf4j.org/manual.html), or [Apache Commons Logging](https://commons.apache.org/proper/commons-logging/).
 
 # `BigDecimal` vs. `Double`
-It is generally considered bad practice to represent currency values in floating-point data types such as `float` or `double` because it can lead to [rounding errors](https://ta4j.github.io/ta4j-wiki/Num.html) and [precision loss](https://stackoverflow.com/a/3730040/4352701) in calculations. However, using floating-point data types can have significant performance benefits compared to using arbitrary-precision number data types, especially in a quantitative finance and algorithmic trading environment. Because of this, `alpaca-java` uses the `Double` data type (the `double` boxed type) when using the Market Data APIs and the `BigDecimal` data type when using the Trading or Broker APIs. The thinking behind this is that exact decimal quantities are important when placing real trades with real money, but less important when performing calculations of financial indicators. The best solution would be to use the [TA4j](https://github.com/ta4j/ta4j) `Num` interface so that you can decide what data type to use based on your use case, but that is on the [TODO list](#todo) for now. By the way, using `BigDecimal` wouldn't matter if Alpaca used floating-point data types in their internal systems or in their REST APIs, but the fact that they use `String` data types in some of the REST API JSON responses and their Trading and Broker OpenAPI specifications don't use the `double` data type, this leads me to believe that using `BigDecimal` does actually matter. 
+It is generally considered bad practice to represent monetary values using floating-point number data types such as `float` or `double` because it can lead to [rounding errors](https://ta4j.github.io/ta4j-wiki/Num.html) and [precision loss](https://stackoverflow.com/a/3730040/4352701). However, using floating-point number data types can have significant performance benefits compared to using fixed-point number data types (via `BigDecimal`), especially in a quantitative finance and algorithmic trading environment. Because of this, `alpaca-java` uses the `Double` data type (the `double` boxed type) when using the Market Data APIs and the `BigDecimal` data type when using the Trading or Broker APIs. The rationale behind this is that exact decimal quantities are important when placing real trades with real money, but less important when performing calculations through technical indicators. The best solution would be to use the [TA4j](https://github.com/ta4j/ta4j) `Num` interface so that you can decide what data type to use based on your use case, but that is on the [TODO list](#todo) for now. By the way, using `BigDecimal` wouldn't matter if Alpaca used floating-point data types in their internal systems, but the fact that they use `String` data types in some of the REST API JSON responses and their Trading and Broker OpenAPI specifications don't use the `double` data type, this leads me to believe that using `BigDecimal` does actually matter. 
 
 # Examples
 Note that the examples below are not exhaustive. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java) for all classes and method signatures.
@@ -70,8 +70,6 @@ final BrokerAPIEndpointType endpointType = BrokerAPIEndpointType.SANDBOX; // or
 final AlpacaAPI alpacaAPI = new AlpacaAPI(brokerAPIKey, brokerAPISecret, endpointType);
 ```
 
-Note that this library uses [OkHttp](https://square.github.io/okhttp/) as its HTTP client library which creates background threads to service requests via a connection pool. These threads persist even if the main thread exits, so if you want to destroy these threads when you're done using [`AlpacaAPI`](src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java), use `alpacaAPI.closeOkHttpClient();`.
-
 ## [`Trader API`](src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java)
 The [`Trader API`](src/main/java/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.java) is used for placing trades, updating account details, getting open positions, and more. Refer to the [Javadoc](https://javadoc.io/doc/net.jacobpeterson.alpaca/alpaca-java/latest/net/jacobpeterson/alpaca/rest/trader/AlpacaTraderAPI.html) for a list of all available method signatures.
 
@@ -168,7 +166,6 @@ Thread.sleep(2000);
 
 // Close the SSE stream and the OkHttpClient to exit cleanly
 sseRequest.close();
-alpacaAPI.closeOkHttpClient();
 ```
 
 ## [`Updates Stream`](src/main/java/net/jacobpeterson/alpaca/websocket/updates/UpdatesWebsocket.java)
@@ -205,7 +202,6 @@ Thread.sleep(5000);
 
 // Disconnect the 'updates' stream and exit cleanly
 alpacaAPI.updatesStream().disconnect();
-alpacaAPI.closeOkHttpClient();
 ```
 
 ## [`Market Data Stream`](src/main/java/net/jacobpeterson/alpaca/websocket/marketdata/MarketDataWebsocket.java)
@@ -243,7 +239,6 @@ Thread.sleep(5000);
 
 // Disconnect the 'stock market data' stream and exit cleanly
 alpacaAPI.stockMarketDataStream().disconnect();
-alpacaAPI.closeOkHttpClient();
 ```
 
 # Building
@@ -258,6 +253,7 @@ To install the built artifacts to your local Maven repository on your machine (t
 ```
 
 # TODO
+- Implement Options market data.
 - Implement better reconnect logic for Websockets and SSE streaming.
 - Implement Unit Testing for REST API and Websocket streaming (both live and mocked).
 - Use [TA4j](https://github.com/ta4j/ta4j) `Num` interface instead of `Double` or `BigDecimal` for number data types so that users can use either `Double` or `BigDecimal` for performance or precision in price data.
diff --git a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
index c1c73096..a4da83cf 100644
--- a/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
+++ b/src/main/java/net/jacobpeterson/alpaca/AlpacaAPI.java
@@ -171,7 +171,7 @@ public AlpacaAPI(String traderKeyID, String traderSecretKey,
 
         // Create default OkHttpClient instance
         if (okHttpClient == null) {
-            OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
+            final OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
             if (LOGGER.isDebugEnabled()) {
                 final HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(LOGGER::debug);
                 loggingInterceptor.setLevel(BODY);
@@ -182,14 +182,6 @@ public AlpacaAPI(String traderKeyID, String traderSecretKey,
         this.okHttpClient = okHttpClient;
     }
 
-    /**
-     * Closes the {@link OkHttpClient}.
-     */
-    public void closeOkHttpClient() {
-        okHttpClient.dispatcher().executorService().shutdown();
-        okHttpClient.connectionPool().evictAll();
-    }
-
     /**
      * Gets the {@link OkHttpClient}.
      *

From d985c839aaa6a645a67858deb6ad5be8d2435a7e Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Sun, 26 Jan 2025 18:09:53 -0800
Subject: [PATCH 79/85] Update OpenAPI spec IDs to v1.2, fix `EventsApiSSE` NTA
 events call

---
 build.gradle                                     | 16 +++++-----------
 .../alpaca/rest/broker/events/EventsApiSSE.java  | 12 +++++++-----
 2 files changed, 12 insertions(+), 16 deletions(-)

diff --git a/build.gradle b/build.gradle
index 5dc0a489..c3948da4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -89,9 +89,9 @@ javadoc {
 
 // These IDs come from the URLs on this page: https://docs.alpaca.markets/openapi
 final def specIDsOfFileNames = Map.of(
-        "trader.json", "657760422129f9005cf4bd58",
-        "broker.json", "657771fc2471d20070183049",
-        "market-data.json", "6577704d1fb6b9007032bf39")
+        "trader.json", "673e19fc6dc876001ff495b2",
+        "broker.json", "673e1a03a0b2160060120aa0",
+        "market-data.json", "673e19f7fd59830026ac651b")
 final File specDownloadPath = new File(project.layout.buildDirectory.get().getAsFile(),
         "/download/openapi/")
 final File generatedClientLibrariesPath = new File(project.layout.buildDirectory.get().getAsFile(),
@@ -196,7 +196,7 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
     }
 
     doLast {
-        // TODO remove these manual fixes once OpenAPI fixes these issues
+        // TODO remove these manual and hacky fixes once OpenAPI fixes these issues
 
         final def sourceFilesPath = "src/main/java/net/jacobpeterson/alpaca/openapi"
 
@@ -250,7 +250,7 @@ final def fixOpenAPIGeneratedClientIssuesTask = tasks.register("fixOpenAPIGenera
             }
         }
 
-        // Remove the necessary authentication header logic from the generate client's ApiClient
+        // Remove the necessary authentication header logic from the generate client's 'ApiClient'
         specIDsOfFileNames.keySet().stream().map { it.replace(".json", "") }.forEach {
             final def apiClientFile = Paths.get(generatedClientLibrariesPath.getPath(), it, sourceFilesPath,
                     it.replace("-", ""), "ApiClient.java").toFile()
@@ -293,20 +293,17 @@ publishing {
             artifactId = projectArtifactID
             version = projectVersion
             from(components["java"])
-
             pom {
                 name = projectArtifactID
                 description = "A Java API for Alpaca, the commission free, algo friendly, stock trading broker."
                 url = "https://github.com/Petersoj/alpaca-java"
                 inceptionYear = "2018"
-
                 licenses {
                     license {
                         name = "MIT License"
                         url = "https://opensource.org/licenses/MIT"
                     }
                 }
-
                 developers {
                     developer {
                         id = "Petersoj"
@@ -317,7 +314,6 @@ publishing {
                         name = "main(String[] args)"
                     }
                 }
-
                 scm {
                     url = "https://github.com/Petersoj/alpaca-java.git"
                     connection = "scm:git:https://github.com/Petersoj/alpaca-java.git"
@@ -326,7 +322,6 @@ publishing {
             }
         }
     }
-
     repositories {
         maven {
             name = "OSSRH"
@@ -339,7 +334,6 @@ publishing {
         }
     }
 }
-
 signing {
     sign publishing.publications.mavenJava
 }
diff --git a/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java b/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
index 65efee8f..2c18b5ff 100644
--- a/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
+++ b/src/main/java/net/jacobpeterson/alpaca/rest/broker/events/EventsApiSSE.java
@@ -6,6 +6,7 @@
 import net.jacobpeterson.alpaca.openapi.broker.api.EventsApi;
 import net.jacobpeterson.alpaca.openapi.broker.model.AccountStatusEvent;
 import net.jacobpeterson.alpaca.openapi.broker.model.JournalStatusEvent;
+import net.jacobpeterson.alpaca.openapi.broker.model.NonTradeActivityEvent;
 import net.jacobpeterson.alpaca.openapi.broker.model.SubscribeToAdminActionSSE200ResponseInner;
 import net.jacobpeterson.alpaca.openapi.broker.model.TradeUpdateEvent;
 import net.jacobpeterson.alpaca.openapi.broker.model.TradeUpdateEventV2;
@@ -25,6 +26,7 @@
 import java.lang.reflect.Type;
 import java.time.LocalDate;
 import java.time.OffsetDateTime;
+import java.util.UUID;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static net.jacobpeterson.alpaca.openapi.broker.JSON.getGson;
@@ -53,17 +55,17 @@ public EventsApiSSE(ApiClient apiClient) {
     }
 
     /**
-     * See {@link EventsApi#getV1EventsNta(String, String, String, Integer, Integer, String, String, Boolean)}.
+     * See {@link EventsApi#getV1EventsNta(String, String, String, Integer, Integer, String, String, Boolean, UUID)}.
      *
      * @return a {@link SSERequest}
      */
     public SSERequest subscribeToNonTradingActivitiesEvents(String id, String since, String until, Integer sinceId,
-            Integer untilId, String sinceUlid, String untilUlid, Boolean includePreprocessing,
-            SSEListener<Object> sseListener) throws ApiException { // TODO OpenAPI response type is broken
+            Integer untilId, String sinceUlid, String untilUlid, Boolean includePreprocessing, UUID groupId,
+            SSEListener<Object> sseListener) throws ApiException {
         final Request request = eventsAPI.getV1EventsNtaCall(id, since, until, sinceId, untilId, sinceUlid, untilUlid,
-                includePreprocessing, null).request();
+                includePreprocessing, groupId, null).request();
         return new SSERequest(eventSourceFactory.newEventSource(request, createEventSourceListener(sseListener,
-                new TypeToken<Object>() {}.getType()))); // TODO OpenAPI response type is broken
+                new TypeToken<NonTradeActivityEvent>() {}.getType())));
     }
 
     /**

From 1230b41d7b45924a10ffce12c3b9629d88415fc1 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 1 Sep 2025 12:37:15 -0600
Subject: [PATCH 80/85] Remove `.editorconfig`

---
 .editorconfig | 1206 -------------------------------------------------
 1 file changed, 1206 deletions(-)
 delete mode 100644 .editorconfig

diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index e8ed66b1..00000000
--- a/.editorconfig
+++ /dev/null
@@ -1,1206 +0,0 @@
-[*]
-charset = utf-8
-end_of_line = lf
-indent_size = 4
-indent_style = space
-insert_final_newline = true
-max_line_length = 120
-tab_width = 4
-ij_continuation_indent_size = 8
-ij_formatter_off_tag = @formatter:off
-ij_formatter_on_tag = @formatter:on
-ij_formatter_tags_enabled = true
-ij_smart_tabs = false
-ij_visual_guides = 
-ij_wrap_on_typing = false
-
-[*.css]
-ij_css_align_closing_brace_with_properties = false
-ij_css_blank_lines_around_nested_selector = 1
-ij_css_blank_lines_between_blocks = 1
-ij_css_block_comment_add_space = false
-ij_css_brace_placement = end_of_line
-ij_css_enforce_quotes_on_format = false
-ij_css_hex_color_long_format = false
-ij_css_hex_color_lower_case = false
-ij_css_hex_color_short_format = false
-ij_css_hex_color_upper_case = false
-ij_css_keep_blank_lines_in_code = 2
-ij_css_keep_indents_on_empty_lines = false
-ij_css_keep_single_line_blocks = false
-ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
-ij_css_space_after_colon = true
-ij_css_space_before_opening_brace = true
-ij_css_use_double_quotes = true
-ij_css_value_alignment = do_not_align
-
-[*.feature]
-indent_size = 2
-ij_gherkin_keep_indents_on_empty_lines = false
-
-[*.java]
-ij_java_align_consecutive_assignments = false
-ij_java_align_consecutive_variable_declarations = false
-ij_java_align_group_field_declarations = false
-ij_java_align_multiline_annotation_parameters = false
-ij_java_align_multiline_array_initializer_expression = false
-ij_java_align_multiline_assignment = false
-ij_java_align_multiline_binary_operation = false
-ij_java_align_multiline_chained_methods = false
-ij_java_align_multiline_deconstruction_list_components = false
-ij_java_align_multiline_extends_list = false
-ij_java_align_multiline_for = false
-ij_java_align_multiline_method_parentheses = false
-ij_java_align_multiline_parameters = false
-ij_java_align_multiline_parameters_in_calls = false
-ij_java_align_multiline_parenthesized_expression = false
-ij_java_align_multiline_records = false
-ij_java_align_multiline_resources = false
-ij_java_align_multiline_ternary_operation = false
-ij_java_align_multiline_text_blocks = false
-ij_java_align_multiline_throws_list = false
-ij_java_align_subsequent_simple_methods = false
-ij_java_align_throws_keyword = false
-ij_java_align_types_in_multi_catch = false
-ij_java_annotation_parameter_wrap = normal
-ij_java_array_initializer_new_line_after_left_brace = false
-ij_java_array_initializer_right_brace_on_new_line = false
-ij_java_array_initializer_wrap = normal
-ij_java_assert_statement_colon_on_next_line = false
-ij_java_assert_statement_wrap = normal
-ij_java_assignment_wrap = normal
-ij_java_binary_operation_sign_on_next_line = false
-ij_java_binary_operation_wrap = normal
-ij_java_blank_lines_after_anonymous_class_header = 0
-ij_java_blank_lines_after_class_header = 1
-ij_java_blank_lines_after_imports = 1
-ij_java_blank_lines_after_package = 1
-ij_java_blank_lines_around_class = 1
-ij_java_blank_lines_around_field = 0
-ij_java_blank_lines_around_field_in_interface = 0
-ij_java_blank_lines_around_initializer = 1
-ij_java_blank_lines_around_method = 1
-ij_java_blank_lines_around_method_in_interface = 1
-ij_java_blank_lines_before_class_end = 0
-ij_java_blank_lines_before_imports = 1
-ij_java_blank_lines_before_method_body = 0
-ij_java_blank_lines_before_package = 0
-ij_java_block_brace_style = end_of_line
-ij_java_block_comment_add_space = false
-ij_java_block_comment_at_first_column = true
-ij_java_builder_methods = 
-ij_java_call_parameters_new_line_after_left_paren = false
-ij_java_call_parameters_right_paren_on_new_line = false
-ij_java_call_parameters_wrap = normal
-ij_java_case_statement_on_separate_line = true
-ij_java_catch_on_new_line = false
-ij_java_class_annotation_wrap = normal
-ij_java_class_brace_style = end_of_line
-ij_java_class_count_to_use_import_on_demand = 999
-ij_java_class_names_in_javadoc = 3
-ij_java_deconstruction_list_wrap = normal
-ij_java_do_not_indent_top_level_class_members = false
-ij_java_do_not_wrap_after_single_annotation = false
-ij_java_do_not_wrap_after_single_annotation_in_parameter = false
-ij_java_do_while_brace_force = always
-ij_java_doc_add_blank_line_after_description = true
-ij_java_doc_add_blank_line_after_param_comments = true
-ij_java_doc_add_blank_line_after_return = true
-ij_java_doc_add_p_tag_on_empty_lines = true
-ij_java_doc_align_exception_comments = true
-ij_java_doc_align_param_comments = true
-ij_java_doc_do_not_wrap_if_one_line = false
-ij_java_doc_enable_formatting = true
-ij_java_doc_enable_leading_asterisks = true
-ij_java_doc_indent_on_continuation = false
-ij_java_doc_keep_empty_lines = true
-ij_java_doc_keep_empty_parameter_tag = true
-ij_java_doc_keep_empty_return_tag = true
-ij_java_doc_keep_empty_throws_tag = true
-ij_java_doc_keep_invalid_tags = true
-ij_java_doc_param_description_on_new_line = false
-ij_java_doc_preserve_line_breaks = false
-ij_java_doc_use_throws_not_exception_tag = true
-ij_java_else_on_new_line = false
-ij_java_entity_dd_prefix = 
-ij_java_entity_dd_suffix = EJB
-ij_java_entity_eb_prefix = 
-ij_java_entity_eb_suffix = Bean
-ij_java_entity_hi_prefix = 
-ij_java_entity_hi_suffix = Home
-ij_java_entity_lhi_prefix = Local
-ij_java_entity_lhi_suffix = Home
-ij_java_entity_li_prefix = Local
-ij_java_entity_li_suffix = 
-ij_java_entity_pk_class = java.lang.String
-ij_java_entity_ri_prefix = 
-ij_java_entity_ri_suffix = 
-ij_java_entity_vo_prefix = 
-ij_java_entity_vo_suffix = VO
-ij_java_enum_constants_wrap = split_into_lines
-ij_java_extends_keyword_wrap = normal
-ij_java_extends_list_wrap = normal
-ij_java_field_annotation_wrap = normal
-ij_java_field_name_prefix = 
-ij_java_field_name_suffix = 
-ij_java_filter_class_prefix = 
-ij_java_filter_class_suffix = 
-ij_java_filter_dd_prefix = 
-ij_java_filter_dd_suffix = 
-ij_java_finally_on_new_line = false
-ij_java_for_brace_force = always
-ij_java_for_statement_new_line_after_left_paren = false
-ij_java_for_statement_right_paren_on_new_line = false
-ij_java_for_statement_wrap = normal
-ij_java_generate_final_locals = false
-ij_java_generate_final_parameters = false
-ij_java_if_brace_force = always
-ij_java_imports_layout = *,|,javax.**,java.**,|,$*
-ij_java_indent_case_from_switch = true
-ij_java_insert_inner_class_imports = false
-ij_java_insert_override_annotation = true
-ij_java_keep_blank_lines_before_right_brace = 1
-ij_java_keep_blank_lines_between_package_declaration_and_header = 1
-ij_java_keep_blank_lines_in_code = 1
-ij_java_keep_blank_lines_in_declarations = 1
-ij_java_keep_builder_methods_indents = false
-ij_java_keep_control_statement_in_one_line = true
-ij_java_keep_first_column_comment = true
-ij_java_keep_indents_on_empty_lines = false
-ij_java_keep_line_breaks = true
-ij_java_keep_multiple_expressions_in_one_line = true
-ij_java_keep_simple_blocks_in_one_line = true
-ij_java_keep_simple_classes_in_one_line = true
-ij_java_keep_simple_lambdas_in_one_line = true
-ij_java_keep_simple_methods_in_one_line = true
-ij_java_label_indent_absolute = false
-ij_java_label_indent_size = 0
-ij_java_lambda_brace_style = end_of_line
-ij_java_layout_static_imports_separately = true
-ij_java_line_comment_add_space = false
-ij_java_line_comment_add_space_on_reformat = false
-ij_java_line_comment_at_first_column = true
-ij_java_listener_class_prefix = 
-ij_java_listener_class_suffix = 
-ij_java_local_variable_name_prefix = 
-ij_java_local_variable_name_suffix = 
-ij_java_message_dd_prefix = 
-ij_java_message_dd_suffix = EJB
-ij_java_message_eb_prefix = 
-ij_java_message_eb_suffix = Bean
-ij_java_method_annotation_wrap = normal
-ij_java_method_brace_style = end_of_line
-ij_java_method_call_chain_wrap = normal
-ij_java_method_parameters_new_line_after_left_paren = false
-ij_java_method_parameters_right_paren_on_new_line = false
-ij_java_method_parameters_wrap = normal
-ij_java_modifier_list_wrap = false
-ij_java_multi_catch_types_wrap = normal
-ij_java_names_count_to_use_import_on_demand = 999
-ij_java_new_line_after_lparen_in_annotation = false
-ij_java_new_line_after_lparen_in_deconstruction_pattern = false
-ij_java_new_line_after_lparen_in_record_header = false
-ij_java_packages_to_use_import_on_demand = 
-ij_java_parameter_annotation_wrap = normal
-ij_java_parameter_name_prefix = 
-ij_java_parameter_name_suffix = 
-ij_java_parentheses_expression_new_line_after_left_paren = false
-ij_java_parentheses_expression_right_paren_on_new_line = false
-ij_java_place_assignment_sign_on_next_line = false
-ij_java_prefer_longer_names = true
-ij_java_prefer_parameters_wrap = false
-ij_java_record_components_wrap = normal
-ij_java_repeat_annotations = 
-ij_java_repeat_synchronized = true
-ij_java_replace_instanceof_and_cast = false
-ij_java_replace_null_check = true
-ij_java_replace_sum_lambda_with_method_ref = true
-ij_java_resource_list_new_line_after_left_paren = false
-ij_java_resource_list_right_paren_on_new_line = false
-ij_java_resource_list_wrap = normal
-ij_java_rparen_on_new_line_in_annotation = false
-ij_java_rparen_on_new_line_in_deconstruction_pattern = false
-ij_java_rparen_on_new_line_in_record_header = false
-ij_java_servlet_class_prefix = 
-ij_java_servlet_class_suffix = 
-ij_java_servlet_dd_prefix = 
-ij_java_servlet_dd_suffix = 
-ij_java_session_dd_prefix = 
-ij_java_session_dd_suffix = EJB
-ij_java_session_eb_prefix = 
-ij_java_session_eb_suffix = Bean
-ij_java_session_hi_prefix = 
-ij_java_session_hi_suffix = Home
-ij_java_session_lhi_prefix = Local
-ij_java_session_lhi_suffix = Home
-ij_java_session_li_prefix = Local
-ij_java_session_li_suffix = 
-ij_java_session_ri_prefix = 
-ij_java_session_ri_suffix = 
-ij_java_session_si_prefix = 
-ij_java_session_si_suffix = Service
-ij_java_space_after_closing_angle_bracket_in_type_argument = false
-ij_java_space_after_colon = true
-ij_java_space_after_comma = true
-ij_java_space_after_comma_in_type_arguments = true
-ij_java_space_after_for_semicolon = true
-ij_java_space_after_quest = true
-ij_java_space_after_type_cast = true
-ij_java_space_before_annotation_array_initializer_left_brace = false
-ij_java_space_before_annotation_parameter_list = false
-ij_java_space_before_array_initializer_left_brace = false
-ij_java_space_before_catch_keyword = true
-ij_java_space_before_catch_left_brace = true
-ij_java_space_before_catch_parentheses = true
-ij_java_space_before_class_left_brace = true
-ij_java_space_before_colon = true
-ij_java_space_before_colon_in_foreach = true
-ij_java_space_before_comma = false
-ij_java_space_before_deconstruction_list = false
-ij_java_space_before_do_left_brace = true
-ij_java_space_before_else_keyword = true
-ij_java_space_before_else_left_brace = true
-ij_java_space_before_finally_keyword = true
-ij_java_space_before_finally_left_brace = true
-ij_java_space_before_for_left_brace = true
-ij_java_space_before_for_parentheses = true
-ij_java_space_before_for_semicolon = false
-ij_java_space_before_if_left_brace = true
-ij_java_space_before_if_parentheses = true
-ij_java_space_before_method_call_parentheses = false
-ij_java_space_before_method_left_brace = true
-ij_java_space_before_method_parentheses = false
-ij_java_space_before_opening_angle_bracket_in_type_parameter = false
-ij_java_space_before_quest = true
-ij_java_space_before_switch_left_brace = true
-ij_java_space_before_switch_parentheses = true
-ij_java_space_before_synchronized_left_brace = true
-ij_java_space_before_synchronized_parentheses = true
-ij_java_space_before_try_left_brace = true
-ij_java_space_before_try_parentheses = true
-ij_java_space_before_type_parameter_list = false
-ij_java_space_before_while_keyword = true
-ij_java_space_before_while_left_brace = true
-ij_java_space_before_while_parentheses = true
-ij_java_space_inside_one_line_enum_braces = false
-ij_java_space_within_empty_array_initializer_braces = false
-ij_java_space_within_empty_method_call_parentheses = false
-ij_java_space_within_empty_method_parentheses = false
-ij_java_spaces_around_additive_operators = true
-ij_java_spaces_around_annotation_eq = true
-ij_java_spaces_around_assignment_operators = true
-ij_java_spaces_around_bitwise_operators = true
-ij_java_spaces_around_equality_operators = true
-ij_java_spaces_around_lambda_arrow = true
-ij_java_spaces_around_logical_operators = true
-ij_java_spaces_around_method_ref_dbl_colon = false
-ij_java_spaces_around_multiplicative_operators = true
-ij_java_spaces_around_relational_operators = true
-ij_java_spaces_around_shift_operators = true
-ij_java_spaces_around_type_bounds_in_type_parameters = true
-ij_java_spaces_around_unary_operator = false
-ij_java_spaces_within_angle_brackets = false
-ij_java_spaces_within_annotation_parentheses = false
-ij_java_spaces_within_array_initializer_braces = false
-ij_java_spaces_within_braces = false
-ij_java_spaces_within_brackets = false
-ij_java_spaces_within_cast_parentheses = false
-ij_java_spaces_within_catch_parentheses = false
-ij_java_spaces_within_deconstruction_list = false
-ij_java_spaces_within_for_parentheses = false
-ij_java_spaces_within_if_parentheses = false
-ij_java_spaces_within_method_call_parentheses = false
-ij_java_spaces_within_method_parentheses = false
-ij_java_spaces_within_parentheses = false
-ij_java_spaces_within_record_header = false
-ij_java_spaces_within_switch_parentheses = false
-ij_java_spaces_within_synchronized_parentheses = false
-ij_java_spaces_within_try_parentheses = false
-ij_java_spaces_within_while_parentheses = false
-ij_java_special_else_if_treatment = true
-ij_java_static_field_name_prefix = 
-ij_java_static_field_name_suffix = 
-ij_java_subclass_name_prefix = 
-ij_java_subclass_name_suffix = Impl
-ij_java_ternary_operation_signs_on_next_line = false
-ij_java_ternary_operation_wrap = normal
-ij_java_test_name_prefix = 
-ij_java_test_name_suffix = Test
-ij_java_throws_keyword_wrap = normal
-ij_java_throws_list_wrap = normal
-ij_java_use_external_annotations = false
-ij_java_use_fq_class_names = false
-ij_java_use_relative_indents = false
-ij_java_use_single_class_imports = true
-ij_java_variable_annotation_wrap = normal
-ij_java_visibility = public
-ij_java_while_brace_force = always
-ij_java_while_on_new_line = false
-ij_java_wrap_comments = true
-ij_java_wrap_first_method_in_call_chain = false
-ij_java_wrap_long_lines = false
-
-[*.less]
-indent_size = 2
-ij_less_align_closing_brace_with_properties = false
-ij_less_blank_lines_around_nested_selector = 1
-ij_less_blank_lines_between_blocks = 1
-ij_less_block_comment_add_space = false
-ij_less_brace_placement = 0
-ij_less_enforce_quotes_on_format = false
-ij_less_hex_color_long_format = false
-ij_less_hex_color_lower_case = false
-ij_less_hex_color_short_format = false
-ij_less_hex_color_upper_case = false
-ij_less_keep_blank_lines_in_code = 2
-ij_less_keep_indents_on_empty_lines = false
-ij_less_keep_single_line_blocks = false
-ij_less_line_comment_add_space = false
-ij_less_line_comment_at_first_column = false
-ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
-ij_less_space_after_colon = true
-ij_less_space_before_opening_brace = true
-ij_less_use_double_quotes = true
-ij_less_value_alignment = 0
-
-[*.proto]
-indent_size = 2
-tab_width = 2
-ij_continuation_indent_size = 4
-ij_protobuf_keep_blank_lines_in_code = 2
-ij_protobuf_keep_indents_on_empty_lines = false
-ij_protobuf_keep_line_breaks = true
-ij_protobuf_space_after_comma = true
-ij_protobuf_space_before_comma = false
-ij_protobuf_spaces_around_assignment_operators = true
-ij_protobuf_spaces_within_braces = false
-ij_protobuf_spaces_within_brackets = false
-
-[*.sass]
-indent_size = 2
-ij_sass_align_closing_brace_with_properties = false
-ij_sass_blank_lines_around_nested_selector = 1
-ij_sass_blank_lines_between_blocks = 1
-ij_sass_brace_placement = 0
-ij_sass_enforce_quotes_on_format = false
-ij_sass_hex_color_long_format = false
-ij_sass_hex_color_lower_case = false
-ij_sass_hex_color_short_format = false
-ij_sass_hex_color_upper_case = false
-ij_sass_keep_blank_lines_in_code = 2
-ij_sass_keep_indents_on_empty_lines = false
-ij_sass_keep_single_line_blocks = false
-ij_sass_line_comment_add_space = false
-ij_sass_line_comment_at_first_column = false
-ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
-ij_sass_space_after_colon = true
-ij_sass_space_before_opening_brace = true
-ij_sass_use_double_quotes = true
-ij_sass_value_alignment = 0
-
-[*.scss]
-indent_size = 2
-ij_scss_align_closing_brace_with_properties = false
-ij_scss_blank_lines_around_nested_selector = 1
-ij_scss_blank_lines_between_blocks = 1
-ij_scss_block_comment_add_space = false
-ij_scss_brace_placement = 0
-ij_scss_enforce_quotes_on_format = false
-ij_scss_hex_color_long_format = false
-ij_scss_hex_color_lower_case = false
-ij_scss_hex_color_short_format = false
-ij_scss_hex_color_upper_case = false
-ij_scss_keep_blank_lines_in_code = 2
-ij_scss_keep_indents_on_empty_lines = false
-ij_scss_keep_single_line_blocks = false
-ij_scss_line_comment_add_space = false
-ij_scss_line_comment_at_first_column = false
-ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
-ij_scss_space_after_colon = true
-ij_scss_space_before_opening_brace = true
-ij_scss_use_double_quotes = true
-ij_scss_value_alignment = 0
-
-[*.vue]
-indent_size = 2
-tab_width = 2
-ij_continuation_indent_size = 4
-ij_vue_indent_children_of_top_level = template
-ij_vue_interpolation_new_line_after_start_delimiter = true
-ij_vue_interpolation_new_line_before_end_delimiter = true
-ij_vue_interpolation_wrap = off
-ij_vue_keep_indents_on_empty_lines = false
-ij_vue_spaces_within_interpolation_expressions = true
-
-[.editorconfig]
-ij_editorconfig_align_group_field_declarations = false
-ij_editorconfig_space_after_colon = false
-ij_editorconfig_space_after_comma = true
-ij_editorconfig_space_before_colon = false
-ij_editorconfig_space_before_comma = false
-ij_editorconfig_spaces_around_assignment_operators = true
-
-[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
-ij_xml_align_attributes = true
-ij_xml_align_text = false
-ij_xml_attribute_wrap = normal
-ij_xml_block_comment_add_space = false
-ij_xml_block_comment_at_first_column = true
-ij_xml_keep_blank_lines = 2
-ij_xml_keep_indents_on_empty_lines = false
-ij_xml_keep_line_breaks = true
-ij_xml_keep_line_breaks_in_text = true
-ij_xml_keep_whitespaces = false
-ij_xml_keep_whitespaces_around_cdata = preserve
-ij_xml_keep_whitespaces_inside_cdata = false
-ij_xml_line_comment_at_first_column = true
-ij_xml_space_after_tag_name = false
-ij_xml_space_around_equals_in_attribute = false
-ij_xml_space_inside_empty_tag = false
-ij_xml_text_wrap = normal
-ij_xml_use_custom_settings = false
-
-[{*.ats,*.cts,*.mts,*.ts}]
-ij_typescript_align_imports = false
-ij_typescript_align_multiline_array_initializer_expression = false
-ij_typescript_align_multiline_binary_operation = false
-ij_typescript_align_multiline_chained_methods = false
-ij_typescript_align_multiline_extends_list = false
-ij_typescript_align_multiline_for = false
-ij_typescript_align_multiline_parameters = false
-ij_typescript_align_multiline_parameters_in_calls = false
-ij_typescript_align_multiline_ternary_operation = false
-ij_typescript_align_object_properties = 0
-ij_typescript_align_union_types = false
-ij_typescript_align_var_statements = 0
-ij_typescript_array_initializer_new_line_after_left_brace = false
-ij_typescript_array_initializer_right_brace_on_new_line = false
-ij_typescript_array_initializer_wrap = normal
-ij_typescript_assignment_wrap = normal
-ij_typescript_binary_operation_sign_on_next_line = false
-ij_typescript_binary_operation_wrap = normal
-ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
-ij_typescript_blank_lines_after_imports = 1
-ij_typescript_blank_lines_around_class = 1
-ij_typescript_blank_lines_around_field = 0
-ij_typescript_blank_lines_around_field_in_interface = 0
-ij_typescript_blank_lines_around_function = 1
-ij_typescript_blank_lines_around_method = 1
-ij_typescript_blank_lines_around_method_in_interface = 1
-ij_typescript_block_brace_style = end_of_line
-ij_typescript_block_comment_add_space = false
-ij_typescript_block_comment_at_first_column = true
-ij_typescript_call_parameters_new_line_after_left_paren = false
-ij_typescript_call_parameters_right_paren_on_new_line = false
-ij_typescript_call_parameters_wrap = normal
-ij_typescript_catch_on_new_line = false
-ij_typescript_chained_call_dot_on_new_line = true
-ij_typescript_class_brace_style = end_of_line
-ij_typescript_comma_on_new_line = false
-ij_typescript_do_while_brace_force = always
-ij_typescript_else_on_new_line = false
-ij_typescript_enforce_trailing_comma = remove
-ij_typescript_enum_constants_wrap = split_into_lines
-ij_typescript_extends_keyword_wrap = normal
-ij_typescript_extends_list_wrap = normal
-ij_typescript_field_prefix = _
-ij_typescript_file_name_style = relaxed
-ij_typescript_finally_on_new_line = false
-ij_typescript_for_brace_force = always
-ij_typescript_for_statement_new_line_after_left_paren = false
-ij_typescript_for_statement_right_paren_on_new_line = false
-ij_typescript_for_statement_wrap = normal
-ij_typescript_force_quote_style = true
-ij_typescript_force_semicolon_style = true
-ij_typescript_function_expression_brace_style = end_of_line
-ij_typescript_if_brace_force = always
-ij_typescript_import_merge_members = global
-ij_typescript_import_prefer_absolute_path = global
-ij_typescript_import_sort_members = true
-ij_typescript_import_sort_module_name = false
-ij_typescript_import_use_node_resolution = true
-ij_typescript_imports_wrap = on_every_item
-ij_typescript_indent_case_from_switch = true
-ij_typescript_indent_chained_calls = true
-ij_typescript_indent_package_children = 0
-ij_typescript_jsdoc_include_types = false
-ij_typescript_jsx_attribute_value = braces
-ij_typescript_keep_blank_lines_in_code = 1
-ij_typescript_keep_first_column_comment = true
-ij_typescript_keep_indents_on_empty_lines = false
-ij_typescript_keep_line_breaks = true
-ij_typescript_keep_simple_blocks_in_one_line = true
-ij_typescript_keep_simple_methods_in_one_line = true
-ij_typescript_line_comment_add_space = true
-ij_typescript_line_comment_at_first_column = false
-ij_typescript_method_brace_style = end_of_line
-ij_typescript_method_call_chain_wrap = normal
-ij_typescript_method_parameters_new_line_after_left_paren = false
-ij_typescript_method_parameters_right_paren_on_new_line = false
-ij_typescript_method_parameters_wrap = normal
-ij_typescript_object_literal_wrap = on_every_item
-ij_typescript_object_types_wrap = on_every_item
-ij_typescript_parentheses_expression_new_line_after_left_paren = false
-ij_typescript_parentheses_expression_right_paren_on_new_line = false
-ij_typescript_place_assignment_sign_on_next_line = false
-ij_typescript_prefer_as_type_cast = false
-ij_typescript_prefer_explicit_types_function_expression_returns = false
-ij_typescript_prefer_explicit_types_function_returns = false
-ij_typescript_prefer_explicit_types_vars_fields = false
-ij_typescript_prefer_parameters_wrap = false
-ij_typescript_property_prefix = 
-ij_typescript_reformat_c_style_comments = false
-ij_typescript_space_after_colon = true
-ij_typescript_space_after_comma = true
-ij_typescript_space_after_dots_in_rest_parameter = false
-ij_typescript_space_after_generator_mult = true
-ij_typescript_space_after_property_colon = true
-ij_typescript_space_after_quest = true
-ij_typescript_space_after_type_colon = true
-ij_typescript_space_after_unary_not = false
-ij_typescript_space_before_async_arrow_lparen = true
-ij_typescript_space_before_catch_keyword = true
-ij_typescript_space_before_catch_left_brace = true
-ij_typescript_space_before_catch_parentheses = true
-ij_typescript_space_before_class_lbrace = true
-ij_typescript_space_before_class_left_brace = true
-ij_typescript_space_before_colon = true
-ij_typescript_space_before_comma = false
-ij_typescript_space_before_do_left_brace = true
-ij_typescript_space_before_else_keyword = true
-ij_typescript_space_before_else_left_brace = true
-ij_typescript_space_before_finally_keyword = true
-ij_typescript_space_before_finally_left_brace = true
-ij_typescript_space_before_for_left_brace = true
-ij_typescript_space_before_for_parentheses = true
-ij_typescript_space_before_for_semicolon = false
-ij_typescript_space_before_function_left_parenth = true
-ij_typescript_space_before_generator_mult = false
-ij_typescript_space_before_if_left_brace = true
-ij_typescript_space_before_if_parentheses = true
-ij_typescript_space_before_method_call_parentheses = false
-ij_typescript_space_before_method_left_brace = true
-ij_typescript_space_before_method_parentheses = false
-ij_typescript_space_before_property_colon = false
-ij_typescript_space_before_quest = true
-ij_typescript_space_before_switch_left_brace = true
-ij_typescript_space_before_switch_parentheses = true
-ij_typescript_space_before_try_left_brace = true
-ij_typescript_space_before_type_colon = false
-ij_typescript_space_before_unary_not = false
-ij_typescript_space_before_while_keyword = true
-ij_typescript_space_before_while_left_brace = true
-ij_typescript_space_before_while_parentheses = true
-ij_typescript_spaces_around_additive_operators = true
-ij_typescript_spaces_around_arrow_function_operator = true
-ij_typescript_spaces_around_assignment_operators = true
-ij_typescript_spaces_around_bitwise_operators = true
-ij_typescript_spaces_around_equality_operators = true
-ij_typescript_spaces_around_logical_operators = true
-ij_typescript_spaces_around_multiplicative_operators = true
-ij_typescript_spaces_around_relational_operators = true
-ij_typescript_spaces_around_shift_operators = true
-ij_typescript_spaces_around_unary_operator = false
-ij_typescript_spaces_within_array_initializer_brackets = false
-ij_typescript_spaces_within_brackets = false
-ij_typescript_spaces_within_catch_parentheses = false
-ij_typescript_spaces_within_for_parentheses = false
-ij_typescript_spaces_within_if_parentheses = false
-ij_typescript_spaces_within_imports = false
-ij_typescript_spaces_within_interpolation_expressions = false
-ij_typescript_spaces_within_method_call_parentheses = false
-ij_typescript_spaces_within_method_parentheses = false
-ij_typescript_spaces_within_object_literal_braces = false
-ij_typescript_spaces_within_object_type_braces = true
-ij_typescript_spaces_within_parentheses = false
-ij_typescript_spaces_within_switch_parentheses = false
-ij_typescript_spaces_within_type_assertion = false
-ij_typescript_spaces_within_union_types = true
-ij_typescript_spaces_within_while_parentheses = false
-ij_typescript_special_else_if_treatment = true
-ij_typescript_ternary_operation_signs_on_next_line = false
-ij_typescript_ternary_operation_wrap = normal
-ij_typescript_union_types_wrap = on_every_item
-ij_typescript_use_chained_calls_group_indents = false
-ij_typescript_use_double_quotes = true
-ij_typescript_use_explicit_js_extension = auto
-ij_typescript_use_path_mapping = always
-ij_typescript_use_public_modifier = false
-ij_typescript_use_semicolon_after_statement = true
-ij_typescript_var_declaration_wrap = normal
-ij_typescript_while_brace_force = always
-ij_typescript_while_on_new_line = false
-ij_typescript_wrap_comments = true
-
-[{*.bash,*.sh,*.zsh}]
-indent_size = 2
-tab_width = 2
-ij_shell_binary_ops_start_line = false
-ij_shell_keep_column_alignment_padding = false
-ij_shell_minify_program = false
-ij_shell_redirect_followed_by_space = false
-ij_shell_switch_cases_indented = false
-ij_shell_use_unix_line_separator = true
-
-[{*.cjs,*.js}]
-ij_javascript_align_imports = false
-ij_javascript_align_multiline_array_initializer_expression = false
-ij_javascript_align_multiline_binary_operation = false
-ij_javascript_align_multiline_chained_methods = false
-ij_javascript_align_multiline_extends_list = false
-ij_javascript_align_multiline_for = false
-ij_javascript_align_multiline_parameters = false
-ij_javascript_align_multiline_parameters_in_calls = false
-ij_javascript_align_multiline_ternary_operation = false
-ij_javascript_align_object_properties = 0
-ij_javascript_align_union_types = false
-ij_javascript_align_var_statements = 0
-ij_javascript_array_initializer_new_line_after_left_brace = false
-ij_javascript_array_initializer_right_brace_on_new_line = false
-ij_javascript_array_initializer_wrap = normal
-ij_javascript_assignment_wrap = normal
-ij_javascript_binary_operation_sign_on_next_line = false
-ij_javascript_binary_operation_wrap = normal
-ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
-ij_javascript_blank_lines_after_imports = 1
-ij_javascript_blank_lines_around_class = 1
-ij_javascript_blank_lines_around_field = 0
-ij_javascript_blank_lines_around_function = 1
-ij_javascript_blank_lines_around_method = 1
-ij_javascript_block_brace_style = end_of_line
-ij_javascript_block_comment_add_space = false
-ij_javascript_block_comment_at_first_column = true
-ij_javascript_call_parameters_new_line_after_left_paren = false
-ij_javascript_call_parameters_right_paren_on_new_line = false
-ij_javascript_call_parameters_wrap = normal
-ij_javascript_catch_on_new_line = false
-ij_javascript_chained_call_dot_on_new_line = true
-ij_javascript_class_brace_style = end_of_line
-ij_javascript_comma_on_new_line = false
-ij_javascript_do_while_brace_force = always
-ij_javascript_else_on_new_line = false
-ij_javascript_enforce_trailing_comma = remove
-ij_javascript_extends_keyword_wrap = normal
-ij_javascript_extends_list_wrap = normal
-ij_javascript_field_prefix = _
-ij_javascript_file_name_style = relaxed
-ij_javascript_finally_on_new_line = false
-ij_javascript_for_brace_force = always
-ij_javascript_for_statement_new_line_after_left_paren = false
-ij_javascript_for_statement_right_paren_on_new_line = false
-ij_javascript_for_statement_wrap = normal
-ij_javascript_force_quote_style = true
-ij_javascript_force_semicolon_style = true
-ij_javascript_function_expression_brace_style = end_of_line
-ij_javascript_if_brace_force = always
-ij_javascript_import_merge_members = global
-ij_javascript_import_prefer_absolute_path = global
-ij_javascript_import_sort_members = true
-ij_javascript_import_sort_module_name = false
-ij_javascript_import_use_node_resolution = true
-ij_javascript_imports_wrap = normal
-ij_javascript_indent_case_from_switch = true
-ij_javascript_indent_chained_calls = true
-ij_javascript_indent_package_children = 0
-ij_javascript_jsx_attribute_value = braces
-ij_javascript_keep_blank_lines_in_code = 1
-ij_javascript_keep_first_column_comment = true
-ij_javascript_keep_indents_on_empty_lines = false
-ij_javascript_keep_line_breaks = true
-ij_javascript_keep_simple_blocks_in_one_line = true
-ij_javascript_keep_simple_methods_in_one_line = true
-ij_javascript_line_comment_add_space = true
-ij_javascript_line_comment_at_first_column = false
-ij_javascript_method_brace_style = end_of_line
-ij_javascript_method_call_chain_wrap = normal
-ij_javascript_method_parameters_new_line_after_left_paren = false
-ij_javascript_method_parameters_right_paren_on_new_line = false
-ij_javascript_method_parameters_wrap = normal
-ij_javascript_object_literal_wrap = on_every_item
-ij_javascript_object_types_wrap = on_every_item
-ij_javascript_parentheses_expression_new_line_after_left_paren = false
-ij_javascript_parentheses_expression_right_paren_on_new_line = false
-ij_javascript_place_assignment_sign_on_next_line = false
-ij_javascript_prefer_as_type_cast = false
-ij_javascript_prefer_explicit_types_function_expression_returns = false
-ij_javascript_prefer_explicit_types_function_returns = false
-ij_javascript_prefer_explicit_types_vars_fields = false
-ij_javascript_prefer_parameters_wrap = false
-ij_javascript_property_prefix = 
-ij_javascript_reformat_c_style_comments = false
-ij_javascript_space_after_colon = true
-ij_javascript_space_after_comma = true
-ij_javascript_space_after_dots_in_rest_parameter = false
-ij_javascript_space_after_generator_mult = true
-ij_javascript_space_after_property_colon = true
-ij_javascript_space_after_quest = true
-ij_javascript_space_after_type_colon = true
-ij_javascript_space_after_unary_not = false
-ij_javascript_space_before_async_arrow_lparen = true
-ij_javascript_space_before_catch_keyword = true
-ij_javascript_space_before_catch_left_brace = true
-ij_javascript_space_before_catch_parentheses = true
-ij_javascript_space_before_class_lbrace = true
-ij_javascript_space_before_class_left_brace = true
-ij_javascript_space_before_colon = true
-ij_javascript_space_before_comma = false
-ij_javascript_space_before_do_left_brace = true
-ij_javascript_space_before_else_keyword = true
-ij_javascript_space_before_else_left_brace = true
-ij_javascript_space_before_finally_keyword = true
-ij_javascript_space_before_finally_left_brace = true
-ij_javascript_space_before_for_left_brace = true
-ij_javascript_space_before_for_parentheses = true
-ij_javascript_space_before_for_semicolon = false
-ij_javascript_space_before_function_left_parenth = true
-ij_javascript_space_before_generator_mult = false
-ij_javascript_space_before_if_left_brace = true
-ij_javascript_space_before_if_parentheses = true
-ij_javascript_space_before_method_call_parentheses = false
-ij_javascript_space_before_method_left_brace = true
-ij_javascript_space_before_method_parentheses = false
-ij_javascript_space_before_property_colon = false
-ij_javascript_space_before_quest = true
-ij_javascript_space_before_switch_left_brace = true
-ij_javascript_space_before_switch_parentheses = true
-ij_javascript_space_before_try_left_brace = true
-ij_javascript_space_before_type_colon = false
-ij_javascript_space_before_unary_not = false
-ij_javascript_space_before_while_keyword = true
-ij_javascript_space_before_while_left_brace = true
-ij_javascript_space_before_while_parentheses = true
-ij_javascript_spaces_around_additive_operators = true
-ij_javascript_spaces_around_arrow_function_operator = true
-ij_javascript_spaces_around_assignment_operators = true
-ij_javascript_spaces_around_bitwise_operators = true
-ij_javascript_spaces_around_equality_operators = true
-ij_javascript_spaces_around_logical_operators = true
-ij_javascript_spaces_around_multiplicative_operators = true
-ij_javascript_spaces_around_relational_operators = true
-ij_javascript_spaces_around_shift_operators = true
-ij_javascript_spaces_around_unary_operator = false
-ij_javascript_spaces_within_array_initializer_brackets = false
-ij_javascript_spaces_within_brackets = false
-ij_javascript_spaces_within_catch_parentheses = false
-ij_javascript_spaces_within_for_parentheses = false
-ij_javascript_spaces_within_if_parentheses = false
-ij_javascript_spaces_within_imports = false
-ij_javascript_spaces_within_interpolation_expressions = false
-ij_javascript_spaces_within_method_call_parentheses = false
-ij_javascript_spaces_within_method_parentheses = false
-ij_javascript_spaces_within_object_literal_braces = false
-ij_javascript_spaces_within_object_type_braces = true
-ij_javascript_spaces_within_parentheses = false
-ij_javascript_spaces_within_switch_parentheses = false
-ij_javascript_spaces_within_type_assertion = false
-ij_javascript_spaces_within_union_types = true
-ij_javascript_spaces_within_while_parentheses = false
-ij_javascript_special_else_if_treatment = true
-ij_javascript_ternary_operation_signs_on_next_line = false
-ij_javascript_ternary_operation_wrap = normal
-ij_javascript_union_types_wrap = on_every_item
-ij_javascript_use_chained_calls_group_indents = false
-ij_javascript_use_double_quotes = true
-ij_javascript_use_explicit_js_extension = auto
-ij_javascript_use_path_mapping = always
-ij_javascript_use_public_modifier = false
-ij_javascript_use_semicolon_after_statement = true
-ij_javascript_var_declaration_wrap = normal
-ij_javascript_while_brace_force = always
-ij_javascript_while_on_new_line = false
-ij_javascript_wrap_comments = true
-
-[{*.ft,*.vm,*.vsl}]
-ij_vtl_keep_indents_on_empty_lines = false
-
-[{*.gant,*.groovy,*.gy}]
-ij_groovy_align_group_field_declarations = false
-ij_groovy_align_multiline_array_initializer_expression = false
-ij_groovy_align_multiline_assignment = false
-ij_groovy_align_multiline_binary_operation = false
-ij_groovy_align_multiline_chained_methods = false
-ij_groovy_align_multiline_extends_list = false
-ij_groovy_align_multiline_for = false
-ij_groovy_align_multiline_list_or_map = false
-ij_groovy_align_multiline_method_parentheses = false
-ij_groovy_align_multiline_parameters = false
-ij_groovy_align_multiline_parameters_in_calls = false
-ij_groovy_align_multiline_resources = false
-ij_groovy_align_multiline_ternary_operation = false
-ij_groovy_align_multiline_throws_list = false
-ij_groovy_align_named_args_in_map = false
-ij_groovy_align_throws_keyword = false
-ij_groovy_array_initializer_new_line_after_left_brace = false
-ij_groovy_array_initializer_right_brace_on_new_line = false
-ij_groovy_array_initializer_wrap = normal
-ij_groovy_assert_statement_wrap = normal
-ij_groovy_assignment_wrap = normal
-ij_groovy_binary_operation_wrap = normal
-ij_groovy_blank_lines_after_class_header = 0
-ij_groovy_blank_lines_after_imports = 1
-ij_groovy_blank_lines_after_package = 1
-ij_groovy_blank_lines_around_class = 1
-ij_groovy_blank_lines_around_field = 0
-ij_groovy_blank_lines_around_field_in_interface = 0
-ij_groovy_blank_lines_around_method = 1
-ij_groovy_blank_lines_around_method_in_interface = 1
-ij_groovy_blank_lines_before_imports = 1
-ij_groovy_blank_lines_before_method_body = 0
-ij_groovy_blank_lines_before_package = 0
-ij_groovy_block_brace_style = end_of_line
-ij_groovy_block_comment_add_space = false
-ij_groovy_block_comment_at_first_column = true
-ij_groovy_call_parameters_new_line_after_left_paren = false
-ij_groovy_call_parameters_right_paren_on_new_line = false
-ij_groovy_call_parameters_wrap = normal
-ij_groovy_catch_on_new_line = false
-ij_groovy_class_annotation_wrap = split_into_lines
-ij_groovy_class_brace_style = end_of_line
-ij_groovy_class_count_to_use_import_on_demand = 999
-ij_groovy_do_while_brace_force = always
-ij_groovy_else_on_new_line = false
-ij_groovy_enable_groovydoc_formatting = true
-ij_groovy_enum_constants_wrap = split_into_lines
-ij_groovy_extends_keyword_wrap = normal
-ij_groovy_extends_list_wrap = normal
-ij_groovy_field_annotation_wrap = split_into_lines
-ij_groovy_finally_on_new_line = false
-ij_groovy_for_brace_force = always
-ij_groovy_for_statement_new_line_after_left_paren = false
-ij_groovy_for_statement_right_paren_on_new_line = false
-ij_groovy_for_statement_wrap = normal
-ij_groovy_ginq_general_clause_wrap_policy = 2
-ij_groovy_ginq_having_wrap_policy = 1
-ij_groovy_ginq_indent_having_clause = true
-ij_groovy_ginq_indent_on_clause = true
-ij_groovy_ginq_on_wrap_policy = 1
-ij_groovy_ginq_space_after_keyword = true
-ij_groovy_if_brace_force = always
-ij_groovy_import_annotation_wrap = 2
-ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
-ij_groovy_indent_case_from_switch = true
-ij_groovy_indent_label_blocks = true
-ij_groovy_insert_inner_class_imports = false
-ij_groovy_keep_blank_lines_before_right_brace = 1
-ij_groovy_keep_blank_lines_in_code = 1
-ij_groovy_keep_blank_lines_in_declarations = 1
-ij_groovy_keep_control_statement_in_one_line = true
-ij_groovy_keep_first_column_comment = true
-ij_groovy_keep_indents_on_empty_lines = false
-ij_groovy_keep_line_breaks = true
-ij_groovy_keep_multiple_expressions_in_one_line = true
-ij_groovy_keep_simple_blocks_in_one_line = true
-ij_groovy_keep_simple_classes_in_one_line = true
-ij_groovy_keep_simple_lambdas_in_one_line = true
-ij_groovy_keep_simple_methods_in_one_line = true
-ij_groovy_label_indent_absolute = false
-ij_groovy_label_indent_size = 0
-ij_groovy_lambda_brace_style = end_of_line
-ij_groovy_layout_static_imports_separately = true
-ij_groovy_line_comment_add_space = false
-ij_groovy_line_comment_add_space_on_reformat = false
-ij_groovy_line_comment_at_first_column = true
-ij_groovy_method_annotation_wrap = split_into_lines
-ij_groovy_method_brace_style = end_of_line
-ij_groovy_method_call_chain_wrap = normal
-ij_groovy_method_parameters_new_line_after_left_paren = false
-ij_groovy_method_parameters_right_paren_on_new_line = false
-ij_groovy_method_parameters_wrap = normal
-ij_groovy_modifier_list_wrap = false
-ij_groovy_names_count_to_use_import_on_demand = 999
-ij_groovy_packages_to_use_import_on_demand = *
-ij_groovy_parameter_annotation_wrap = normal
-ij_groovy_parentheses_expression_new_line_after_left_paren = false
-ij_groovy_parentheses_expression_right_paren_on_new_line = false
-ij_groovy_prefer_parameters_wrap = false
-ij_groovy_resource_list_new_line_after_left_paren = false
-ij_groovy_resource_list_right_paren_on_new_line = false
-ij_groovy_resource_list_wrap = normal
-ij_groovy_space_after_assert_separator = true
-ij_groovy_space_after_colon = true
-ij_groovy_space_after_comma = true
-ij_groovy_space_after_comma_in_type_arguments = true
-ij_groovy_space_after_for_semicolon = true
-ij_groovy_space_after_quest = true
-ij_groovy_space_after_type_cast = true
-ij_groovy_space_before_annotation_parameter_list = false
-ij_groovy_space_before_array_initializer_left_brace = false
-ij_groovy_space_before_assert_separator = false
-ij_groovy_space_before_catch_keyword = true
-ij_groovy_space_before_catch_left_brace = true
-ij_groovy_space_before_catch_parentheses = true
-ij_groovy_space_before_class_left_brace = true
-ij_groovy_space_before_closure_left_brace = true
-ij_groovy_space_before_colon = true
-ij_groovy_space_before_comma = false
-ij_groovy_space_before_do_left_brace = true
-ij_groovy_space_before_else_keyword = true
-ij_groovy_space_before_else_left_brace = true
-ij_groovy_space_before_finally_keyword = true
-ij_groovy_space_before_finally_left_brace = true
-ij_groovy_space_before_for_left_brace = true
-ij_groovy_space_before_for_parentheses = true
-ij_groovy_space_before_for_semicolon = false
-ij_groovy_space_before_if_left_brace = true
-ij_groovy_space_before_if_parentheses = true
-ij_groovy_space_before_method_call_parentheses = false
-ij_groovy_space_before_method_left_brace = true
-ij_groovy_space_before_method_parentheses = false
-ij_groovy_space_before_quest = true
-ij_groovy_space_before_record_parentheses = false
-ij_groovy_space_before_switch_left_brace = true
-ij_groovy_space_before_switch_parentheses = true
-ij_groovy_space_before_synchronized_left_brace = true
-ij_groovy_space_before_synchronized_parentheses = true
-ij_groovy_space_before_try_left_brace = true
-ij_groovy_space_before_try_parentheses = true
-ij_groovy_space_before_while_keyword = true
-ij_groovy_space_before_while_left_brace = true
-ij_groovy_space_before_while_parentheses = true
-ij_groovy_space_in_named_argument = true
-ij_groovy_space_in_named_argument_before_colon = false
-ij_groovy_space_within_empty_array_initializer_braces = false
-ij_groovy_space_within_empty_method_call_parentheses = false
-ij_groovy_spaces_around_additive_operators = true
-ij_groovy_spaces_around_assignment_operators = true
-ij_groovy_spaces_around_bitwise_operators = true
-ij_groovy_spaces_around_equality_operators = true
-ij_groovy_spaces_around_lambda_arrow = true
-ij_groovy_spaces_around_logical_operators = true
-ij_groovy_spaces_around_multiplicative_operators = true
-ij_groovy_spaces_around_regex_operators = true
-ij_groovy_spaces_around_relational_operators = true
-ij_groovy_spaces_around_shift_operators = true
-ij_groovy_spaces_within_annotation_parentheses = false
-ij_groovy_spaces_within_array_initializer_braces = false
-ij_groovy_spaces_within_braces = true
-ij_groovy_spaces_within_brackets = false
-ij_groovy_spaces_within_cast_parentheses = false
-ij_groovy_spaces_within_catch_parentheses = false
-ij_groovy_spaces_within_for_parentheses = false
-ij_groovy_spaces_within_gstring_injection_braces = false
-ij_groovy_spaces_within_if_parentheses = false
-ij_groovy_spaces_within_list_or_map = false
-ij_groovy_spaces_within_method_call_parentheses = false
-ij_groovy_spaces_within_method_parentheses = false
-ij_groovy_spaces_within_parentheses = false
-ij_groovy_spaces_within_switch_parentheses = false
-ij_groovy_spaces_within_synchronized_parentheses = false
-ij_groovy_spaces_within_try_parentheses = false
-ij_groovy_spaces_within_tuple_expression = false
-ij_groovy_spaces_within_while_parentheses = false
-ij_groovy_special_else_if_treatment = true
-ij_groovy_ternary_operation_wrap = normal
-ij_groovy_throws_keyword_wrap = normal
-ij_groovy_throws_list_wrap = normal
-ij_groovy_use_flying_geese_braces = false
-ij_groovy_use_fq_class_names = false
-ij_groovy_use_fq_class_names_in_javadoc = false
-ij_groovy_use_relative_indents = false
-ij_groovy_use_single_class_imports = true
-ij_groovy_variable_annotation_wrap = normal
-ij_groovy_while_brace_force = always
-ij_groovy_while_on_new_line = false
-ij_groovy_wrap_chain_calls_after_dot = false
-ij_groovy_wrap_long_lines = false
-
-[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
-indent_size = 2
-ij_json_array_wrapping = split_into_lines
-ij_json_keep_blank_lines_in_code = 0
-ij_json_keep_indents_on_empty_lines = false
-ij_json_keep_line_breaks = false
-ij_json_keep_trailing_comma = false
-ij_json_object_wrapping = split_into_lines
-ij_json_property_alignment = do_not_align
-ij_json_space_after_colon = true
-ij_json_space_after_comma = true
-ij_json_space_before_colon = false
-ij_json_space_before_comma = false
-ij_json_spaces_within_braces = false
-ij_json_spaces_within_brackets = false
-ij_json_wrap_long_lines = false
-
-[{*.htm,*.html,*.ng,*.peb,*.sht,*.shtm,*.shtml}]
-ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
-ij_html_align_attributes = false
-ij_html_align_text = false
-ij_html_attribute_wrap = normal
-ij_html_block_comment_add_space = false
-ij_html_block_comment_at_first_column = true
-ij_html_do_not_align_children_of_min_lines = 0
-ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
-ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
-ij_html_enforce_quotes = true
-ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
-ij_html_keep_blank_lines = 1
-ij_html_keep_indents_on_empty_lines = false
-ij_html_keep_line_breaks = true
-ij_html_keep_line_breaks_in_text = true
-ij_html_keep_whitespaces = false
-ij_html_keep_whitespaces_inside = span,pre,textarea
-ij_html_line_comment_at_first_column = true
-ij_html_new_line_after_last_attribute = never
-ij_html_new_line_before_first_attribute = never
-ij_html_quote_style = double
-ij_html_remove_new_line_before_tags = br
-ij_html_space_after_tag_name = false
-ij_html_space_around_equality_in_attribute = false
-ij_html_space_inside_empty_tag = false
-ij_html_text_wrap = normal
-
-[{*.http,*.rest}]
-indent_size = 0
-ij_continuation_indent_size = 4
-ij_http-request_call_parameters_wrap = normal
-ij_http-request_method_parameters_wrap = split_into_lines
-ij_http-request_space_before_comma = true
-ij_http-request_spaces_around_assignment_operators = true
-
-[{*.jsf,*.jsp,*.jspf,*.tag,*.tagf,*.xjsp}]
-ij_jsp_jsp_prefer_comma_separated_import_list = false
-ij_jsp_keep_indents_on_empty_lines = false
-
-[{*.jspx,*.tagx}]
-ij_jspx_keep_indents_on_empty_lines = false
-
-[{*.kt,*.kts}]
-ij_kotlin_align_in_columns_case_branch = false
-ij_kotlin_align_multiline_binary_operation = false
-ij_kotlin_align_multiline_extends_list = false
-ij_kotlin_align_multiline_method_parentheses = false
-ij_kotlin_align_multiline_parameters = false
-ij_kotlin_align_multiline_parameters_in_calls = false
-ij_kotlin_allow_trailing_comma = false
-ij_kotlin_allow_trailing_comma_on_call_site = false
-ij_kotlin_assignment_wrap = normal
-ij_kotlin_blank_lines_after_class_header = 0
-ij_kotlin_blank_lines_around_block_when_branches = 0
-ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
-ij_kotlin_block_comment_add_space = false
-ij_kotlin_block_comment_at_first_column = true
-ij_kotlin_call_parameters_new_line_after_left_paren = false
-ij_kotlin_call_parameters_right_paren_on_new_line = false
-ij_kotlin_call_parameters_wrap = normal
-ij_kotlin_catch_on_new_line = false
-ij_kotlin_class_annotation_wrap = split_into_lines
-ij_kotlin_continuation_indent_for_chained_calls = true
-ij_kotlin_continuation_indent_for_expression_bodies = true
-ij_kotlin_continuation_indent_in_argument_lists = true
-ij_kotlin_continuation_indent_in_elvis = true
-ij_kotlin_continuation_indent_in_if_conditions = true
-ij_kotlin_continuation_indent_in_parameter_lists = true
-ij_kotlin_continuation_indent_in_supertype_lists = true
-ij_kotlin_else_on_new_line = false
-ij_kotlin_enum_constants_wrap = split_into_lines
-ij_kotlin_extends_list_wrap = normal
-ij_kotlin_field_annotation_wrap = split_into_lines
-ij_kotlin_finally_on_new_line = false
-ij_kotlin_if_rparen_on_new_line = false
-ij_kotlin_import_nested_classes = false
-ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
-ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
-ij_kotlin_keep_blank_lines_before_right_brace = 1
-ij_kotlin_keep_blank_lines_in_code = 1
-ij_kotlin_keep_blank_lines_in_declarations = 1
-ij_kotlin_keep_first_column_comment = true
-ij_kotlin_keep_indents_on_empty_lines = false
-ij_kotlin_keep_line_breaks = true
-ij_kotlin_lbrace_on_next_line = false
-ij_kotlin_line_break_after_multiline_when_entry = true
-ij_kotlin_line_comment_add_space = false
-ij_kotlin_line_comment_add_space_on_reformat = false
-ij_kotlin_line_comment_at_first_column = true
-ij_kotlin_method_annotation_wrap = split_into_lines
-ij_kotlin_method_call_chain_wrap = normal
-ij_kotlin_method_parameters_new_line_after_left_paren = false
-ij_kotlin_method_parameters_right_paren_on_new_line = false
-ij_kotlin_method_parameters_wrap = normal
-ij_kotlin_name_count_to_use_star_import = 2147483647
-ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
-ij_kotlin_packages_to_use_import_on_demand = 
-ij_kotlin_parameter_annotation_wrap = normal
-ij_kotlin_space_after_comma = true
-ij_kotlin_space_after_extend_colon = true
-ij_kotlin_space_after_type_colon = true
-ij_kotlin_space_before_catch_parentheses = true
-ij_kotlin_space_before_comma = false
-ij_kotlin_space_before_extend_colon = true
-ij_kotlin_space_before_for_parentheses = true
-ij_kotlin_space_before_if_parentheses = true
-ij_kotlin_space_before_lambda_arrow = true
-ij_kotlin_space_before_type_colon = false
-ij_kotlin_space_before_when_parentheses = true
-ij_kotlin_space_before_while_parentheses = true
-ij_kotlin_spaces_around_additive_operators = true
-ij_kotlin_spaces_around_assignment_operators = true
-ij_kotlin_spaces_around_equality_operators = true
-ij_kotlin_spaces_around_function_type_arrow = true
-ij_kotlin_spaces_around_logical_operators = true
-ij_kotlin_spaces_around_multiplicative_operators = true
-ij_kotlin_spaces_around_range = false
-ij_kotlin_spaces_around_relational_operators = true
-ij_kotlin_spaces_around_unary_operator = false
-ij_kotlin_spaces_around_when_arrow = true
-ij_kotlin_variable_annotation_wrap = normal
-ij_kotlin_while_on_new_line = false
-ij_kotlin_wrap_elvis_expressions = 1
-ij_kotlin_wrap_expression_body_functions = 1
-ij_kotlin_wrap_first_method_in_call_chain = false
-
-[{*.markdown,*.md}]
-ij_markdown_force_one_space_after_blockquote_symbol = true
-ij_markdown_force_one_space_after_header_symbol = true
-ij_markdown_force_one_space_after_list_bullet = true
-ij_markdown_force_one_space_between_words = true
-ij_markdown_format_tables = true
-ij_markdown_insert_quote_arrows_on_wrap = true
-ij_markdown_keep_indents_on_empty_lines = false
-ij_markdown_keep_line_breaks_inside_text_blocks = true
-ij_markdown_max_lines_around_block_elements = 1
-ij_markdown_max_lines_around_header = 1
-ij_markdown_max_lines_between_paragraphs = 1
-ij_markdown_min_lines_around_block_elements = 1
-ij_markdown_min_lines_around_header = 1
-ij_markdown_min_lines_between_paragraphs = 1
-ij_markdown_wrap_text_if_long = true
-ij_markdown_wrap_text_inside_blockquotes = true
-
-[{*.pb,*.textproto}]
-indent_size = 2
-tab_width = 2
-ij_continuation_indent_size = 4
-ij_prototext_keep_blank_lines_in_code = 2
-ij_prototext_keep_indents_on_empty_lines = false
-ij_prototext_keep_line_breaks = true
-ij_prototext_space_after_colon = true
-ij_prototext_space_after_comma = true
-ij_prototext_space_before_colon = false
-ij_prototext_space_before_comma = false
-ij_prototext_spaces_within_braces = true
-ij_prototext_spaces_within_brackets = false
-
-[{*.properties,spring.handlers,spring.schemas}]
-ij_properties_align_group_field_declarations = false
-ij_properties_keep_blank_lines = true
-ij_properties_key_value_delimiter = colon
-ij_properties_spaces_around_key_value_delimiter = false
-
-[{*.qute.htm,*.qute.html,*.qute.json,*.qute.txt,*.qute.yaml,*.qute.yml}]
-ij_qute_keep_indents_on_empty_lines = false
-
-[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
-ij_toml_keep_indents_on_empty_lines = false
-
-[{*.yaml,*.yml}]
-indent_size = 2
-ij_yaml_align_values_properties = do_not_align
-ij_yaml_autoinsert_sequence_marker = true
-ij_yaml_block_mapping_on_new_line = false
-ij_yaml_indent_sequence_value = true
-ij_yaml_keep_indents_on_empty_lines = false
-ij_yaml_keep_line_breaks = true
-ij_yaml_sequence_on_new_line = false
-ij_yaml_space_before_colon = false
-ij_yaml_spaces_within_braces = true
-ij_yaml_spaces_within_brackets = true

From 3190abb18dd088409236cdc9b0574a4d409d2799 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 1 Sep 2025 12:37:50 -0600
Subject: [PATCH 81/85] Update Gradle wrapper

---
 gradle.properties                        |   3 +
 gradle/wrapper/gradle-wrapper.jar        | Bin 59203 -> 43764 bytes
 gradle/wrapper/gradle-wrapper.properties |   2 +-
 gradlew                                  | 286 ++++++++++++++---------
 gradlew.bat                              |  41 ++--
 5 files changed, 203 insertions(+), 129 deletions(-)
 create mode 100644 gradle.properties

diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..31634b4e
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.logging.level=info
+org.gradle.logging.stacktrace=all
+org.gradle.jvmargs=-XX:MaxRAMPercentage=100.0 -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=15
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..1b33c55baabb587c669f562ae36f953de2481846 100644
GIT binary patch
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 2ea3535d..ff23a68d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 4f906e0c..23d15a93 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
 
 #
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,81 +15,115 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+# SPDX-License-Identifier: Apache-2.0
+#
 
 ##############################################################################
-##
-##  Gradle start up script for UN*X
-##
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
 ##############################################################################
 
 # Attempt to set APP_HOME
+
 # Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
 done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
 
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
 
 warn () {
     echo "$*"
-}
+} >&2
 
 die () {
     echo
     echo "$*"
     echo
     exit 1
-}
+} >&2
 
 # OS specific support (must be 'true' or 'false').
 cygwin=false
 msys=false
 darwin=false
 nonstop=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-  NONSTOP* )
-    nonstop=true
-    ;;
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
 esac
 
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+CLASSPATH="\\\"\\\""
 
 
 # Determine the Java command to use to start the JVM.
 if [ -n "$JAVA_HOME" ] ; then
     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
         # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
+        JAVACMD=$JAVA_HOME/jre/sh/java
     else
-        JAVACMD="$JAVA_HOME/bin/java"
+        JAVACMD=$JAVA_HOME/bin/java
     fi
     if [ ! -x "$JAVACMD" ] ; then
         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
     fi
 else
-    JAVACMD="java"
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
 fi
 
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
 
 # For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    JAVACMD=`cygpath --unix "$JAVACMD"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
     # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
         fi
-        i=`expr $i + 1`
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
     done
-    case $i in
-        0) set -- ;;
-        1) set -- "$args0" ;;
-        2) set -- "$args0" "$args1" ;;
-        3) set -- "$args0" "$args1" "$args2" ;;
-        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
 fi
 
-# Escape application args
-save () {
-    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
-    echo " "
-}
-APP_ARGS=`save "$@"`
 
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
 
 exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 107acd32..db3a6ac2 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,8 +13,10 @@
 @rem See the License for the specific language governing permissions and
 @rem limitations under the License.
 @rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
 
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
 @rem ##########################################################################
 @rem
 @rem  Gradle startup script for Windows
@@ -25,7 +27,8 @@
 if "%OS%"=="Windows_NT" setlocal
 
 set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
 
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 goto fail
 
@@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
 if exist "%JAVA_EXE%" goto execute
 
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 goto fail
 
 :execute
 @rem Setup the command line
 
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+set CLASSPATH=
 
 
 @rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
 
 :end
 @rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
 
 :fail
 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
 rem the _cmd.exe /c_ return code!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
 
 :mainEnd
 if "%OS%"=="Windows_NT" endlocal

From 1d77898ee793a39e7cdd3a8acbd9a7cb12467292 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Mon, 1 Sep 2025 12:40:06 -0600
Subject: [PATCH 82/85] Remove `.gitattributes`

---
 .gitattributes | 7 -------
 1 file changed, 7 deletions(-)
 delete mode 100644 .gitattributes

diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 710d818f..00000000
--- a/.gitattributes
+++ /dev/null
@@ -1,7 +0,0 @@
-# Handle line endings automatically for files detected as text and leave all files detected as binary untouched.
-* text=auto
-
-# Declare files that should always have CRLF line endings at checkout.
-*.sln           text eol=crlf
-*.bat           text eol=crlf
-*.cmd           text eol=crlf

From 0b8f28aba489db35dca3a68b8a44bfddabf79704 Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 23 Sep 2025 11:13:36 -0600
Subject: [PATCH 83/85] Update Gradle wrapper

---
 gradle/wrapper/gradle-wrapper.jar        | Bin 43764 -> 45457 bytes
 gradle/wrapper/gradle-wrapper.properties |   2 +-
 gradlew                                  |   5 +-
 gradlew.bat                              | 187 +++++++++++------------
 4 files changed, 95 insertions(+), 99 deletions(-)

diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644
GIT binary patch
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ff23a68d..2e111328 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 23d15a93..adff685a 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright © 2015-2021 the original authors.
+# Copyright © 2015 the original authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -114,7 +114,6 @@ case "$( uname )" in                #(
   NONSTOP* )        nonstop=true ;;
 esac
 
-CLASSPATH="\\\"\\\""
 
 
 # Determine the Java command to use to start the JVM.
@@ -172,7 +171,6 @@ fi
 # For Cygwin or MSYS, switch paths to Windows format before running java
 if "$cygwin" || "$msys" ; then
     APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
-    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
 
     JAVACMD=$( cygpath --unix "$JAVACMD" )
 
@@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
-        -classpath "$CLASSPATH" \
         -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
         "$@"
 
diff --git a/gradlew.bat b/gradlew.bat
index db3a6ac2..e509b2dd 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,94 +1,93 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem      https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem  Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

From b692f8ecc8eb0793327cee710063f4c892ec826e Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Thu, 9 Oct 2025 13:43:04 -0600
Subject: [PATCH 84/85] Add `.github/FUNDING.yml`

---
 .github/FUNDING.yml | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .github/FUNDING.yml

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..55ede737
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: Petersoj
\ No newline at end of file

From 1888a791aedda3fb1deb60aed4ab38c3a28e3e1a Mon Sep 17 00:00:00 2001
From: Jacob Peterson <14127217+Petersoj@users.noreply.github.com>
Date: Tue, 9 Jun 2026 13:27:13 -0600
Subject: [PATCH 85/85] Update Gradle wrapper

---
 gradle/wrapper/gradle-wrapper.jar        | Bin 45457 -> 48966 bytes
 gradle/wrapper/gradle-wrapper.properties |   2 +-
 gradlew                                  |   2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8bdaf60c75ab801e22807dde59e12a8735a34077..d997cfc60f4cff0e7451d19d49a82fa986695d07 100644
GIT binary patch
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 2e111328..1a704683 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index adff685a..739907df 100755
--- a/gradlew
+++ b/gradlew
@@ -57,7 +57,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
