Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update WebSocket message parsing and types #116

Closed
wants to merge 7 commits into from

Conversation

justinpolygon
Copy link
Contributor

@justinpolygon justinpolygon commented Jul 20, 2023

This PR brings several major breaking changes but hopefully the enhancements around routing, message parsing, and improved spec alignment are worth it and make it easier to maintain. There are five areas that have changes but they are very interrelated and here's a breakdown:

WebSocket Connection Refactoring
Added the ability for users to select the Market and Feed type they are interested in. Previously, all WebSocket traffic ran through socket.polygon.io, and with this explicit selection, users will know exactly what they're connecting to. I think this also enables folks to use delayed feeds like the go and python clients. But, the major downstream effect is that this feature also enables us to route messages based on market type of easier message parsing.

Message Routing / Parsing Overhaul
Added logic for routing and parsing WebSocket messages, enabling parsing of each message into specific message types based on the market selected. The need for this arose due to Stocks, Options, Indices, and LaunchPad message types with A.* or AM.* being parsed incorrectly as StocksMessage.Aggregate. The fields in the spec for these message types didn't align accurately with StocksMessage.Aggregate. With the updated logic, we can now correctly parse messages based on the Market type, ensuring the correct message type is assigned each time and there are not missing or null fields.

Message Type Revision
The message types for Stocks, Options, Indices, Forex, Crypto, and LaunchPad have been reviewed and updated to align with the latest spec. This ensures fields and their data types provide an accurate representation of data. I added some new fields and removed older ones. We should be in 100% compliance now.

Launchpad Message Organization
Launchpad messages are routed into their own message parser, providing future flexibility in case of spec modifications, in that if options, or indices, etc, gets more fields we can easily added them.

Dedicated Examples
Added dedicated examples for each asset class along with Launchpad. Should make onboarding very easy.

Collectively, these changes will hopefully greatly enhance the WebSocket functionality, offering better alignment with the spec and improved maintainability. Also, added in the changed from PR #115 into here.

Here's what an example client looks like now (note the feed and market types are specified):

package io.polygon.kotlin.sdk.sample

import io.polygon.kotlin.sdk.websocket.*
import kotlinx.coroutines.delay

suspend fun stocksWebsocketSample(polygonKey: String) {
    val websocketClient = PolygonWebSocketClient(
        polygonKey,
        Feed.RealTime,
        Market.Stocks,
        object : PolygonWebSocketListener {
            override fun onAuthenticated(client: PolygonWebSocketClient) {
                println("Connected!")
            }

            override fun onReceive(
                client: PolygonWebSocketClient,
                message: PolygonWebSocketMessage
            ) {
                when (message) {
                    is PolygonWebSocketMessage.RawMessage -> println(String(message.data))
                    else -> println("Receieved Message: $message")
                }
            }

            override fun onDisconnect(client: PolygonWebSocketClient) {
                println("Disconnected!")
            }

            override fun onError(client: PolygonWebSocketClient, error: Throwable) {
                println("Error: ")
                error.printStackTrace()
            }

        })

    val subscriptions = listOf(
        PolygonWebSocketSubscription(PolygonWebSocketChannel.Stocks.Trades, "*"),
        //PolygonWebSocketSubscription(PolygonWebSocketChannel.Stocks.Quotes, "*"),
        //PolygonWebSocketSubscription(PolygonWebSocketChannel.Stocks.AggPerSecond, "*"),
        //PolygonWebSocketSubscription(PolygonWebSocketChannel.Stocks.AggPerMinute, "*")
    )

    websocketClient.connect()
    websocketClient.subscribe(subscriptions)
    delay(65_000)
    websocketClient.unsubscribe(subscriptions)
    websocketClient.disconnect()
}

@justinpolygon justinpolygon marked this pull request as ready for review July 20, 2023 22:10
@justinpolygon justinpolygon changed the title [WIP] Update webSocket message parsing and types Update webSocket message parsing and types Jul 20, 2023
@justinpolygon justinpolygon changed the title Update webSocket message parsing and types Update WebSocket message parsing and types Jul 20, 2023
@justinpolygon
Copy link
Contributor Author

FYI - Added a few more reviewers since this is a pretty large change. Just wanted to make sure it was cool. Also, I'm not really sure who wants to review all this stuff. So, if it's a pain for anyone let me know and I'll stop.

@justinpolygon justinpolygon mentioned this pull request Jul 21, 2023
justinpolygon added a commit that referenced this pull request Jul 22, 2023
Updated the readme with some examples. This PR depends on #116 and #114.
@justinpolygon justinpolygon mentioned this pull request Jul 22, 2023
Copy link
Contributor

@mmoghaddam385 mmoghaddam385 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the changes make sense, LMK what you think about using a sealed class instead of enum for Feed and Market.

Also I can't believe how many fields we were missing in our message models 😅

Comment on lines +25 to +47
enum class Feed(val url: String) {
Delayed("delayed.polygon.io"),
RealTime("socket.polygon.io"),
Nasdaq("nasdaqfeed.polygon.io"),
PolyFeed("polyfeed.polygon.io"),
PolyFeedPlus("polyfeedplus.polygon.io"),
StarterFeed("starterfeed.polygon.io"),
LaunchpadFeed("launchpad.polygon.io")
}

/**
* Market is the type of market (e.g. Stocks, Crypto) used to connect to the server.
*/
enum class Market(val market: String) {
Stocks("stocks"),
Options("options"),
Forex("forex"),
Crypto("crypto"),
Options("options"),
Indices("indices"),
LaunchpadStocks("stocks"),
LaunchpadOptions("options"),
LaunchpadForex("forex"),
LaunchpadCrypto("crypto")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I experimented with using a sealed class here instead of enum to support an "Other" option in this PR: #119

LMK what you think about the trade-offs there. Personally I really like the forward-compatibility that the sealed class supports even with the slight trade-off of how Java usages access these fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment in the other thread #119 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #122

return collector
}

private fun parseStockMessage(frame: JsonObject): PolygonWebSocketMessage? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this or any of the other parse... functions need to return a nullable result, since all of them have else branches they all return a non-null PolygonWebsocketMessage

As an added bonus if all of the parse functions return a non-null result, we don't need that finalMessage line that null-coalesces anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #122

…etClient.kt

Co-authored-by: Michael Moghaddam <6424916+mmoghaddam385@users.noreply.github.com>
@justinpolygon
Copy link
Contributor Author

Closing this in favour of #122.

justinpolygon added a commit that referenced this pull request Aug 11, 2023
Updated the readme with some examples. This PR depends on #116 and #114.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants