Skip to content

Commit 430b368

Browse files
committed
Add websocket support
1 parent 01e0d04 commit 430b368

File tree

4 files changed

+156
-43
lines changed

4 files changed

+156
-43
lines changed

polygon/websocket/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ async def connect(
140140
if m["ev"] == "status":
141141
logger.debug("status: %s", m["message"])
142142
continue
143-
cmsg = parse(msgJson, logger)
143+
cmsg = parse(msgJson, logger, self.market)
144144

145145
if len(cmsg) > 0:
146146
await processor(cmsg) # type: ignore

polygon/websocket/models/__init__.py

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,72 @@
33
from .models import *
44
import logging
55

6+
# Define the mapping of market and event type to model class
7+
MARKET_EVENT_MAP = {
8+
Market.Stocks: {
9+
"A": EquityAgg,
10+
"AM": EquityAgg,
11+
"T": EquityTrade,
12+
"Q": EquityQuote,
13+
"LULD": LimitUpLimitDown,
14+
"FMV": FairMarketValue,
15+
"NOI": Imbalance,
16+
"LV": LaunchpadValue,
17+
},
18+
Market.Options: {
19+
"A": EquityAgg,
20+
"AM": EquityAgg,
21+
"T": EquityTrade,
22+
"Q": EquityQuote,
23+
"FMV": FairMarketValue,
24+
},
25+
Market.Indices: {
26+
"A": EquityAgg,
27+
"AM": EquityAgg,
28+
"V": IndexValue,
29+
},
30+
Market.Futures: {
31+
"A": FuturesAgg,
32+
"AM": FuturesAgg,
33+
"T": FuturesTrade,
34+
"Q": FuturesQuote,
35+
},
36+
Market.Crypto: {
37+
"XA": CurrencyAgg,
38+
"XAS": CurrencyAgg,
39+
"XT": CryptoTrade,
40+
"XQ": CryptoQuote,
41+
"XL2": Level2Book,
42+
"FMV": FairMarketValue,
43+
},
44+
Market.Forex: {
45+
"CA": CurrencyAgg,
46+
"CAS": CurrencyAgg,
47+
"C": ForexQuote,
48+
"FMV": FairMarketValue,
49+
},
50+
}
651

7-
def parse_single(data: Dict[str, Any]):
52+
53+
def parse_single(data: Dict[str, Any], market: Market) -> Any:
854
event_type = data["ev"]
9-
if event_type in [EventType.EquityAgg.value, EventType.EquityAggMin.value]:
10-
return EquityAgg.from_dict(data)
11-
elif event_type in [
12-
EventType.CryptoAgg.value,
13-
EventType.CryptoAggSec.value,
14-
EventType.ForexAgg.value,
15-
EventType.ForexAggSec.value,
16-
]:
17-
return CurrencyAgg.from_dict(data)
18-
elif event_type == EventType.EquityTrade.value:
19-
return EquityTrade.from_dict(data)
20-
elif event_type == EventType.CryptoTrade.value:
21-
return CryptoTrade.from_dict(data)
22-
elif event_type == EventType.EquityQuote.value:
23-
return EquityQuote.from_dict(data)
24-
elif event_type == EventType.ForexQuote.value:
25-
return ForexQuote.from_dict(data)
26-
elif event_type == EventType.CryptoQuote.value:
27-
return CryptoQuote.from_dict(data)
28-
elif event_type == EventType.Imbalances.value:
29-
return Imbalance.from_dict(data)
30-
elif event_type == EventType.LimitUpLimitDown.value:
31-
return LimitUpLimitDown.from_dict(data)
32-
elif event_type == EventType.CryptoL2.value:
33-
return Level2Book.from_dict(data)
34-
elif event_type == EventType.Value.value:
35-
return IndexValue.from_dict(data)
36-
elif event_type == EventType.LaunchpadValue.value:
37-
return LaunchpadValue.from_dict(data)
38-
elif event_type == EventType.BusinessFairMarketValue.value:
39-
return FairMarketValue.from_dict(data)
40-
return None
55+
# Look up the model class based on market and event type
56+
model_class = MARKET_EVENT_MAP.get(market, {}).get(event_type)
57+
if model_class:
58+
return model_class.from_dict(data)
59+
else:
60+
# Log a warning for unrecognized event types, unless it's a status message
61+
if event_type != "status":
62+
logger.warning("Unknown event type '%s' for market %s", event_type, market)
63+
return None
4164

4265

43-
def parse(msg: List[Dict[str, Any]], logger: logging.Logger) -> List[WebSocketMessage]:
66+
def parse(
67+
msg: List[Dict[str, Any]], logger: logging.Logger, market: Market
68+
) -> List[WebSocketMessage]:
4469
res = []
4570
for m in msg:
46-
parsed = parse_single(m)
71+
parsed = parse_single(m, market)
4772
if parsed is None:
4873
if m["ev"] != "status":
4974
logger.warning("could not parse message %s", m)

polygon/websocket/models/common.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ class Feed(Enum):
1111
Launchpad = "launchpad.polygon.io"
1212
Business = "business.polygon.io"
1313
EdgxBusiness = "edgx-business.polygon.io"
14-
IEXBusiness = "iex-business.polygon.io"
1514
DelayedBusiness = "delayed-business.polygon.io"
1615
DelayedEdgxBusiness = "delayed-edgx-business.polygon.io"
1716
DelayedNasdaqLastSaleBusiness = "delayed-nasdaq-last-sale-business.polygon.io"
@@ -28,6 +27,7 @@ class Market(Enum):
2827
Forex = "forex"
2928
Crypto = "crypto"
3029
Indices = "indices"
30+
Futures = "futures"
3131

3232

3333
class EventType(Enum):
@@ -46,12 +46,10 @@ class EventType(Enum):
4646
LimitUpLimitDown = "LULD"
4747
CryptoL2 = "XL2"
4848
Value = "V"
49-
"""Launchpad* EventTypes are only available to Launchpad users. These values are the same across all asset classes (
50-
stocks, options, forex, crypto).
51-
"""
5249
LaunchpadValue = "LV"
5350
LaunchpadAggMin = "AM"
54-
"""Business* EventTypes are only available to Business users. These values are the same across all asset classes (
55-
stocks, options, forex, crypto).
56-
"""
5751
BusinessFairMarketValue = "FMV"
52+
FuturesTrade = "T"
53+
FuturesQuote = "Q"
54+
FuturesAgg = "A"
55+
FuturesAggMin = "AM"

polygon/websocket/models/models.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,93 @@ def from_dict(d):
359359
)
360360

361361

362+
@modelclass
363+
class FuturesTrade:
364+
event_type: Optional[str] = None
365+
symbol: Optional[str] = None
366+
price: Optional[float] = None
367+
size: Optional[int] = None
368+
timestamp: Optional[int] = None
369+
sequence_number: Optional[int] = None
370+
371+
@staticmethod
372+
def from_dict(d):
373+
return FuturesTrade(
374+
event_type=d.get("ev"),
375+
symbol=d.get("sym"),
376+
price=d.get("p"),
377+
size=d.get("s"),
378+
timestamp=d.get("t"),
379+
sequence_number=d.get("q"),
380+
)
381+
382+
383+
@modelclass
384+
class FuturesQuote:
385+
event_type: Optional[str] = None
386+
symbol: Optional[str] = None
387+
bid_price: Optional[float] = None
388+
bid_size: Optional[int] = None
389+
bid_timestamp: Optional[int] = None
390+
ask_price: Optional[float] = None
391+
ask_size: Optional[int] = None
392+
ask_timestamp: Optional[int] = None
393+
sip_timestamp: Optional[int] = None
394+
395+
@staticmethod
396+
def from_dict(d):
397+
return FuturesQuote(
398+
event_type=d.get("ev"),
399+
symbol=d.get("sym"),
400+
bid_price=d.get("bp"),
401+
bid_size=d.get("bs"),
402+
bid_timestamp=d.get("bt"),
403+
ask_price=d.get("ap"),
404+
ask_size=d.get("as"),
405+
ask_timestamp=d.get("at"),
406+
sip_timestamp=d.get("t"),
407+
)
408+
409+
410+
@modelclass
411+
class FuturesAgg:
412+
event_type: Optional[str] = None
413+
symbol: Optional[str] = None
414+
volume: Optional[float] = None
415+
accumulated_volume: Optional[float] = None
416+
official_open_price: Optional[float] = None
417+
vwap: Optional[float] = None
418+
open: Optional[float] = None
419+
close: Optional[float] = None
420+
high: Optional[float] = None
421+
low: Optional[float] = None
422+
aggregate_vwap: Optional[float] = None
423+
average_size: Optional[float] = None
424+
start_timestamp: Optional[int] = None
425+
end_timestamp: Optional[int] = None
426+
otc: Optional[bool] = None # If present
427+
428+
@staticmethod
429+
def from_dict(d):
430+
return FuturesAgg(
431+
event_type=d.get("ev"),
432+
symbol=d.get("sym"),
433+
volume=d.get("v"),
434+
accumulated_volume=d.get("av"),
435+
official_open_price=d.get("op"),
436+
vwap=d.get("vw"),
437+
open=d.get("o"),
438+
close=d.get("c"),
439+
high=d.get("h"),
440+
low=d.get("l"),
441+
aggregate_vwap=d.get("a"),
442+
average_size=d.get("z"),
443+
start_timestamp=d.get("s"),
444+
end_timestamp=d.get("e"),
445+
otc=d.get("otc"),
446+
)
447+
448+
362449
WebSocketMessage = NewType(
363450
"WebSocketMessage",
364451
List[
@@ -376,6 +463,9 @@ def from_dict(d):
376463
IndexValue,
377464
LaunchpadValue,
378465
FairMarketValue,
466+
FuturesTrade,
467+
FuturesQuote,
468+
FuturesAgg,
379469
]
380470
],
381471
)

0 commit comments

Comments
 (0)