@nevuamarkets/poly-websockets 0.0.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.
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Represents a single price level in the order book
3
+ * @example
4
+ * { price: "0.01", size: "510000" }
5
+ */
6
+ export type PriceLevel = {
7
+ price: string;
8
+ size: string;
9
+ };
10
+
11
+ /**
12
+ * Represents a price_change event from Polymarket WebSocket
13
+ * @example
14
+ * {
15
+ * asset_id: "39327269875426915204597944387916069897800289788920336317845465327697809453999",
16
+ * changes: [
17
+ * { price: "0.044", side: "SELL", size: "611" }
18
+ * ],
19
+ * event_type: "price_change",
20
+ * hash: "a0b7cadf869fc288dbbf65704996fe818cc97d6a",
21
+ * market: "0x5412ae25e97078f814157de948459d59c6221b4c4c495fdd57b536543ad36729",
22
+ * timestamp: "1749371014925"
23
+ * }
24
+ */
25
+ export type PriceChangeEvent = {
26
+ asset_id: string;
27
+ changes: { price: string; side: string; size: string }[];
28
+ event_type: 'price_change';
29
+ hash: string;
30
+ market: string;
31
+ timestamp: string;
32
+ };
33
+
34
+ /**
35
+ * Represents a Polymarket book
36
+ * @example
37
+ * {
38
+ * bids: [
39
+ * { price: "0.01", size: "510000" },
40
+ * { price: "0.02", size: "3100" }
41
+ * ],
42
+ * asks: [
43
+ * { price: "0.99", size: "58.07" },
44
+ * { price: "0.97", size: "178.73" }
45
+ * }
46
+ */
47
+ export type Book = {
48
+ bids: PriceLevel[];
49
+ asks: PriceLevel[];
50
+ };
51
+
52
+ /**
53
+ * Represents a book event from Polymarket WebSocket
54
+ * @example
55
+ * {
56
+ * market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
57
+ * asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
58
+ * timestamp: "1740759191594",
59
+ * hash: "c0e51b1cfdbcb1b2aec58feaf7b01004019a89c6",
60
+ * bids: [
61
+ * { price: "0.01", size: "510000" },
62
+ * { price: "0.02", size: "3100" }
63
+ * ],
64
+ * asks: [
65
+ * { price: "0.99", size: "58.07" },
66
+ * { price: "0.97", size: "178.73" }
67
+ * ],
68
+ * event_type: "book"
69
+ * }
70
+ */
71
+ export type BookEvent = {
72
+ market: string;
73
+ asset_id: string;
74
+ timestamp: string;
75
+ hash: string;
76
+ bids: PriceLevel[];
77
+ asks: PriceLevel[];
78
+ event_type: 'book';
79
+ };
80
+
81
+ /**
82
+ * Represents a last trade price event from Polymarket WebSocket
83
+ * @example
84
+ * {
85
+ * asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
86
+ * event_type: "last_trade_price",
87
+ * fee_rate_bps: "0",
88
+ * market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
89
+ * price: "0.12",
90
+ * side: "BUY",
91
+ * size: "8.333332",
92
+ * timestamp: "1740760245471"
93
+ * }
94
+ */
95
+ export type LastTradePriceEvent = {
96
+ asset_id: string;
97
+ event_type: 'last_trade_price';
98
+ fee_rate_bps: string;
99
+ market: string;
100
+ price: string;
101
+ side: 'BUY' | 'SELL';
102
+ size: string;
103
+ timestamp: string;
104
+ };
105
+
106
+ /**
107
+ * Represents a tick size change event from Polymarket WebSocket
108
+ * @example
109
+ * {
110
+ * event_type: "tick_size_change",
111
+ * asset_id: "65818619657568813474341868652308942079804919287380422192892211131408793125422",
112
+ * market: "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
113
+ * old_tick_size: "0.01",
114
+ * new_tick_size: "0.001",
115
+ * timestamp: "100000000"
116
+ * }
117
+ */
118
+ export type TickSizeChangeEvent = {
119
+ asset_id: string;
120
+ event_type: 'tick_size_change';
121
+ market: string;
122
+ old_tick_size: string;
123
+ new_tick_size: string;
124
+ timestamp: string;
125
+ };
126
+
127
+ /**
128
+ * Union type representing all possible event types from Polymarket WebSocket
129
+ * @example BookEvent
130
+ * {
131
+ * market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
132
+ * asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
133
+ * timestamp: "1740759191594",
134
+ * hash: "c0e51b1cfdbcb1b2aec58feaf7b01004019a89c6",
135
+ * bids: [{ price: "0.01", size: "510000" }],
136
+ * asks: [{ price: "0.99", size: "58.07" }],
137
+ * event_type: "book"
138
+ * }
139
+ *
140
+ * @example LastTradePriceEvent
141
+ * {
142
+ * asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
143
+ * event_type: "last_trade_price",
144
+ * fee_rate_bps: "0",
145
+ * market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
146
+ * price: "0.12",
147
+ * side: "BUY",
148
+ * size: "8.333332",
149
+ * timestamp: "1740760245471"
150
+ * }
151
+ *
152
+ * @example PriceChangeEvent
153
+ * {
154
+ * asset_id: "39327269875426915204597944387916069897800289788920336317845465327697809453999",
155
+ * changes: [
156
+ * { price: "0.044", side: "SELL", size: "611" }
157
+ * ],
158
+ * event_type: "price_change",
159
+ * hash: "a0b7cadf869fc288dbbf65704996fe818cc97d6a",
160
+ * market: "0x5412ae25e97078f814157de948459d59c6221b4c4c495fdd57b536543ad36729",
161
+ * timestamp: "1749371014925"
162
+ * }
163
+ *
164
+ * @example TickSizeChangeEvent
165
+ * {
166
+ * event_type: "tick_size_change",
167
+ * asset_id: "65818619657568813474341868652308942079804919287380422192892211131408793125422",
168
+ * market: "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
169
+ * old_tick_size: "0.01",
170
+ * new_tick_size: "0.001",
171
+ * timestamp: "100000000"
172
+ * }
173
+ */
174
+ export type PolymarketWSEvent = BookEvent | LastTradePriceEvent | PriceChangeEvent | TickSizeChangeEvent;
175
+
176
+ /**
177
+ * Represents a price update event
178
+ *
179
+ * This is an event that is emitted to faciliate price update events. It is
180
+ * not emitted by the Polymarket WebSocket directly.
181
+ *
182
+ * See https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated
183
+ *
184
+ * TLDR: The prices displayed on Polymarket are the midpoint of the bid-ask spread in the orderbook,
185
+ * UNLESS that spread is over $0.10, in which case the **last traded price** is used.
186
+ */
187
+ export interface PolymarketPriceUpdateEvent {
188
+ event_type: 'price_update';
189
+ asset_id: string;
190
+ timestamp: string;
191
+ triggeringEvent: LastTradePriceEvent | PriceChangeEvent;
192
+ book: Book;
193
+ price: string;
194
+ midpoint: string;
195
+ spread: string;
196
+ }
197
+
198
+ /**
199
+ * Represents the handlers for the Polymarket WebSocket
200
+ */
201
+ export type WebSocketHandlers = {
202
+
203
+ /*
204
+ Polymarket WebSocket event handlers
205
+ */
206
+
207
+ // https://docs.polymarket.com/developers/CLOB/websocket/market-channel#book-message
208
+ onBook?: (events: BookEvent[]) => Promise<void>;
209
+
210
+ // Currently undocumented, but is emitted when a trade occurs
211
+ onLastTradePrice?: (events: LastTradePriceEvent[]) => Promise<void>;
212
+
213
+ // https://docs.polymarket.com/developers/CLOB/websocket/market-channel#tick-size-change-message
214
+ onTickSizeChange?: (events: TickSizeChangeEvent[]) => Promise<void>;
215
+
216
+ // https://docs.polymarket.com/developers/CLOB/websocket/market-channel#price-change-message
217
+ onPriceChange?: (events: PriceChangeEvent[]) => Promise<void>;
218
+
219
+ /*
220
+ Also mentioned as 'Future Price', this is the price that is displayed on the Polymarket UI
221
+ and denotes the probability of an event happening. Read more about it here:
222
+ https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated#future-price
223
+
224
+ This is an aggregate event that is not emmited by the Polymarket WebSocket directly.
225
+ */
226
+ onPolymarketPriceUpdate?: (events: PolymarketPriceUpdateEvent[]) => Promise<void>;
227
+
228
+ // Error handling
229
+ onError?: (error: Error) => Promise<void>;
230
+ onWSClose?: (groupId: string, code: number, reason: string) => Promise<void>;
231
+ onWSOpen?: (groupId: string, assetIds: string[]) => Promise<void>;
232
+ }
233
+
234
+ /**
235
+ * Type guard to check if an event is a BookEvent
236
+ * @example
237
+ * if (isBookEvent(event)) {
238
+ * // event is now typed as BookEvent
239
+ * console.log(event.bids);
240
+ * }
241
+ */
242
+ export function isBookEvent(event: PolymarketWSEvent): event is BookEvent {
243
+ return event?.event_type === 'book';
244
+ }
245
+
246
+ /**
247
+ * Type guard to check if an event is a LastTradePriceEvent
248
+ * @example
249
+ * if (isLastTradePriceEvent(event)) {
250
+ * // event is now typed as LastTradePriceEvent
251
+ * console.log(event.side);
252
+ * }
253
+ */
254
+ export function isLastTradePriceEvent(event: PolymarketWSEvent): event is LastTradePriceEvent {
255
+ return event?.event_type === 'last_trade_price';
256
+ }
257
+
258
+ /**
259
+ * Type guard to check if an event is a PriceChangeEvent
260
+ * @example
261
+ * if (isPriceChangeEvent(event)) {
262
+ * // event is now typed as PriceChangeEvent
263
+ * console.log(event.changes);
264
+ * }
265
+ */
266
+ export function isPriceChangeEvent(event: PolymarketWSEvent): event is PriceChangeEvent {
267
+ return event?.event_type === 'price_change';
268
+ }
269
+
270
+ /**
271
+ * Type guard to check if an event is a TickSizeChangeEvent
272
+ * @example
273
+ * if (isTickSizeChangeEvent(event)) {
274
+ * // event is now typed as TickSizeChangeEvent
275
+ * console.log(event.old_tick_size);
276
+ * }
277
+ */
278
+ export function isTickSizeChangeEvent(event: PolymarketWSEvent): event is TickSizeChangeEvent {
279
+ return event?.event_type === 'tick_size_change';
280
+ }
@@ -0,0 +1,26 @@
1
+ import Bottleneck from 'bottleneck';
2
+ import WebSocket from 'ws';
3
+
4
+ export enum WebSocketStatus {
5
+ PENDING = 'pending', // New group that is pending connection
6
+ ALIVE = 'alive', // Group is connected and receiving events
7
+ DEAD = 'dead', // Group is disconnected
8
+ CLEANUP = 'cleanup' // Group is marked for cleanup
9
+ }
10
+
11
+ export type WebSocketGroup = {
12
+ groupId: string;
13
+ assetIds: Set<string>;
14
+ wsClient: WebSocket | null;
15
+ status: WebSocketStatus;
16
+ };
17
+
18
+ export type SubscriptionManagerOptions = {
19
+ burstLimiter?: Bottleneck;
20
+
21
+ // How often to check for groups to reconnect and cleanup
22
+ reconnectAndCleanupIntervalMs?: number;
23
+
24
+ // How many assets to allow per WebSocket
25
+ maxMarketsPerWS?: number;
26
+ }