This repository demonstrates the second-generation (V2) WebSocket flow for Quant users.
V2 sends analytics and spot as separate messages. Each ticker needs:
- One analytics group for each selected package and category.
- One
{ticker}_spotgroup on each V2 hub that carries analytics for that ticker.
Analytics and spot use separate group memberships on the same Azure Web PubSub connection.
The client flow is:
- Send unprefixed analytics groups and one unprefixed spot group per ticker to
POST /v2/negotiate. - Connect to the returned
v2_*hub URLs. - Decode analytics and
proto.spotmessages separately. - Use the ticker and timestamp to correlate analytics with spot when the application needs one combined record.
- Send the complete hub-qualified membership set to
PATCH /v2/negotiatewhen subscriptions change.
.
├── proto/ # Source protobuf definitions
├── generated_proto/ # Locally generated Python protobuf files
├── main.py # POST and PATCH V2 example
├── multi.py # Quant discovery and multi-ticker V2 example
├── group_config.py # Colocated spot-group payload helpers
├── decompression_utils.py # Analytics and spot decompression helpers
├── test_group_config.py # Group payload tests
├── test_decompression_utils.py # Spot wire-contract tests
├── requirements.txt # Python dependencies
└── README.md
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtmkdir -p generated_proto
touch generated_proto/__init__.py
python3 -m grpc_tools.protoc \
-I=proto \
--python_out=generated_proto \
--pyi_out=generated_proto \
proto/*.protoThe generated files are not committed. Run this command after each protobuf change.
Set all required environment variables.
export GEXBOT_API_KEY="your_api_key_here"export GEXBOT_USER_AGENT="YourClientApp/1.0"export BASE_URL="https://api.gex.bot/v2"The scripts also load these exact names from quant-sockets/.env.
Edit these analytics dictionaries near the top of main.py:
INITIAL_ANALYTICS_GROUP_CONFIGUPDATED_ANALYTICS_GROUP_CONFIG
Use V2 hub names as dictionary keys:
v2_classic
v2_state_gex
v2_state_greeks_zero
v2_state_greeks_one
v2_state_greeks
v2_orderflow
Configure analytics groups only.
group_config.py adds the required spot groups.
Analytics groups use this unprefixed shape:
{ticker}_{package}_{category}
Spot groups use this unprefixed shape:
{ticker}_spot
Examples:
SPX_classic_gex_full
SPX_state_gamma_zero
ES_SPX_orderflow_orderflow
SPX_spot
ES_SPX_spot
The server adds the configured color prefix before it joins a group. Do not add that prefix to a request.
Get the current ticker list from:
GET https://api.gex.bot/v2/tickers
Get package categories from:
GET https://api.gex.bot/classic/categories
GET https://api.gex.bot/state/categories
GET https://api.gex.bot/orderflow/categories
Get supplemental Quant tickers from:
GET https://api.gex.bot/tickers/quant
Supplemental Quant tickers are WebSocket-only. A V2 request must still include each matching spot-group membership. A live spot message is available only when the publisher provides spot for that ticker.
{ticker}_classic_gex_full
{ticker}_classic_gex_zero
{ticker}_classic_gex_one
{ticker}_state_gex_full
{ticker}_state_gex_zero
{ticker}_state_gex_one
{ticker}_state_delta_zero
{ticker}_state_delta_one
{ticker}_state_gamma_zero
{ticker}_state_gamma_one
{ticker}_state_vanna_zero
{ticker}_state_vanna_one
{ticker}_state_charm_zero
{ticker}_state_charm_one
{ticker}_orderflow_orderflow
Discover valid expiration dates within the published 90-day horizon:
GET https://api.gex.bot/v2/options/{ticker}/expiries
Convert YYYY-MM-DD to YYYYMMDD for the group suffix:
{ticker}_classic_gex_YYYYMMDD
{ticker}_state_gex_YYYYMMDD
{ticker}_state_delta_YYYYMMDD
{ticker}_state_gamma_YYYYMMDD
{ticker}_state_vanna_YYYYMMDD
{ticker}_state_charm_YYYYMMDD
{ticker}_orderflow_YYYYMMDD
Use these V2 hubs:
v2_classicfor Classic GEX.v2_state_gexfor State GEX.v2_state_greeksfor explicit State Greek expirations.v2_orderflowfor explicit Orderflow expirations.
Explicit expiration groups are real-time only. They publish at a lower cadence than standard groups.
POST /v2/negotiate accepts unqualified group names.
Send each analytics group and one spot name per unique ticker.
Example:
{
"groups": [
"SPX_state_gex_full",
"SPX_state_gamma_zero",
"SPX_spot"
]
}The presence of complete matching spot groups selects the V2 hubs.
The backend places SPX_spot on both selected V2 hubs.
Initial analytics and spot memberships are joined by the server.
Do not call Azure joinGroup for this flow.
The response returns URLs with V2 keys such as:
v2_classic
v2_state_gex
v2_state_greeks_zero
v2_state_greeks_one
v2_state_greeks
v2_orderflow
PATCH /v2/negotiate is a full replacement.
It requires explicit hub names.
Repeat the ticker spot group on each V2 hub that has analytics for that ticker.
Example:
{
"groups": [
{ "hub": "v2_classic", "group": "SPX_classic_gex_full" },
{ "hub": "v2_classic", "group": "SPX_spot" },
{ "hub": "v2_state_gex", "group": "NDX_state_gex_full" },
{ "hub": "v2_state_gex", "group": "NDX_spot" },
{ "hub": "v2_state_greeks_zero", "group": "SPX_state_gamma_zero" },
{ "hub": "v2_state_greeks_zero", "group": "SPX_spot" }
]
}Any membership omitted from the PATCH body is removed.
main.py generates the complete PATCH body from the analytics configuration.
Spot arrives as a google.protobuf.Any envelope with this type URL:
proto.spot
The compressed value contains spot_price.SpotPrice:
timestamp
ticker
spot
source_timestamp_ms
The wire spot value is multiplied by 100.
decompress_spot_message converts it back to the decimal price.
Analytics and spot for one calculation use the same ticker and timestamp. Do not depend on message arrival order. Cache either message briefly if the application must combine them.
The sample scripts print spot independently from analytics.
python main.pymain.py:
- Adds matching spot groups automatically.
- Selects V2 hubs.
- Opens one connection for each returned V2 hub.
- Prints analytics and spot messages separately.
- Replaces the complete group set after the configured delay.
python multi.pymulti.py:
- Selects
SPXandNDXfrom/v2/tickers. - Confirms the required Classic and State categories.
- Selects five supplemental Quant tickers.
- Gets the next three valid expirations for each supplemental ticker.
- Builds Classic, State GEX, standard gamma, and explicit gamma analytics groups.
- Adds one matching spot membership per ticker and V2 hub.
- Negotiates and opens the selected V2 connections.
- Prints analytics and spot messages separately.
With two standard tickers, five supplemental tickers, and three expirations per supplemental ticker, the example selects:
- 31 analytics memberships.
- 21 colocated spot memberships.
- 52 total memberships from the Quant limit of 150.
The POST body contains fewer spot names because it sends each ticker spot name once. The backend expands that name onto each applicable V2 hub.
Compile the Protocol Buffer definitions first. Then run:
python -m unittest test_group_config.py test_decompression_utils.py