@nevuamarkets/poly-websockets 1.0.0-beta.0 β†’ 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Poly-WebSockets
2
2
 
3
- A TypeScript library for **real-time Polymarket market price alerts** over **Websocket** with **automatic reconnections** and **easy subscription management**.
3
+ A TypeScript library for **real-time Polymarket market data** over **WebSocket** with **automatic reconnections** and **easy subscription management**.
4
4
 
5
5
  Powering [Nevua Markets](https://nevua.markets)
6
6
 
@@ -12,43 +12,44 @@ npm install @nevuamarkets/poly-websockets
12
12
 
13
13
  ## Features
14
14
 
15
- - πŸ“Š **Real-time Market Updates**: Get `book` , `price_change`, `tick_size_change` and `last_trade_price` real-time market events from Polymarket WSS
16
- - 🎯 **Derived Future Price Event**: Implements Polymarket's [price calculation logic](https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated#future-price) (midpoint vs last trade price based on spread)
17
- - πŸ”— **Dynamic Subscriptions**: Subscribe and unsubscribe to assets on existing WebSocket connections without reconnecting
18
- - πŸ”„ **Automatic Connection Management**: Handles WebSocket connections, reconnections, and cleanup
19
- - 🚦 **Rate Limiting**: Built-in rate limiting to respect Polymarket API limits
15
+ - πŸ“Š **Real-time Market Updates**: Get `book`, `price_change`, `tick_size_change` and `last_trade_price` events from Polymarket WebSocket
16
+ - 🎯 **Derived Price Event**: Implements Polymarket's [price calculation logic](https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated#future-price) (midpoint vs last trade price based on spread)
17
+ - πŸ”— **Dynamic Subscriptions**: Subscribe and unsubscribe to assets without reconnecting
18
+ - πŸ”„ **Automatic Reconnection**: Handles connection drops with automatic reconnection
20
19
  - πŸ’ͺ **TypeScript Support**: Full TypeScript definitions for all events and handlers
21
- - ♾️ **Unlimited Assets**: By default, all subscriptions go through a single WebSocket connection (Polymarket now supports unlimited assets per connection)
20
+ - πŸ”’ **Independent Instances**: Each manager instance is fully isolated with its own WebSocket connection
22
21
 
23
22
  ## Quick Start
24
23
 
25
24
  ```typescript
26
- import {
27
- WSSubscriptionManager,
28
- WebSocketHandlers
29
- } from '@nevuamarkets/poly-websockets';
25
+ import { WSSubscriptionManager } from '@nevuamarkets/poly-websockets';
30
26
 
31
- // Create the subscription manager with your own handlers
32
27
  const manager = new WSSubscriptionManager({
33
- onBook: async (events: BookEvent[]) => {
34
- for (const event of events) {
35
- console.log('book event', JSON.stringify(event, null, 2))
36
- }
28
+ onBook: async (events) => {
29
+ console.log('Book events:', events);
37
30
  },
38
- onPriceChange: async (events: PriceChangeEvent[]) => {
39
- for (const event of events) {
40
- console.log('price change event', JSON.stringify(event, null, 2))
41
- }
31
+ onPriceChange: async (events) => {
32
+ console.log('Price change events:', events);
33
+ },
34
+ onPolymarketPriceUpdate: async (events) => {
35
+ // Derived price following Polymarket's display logic
36
+ console.log('Price updates:', events);
37
+ },
38
+ onError: async (error) => {
39
+ console.error('Error:', error.message);
42
40
  }
43
41
  });
44
42
 
45
43
  // Subscribe to assets
46
44
  await manager.addSubscriptions(['asset-id-1', 'asset-id-2']);
47
45
 
46
+ // Get monitored assets
47
+ console.log('Monitored:', manager.getAssetIds());
48
+
48
49
  // Remove subscriptions
49
50
  await manager.removeSubscriptions(['asset-id-1']);
50
51
 
51
- // Clear all subscriptions and connections
52
+ // Clear all subscriptions and close connection
52
53
  await manager.clearState();
53
54
  ```
54
55
 
@@ -56,8 +57,6 @@ await manager.clearState();
56
57
 
57
58
  ### WSSubscriptionManager
58
59
 
59
- The main class that manages WebSocket connections and subscriptions.
60
-
61
60
  #### Constructor
62
61
 
63
62
  ```typescript
@@ -65,118 +64,81 @@ new WSSubscriptionManager(handlers: WebSocketHandlers, options?: SubscriptionMan
65
64
  ```
66
65
 
67
66
  **Parameters:**
68
- - `handlers` - Event handlers for different WebSocket events
69
- - `options` - Optional configuration object:
70
- - `maxMarketsPerWS?: number` - Maximum assets per WebSocket connection (default: `Infinity` - unlimited, single connection for all assets)
71
- - `reconnectAndCleanupIntervalMs?: number` - Interval for reconnection attempts (default: 10s)
72
- - `burstLimiter?: Bottleneck` - Custom rate limiter instance. If none is provided, one will be created and used internally in the component.
67
+ - `handlers` - Event handlers for WebSocket events
68
+ - `options` - Optional configuration:
69
+ - `reconnectAndCleanupIntervalMs?: number` - Reconnection check interval (default: 5000ms)
70
+ - `pendingFlushIntervalMs?: number` - How often to flush pending subscriptions (default: 100ms)
71
+ - ~~`burstLimiter`~~ - **@deprecated** No longer used
72
+ - ~~`maxMarketsPerWS`~~ - **@deprecated** No longer used. Create multiple manager instances instead
73
73
 
74
74
  #### Methods
75
75
 
76
- ##### `addSubscriptions(assetIds: string[]): Promise<void>`
77
-
78
- Adds new asset subscriptions. The manager will:
79
- - Filter out already subscribed assets
80
- - If an active WebSocket connection exists with capacity, send a subscribe message on that connection
81
- - Otherwise, create a new WebSocket connection
82
-
83
- ##### `removeSubscriptions(assetIds: string[]): Promise<void>`
84
-
85
- Removes asset subscriptions. The manager will:
86
- - Send an unsubscribe message on the active connection
87
- - Clear the internal order book cache for removed assets
88
- - Connections are kept alive to avoid missing events for other subscribed assets
89
-
90
- ##### `clearState(): Promise<void>`
76
+ | Method | Description |
77
+ |--------|-------------|
78
+ | `addSubscriptions(assetIds: string[])` | Add assets to monitor |
79
+ | `removeSubscriptions(assetIds: string[])` | Stop monitoring assets |
80
+ | `getAssetIds(): string[]` | Get all monitored asset IDs (subscribed + pending) |
81
+ | `getStatistics()` | Get connection and subscription statistics |
82
+ | `clearState()` | Clear all subscriptions and close connection |
91
83
 
92
- Clears all subscriptions and state:
93
- - Removes all asset subscriptions
94
- - Closes all WebSocket connections
95
- - Clears the internal order book cache
84
+ #### Statistics Object
96
85
 
97
- ##### `getStatistics(): { openWebSockets: number; subscribedAssetIds: number }`
98
-
99
- Returns statistics about the current state of the subscription manager:
100
- - `openWebSockets`: The number of websockets that are currently in OPEN state
101
- - `subscribedAssetIds`: The number of unique asset IDs that are currently subscribed
86
+ ```typescript
87
+ manager.getStatistics() // Returns:
88
+ {
89
+ openWebSockets: number; // 1 if connected, 0 otherwise
90
+ assetIds: number; // Total monitored assets
91
+ pendingSubscribeCount: number; // Assets waiting to be subscribed
92
+ pendingUnsubscribeCount: number; // Assets waiting to be unsubscribed
93
+ }
94
+ ```
102
95
 
103
96
  ### WebSocketHandlers
104
97
 
105
- Interface defining event handlers for different WebSocket events.
106
-
107
98
  ```typescript
108
99
  interface WebSocketHandlers {
109
- // Core Polymarket WebSocket events
100
+ // Polymarket WebSocket events
110
101
  onBook?: (events: BookEvent[]) => Promise<void>;
111
102
  onLastTradePrice?: (events: LastTradePriceEvent[]) => Promise<void>;
112
103
  onPriceChange?: (events: PriceChangeEvent[]) => Promise<void>;
113
104
  onTickSizeChange?: (events: TickSizeChangeEvent[]) => Promise<void>;
114
105
 
115
- // Derived polymarket price update event
106
+ // Derived price update (implements Polymarket's display logic)
116
107
  onPolymarketPriceUpdate?: (events: PolymarketPriceUpdateEvent[]) => Promise<void>;
117
108
 
118
- // Connection lifecycle events
119
- onWSOpen?: (groupId: string, assetIds: string[]) => Promise<void>;
120
- onWSClose?: (groupId: string, code: number, reason: string) => Promise<void>;
109
+ // Connection events
110
+ onWSOpen?: (managerId: string, pendingAssetIds: string[]) => Promise<void>;
111
+ onWSClose?: (managerId: string, code: number, reason: string) => Promise<void>;
121
112
  onError?: (error: Error) => Promise<void>;
122
113
  }
123
114
  ```
124
115
 
125
- #### Key Event Types
126
-
127
- **BookEvent**
128
- - See // https://docs.polymarket.com/developers/CLOB/websocket/market-channel#book-message
129
-
130
- **PriceChangeEvent**
131
- - See https://docs.polymarket.com/developers/CLOB/websocket/market-channel#price-change-message
116
+ ### Event Types
132
117
 
133
- **onTickSizeChange**
134
- - See https://docs.polymarket.com/developers/CLOB/websocket/market-channel#tick-size-change-message
118
+ | Event | Description |
119
+ |-------|-------------|
120
+ | `BookEvent` | Full order book snapshot ([docs](https://docs.polymarket.com/developers/CLOB/websocket/market-channel#book-message)) |
121
+ | `PriceChangeEvent` | Order book price level changes ([docs](https://docs.polymarket.com/developers/CLOB/websocket/market-channel#price-change-message)) |
122
+ | `TickSizeChangeEvent` | Tick size changes ([docs](https://docs.polymarket.com/developers/CLOB/websocket/market-channel#tick-size-change-message)) |
123
+ | `LastTradePriceEvent` | Trade executions |
124
+ | `PolymarketPriceUpdateEvent` | Derived price using Polymarket's display logic |
135
125
 
136
- **LastTradePriceEvent**
137
- - Currently undocumented, but is emitted when a trade occurs
126
+ ## Multiple Independent Connections
138
127
 
139
- **PolymarketPriceUpdateEvent**
140
- - Derived price update following Polymarket's display logic
141
- - Uses midpoint when spread <= $0.10, otherwise uses last trade price
142
- - Includes full order book context
143
-
144
- ### Custom Rate Limiting
128
+ Each `WSSubscriptionManager` instance maintains its own WebSocket connection:
145
129
 
146
130
  ```typescript
147
- import Bottleneck from 'bottleneck';
148
-
149
- const customLimiter = new Bottleneck({
150
- reservoir: 10,
151
- reservoirRefreshAmount: 10,
152
- reservoirRefreshInterval: 1000,
153
- maxConcurrent: 10
154
- });
131
+ // Two separate connections for different asset groups
132
+ const manager1 = new WSSubscriptionManager(handlers1);
133
+ const manager2 = new WSSubscriptionManager(handlers2);
155
134
 
156
- const manager = new WSSubscriptionManager(handlers, {
157
- burstLimiter: customLimiter
158
- });
135
+ await manager1.addSubscriptions(['asset-1', 'asset-2']);
136
+ await manager2.addSubscriptions(['asset-3', 'asset-4']);
159
137
  ```
160
138
 
161
139
  ## Examples
162
140
 
163
- Check the [examples](./examples) folder for complete working examples
164
-
165
- ## Error Handling
166
-
167
- The library includes error handling:
168
- - Automatic reconnection on connection drops
169
- - User-defined error callbacks for custom handling
170
-
171
- ## Rate Limits
172
-
173
- Respects Polymarket's API rate limits:
174
- - Default: 5 requests per second burst limit
175
- - Configurable through custom Bottleneck instances
176
-
177
- ## License
178
-
179
- AGPL-3
141
+ See the [examples](./examples) folder for complete working examples.
180
142
 
181
143
  ## Testing
182
144
 
@@ -184,18 +146,10 @@ AGPL-3
184
146
  npm test
185
147
  ```
186
148
 
187
- ## TypeScript Support
149
+ ## License
188
150
 
189
- Full TypeScript definitions included.
151
+ AGPL-3.0
190
152
 
191
153
  ## Disclaimer
192
154
 
193
- This software is provided "as is", without warranty of any kind, express or implied. The author(s) are not responsible for:
194
-
195
- - Any financial losses incurred from using this software
196
- - Trading decisions made based on the data provided
197
- - Bugs, errors, or inaccuracies in the data
198
- - System failures or downtime
199
- - Any other damages arising from the use of this software
200
-
201
- Use at your own risk. Always verify data independently and never rely solely on automated systems for trading decisions.
155
+ This software is provided "as is", without warranty of any kind. The author(s) are not responsible for any financial losses, trading decisions, bugs, or system failures. Use at your own risk.
@@ -1,65 +1,145 @@
1
1
  import { WebSocketHandlers } from './types/PolymarketWebSocket';
2
2
  import { SubscriptionManagerOptions } from './types/WebSocketSubscriptions';
3
+ /**
4
+ * WebSocket Subscription Manager for Polymarket CLOB WebSocket.
5
+ *
6
+ * Each instance manages a single WebSocket connection and tracks:
7
+ * - Subscribed assets: Successfully subscribed to the WebSocket
8
+ * - Pending assets: Waiting to be subscribed (batched and flushed periodically)
9
+ *
10
+ * Instances are fully independent - no shared state between managers.
11
+ */
3
12
  declare class WSSubscriptionManager {
13
+ private readonly managerId;
4
14
  private handlers;
5
- private burstLimiter;
6
- private groupRegistry;
7
15
  private bookCache;
8
- private reconnectAndCleanupIntervalMs;
9
- private maxMarketsPerWS;
16
+ private wsClient;
17
+ private status;
18
+ private connecting;
19
+ private subscribedAssetIds;
20
+ private pendingSubscribeAssetIds;
21
+ private pendingUnsubscribeAssetIds;
22
+ private reconnectIntervalMs;
23
+ private pendingFlushIntervalMs;
10
24
  private reconnectInterval?;
25
+ private pendingFlushInterval?;
26
+ private pingInterval?;
27
+ private connectionTimeout?;
11
28
  constructor(userHandlers: WebSocketHandlers, options?: SubscriptionManagerOptions);
12
29
  /**
13
30
  * Clears all WebSocket subscriptions and state.
14
31
  *
15
32
  * This will:
16
- *
17
- * 1. Stop the reconnection interval
18
- * 2. Remove all subscriptions and groups (including GroupSockets)
19
- * 3. Close all WebSocket connections
33
+ * 1. Stop all timers
34
+ * 2. Close the WebSocket connection
35
+ * 3. Clear all asset tracking
20
36
  * 4. Clear the order book cache
21
37
  */
22
38
  clearState(): Promise<void>;
23
39
  /**
24
- * This function is called when:
25
- * - a websocket event is received from the Polymarket WS
26
- * - a price update event detected, either by after a 'last_trade_price' event or a 'price_change' event
27
- * depending on the current bid-ask spread (see https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated)
28
- *
29
- * The user handlers will be called **ONLY** for assets that are actively subscribed to by any groups.
30
- *
31
- * @param events - The events to process.
32
- * @param action - The action to perform on the filtered events.
40
+ * Filters events to only include those for subscribed assets.
41
+ * Wraps user handler calls in try-catch to prevent user errors from breaking internal logic.
42
+ * Does not call the handler if all events are filtered out.
33
43
  */
34
44
  private actOnSubscribedEvents;
35
45
  /**
36
46
  * Adds new subscriptions.
37
47
  *
38
- * - Filters out assets that are already subscribed
39
- * - If an ALIVE group exists with capacity, sends subscribe message on that connection
40
- * - Otherwise creates a new group and WebSocket connection
48
+ * Assets are added to a pending queue and will be subscribed when:
49
+ * - The WebSocket connects (initial subscription)
50
+ * - The pending flush timer fires (for new assets on an existing connection)
41
51
  *
42
52
  * @param assetIdsToAdd - The asset IDs to add subscriptions for.
43
53
  */
44
54
  addSubscriptions(assetIdsToAdd: string[]): Promise<void>;
55
+ /**
56
+ * Ensures the periodic intervals are running.
57
+ * Called after clearState() when new subscriptions are added.
58
+ */
59
+ private ensureIntervalsRunning;
60
+ /**
61
+ * Schedules the next reconnection check.
62
+ * Uses a fixed interval (default 5 seconds) between checks.
63
+ */
64
+ private scheduleReconnectionCheck;
65
+ /**
66
+ * Safely calls the error handler, catching any exceptions thrown by it.
67
+ * Prevents user handler exceptions from breaking internal logic.
68
+ */
69
+ private safeCallErrorHandler;
45
70
  /**
46
71
  * Removes subscriptions.
47
- * Sends unsubscribe messages to ALIVE connections.
48
72
  *
49
73
  * @param assetIdsToRemove - The asset IDs to remove subscriptions for.
50
74
  */
51
75
  removeSubscriptions(assetIdsToRemove: string[]): Promise<void>;
52
76
  /**
53
- * This function runs periodically and:
77
+ * Get all currently monitored asset IDs.
78
+ * This includes both successfully subscribed assets and pending subscriptions.
54
79
  *
55
- * - Tries to reconnect groups that have assets and are disconnected
56
- * - Cleans up groups that have no assets
80
+ * @returns Array of asset IDs being monitored.
81
+ */
82
+ getAssetIds(): string[];
83
+ /**
84
+ * Returns statistics about the current state of the subscription manager.
57
85
  */
58
- private reconnectAndCleanupGroups;
59
86
  getStatistics(): {
60
87
  openWebSockets: number;
61
- subscribedAssetIds: number;
88
+ assetIds: number;
89
+ pendingSubscribeCount: number;
90
+ pendingUnsubscribeCount: number;
91
+ /** @deprecated Use pendingSubscribeCount + pendingUnsubscribeCount instead */
92
+ pendingAssetIds: number;
62
93
  };
63
- private createWebSocketClient;
94
+ /**
95
+ * Flush pending subscriptions and unsubscriptions to the WebSocket.
96
+ *
97
+ * SUBSCRIPTION PROTOCOL NOTE:
98
+ * The Polymarket WebSocket protocol does NOT send any confirmation or acknowledgment
99
+ * messages for subscribe/unsubscribe operations. The server silently processes these
100
+ * requests. We optimistically assume success after sending. If the server rejects
101
+ * a request (e.g., invalid asset ID), events for those assets simply won't arrive -
102
+ * there is no error response to handle.
103
+ *
104
+ * This means:
105
+ * - We cannot definitively know if a subscription succeeded
106
+ * - We cannot definitively know if an unsubscription succeeded
107
+ * - The only indication of failure is the absence of expected events
108
+ */
109
+ private flushPendingSubscriptions;
110
+ /**
111
+ * Closes the WebSocket connection and cleans up related resources.
112
+ */
113
+ private closeWebSocket;
114
+ /**
115
+ * Check if we need to reconnect.
116
+ * Note: Assets are moved to pending in handleClose/handleError handlers,
117
+ * so this method only needs to check if reconnection is needed.
118
+ */
119
+ private checkReconnection;
120
+ /**
121
+ * Establish the WebSocket connection.
122
+ */
123
+ private connect;
124
+ /**
125
+ * Sets up event handlers for the WebSocket connection.
126
+ */
127
+ private setupEventHandlers;
128
+ /**
129
+ * Handles book events by updating the cache and notifying listeners.
130
+ */
131
+ private handleBookEvents;
132
+ /**
133
+ * Handles tick size change events by notifying listeners.
134
+ */
135
+ private handleTickEvents;
136
+ /**
137
+ * Handles price change events.
138
+ */
139
+ private handlePriceChangeEvents;
140
+ /**
141
+ * Handles last trade price events.
142
+ */
143
+ private handleLastTradeEvents;
64
144
  }
65
145
  export { WSSubscriptionManager, WebSocketHandlers };