@nevuamarkets/poly-websockets 0.3.1 → 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 +69 -111
- package/dist/WSSubscriptionManager.d.ts +111 -30
- package/dist/WSSubscriptionManager.js +822 -118
- package/dist/modules/OrderBookCache.js +19 -4
- package/dist/types/PolymarketWebSocket.d.ts +61 -3
- package/dist/types/WebSocketSubscriptions.d.ts +30 -15
- package/dist/types/WebSocketSubscriptions.js +10 -8
- package/package.json +4 -5
- package/src/WSSubscriptionManager.ts +902 -129
- package/src/index.ts +1 -1
- package/src/modules/OrderBookCache.ts +18 -4
- package/src/types/PolymarketWebSocket.ts +75 -3
- package/src/types/WebSocketSubscriptions.ts +32 -20
- package/dist/modules/GroupRegistry.d.ts +0 -105
- package/dist/modules/GroupRegistry.js +0 -300
- package/dist/modules/GroupSocket.d.ts +0 -59
- package/dist/modules/GroupSocket.js +0 -411
- package/src/modules/GroupRegistry.ts +0 -319
- package/src/modules/GroupSocket.ts +0 -443
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Poly-WebSockets
|
|
2
2
|
|
|
3
|
-
A TypeScript library for **real-time Polymarket market
|
|
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,42 +12,44 @@ npm install @nevuamarkets/poly-websockets
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
- 📊 **Real-time Market Updates**: Get `book
|
|
16
|
-
- 🎯 **Derived
|
|
17
|
-
- 🔗 **
|
|
18
|
-
- 🔄 **Automatic
|
|
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
|
|
20
|
+
- 🔒 **Independent Instances**: Each manager instance is fully isolated with its own WebSocket connection
|
|
21
21
|
|
|
22
22
|
## Quick Start
|
|
23
23
|
|
|
24
24
|
```typescript
|
|
25
|
-
import {
|
|
26
|
-
WSSubscriptionManager,
|
|
27
|
-
WebSocketHandlers
|
|
28
|
-
} from '@nevuamarkets/poly-websockets';
|
|
25
|
+
import { WSSubscriptionManager } from '@nevuamarkets/poly-websockets';
|
|
29
26
|
|
|
30
|
-
// Create the subscription manager with your own handlers
|
|
31
27
|
const manager = new WSSubscriptionManager({
|
|
32
|
-
onBook: async (events
|
|
33
|
-
|
|
34
|
-
console.log('book event', JSON.stringify(event, null, 2))
|
|
35
|
-
}
|
|
28
|
+
onBook: async (events) => {
|
|
29
|
+
console.log('Book events:', events);
|
|
36
30
|
},
|
|
37
|
-
onPriceChange: async (events
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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);
|
|
41
40
|
}
|
|
42
41
|
});
|
|
43
42
|
|
|
44
43
|
// Subscribe to assets
|
|
45
44
|
await manager.addSubscriptions(['asset-id-1', 'asset-id-2']);
|
|
46
45
|
|
|
46
|
+
// Get monitored assets
|
|
47
|
+
console.log('Monitored:', manager.getAssetIds());
|
|
48
|
+
|
|
47
49
|
// Remove subscriptions
|
|
48
50
|
await manager.removeSubscriptions(['asset-id-1']);
|
|
49
51
|
|
|
50
|
-
// Clear all subscriptions and
|
|
52
|
+
// Clear all subscriptions and close connection
|
|
51
53
|
await manager.clearState();
|
|
52
54
|
```
|
|
53
55
|
|
|
@@ -55,8 +57,6 @@ await manager.clearState();
|
|
|
55
57
|
|
|
56
58
|
### WSSubscriptionManager
|
|
57
59
|
|
|
58
|
-
The main class that manages WebSocket connections and subscriptions.
|
|
59
|
-
|
|
60
60
|
#### Constructor
|
|
61
61
|
|
|
62
62
|
```typescript
|
|
@@ -64,115 +64,81 @@ new WSSubscriptionManager(handlers: WebSocketHandlers, options?: SubscriptionMan
|
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
**Parameters:**
|
|
67
|
-
- `handlers` - Event handlers for
|
|
68
|
-
- `options` - Optional configuration
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
71
|
-
-
|
|
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
|
|
72
73
|
|
|
73
74
|
#### Methods
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
##### `removeSubscriptions(assetIds: string[]): Promise<void>`
|
|
83
|
-
|
|
84
|
-
Removes asset subscriptions. **Connections are kept alive to avoid missing events**, and unused groups are cleaned up during the next reconnection cycle.
|
|
85
|
-
|
|
86
|
-
##### `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 |
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
- Removes all asset subscriptions
|
|
90
|
-
- Closes all WebSocket connections
|
|
91
|
-
- Clears the internal order book cache
|
|
84
|
+
#### Statistics Object
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
```
|
|
98
95
|
|
|
99
96
|
### WebSocketHandlers
|
|
100
97
|
|
|
101
|
-
Interface defining event handlers for different WebSocket events.
|
|
102
|
-
|
|
103
98
|
```typescript
|
|
104
99
|
interface WebSocketHandlers {
|
|
105
|
-
//
|
|
100
|
+
// Polymarket WebSocket events
|
|
106
101
|
onBook?: (events: BookEvent[]) => Promise<void>;
|
|
107
102
|
onLastTradePrice?: (events: LastTradePriceEvent[]) => Promise<void>;
|
|
108
103
|
onPriceChange?: (events: PriceChangeEvent[]) => Promise<void>;
|
|
109
104
|
onTickSizeChange?: (events: TickSizeChangeEvent[]) => Promise<void>;
|
|
110
105
|
|
|
111
|
-
// Derived
|
|
106
|
+
// Derived price update (implements Polymarket's display logic)
|
|
112
107
|
onPolymarketPriceUpdate?: (events: PolymarketPriceUpdateEvent[]) => Promise<void>;
|
|
113
108
|
|
|
114
|
-
// Connection
|
|
115
|
-
onWSOpen?: (
|
|
116
|
-
onWSClose?: (
|
|
109
|
+
// Connection events
|
|
110
|
+
onWSOpen?: (managerId: string, pendingAssetIds: string[]) => Promise<void>;
|
|
111
|
+
onWSClose?: (managerId: string, code: number, reason: string) => Promise<void>;
|
|
117
112
|
onError?: (error: Error) => Promise<void>;
|
|
118
113
|
}
|
|
119
114
|
```
|
|
120
115
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
**BookEvent**
|
|
124
|
-
- See // https://docs.polymarket.com/developers/CLOB/websocket/market-channel#book-message
|
|
125
|
-
|
|
126
|
-
**PriceChangeEvent**
|
|
127
|
-
- See https://docs.polymarket.com/developers/CLOB/websocket/market-channel#price-change-message
|
|
116
|
+
### Event Types
|
|
128
117
|
|
|
129
|
-
|
|
130
|
-
|
|
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 |
|
|
131
125
|
|
|
132
|
-
|
|
133
|
-
- Currently undocumented, but is emitted when a trade occurs
|
|
126
|
+
## Multiple Independent Connections
|
|
134
127
|
|
|
135
|
-
|
|
136
|
-
- Derived price update following Polymarket's display logic
|
|
137
|
-
- Uses midpoint when spread <= $0.10, otherwise uses last trade price
|
|
138
|
-
- Includes full order book context
|
|
139
|
-
|
|
140
|
-
### Custom Rate Limiting
|
|
128
|
+
Each `WSSubscriptionManager` instance maintains its own WebSocket connection:
|
|
141
129
|
|
|
142
130
|
```typescript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
reservoir: 10,
|
|
147
|
-
reservoirRefreshAmount: 10,
|
|
148
|
-
reservoirRefreshInterval: 1000,
|
|
149
|
-
maxConcurrent: 10
|
|
150
|
-
});
|
|
131
|
+
// Two separate connections for different asset groups
|
|
132
|
+
const manager1 = new WSSubscriptionManager(handlers1);
|
|
133
|
+
const manager2 = new WSSubscriptionManager(handlers2);
|
|
151
134
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
});
|
|
135
|
+
await manager1.addSubscriptions(['asset-1', 'asset-2']);
|
|
136
|
+
await manager2.addSubscriptions(['asset-3', 'asset-4']);
|
|
155
137
|
```
|
|
156
138
|
|
|
157
139
|
## Examples
|
|
158
140
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
## Error Handling
|
|
162
|
-
|
|
163
|
-
The library includes error handling:
|
|
164
|
-
- Automatic reconnection on connection drops
|
|
165
|
-
- User-defined error callbacks for custom handling
|
|
166
|
-
|
|
167
|
-
## Rate Limits
|
|
168
|
-
|
|
169
|
-
Respects Polymarket's API rate limits:
|
|
170
|
-
- Default: 5 requests per second burst limit
|
|
171
|
-
- Configurable through custom Bottleneck instances
|
|
172
|
-
|
|
173
|
-
## License
|
|
174
|
-
|
|
175
|
-
AGPL-3
|
|
141
|
+
See the [examples](./examples) folder for complete working examples.
|
|
176
142
|
|
|
177
143
|
## Testing
|
|
178
144
|
|
|
@@ -180,18 +146,10 @@ AGPL-3
|
|
|
180
146
|
npm test
|
|
181
147
|
```
|
|
182
148
|
|
|
183
|
-
##
|
|
149
|
+
## License
|
|
184
150
|
|
|
185
|
-
|
|
151
|
+
AGPL-3.0
|
|
186
152
|
|
|
187
153
|
## Disclaimer
|
|
188
154
|
|
|
189
|
-
This software is provided "as is", without warranty of any kind
|
|
190
|
-
|
|
191
|
-
- Any financial losses incurred from using this software
|
|
192
|
-
- Trading decisions made based on the data provided
|
|
193
|
-
- Bugs, errors, or inaccuracies in the data
|
|
194
|
-
- System failures or downtime
|
|
195
|
-
- Any other damages arising from the use of this software
|
|
196
|
-
|
|
197
|
-
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,64 +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
|
|
9
|
-
private
|
|
16
|
+
private wsClient;
|
|
17
|
+
private status;
|
|
18
|
+
private connecting;
|
|
19
|
+
private subscribedAssetIds;
|
|
20
|
+
private pendingSubscribeAssetIds;
|
|
21
|
+
private pendingUnsubscribeAssetIds;
|
|
22
|
+
private reconnectIntervalMs;
|
|
23
|
+
private pendingFlushIntervalMs;
|
|
24
|
+
private reconnectInterval?;
|
|
25
|
+
private pendingFlushInterval?;
|
|
26
|
+
private pingInterval?;
|
|
27
|
+
private connectionTimeout?;
|
|
10
28
|
constructor(userHandlers: WebSocketHandlers, options?: SubscriptionManagerOptions);
|
|
11
29
|
/**
|
|
12
30
|
* Clears all WebSocket subscriptions and state.
|
|
13
31
|
*
|
|
14
32
|
* This will:
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
33
|
+
* 1. Stop all timers
|
|
34
|
+
* 2. Close the WebSocket connection
|
|
35
|
+
* 3. Clear all asset tracking
|
|
36
|
+
* 4. Clear the order book cache
|
|
19
37
|
*/
|
|
20
38
|
clearState(): Promise<void>;
|
|
21
39
|
/**
|
|
22
|
-
*
|
|
23
|
-
* -
|
|
24
|
-
*
|
|
25
|
-
* depending on the current bid-ask spread (see https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated)
|
|
26
|
-
*
|
|
27
|
-
* The user handlers will be called **ONLY** for assets that are actively subscribed to by any groups.
|
|
28
|
-
*
|
|
29
|
-
* @param events - The events to process.
|
|
30
|
-
* @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.
|
|
31
43
|
*/
|
|
32
44
|
private actOnSubscribedEvents;
|
|
33
45
|
/**
|
|
34
|
-
*
|
|
46
|
+
* Adds new subscriptions.
|
|
35
47
|
*
|
|
36
|
-
*
|
|
37
|
-
* -
|
|
38
|
-
* -
|
|
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)
|
|
39
51
|
*
|
|
40
52
|
* @param assetIdsToAdd - The asset IDs to add subscriptions for.
|
|
41
53
|
*/
|
|
42
54
|
addSubscriptions(assetIdsToAdd: string[]): Promise<void>;
|
|
43
55
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
|
|
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;
|
|
70
|
+
/**
|
|
71
|
+
* Removes subscriptions.
|
|
47
72
|
*
|
|
48
73
|
* @param assetIdsToRemove - The asset IDs to remove subscriptions for.
|
|
49
74
|
*/
|
|
50
75
|
removeSubscriptions(assetIdsToRemove: string[]): Promise<void>;
|
|
51
76
|
/**
|
|
52
|
-
*
|
|
77
|
+
* Get all currently monitored asset IDs.
|
|
78
|
+
* This includes both successfully subscribed assets and pending subscriptions.
|
|
53
79
|
*
|
|
54
|
-
*
|
|
55
|
-
|
|
80
|
+
* @returns Array of asset IDs being monitored.
|
|
81
|
+
*/
|
|
82
|
+
getAssetIds(): string[];
|
|
83
|
+
/**
|
|
84
|
+
* Returns statistics about the current state of the subscription manager.
|
|
56
85
|
*/
|
|
57
|
-
private reconnectAndCleanupGroups;
|
|
58
86
|
getStatistics(): {
|
|
59
87
|
openWebSockets: number;
|
|
60
|
-
|
|
88
|
+
assetIds: number;
|
|
89
|
+
pendingSubscribeCount: number;
|
|
90
|
+
pendingUnsubscribeCount: number;
|
|
91
|
+
/** @deprecated Use pendingSubscribeCount + pendingUnsubscribeCount instead */
|
|
92
|
+
pendingAssetIds: number;
|
|
61
93
|
};
|
|
62
|
-
|
|
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;
|
|
63
144
|
}
|
|
64
145
|
export { WSSubscriptionManager, WebSocketHandlers };
|