@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.
- package/LICENSE +661 -0
- package/README.md +251 -0
- package/dist/WSSubscriptionManager.d.ts +18 -0
- package/dist/WSSubscriptionManager.js +174 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +21 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +34 -0
- package/dist/modules/GroupRegistry.d.ts +85 -0
- package/dist/modules/GroupRegistry.js +261 -0
- package/dist/modules/GroupSocket.d.ts +22 -0
- package/dist/modules/GroupSocket.js +311 -0
- package/dist/modules/OrderBookCache.d.ts +49 -0
- package/dist/modules/OrderBookCache.js +173 -0
- package/dist/types/PolymarketWebSocket.d.ts +242 -0
- package/dist/types/PolymarketWebSocket.js +50 -0
- package/dist/types/WebSocketSubscriptions.d.ts +19 -0
- package/dist/types/WebSocketSubscriptions.js +10 -0
- package/package.json +49 -0
- package/src/WSSubscriptionManager.ts +201 -0
- package/src/index.ts +3 -0
- package/src/logger.ts +37 -0
- package/src/modules/GroupRegistry.ts +274 -0
- package/src/modules/GroupSocket.ts +338 -0
- package/src/modules/OrderBookCache.ts +208 -0
- package/src/types/PolymarketWebSocket.ts +280 -0
- package/src/types/WebSocketSubscriptions.ts +26 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrderBookCache = void 0;
|
|
4
|
+
exports.sortDescendingInPlace = sortDescendingInPlace;
|
|
5
|
+
function sortDescendingInPlace(bookSide) {
|
|
6
|
+
bookSide.sort((a, b) => parseFloat(b.price) - parseFloat(a.price));
|
|
7
|
+
}
|
|
8
|
+
function sortAscendingInPlace(bookSide) {
|
|
9
|
+
bookSide.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
|
|
10
|
+
}
|
|
11
|
+
class OrderBookCache {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.bookCache = {};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Replace full book (after a `book` event)
|
|
17
|
+
*/
|
|
18
|
+
replaceBook(event) {
|
|
19
|
+
let lastPrice = null;
|
|
20
|
+
let lastMidpoint = null;
|
|
21
|
+
let lastSpread = null;
|
|
22
|
+
if (this.bookCache[event.asset_id]) {
|
|
23
|
+
lastPrice = this.bookCache[event.asset_id].price;
|
|
24
|
+
lastMidpoint = this.bookCache[event.asset_id].midpoint;
|
|
25
|
+
lastSpread = this.bookCache[event.asset_id].spread;
|
|
26
|
+
}
|
|
27
|
+
this.bookCache[event.asset_id] = {
|
|
28
|
+
bids: [...event.bids],
|
|
29
|
+
asks: [...event.asks],
|
|
30
|
+
price: lastPrice,
|
|
31
|
+
midpoint: lastMidpoint,
|
|
32
|
+
spread: lastSpread,
|
|
33
|
+
};
|
|
34
|
+
/* Polymarket book events are currently sorted as such:
|
|
35
|
+
* - bids (buys) ascending
|
|
36
|
+
* - asks (sells) descending
|
|
37
|
+
*
|
|
38
|
+
* So we maintain this order in the cache.
|
|
39
|
+
*/
|
|
40
|
+
sortAscendingInPlace(this.bookCache[event.asset_id].bids);
|
|
41
|
+
sortDescendingInPlace(this.bookCache[event.asset_id].asks);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Update a cached book from a `price_change` event.
|
|
45
|
+
*
|
|
46
|
+
* Returns true if the book was updated.
|
|
47
|
+
* Throws if the book is not found.
|
|
48
|
+
*/
|
|
49
|
+
upsertPriceChange(event) {
|
|
50
|
+
const book = this.bookCache[event.asset_id];
|
|
51
|
+
if (!book) {
|
|
52
|
+
throw new Error(`Book not found for asset ${event.asset_id}`);
|
|
53
|
+
}
|
|
54
|
+
for (const change of event.changes) {
|
|
55
|
+
const { price, size, side } = change;
|
|
56
|
+
if (side === 'BUY') {
|
|
57
|
+
const i = book.bids.findIndex(bid => bid.price === price);
|
|
58
|
+
if (i !== -1) {
|
|
59
|
+
book.bids[i].size = size;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
book.bids.push({ price, size });
|
|
63
|
+
// Ensure the bids are sorted ascending
|
|
64
|
+
sortAscendingInPlace(book.bids);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const i = book.asks.findIndex(ask => ask.price === price);
|
|
69
|
+
if (i !== -1) {
|
|
70
|
+
book.asks[i].size = size;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
book.asks.push({ price, size });
|
|
74
|
+
// Ensure the asks are sorted descending
|
|
75
|
+
sortDescendingInPlace(book.asks);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Return `true` if best-bid/best-ask spread exceeds `cents`.
|
|
82
|
+
*
|
|
83
|
+
* Side effect: updates the book's spread
|
|
84
|
+
*
|
|
85
|
+
* Throws if either side of the book is empty.
|
|
86
|
+
*/
|
|
87
|
+
spreadOver(assetId, cents = 0.1) {
|
|
88
|
+
const book = this.bookCache[assetId];
|
|
89
|
+
if (!book)
|
|
90
|
+
throw new Error(`Book for ${assetId} not cached`);
|
|
91
|
+
if (book.asks.length === 0)
|
|
92
|
+
throw new Error('No asks in book');
|
|
93
|
+
if (book.bids.length === 0)
|
|
94
|
+
throw new Error('No bids in book');
|
|
95
|
+
/*
|
|
96
|
+
* Polymarket book events are currently sorted as such:
|
|
97
|
+
* - bids ascending
|
|
98
|
+
* - asks descending
|
|
99
|
+
*/
|
|
100
|
+
const highestBid = book.bids[book.bids.length - 1].price;
|
|
101
|
+
const lowestAsk = book.asks[book.asks.length - 1].price;
|
|
102
|
+
const highestBidNum = parseFloat(highestBid);
|
|
103
|
+
const lowestAskNum = parseFloat(lowestAsk);
|
|
104
|
+
const spread = lowestAskNum - highestBidNum;
|
|
105
|
+
if (isNaN(spread)) {
|
|
106
|
+
throw new Error(`Spread is NaN: lowestAsk '${lowestAsk}' highestBid '${highestBid}'`);
|
|
107
|
+
}
|
|
108
|
+
/*
|
|
109
|
+
* Update spead, 3 precision decimal places, trim trailing zeros
|
|
110
|
+
*/
|
|
111
|
+
book.spread = parseFloat(spread.toFixed(3)).toString();
|
|
112
|
+
// Should be safe for 0.### - precision values
|
|
113
|
+
return spread > cents;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Calculate the midpoint of the book, rounded to 3dp, no trailing zeros
|
|
117
|
+
*
|
|
118
|
+
* Side effect: updates the book's midpoint
|
|
119
|
+
*
|
|
120
|
+
* Throws if
|
|
121
|
+
* - the book is not found or missing either bid or ask
|
|
122
|
+
* - the midpoint is NaN.
|
|
123
|
+
*/
|
|
124
|
+
midpoint(assetId) {
|
|
125
|
+
const book = this.bookCache[assetId];
|
|
126
|
+
if (!book)
|
|
127
|
+
throw new Error(`Book for ${assetId} not cached`);
|
|
128
|
+
if (book.asks.length === 0)
|
|
129
|
+
throw new Error('No asks in book');
|
|
130
|
+
if (book.bids.length === 0)
|
|
131
|
+
throw new Error('No bids in book');
|
|
132
|
+
/*
|
|
133
|
+
* Polymarket book events are currently sorted as such:
|
|
134
|
+
* - bids ascending
|
|
135
|
+
* - asks descending
|
|
136
|
+
*/
|
|
137
|
+
const highestBid = book.bids[book.bids.length - 1].price;
|
|
138
|
+
const lowestAsk = book.asks[book.asks.length - 1].price;
|
|
139
|
+
const highestBidNum = parseFloat(highestBid);
|
|
140
|
+
const lowestAskNum = parseFloat(lowestAsk);
|
|
141
|
+
const midpoint = (highestBidNum + lowestAskNum) / 2;
|
|
142
|
+
if (isNaN(midpoint)) {
|
|
143
|
+
throw new Error(`Midpoint is NaN: lowestAsk '${lowestAsk}' highestBid '${highestBid}'`);
|
|
144
|
+
}
|
|
145
|
+
/*
|
|
146
|
+
* Update midpoint, 3 precision decimal places, trim trailing zeros
|
|
147
|
+
*/
|
|
148
|
+
book.midpoint = parseFloat(midpoint.toFixed(3)).toString();
|
|
149
|
+
return parseFloat(midpoint.toFixed(3)).toString();
|
|
150
|
+
}
|
|
151
|
+
clear(assetId) {
|
|
152
|
+
if (assetId) {
|
|
153
|
+
delete this.bookCache[assetId];
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
for (const k of Object.keys(this.bookCache)) {
|
|
157
|
+
delete this.bookCache[k];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get a book entry by asset id.
|
|
163
|
+
*
|
|
164
|
+
* Return null if the book is not found.
|
|
165
|
+
*/
|
|
166
|
+
getBookEntry(assetId) {
|
|
167
|
+
if (!this.bookCache[assetId]) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return this.bookCache[assetId];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.OrderBookCache = OrderBookCache;
|
|
@@ -0,0 +1,242 @@
|
|
|
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
|
+
* Represents a price_change event from Polymarket WebSocket
|
|
12
|
+
* @example
|
|
13
|
+
* {
|
|
14
|
+
* asset_id: "39327269875426915204597944387916069897800289788920336317845465327697809453999",
|
|
15
|
+
* changes: [
|
|
16
|
+
* { price: "0.044", side: "SELL", size: "611" }
|
|
17
|
+
* ],
|
|
18
|
+
* event_type: "price_change",
|
|
19
|
+
* hash: "a0b7cadf869fc288dbbf65704996fe818cc97d6a",
|
|
20
|
+
* market: "0x5412ae25e97078f814157de948459d59c6221b4c4c495fdd57b536543ad36729",
|
|
21
|
+
* timestamp: "1749371014925"
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
export type PriceChangeEvent = {
|
|
25
|
+
asset_id: string;
|
|
26
|
+
changes: {
|
|
27
|
+
price: string;
|
|
28
|
+
side: string;
|
|
29
|
+
size: string;
|
|
30
|
+
}[];
|
|
31
|
+
event_type: 'price_change';
|
|
32
|
+
hash: string;
|
|
33
|
+
market: string;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Represents a Polymarket book
|
|
38
|
+
* @example
|
|
39
|
+
* {
|
|
40
|
+
* bids: [
|
|
41
|
+
* { price: "0.01", size: "510000" },
|
|
42
|
+
* { price: "0.02", size: "3100" }
|
|
43
|
+
* ],
|
|
44
|
+
* asks: [
|
|
45
|
+
* { price: "0.99", size: "58.07" },
|
|
46
|
+
* { price: "0.97", size: "178.73" }
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
export type Book = {
|
|
50
|
+
bids: PriceLevel[];
|
|
51
|
+
asks: PriceLevel[];
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Represents a book event from Polymarket WebSocket
|
|
55
|
+
* @example
|
|
56
|
+
* {
|
|
57
|
+
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
|
|
58
|
+
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
|
|
59
|
+
* timestamp: "1740759191594",
|
|
60
|
+
* hash: "c0e51b1cfdbcb1b2aec58feaf7b01004019a89c6",
|
|
61
|
+
* bids: [
|
|
62
|
+
* { price: "0.01", size: "510000" },
|
|
63
|
+
* { price: "0.02", size: "3100" }
|
|
64
|
+
* ],
|
|
65
|
+
* asks: [
|
|
66
|
+
* { price: "0.99", size: "58.07" },
|
|
67
|
+
* { price: "0.97", size: "178.73" }
|
|
68
|
+
* ],
|
|
69
|
+
* event_type: "book"
|
|
70
|
+
* }
|
|
71
|
+
*/
|
|
72
|
+
export type BookEvent = {
|
|
73
|
+
market: string;
|
|
74
|
+
asset_id: string;
|
|
75
|
+
timestamp: string;
|
|
76
|
+
hash: string;
|
|
77
|
+
bids: PriceLevel[];
|
|
78
|
+
asks: PriceLevel[];
|
|
79
|
+
event_type: 'book';
|
|
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
|
+
* Represents a tick size change event from Polymarket WebSocket
|
|
107
|
+
* @example
|
|
108
|
+
* {
|
|
109
|
+
* event_type: "tick_size_change",
|
|
110
|
+
* asset_id: "65818619657568813474341868652308942079804919287380422192892211131408793125422",
|
|
111
|
+
* market: "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
|
|
112
|
+
* old_tick_size: "0.01",
|
|
113
|
+
* new_tick_size: "0.001",
|
|
114
|
+
* timestamp: "100000000"
|
|
115
|
+
* }
|
|
116
|
+
*/
|
|
117
|
+
export type TickSizeChangeEvent = {
|
|
118
|
+
asset_id: string;
|
|
119
|
+
event_type: 'tick_size_change';
|
|
120
|
+
market: string;
|
|
121
|
+
old_tick_size: string;
|
|
122
|
+
new_tick_size: string;
|
|
123
|
+
timestamp: string;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Union type representing all possible event types from Polymarket WebSocket
|
|
127
|
+
* @example BookEvent
|
|
128
|
+
* {
|
|
129
|
+
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
|
|
130
|
+
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
|
|
131
|
+
* timestamp: "1740759191594",
|
|
132
|
+
* hash: "c0e51b1cfdbcb1b2aec58feaf7b01004019a89c6",
|
|
133
|
+
* bids: [{ price: "0.01", size: "510000" }],
|
|
134
|
+
* asks: [{ price: "0.99", size: "58.07" }],
|
|
135
|
+
* event_type: "book"
|
|
136
|
+
* }
|
|
137
|
+
*
|
|
138
|
+
* @example LastTradePriceEvent
|
|
139
|
+
* {
|
|
140
|
+
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
|
|
141
|
+
* event_type: "last_trade_price",
|
|
142
|
+
* fee_rate_bps: "0",
|
|
143
|
+
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
|
|
144
|
+
* price: "0.12",
|
|
145
|
+
* side: "BUY",
|
|
146
|
+
* size: "8.333332",
|
|
147
|
+
* timestamp: "1740760245471"
|
|
148
|
+
* }
|
|
149
|
+
*
|
|
150
|
+
* @example PriceChangeEvent
|
|
151
|
+
* {
|
|
152
|
+
* asset_id: "39327269875426915204597944387916069897800289788920336317845465327697809453999",
|
|
153
|
+
* changes: [
|
|
154
|
+
* { price: "0.044", side: "SELL", size: "611" }
|
|
155
|
+
* ],
|
|
156
|
+
* event_type: "price_change",
|
|
157
|
+
* hash: "a0b7cadf869fc288dbbf65704996fe818cc97d6a",
|
|
158
|
+
* market: "0x5412ae25e97078f814157de948459d59c6221b4c4c495fdd57b536543ad36729",
|
|
159
|
+
* timestamp: "1749371014925"
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
* @example TickSizeChangeEvent
|
|
163
|
+
* {
|
|
164
|
+
* event_type: "tick_size_change",
|
|
165
|
+
* asset_id: "65818619657568813474341868652308942079804919287380422192892211131408793125422",
|
|
166
|
+
* market: "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
|
|
167
|
+
* old_tick_size: "0.01",
|
|
168
|
+
* new_tick_size: "0.001",
|
|
169
|
+
* timestamp: "100000000"
|
|
170
|
+
* }
|
|
171
|
+
*/
|
|
172
|
+
export type PolymarketWSEvent = BookEvent | LastTradePriceEvent | PriceChangeEvent | TickSizeChangeEvent;
|
|
173
|
+
/**
|
|
174
|
+
* Represents a price update event
|
|
175
|
+
*
|
|
176
|
+
* This is an event that is emitted to faciliate price update events. It is
|
|
177
|
+
* not emitted by the Polymarket WebSocket directly.
|
|
178
|
+
*
|
|
179
|
+
* See https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated
|
|
180
|
+
*
|
|
181
|
+
* TLDR: The prices displayed on Polymarket are the midpoint of the bid-ask spread in the orderbook,
|
|
182
|
+
* UNLESS that spread is over $0.10, in which case the **last traded price** is used.
|
|
183
|
+
*/
|
|
184
|
+
export interface PolymarketPriceUpdateEvent {
|
|
185
|
+
event_type: 'price_update';
|
|
186
|
+
asset_id: string;
|
|
187
|
+
timestamp: string;
|
|
188
|
+
triggeringEvent: LastTradePriceEvent | PriceChangeEvent;
|
|
189
|
+
book: Book;
|
|
190
|
+
price: string;
|
|
191
|
+
midpoint: string;
|
|
192
|
+
spread: string;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Represents the handlers for the Polymarket WebSocket
|
|
196
|
+
*/
|
|
197
|
+
export type WebSocketHandlers = {
|
|
198
|
+
onBook?: (events: BookEvent[]) => Promise<void>;
|
|
199
|
+
onLastTradePrice?: (events: LastTradePriceEvent[]) => Promise<void>;
|
|
200
|
+
onTickSizeChange?: (events: TickSizeChangeEvent[]) => Promise<void>;
|
|
201
|
+
onPriceChange?: (events: PriceChangeEvent[]) => Promise<void>;
|
|
202
|
+
onPolymarketPriceUpdate?: (events: PolymarketPriceUpdateEvent[]) => Promise<void>;
|
|
203
|
+
onError?: (error: Error) => Promise<void>;
|
|
204
|
+
onWSClose?: (groupId: string, code: number, reason: string) => Promise<void>;
|
|
205
|
+
onWSOpen?: (groupId: string, assetIds: string[]) => Promise<void>;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Type guard to check if an event is a BookEvent
|
|
209
|
+
* @example
|
|
210
|
+
* if (isBookEvent(event)) {
|
|
211
|
+
* // event is now typed as BookEvent
|
|
212
|
+
* console.log(event.bids);
|
|
213
|
+
* }
|
|
214
|
+
*/
|
|
215
|
+
export declare function isBookEvent(event: PolymarketWSEvent): event is BookEvent;
|
|
216
|
+
/**
|
|
217
|
+
* Type guard to check if an event is a LastTradePriceEvent
|
|
218
|
+
* @example
|
|
219
|
+
* if (isLastTradePriceEvent(event)) {
|
|
220
|
+
* // event is now typed as LastTradePriceEvent
|
|
221
|
+
* console.log(event.side);
|
|
222
|
+
* }
|
|
223
|
+
*/
|
|
224
|
+
export declare function isLastTradePriceEvent(event: PolymarketWSEvent): event is LastTradePriceEvent;
|
|
225
|
+
/**
|
|
226
|
+
* Type guard to check if an event is a PriceChangeEvent
|
|
227
|
+
* @example
|
|
228
|
+
* if (isPriceChangeEvent(event)) {
|
|
229
|
+
* // event is now typed as PriceChangeEvent
|
|
230
|
+
* console.log(event.changes);
|
|
231
|
+
* }
|
|
232
|
+
*/
|
|
233
|
+
export declare function isPriceChangeEvent(event: PolymarketWSEvent): event is PriceChangeEvent;
|
|
234
|
+
/**
|
|
235
|
+
* Type guard to check if an event is a TickSizeChangeEvent
|
|
236
|
+
* @example
|
|
237
|
+
* if (isTickSizeChangeEvent(event)) {
|
|
238
|
+
* // event is now typed as TickSizeChangeEvent
|
|
239
|
+
* console.log(event.old_tick_size);
|
|
240
|
+
* }
|
|
241
|
+
*/
|
|
242
|
+
export declare function isTickSizeChangeEvent(event: PolymarketWSEvent): event is TickSizeChangeEvent;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isBookEvent = isBookEvent;
|
|
4
|
+
exports.isLastTradePriceEvent = isLastTradePriceEvent;
|
|
5
|
+
exports.isPriceChangeEvent = isPriceChangeEvent;
|
|
6
|
+
exports.isTickSizeChangeEvent = isTickSizeChangeEvent;
|
|
7
|
+
/**
|
|
8
|
+
* Type guard to check if an event is a BookEvent
|
|
9
|
+
* @example
|
|
10
|
+
* if (isBookEvent(event)) {
|
|
11
|
+
* // event is now typed as BookEvent
|
|
12
|
+
* console.log(event.bids);
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
function isBookEvent(event) {
|
|
16
|
+
return (event === null || event === void 0 ? void 0 : event.event_type) === 'book';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Type guard to check if an event is a LastTradePriceEvent
|
|
20
|
+
* @example
|
|
21
|
+
* if (isLastTradePriceEvent(event)) {
|
|
22
|
+
* // event is now typed as LastTradePriceEvent
|
|
23
|
+
* console.log(event.side);
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
function isLastTradePriceEvent(event) {
|
|
27
|
+
return (event === null || event === void 0 ? void 0 : event.event_type) === 'last_trade_price';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Type guard to check if an event is a PriceChangeEvent
|
|
31
|
+
* @example
|
|
32
|
+
* if (isPriceChangeEvent(event)) {
|
|
33
|
+
* // event is now typed as PriceChangeEvent
|
|
34
|
+
* console.log(event.changes);
|
|
35
|
+
* }
|
|
36
|
+
*/
|
|
37
|
+
function isPriceChangeEvent(event) {
|
|
38
|
+
return (event === null || event === void 0 ? void 0 : event.event_type) === 'price_change';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Type guard to check if an event is a TickSizeChangeEvent
|
|
42
|
+
* @example
|
|
43
|
+
* if (isTickSizeChangeEvent(event)) {
|
|
44
|
+
* // event is now typed as TickSizeChangeEvent
|
|
45
|
+
* console.log(event.old_tick_size);
|
|
46
|
+
* }
|
|
47
|
+
*/
|
|
48
|
+
function isTickSizeChangeEvent(event) {
|
|
49
|
+
return (event === null || event === void 0 ? void 0 : event.event_type) === 'tick_size_change';
|
|
50
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Bottleneck from 'bottleneck';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
export declare enum WebSocketStatus {
|
|
4
|
+
PENDING = "pending",// New group that is pending connection
|
|
5
|
+
ALIVE = "alive",// Group is connected and receiving events
|
|
6
|
+
DEAD = "dead",// Group is disconnected
|
|
7
|
+
CLEANUP = "cleanup"
|
|
8
|
+
}
|
|
9
|
+
export type WebSocketGroup = {
|
|
10
|
+
groupId: string;
|
|
11
|
+
assetIds: Set<string>;
|
|
12
|
+
wsClient: WebSocket | null;
|
|
13
|
+
status: WebSocketStatus;
|
|
14
|
+
};
|
|
15
|
+
export type SubscriptionManagerOptions = {
|
|
16
|
+
burstLimiter?: Bottleneck;
|
|
17
|
+
reconnectAndCleanupIntervalMs?: number;
|
|
18
|
+
maxMarketsPerWS?: number;
|
|
19
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebSocketStatus = void 0;
|
|
4
|
+
var WebSocketStatus;
|
|
5
|
+
(function (WebSocketStatus) {
|
|
6
|
+
WebSocketStatus["PENDING"] = "pending";
|
|
7
|
+
WebSocketStatus["ALIVE"] = "alive";
|
|
8
|
+
WebSocketStatus["DEAD"] = "dead";
|
|
9
|
+
WebSocketStatus["CLEANUP"] = "cleanup"; // Group is marked for cleanup
|
|
10
|
+
})(WebSocketStatus || (exports.WebSocketStatus = WebSocketStatus = {}));
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nevuamarkets/poly-websockets",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Plug-and-play Polymarket WebSocket price alerts",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"prepare": "npm run build",
|
|
14
|
+
"test": "vitest run"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/nevuamarkets/poly-websockets.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"polymarket",
|
|
22
|
+
"websocket",
|
|
23
|
+
"price",
|
|
24
|
+
"market",
|
|
25
|
+
"alerts"
|
|
26
|
+
],
|
|
27
|
+
"author": "Konstantinos Lekkas",
|
|
28
|
+
"license": "AGPL-3.0",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/nevuamarkets/poly-websockets/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/nevuamarkets/poly-websockets#readme",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"async-mutex": "^0.5.0",
|
|
35
|
+
"bottleneck": "^2.19.5",
|
|
36
|
+
"lodash": "^4.17.21",
|
|
37
|
+
"uuid": "^11.1.0",
|
|
38
|
+
"winston": "^3.17.0",
|
|
39
|
+
"ws": "^8.18.2"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/lodash": "^4.17.17",
|
|
43
|
+
"@types/ms": "^2.1.0",
|
|
44
|
+
"@types/ws": "^8.18.1",
|
|
45
|
+
"typescript": "^5.4.2",
|
|
46
|
+
"vitest": "^3.0.7",
|
|
47
|
+
"@types/node": "^22.13.11"
|
|
48
|
+
}
|
|
49
|
+
}
|