@nevuamarkets/poly-websockets
Version:
Plug-and-play Polymarket WebSocket price alerts
320 lines (319 loc) • 9.97 kB
TypeScript
/**
* Represents a single price level in the order book
* @example
* { price: "0.01", size: "510000" }
*/
export type PriceLevel = {
price: string;
size: string;
};
/**
* Initial subscription message sent when connecting to the market WebSocket.
*
* @example
* {
* assets_ids: ["71321045679252212594626385532706912750332728571942532289631379312455583992563"],
* type: "market"
* }
*/
export type MarketSubscriptionMessage = {
assets_ids: string[];
type: 'market';
custom_feature_enabled?: boolean;
};
/**
* Subscribe to additional assets on an existing connection.
*
* IMPORTANT: The Polymarket WebSocket protocol does NOT send any confirmation or
* acknowledgment message for subscribe operations. The server silently accepts the
* request, and the client will start receiving events for the subscribed assets.
* There is no way to know if a subscription was rejected (events simply won't arrive).
*
* @example
* {
* operation: "subscribe",
* assets_ids: ["71321045679252212594626385532706912750332728571942532289631379312455583992563"]
* }
*/
export type SubscribeMessage = {
operation: 'subscribe';
assets_ids: string[];
custom_feature_enabled?: boolean;
};
/**
* Unsubscribe from assets on an existing connection.
*
* IMPORTANT: The Polymarket WebSocket protocol does NOT send any confirmation or
* acknowledgment message for unsubscribe operations. The server silently accepts the
* request, and the client will stop receiving events for the unsubscribed assets.
* There is no way to know if an unsubscription was rejected.
*
* @example
* {
* operation: "unsubscribe",
* assets_ids: ["71321045679252212594626385532706912750332728571942532289631379312455583992563"]
* }
*/
export type UnsubscribeMessage = {
operation: 'unsubscribe';
assets_ids: string[];
custom_feature_enabled?: boolean;
};
/**
* Union type for all client-to-server messages.
*/
export type PolymarketWSMessage = MarketSubscriptionMessage | SubscribeMessage | UnsubscribeMessage;
/**
* Represents a single price change item
*/
export type PriceChangeItem = {
asset_id: string;
price: string;
size: string;
side: 'BUY' | 'SELL';
hash: string;
best_bid: string;
best_ask: string;
};
/**
* Represents a price_change event from Polymarket WebSocket
*
* @example
* {
* market: "0x5f65177b394277fd294cd75650044e32ba009a95022d88a0c1d565897d72f8f1",
* price_changes: [
* {
* asset_id: "71321045679252212594626385532706912750332728571942532289631379312455583992563",
* price: "0.5",
* size: "200",
* side: "BUY",
* hash: "56621a121a47ed9333273e21c83b660cff37ae50",
* best_bid: "0.5",
* best_ask: "1"
* }
* ],
* timestamp: "1757908892351",
* event_type: "price_change"
* }
*/
export type PriceChangeEvent = {
event_type: 'price_change';
market: string;
timestamp: string;
price_changes: PriceChangeItem[];
};
/**
* Represents a Polymarket book
* @example
* {
* bids: [
* { price: "0.01", size: "510000" },
* { price: "0.02", size: "3100" }
* ],
* asks: [
* { price: "0.99", size: "58.07" },
* { price: "0.97", size: "178.73" }
* }
*/
export type Book = {
bids: PriceLevel[];
asks: PriceLevel[];
};
/**
* Represents a book event from Polymarket WebSocket
* @example
* {
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
* timestamp: "1740759191594",
* hash: "c0e51b1cfdbcb1b2aec58feaf7b01004019a89c6",
* bids: [
* { price: "0.01", size: "510000" },
* { price: "0.02", size: "3100" }
* ],
* asks: [
* { price: "0.99", size: "58.07" },
* { price: "0.97", size: "178.73" }
* ],
* event_type: "book"
* }
*/
export type BookEvent = {
market: string;
asset_id: string;
timestamp: string;
hash: string;
bids: PriceLevel[];
asks: PriceLevel[];
event_type: 'book';
};
/**
* Represents a last trade price event from Polymarket WebSocket
* @example
* {
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
* event_type: "last_trade_price",
* fee_rate_bps: "0",
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
* price: "0.12",
* side: "BUY",
* size: "8.333332",
* timestamp: "1740760245471",
* transaction_hash: "0xd449923990fce41c5fcd1fef8079df5b1dc55fa00c2df62831d0bd3a7cdcc2aa"
* }
*/
export type LastTradePriceEvent = {
asset_id: string;
event_type: 'last_trade_price';
fee_rate_bps: string;
market: string;
price: string;
side: 'BUY' | 'SELL';
size: string;
timestamp: string;
transaction_hash: string;
};
/**
* Represents a tick size change event from Polymarket WebSocket
* @example
* {
* event_type: "tick_size_change",
* asset_id: "65818619657568813474341868652308942079804919287380422192892211131408793125422",
* market: "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
* old_tick_size: "0.01",
* new_tick_size: "0.001",
* timestamp: "100000000"
* }
*/
export type TickSizeChangeEvent = {
asset_id: string;
event_type: 'tick_size_change';
market: string;
old_tick_size: string;
new_tick_size: string;
timestamp: string;
};
/**
* Union type representing all possible event types from Polymarket WebSocket
* @example BookEvent
* {
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
* timestamp: "1740759191594",
* hash: "c0e51b1cfdbcb1b2aec58feaf7b01004019a89c6",
* bids: [{ price: "0.01", size: "510000" }],
* asks: [{ price: "0.99", size: "58.07" }],
* event_type: "book"
* }
*
* @example LastTradePriceEvent
* {
* asset_id: "101007741586870489619361069512452187353898396425142157315847015703471254508752",
* event_type: "last_trade_price",
* fee_rate_bps: "0",
* market: "0xf83fb46dd70a4459fcc441a8511701c463374c5c3c250f585d74fda85ddfb7c9",
* price: "0.12",
* side: "BUY",
* size: "8.333332",
* timestamp: "1740760245471"
* }
*
* @example PriceChangeEvent
* {
* market: "0x5f65177b394277fd294cd75650044e32ba009a95022d88a0c1d565897d72f8f1",
* price_changes: [
* {
* asset_id: "71321045679252212594626385532706912750332728571942532289631379312455583992563",
* price: "0.5",
* size: "200",
* side: "BUY",
* hash: "56621a121a47ed9333273e21c83b660cff37ae50",
* best_bid: "0.5",
* best_ask: "1"
* }
* ],
* timestamp: "1757908892351",
* event_type: "price_change"
* }
*
* @example TickSizeChangeEvent
* {
* event_type: "tick_size_change",
* asset_id: "65818619657568813474341868652308942079804919287380422192892211131408793125422",
* market: "0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af",
* old_tick_size: "0.01",
* new_tick_size: "0.001",
* timestamp: "100000000"
* }
*/
export type PolymarketWSEvent = BookEvent | LastTradePriceEvent | PriceChangeEvent | TickSizeChangeEvent;
/**
* Represents a price update event
*
* This is an event that is emitted to faciliate price update events. It is
* not emitted by the Polymarket WebSocket directly.
*
* See https://docs.polymarket.com/polymarket-learn/trading/how-are-prices-calculated
*
* TLDR: The prices displayed on Polymarket are the midpoint of the bid-ask spread in the orderbook,
* UNLESS that spread is over $0.10, in which case the **last traded price** is used.
*/
export interface PolymarketPriceUpdateEvent {
event_type: 'price_update';
asset_id: string;
timestamp: string;
triggeringEvent: LastTradePriceEvent | PriceChangeEvent;
book: Book;
price: string;
midpoint: string;
spread: string;
}
/**
* Represents the handlers for the Polymarket WebSocket
*/
export type WebSocketHandlers = {
onBook?: (events: BookEvent[]) => Promise<void>;
onLastTradePrice?: (events: LastTradePriceEvent[]) => Promise<void>;
onTickSizeChange?: (events: TickSizeChangeEvent[]) => Promise<void>;
onPriceChange?: (events: PriceChangeEvent[]) => Promise<void>;
onPolymarketPriceUpdate?: (events: PolymarketPriceUpdateEvent[]) => Promise<void>;
onError?: (error: Error) => Promise<void>;
onWSClose?: (managerId: string, code: number, reason: string) => Promise<void>;
onWSOpen?: (managerId: string, pendingAssetIds: string[]) => Promise<void>;
};
/**
* Type guard to check if an event is a BookEvent
* @example
* if (isBookEvent(event)) {
* // event is now typed as BookEvent
* console.log(event.bids);
* }
*/
export declare function isBookEvent(event: PolymarketWSEvent | PolymarketPriceUpdateEvent): event is BookEvent;
/**
* Type guard to check if an event is a LastTradePriceEvent
* @example
* if (isLastTradePriceEvent(event)) {
* // event is now typed as LastTradePriceEvent
* console.log(event.side);
* }
*/
export declare function isLastTradePriceEvent(event: PolymarketWSEvent | PolymarketPriceUpdateEvent): event is LastTradePriceEvent;
/**
* Type guard to check if an event is a PriceChangeEvent
* @example
* if (isPriceChangeEvent(event)) {
* // event is now typed as PriceChangeEvent
* console.log(event.changes);
* }
*/
export declare function isPriceChangeEvent(event: PolymarketWSEvent | PolymarketPriceUpdateEvent): event is PriceChangeEvent;
/**
* Type guard to check if an event is a TickSizeChangeEvent
* @example
* if (isTickSizeChangeEvent(event)) {
* // event is now typed as TickSizeChangeEvent
* console.log(event.old_tick_size);
* }
*/
export declare function isTickSizeChangeEvent(event: PolymarketWSEvent | PolymarketPriceUpdateEvent): event is TickSizeChangeEvent;