massive 2.7.0__tar.gz → 2.9.0__tar.gz
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.
- {massive-2.7.0 → massive-2.9.0}/PKG-INFO +3 -3
- massive-2.9.0/massive/certs.py +27 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/__init__.py +26 -1
- {massive-2.7.0 → massive-2.9.0}/massive/rest/base.py +33 -10
- {massive-2.7.0 → massive-2.9.0}/massive/rest/futures.py +0 -1
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/aggs.py +4 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/conditions.py +5 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/contracts.py +2 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/dividends.py +1 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/exchanges.py +1 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/financials.py +57 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/futures.py +15 -9
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/indicators.py +5 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/markets.py +5 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/quotes.py +5 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/snapshot.py +15 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/splits.py +1 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/summaries.py +3 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/tickers.py +8 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/trades.py +2 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/reference.py +84 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/snapshot.py +55 -0
- {massive-2.7.0 → massive-2.9.0}/massive/websocket/__init__.py +55 -7
- {massive-2.7.0 → massive-2.9.0}/massive/websocket/models/models.py +9 -0
- {massive-2.7.0 → massive-2.9.0}/pyproject.toml +4 -4
- {massive-2.7.0 → massive-2.9.0}/LICENSE +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/__init__.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/exceptions.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/logging.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/modelclass.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/aggs.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/benzinga.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/economy.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/etf_global.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/financials.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/indicators.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/__init__.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/benzinga.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/common.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/economy.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/etf_global.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/request.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/models/tmx.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/quotes.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/summaries.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/tmx.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/trades.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/rest/vX.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/websocket/models/__init__.py +0 -0
- {massive-2.7.0 → massive-2.9.0}/massive/websocket/models/common.py +0 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: massive
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.0
|
|
4
4
|
Summary: Official Massive (formerly Polygon.io) REST and Websocket client.
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Keywords: massive,free,rest,stock,market,data,api,massive.com,websocket,client
|
|
8
8
|
Author: massive.com
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
18
|
Requires-Dist: certifi (>=2022.5.18,<2027.0.0)
|
|
19
19
|
Requires-Dist: urllib3 (>=1.26.9)
|
|
20
20
|
Requires-Dist: websockets (>=14.0)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import certifi
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def ca_bundle_path(logger: Optional[logging.Logger] = None) -> str:
|
|
9
|
+
"""
|
|
10
|
+
Path of the CA bundle used to verify TLS connections.
|
|
11
|
+
|
|
12
|
+
Honors the standard ``SSL_CERT_FILE`` and ``REQUESTS_CA_BUNDLE`` environment
|
|
13
|
+
variables (used for example behind corporate TLS inspection), falling back to
|
|
14
|
+
the certifi bundle. A variable that doesn't point to an existing file is
|
|
15
|
+
skipped with a warning, so a stale value doesn't break connections.
|
|
16
|
+
"""
|
|
17
|
+
for env_var in ("SSL_CERT_FILE", "REQUESTS_CA_BUNDLE"):
|
|
18
|
+
path = os.environ.get(env_var)
|
|
19
|
+
if not path:
|
|
20
|
+
continue
|
|
21
|
+
if os.path.isfile(path):
|
|
22
|
+
return path
|
|
23
|
+
if logger:
|
|
24
|
+
logger.warning(
|
|
25
|
+
"%s is set to %r, which isn't a file; ignoring it", env_var, path
|
|
26
|
+
)
|
|
27
|
+
return certifi.where()
|
|
@@ -20,7 +20,9 @@ from .reference import (
|
|
|
20
20
|
ContractsClient,
|
|
21
21
|
)
|
|
22
22
|
from .vX import VXClient
|
|
23
|
-
from
|
|
23
|
+
from .base import logger
|
|
24
|
+
from ..certs import ca_bundle_path
|
|
25
|
+
from typing import Optional, Any, Dict
|
|
24
26
|
import os
|
|
25
27
|
|
|
26
28
|
BASE = "https://api.massive.com"
|
|
@@ -60,7 +62,22 @@ class RESTClient(
|
|
|
60
62
|
verbose: bool = False,
|
|
61
63
|
trace: bool = False,
|
|
62
64
|
custom_json: Optional[Any] = None,
|
|
65
|
+
maxsize: int = 10,
|
|
66
|
+
ca_certs: Optional[str] = None,
|
|
67
|
+
proxy: Optional[str] = None,
|
|
68
|
+
connection_pool_kw: Optional[Dict[str, Any]] = None,
|
|
63
69
|
):
|
|
70
|
+
"""
|
|
71
|
+
:param maxsize: Connections kept open per host, for reuse across threads.
|
|
72
|
+
:param ca_certs: Path to a CA bundle to verify TLS with. Defaults to the
|
|
73
|
+
SSL_CERT_FILE or REQUESTS_CA_BUNDLE environment variable, then certifi.
|
|
74
|
+
:param proxy: Proxy URL to send requests through, e.g. "http://user:pass@proxy:8080".
|
|
75
|
+
Environment proxy variables (HTTPS_PROXY etc.) are not used.
|
|
76
|
+
:param connection_pool_kw: Other urllib3 PoolManager options. They override
|
|
77
|
+
the client's defaults.
|
|
78
|
+
"""
|
|
79
|
+
# resolve once, so the main and vx clients share it and warn only once
|
|
80
|
+
ca_certs = ca_certs or ca_bundle_path(logger)
|
|
64
81
|
super().__init__(
|
|
65
82
|
api_key=api_key,
|
|
66
83
|
connect_timeout=connect_timeout,
|
|
@@ -72,6 +89,10 @@ class RESTClient(
|
|
|
72
89
|
verbose=verbose,
|
|
73
90
|
trace=trace,
|
|
74
91
|
custom_json=custom_json,
|
|
92
|
+
maxsize=maxsize,
|
|
93
|
+
ca_certs=ca_certs,
|
|
94
|
+
proxy=proxy,
|
|
95
|
+
connection_pool_kw=connection_pool_kw,
|
|
75
96
|
)
|
|
76
97
|
self.vx = VXClient(
|
|
77
98
|
api_key=api_key,
|
|
@@ -84,4 +105,8 @@ class RESTClient(
|
|
|
84
105
|
verbose=verbose,
|
|
85
106
|
trace=trace,
|
|
86
107
|
custom_json=custom_json,
|
|
108
|
+
maxsize=maxsize,
|
|
109
|
+
ca_certs=ca_certs,
|
|
110
|
+
proxy=proxy,
|
|
111
|
+
connection_pool_kw=connection_pool_kw,
|
|
87
112
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import certifi
|
|
2
1
|
import json
|
|
3
2
|
import urllib3
|
|
4
3
|
import inspect
|
|
@@ -8,6 +7,7 @@ from typing import Optional, Any, Dict
|
|
|
8
7
|
from datetime import datetime
|
|
9
8
|
from importlib.metadata import version, PackageNotFoundError
|
|
10
9
|
from .models.request import RequestOptionBuilder
|
|
10
|
+
from ..certs import ca_bundle_path
|
|
11
11
|
from ..logging import get_logger
|
|
12
12
|
import logging
|
|
13
13
|
from urllib.parse import urlencode, urlparse
|
|
@@ -34,6 +34,10 @@ class BaseClient:
|
|
|
34
34
|
verbose: bool,
|
|
35
35
|
trace: bool,
|
|
36
36
|
custom_json: Optional[Any] = None,
|
|
37
|
+
maxsize: int = 10,
|
|
38
|
+
ca_certs: Optional[str] = None,
|
|
39
|
+
proxy: Optional[str] = None,
|
|
40
|
+
connection_pool_kw: Optional[Dict[str, Any]] = None,
|
|
37
41
|
):
|
|
38
42
|
if api_key is None:
|
|
39
43
|
raise AuthError(
|
|
@@ -73,14 +77,30 @@ class BaseClient:
|
|
|
73
77
|
|
|
74
78
|
# https://urllib3.readthedocs.io/en/stable/reference/urllib3.poolmanager.html
|
|
75
79
|
# https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool
|
|
76
|
-
|
|
77
|
-
num_pools
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
pool_kw: Dict[str, Any] = {
|
|
81
|
+
"num_pools": num_pools,
|
|
82
|
+
# connections kept per host; urllib3 defaults to 1, which makes threaded
|
|
83
|
+
# clients reconnect (and redo the TLS handshake) for most requests
|
|
84
|
+
"maxsize": maxsize,
|
|
85
|
+
"headers": self.headers, # default headers sent with each request.
|
|
86
|
+
"ca_certs": ca_certs or ca_bundle_path(logger),
|
|
87
|
+
"cert_reqs": "CERT_REQUIRED",
|
|
88
|
+
"retries": retry_strategy, # use the customized Retry instance
|
|
89
|
+
"timeout": self.timeout, # set timeout for each request
|
|
90
|
+
}
|
|
91
|
+
# any other urllib3 pool options, which take precedence over the defaults
|
|
92
|
+
pool_kw.update(connection_pool_kw or {})
|
|
93
|
+
self.client: urllib3.PoolManager
|
|
94
|
+
if proxy:
|
|
95
|
+
# https://urllib3.readthedocs.io/en/stable/advanced-usage.html#proxies
|
|
96
|
+
proxy_auth = urllib3.util.parse_url(proxy).auth
|
|
97
|
+
if proxy_auth:
|
|
98
|
+
pool_kw.setdefault(
|
|
99
|
+
"proxy_headers", urllib3.make_headers(proxy_basic_auth=proxy_auth)
|
|
100
|
+
)
|
|
101
|
+
self.client = urllib3.ProxyManager(proxy, **pool_kw)
|
|
102
|
+
else:
|
|
103
|
+
self.client = urllib3.PoolManager(**pool_kw)
|
|
84
104
|
|
|
85
105
|
if verbose:
|
|
86
106
|
logger.setLevel(logging.DEBUG)
|
|
@@ -194,7 +214,10 @@ class BaseClient:
|
|
|
194
214
|
# using ., and not _ (removesuffix would work)
|
|
195
215
|
# but that is python 3.9+
|
|
196
216
|
argname = argname[: -len(f"_{ext}")] + f".{ext}"
|
|
197
|
-
|
|
217
|
+
# any_of accepts a comma-separated str (per the spec) or a
|
|
218
|
+
# list of values; only join the latter, since joining a str
|
|
219
|
+
# would split it into single characters
|
|
220
|
+
if argname.endswith("any_of") and not isinstance(val, str):
|
|
198
221
|
val = ",".join(val)
|
|
199
222
|
params[argname] = val
|
|
200
223
|
|
|
@@ -239,7 +239,6 @@ class FuturesClient(BaseClient):
|
|
|
239
239
|
product_code_lt: Optional[str] = None,
|
|
240
240
|
product_code_lte: Optional[str] = None,
|
|
241
241
|
session_end_date: Optional[str] = None,
|
|
242
|
-
session_end_date_any_of: Optional[str] = None,
|
|
243
242
|
session_end_date_gt: Optional[str] = None,
|
|
244
243
|
session_end_date_gte: Optional[str] = None,
|
|
245
244
|
session_end_date_lt: Optional[str] = None,
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class Agg:
|
|
7
7
|
"Contains aggregate data for a given ticker symbol over a given date range in a custom time window size."
|
|
8
|
+
|
|
8
9
|
open: Optional[float] = None
|
|
9
10
|
high: Optional[float] = None
|
|
10
11
|
low: Optional[float] = None
|
|
@@ -33,6 +34,7 @@ class Agg:
|
|
|
33
34
|
@modelclass
|
|
34
35
|
class GroupedDailyAgg:
|
|
35
36
|
"Contains daily open, high, low, and close (OHLC) data for a given date."
|
|
37
|
+
|
|
36
38
|
ticker: Optional[str] = None
|
|
37
39
|
open: Optional[float] = None
|
|
38
40
|
high: Optional[float] = None
|
|
@@ -63,6 +65,7 @@ class GroupedDailyAgg:
|
|
|
63
65
|
@modelclass
|
|
64
66
|
class DailyOpenCloseAgg:
|
|
65
67
|
"Contains data for open, close and afterhours prices of a ticker symbol on a specified date."
|
|
68
|
+
|
|
66
69
|
after_hours: Optional[float] = None
|
|
67
70
|
close: Optional[float] = None
|
|
68
71
|
from_: Optional[str] = None
|
|
@@ -95,6 +98,7 @@ class DailyOpenCloseAgg:
|
|
|
95
98
|
@modelclass
|
|
96
99
|
class PreviousCloseAgg:
|
|
97
100
|
"Contains data for the previous day's open, high, low, and close (OHLC) of the specified stock ticker."
|
|
101
|
+
|
|
98
102
|
ticker: Optional[str] = None
|
|
99
103
|
close: Optional[float] = None
|
|
100
104
|
high: Optional[float] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class SipMapping:
|
|
7
7
|
"Contains data for a mapping to a symbol for each SIP that has a given condition."
|
|
8
|
+
|
|
8
9
|
CTA: Optional[str] = None
|
|
9
10
|
OPRA: Optional[str] = None
|
|
10
11
|
UTP: Optional[str] = None
|
|
@@ -17,6 +18,7 @@ class SipMapping:
|
|
|
17
18
|
@modelclass
|
|
18
19
|
class Consolidated:
|
|
19
20
|
"Contains data for aggregation rules on a consolidated (all exchanges) basis."
|
|
21
|
+
|
|
20
22
|
updates_high_low: Optional[bool] = None
|
|
21
23
|
updates_open_close: Optional[bool] = None
|
|
22
24
|
updates_volume: Optional[bool] = None
|
|
@@ -29,6 +31,7 @@ class Consolidated:
|
|
|
29
31
|
@modelclass
|
|
30
32
|
class MarketCenter:
|
|
31
33
|
"Contains data for aggregation rules on a per-market-center basis."
|
|
34
|
+
|
|
32
35
|
updates_high_low: Optional[bool] = None
|
|
33
36
|
updates_open_close: Optional[bool] = None
|
|
34
37
|
updates_volume: Optional[bool] = None
|
|
@@ -41,6 +44,7 @@ class MarketCenter:
|
|
|
41
44
|
@modelclass
|
|
42
45
|
class UpdateRules:
|
|
43
46
|
"Contains data for a list of aggregation rules."
|
|
47
|
+
|
|
44
48
|
consolidated: Optional[Consolidated] = None
|
|
45
49
|
market_center: Optional[MarketCenter] = None
|
|
46
50
|
|
|
@@ -63,6 +67,7 @@ class UpdateRules:
|
|
|
63
67
|
@modelclass
|
|
64
68
|
class Condition:
|
|
65
69
|
"Condition contains data for a condition that Massive.com uses."
|
|
70
|
+
|
|
66
71
|
abbreviation: Optional[str] = None
|
|
67
72
|
asset_class: Optional[str] = None
|
|
68
73
|
data_types: Optional[List[str]] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class Underlying:
|
|
7
7
|
"Underlying contains data for an underlying or deliverable associated with an option contract."
|
|
8
|
+
|
|
8
9
|
amount: Optional[float] = None
|
|
9
10
|
type: Optional[str] = None
|
|
10
11
|
underlying: Optional[str] = None
|
|
@@ -17,6 +18,7 @@ class Underlying:
|
|
|
17
18
|
@modelclass
|
|
18
19
|
class OptionsContract:
|
|
19
20
|
"OptionsContract contains data for a specified ticker symbol."
|
|
21
|
+
|
|
20
22
|
additional_underlyings: Optional[List[Underlying]] = None
|
|
21
23
|
cfi: Optional[str] = None
|
|
22
24
|
contract_type: Optional[str] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class Dividend:
|
|
7
7
|
"Dividend contains data for a historical cash dividend, including the ticker symbol, declaration date, ex-dividend date, record date, pay date, frequency, and amount."
|
|
8
|
+
|
|
8
9
|
id: Optional[int] = None
|
|
9
10
|
cash_amount: Optional[float] = None
|
|
10
11
|
currency: Optional[str] = None
|
|
@@ -922,6 +922,63 @@ class RiskFactorTaxonomy:
|
|
|
922
922
|
)
|
|
923
923
|
|
|
924
924
|
|
|
925
|
+
@modelclass
|
|
926
|
+
class Disclosure:
|
|
927
|
+
"""
|
|
928
|
+
A single tagged disclosure within an SEC 8-K filing. A filing can produce
|
|
929
|
+
multiple disclosures, each classified into primary, secondary, and tertiary
|
|
930
|
+
categories with a supporting text excerpt.
|
|
931
|
+
"""
|
|
932
|
+
|
|
933
|
+
accession_number: Optional[str] = None
|
|
934
|
+
cik: Optional[str] = None
|
|
935
|
+
filing_date: Optional[str] = None
|
|
936
|
+
filing_url: Optional[str] = None
|
|
937
|
+
primary_category: Optional[str] = None
|
|
938
|
+
secondary_category: Optional[str] = None
|
|
939
|
+
supporting_text: Optional[str] = None
|
|
940
|
+
tertiary_category: Optional[str] = None
|
|
941
|
+
tickers: Optional[List[str]] = None
|
|
942
|
+
|
|
943
|
+
@staticmethod
|
|
944
|
+
def from_dict(d):
|
|
945
|
+
return Disclosure(
|
|
946
|
+
accession_number=d.get("accession_number"),
|
|
947
|
+
cik=d.get("cik"),
|
|
948
|
+
filing_date=d.get("filing_date"),
|
|
949
|
+
filing_url=d.get("filing_url"),
|
|
950
|
+
primary_category=d.get("primary_category"),
|
|
951
|
+
secondary_category=d.get("secondary_category"),
|
|
952
|
+
supporting_text=d.get("supporting_text"),
|
|
953
|
+
tertiary_category=d.get("tertiary_category"),
|
|
954
|
+
tickers=d.get("tickers"),
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
@modelclass
|
|
959
|
+
class DisclosureTaxonomy:
|
|
960
|
+
"""
|
|
961
|
+
A single 8-K disclosure classification, part of the complete list of
|
|
962
|
+
classifications used in the 8-K disclosures endpoint.
|
|
963
|
+
"""
|
|
964
|
+
|
|
965
|
+
description: Optional[str] = None
|
|
966
|
+
primary_category: Optional[str] = None
|
|
967
|
+
secondary_category: Optional[str] = None
|
|
968
|
+
taxonomy: Optional[str] = None
|
|
969
|
+
tertiary_category: Optional[str] = None
|
|
970
|
+
|
|
971
|
+
@staticmethod
|
|
972
|
+
def from_dict(d):
|
|
973
|
+
return DisclosureTaxonomy(
|
|
974
|
+
description=d.get("description"),
|
|
975
|
+
primary_category=d.get("primary_category"),
|
|
976
|
+
secondary_category=d.get("secondary_category"),
|
|
977
|
+
taxonomy=d.get("taxonomy"),
|
|
978
|
+
tertiary_category=d.get("tertiary_category"),
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
|
|
925
982
|
@modelclass
|
|
926
983
|
@dataclass
|
|
927
984
|
class Filing13F:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Optional, List
|
|
1
|
+
from typing import Optional, List, Union
|
|
2
2
|
from ...modelclass import modelclass
|
|
3
3
|
|
|
4
4
|
|
|
@@ -150,6 +150,7 @@ class FuturesQuote:
|
|
|
150
150
|
bid_price: Optional[float] = None
|
|
151
151
|
bid_size: Optional[float] = None
|
|
152
152
|
bid_timestamp: Optional[int] = None
|
|
153
|
+
channel: Optional[int] = None
|
|
153
154
|
report_sequence: Optional[int] = None
|
|
154
155
|
sequence_number: Optional[int] = None
|
|
155
156
|
|
|
@@ -165,6 +166,7 @@ class FuturesQuote:
|
|
|
165
166
|
bid_price=d.get("bid_price"),
|
|
166
167
|
bid_size=d.get("bid_size"),
|
|
167
168
|
bid_timestamp=d.get("bid_timestamp"),
|
|
169
|
+
channel=d.get("channel"),
|
|
168
170
|
report_sequence=d.get("report_sequence"),
|
|
169
171
|
sequence_number=d.get("sequence_number"),
|
|
170
172
|
)
|
|
@@ -180,6 +182,7 @@ class FuturesTrade:
|
|
|
180
182
|
ticker: Optional[str] = None
|
|
181
183
|
timestamp: Optional[int] = None
|
|
182
184
|
session_end_date: Optional[str] = None
|
|
185
|
+
channel: Optional[int] = None
|
|
183
186
|
price: Optional[float] = None
|
|
184
187
|
size: Optional[float] = None
|
|
185
188
|
report_sequence: Optional[int] = None
|
|
@@ -191,6 +194,7 @@ class FuturesTrade:
|
|
|
191
194
|
ticker=d.get("ticker"),
|
|
192
195
|
timestamp=d.get("timestamp"),
|
|
193
196
|
session_end_date=d.get("session_end_date"),
|
|
197
|
+
channel=d.get("channel"),
|
|
194
198
|
price=d.get("price"),
|
|
195
199
|
size=d.get("size"),
|
|
196
200
|
report_sequence=d.get("report_sequence"),
|
|
@@ -248,13 +252,17 @@ class FuturesMarketStatus:
|
|
|
248
252
|
@modelclass
|
|
249
253
|
class FuturesSnapshotDetails:
|
|
250
254
|
open_interest: Optional[int] = None
|
|
251
|
-
settlement_date: Optional[int] = None
|
|
255
|
+
settlement_date: Optional[Union[str, int]] = None
|
|
256
|
+
ticker: Optional[str] = None
|
|
257
|
+
product_code: Optional[str] = None
|
|
252
258
|
|
|
253
259
|
@staticmethod
|
|
254
260
|
def from_dict(d):
|
|
255
261
|
return FuturesSnapshotDetails(
|
|
256
262
|
open_interest=d.get("open_interest"),
|
|
257
263
|
settlement_date=d.get("settlement_date"),
|
|
264
|
+
ticker=d.get("ticker"),
|
|
265
|
+
product_code=d.get("product_code"),
|
|
258
266
|
)
|
|
259
267
|
|
|
260
268
|
|
|
@@ -354,6 +362,7 @@ class FuturesSnapshotSession:
|
|
|
354
362
|
class FuturesSnapshot:
|
|
355
363
|
ticker: Optional[str] = None
|
|
356
364
|
product_code: Optional[str] = None
|
|
365
|
+
|
|
357
366
|
details: Optional[FuturesSnapshotDetails] = None
|
|
358
367
|
last_minute: Optional[FuturesSnapshotMinute] = None
|
|
359
368
|
last_quote: Optional[FuturesSnapshotQuote] = None
|
|
@@ -362,14 +371,11 @@ class FuturesSnapshot:
|
|
|
362
371
|
|
|
363
372
|
@staticmethod
|
|
364
373
|
def from_dict(d):
|
|
374
|
+
details_dict = d.get("details") or {}
|
|
365
375
|
return FuturesSnapshot(
|
|
366
|
-
ticker=d.get("ticker"),
|
|
367
|
-
product_code=d.get("product_code"),
|
|
368
|
-
details=(
|
|
369
|
-
FuturesSnapshotDetails.from_dict(d.get("details", {}))
|
|
370
|
-
if d.get("details")
|
|
371
|
-
else None
|
|
372
|
-
),
|
|
376
|
+
ticker=d.get("ticker") or details_dict.get("ticker"),
|
|
377
|
+
product_code=d.get("product_code") or details_dict.get("product_code"),
|
|
378
|
+
details=FuturesSnapshotDetails.from_dict(details_dict),
|
|
373
379
|
last_minute=(
|
|
374
380
|
FuturesSnapshotMinute.from_dict(d.get("last_minute", {}))
|
|
375
381
|
if d.get("last_minute")
|
|
@@ -6,6 +6,7 @@ from .aggs import Agg
|
|
|
6
6
|
@modelclass
|
|
7
7
|
class IndicatorValue:
|
|
8
8
|
"Contains one datum for indicators with a single value."
|
|
9
|
+
|
|
9
10
|
timestamp: Optional[int] = None
|
|
10
11
|
value: Optional[float] = None
|
|
11
12
|
|
|
@@ -20,6 +21,7 @@ class IndicatorValue:
|
|
|
20
21
|
@modelclass
|
|
21
22
|
class MACDIndicatorValue:
|
|
22
23
|
"Contains one datum for all MACD values."
|
|
24
|
+
|
|
23
25
|
timestamp: Optional[int] = None
|
|
24
26
|
value: Optional[float] = None
|
|
25
27
|
signal: Optional[float] = None
|
|
@@ -38,6 +40,7 @@ class MACDIndicatorValue:
|
|
|
38
40
|
@modelclass
|
|
39
41
|
class IndicatorUnderlying:
|
|
40
42
|
"Contains the URL to call to get the aggs used for building the indicator."
|
|
43
|
+
|
|
41
44
|
url: Optional[str] = None
|
|
42
45
|
aggregates: Optional[List[Agg]] = None
|
|
43
46
|
|
|
@@ -52,6 +55,7 @@ class IndicatorUnderlying:
|
|
|
52
55
|
@modelclass
|
|
53
56
|
class SingleIndicatorResults:
|
|
54
57
|
"Contains indicator values and Underlying."
|
|
58
|
+
|
|
55
59
|
values: Optional[List[IndicatorValue]] = None
|
|
56
60
|
underlying: Optional[IndicatorUnderlying] = None
|
|
57
61
|
|
|
@@ -71,6 +75,7 @@ RSIIndicatorResults = SingleIndicatorResults
|
|
|
71
75
|
@modelclass
|
|
72
76
|
class MACDIndicatorResults:
|
|
73
77
|
"Contains indicator values and Underlying."
|
|
78
|
+
|
|
74
79
|
values: Optional[List[MACDIndicatorValue]] = None
|
|
75
80
|
underlying: Optional[IndicatorUnderlying] = None
|
|
76
81
|
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class MarketCurrencies:
|
|
7
7
|
"Contains currency market status data."
|
|
8
|
+
|
|
8
9
|
crypto: Optional[str] = None
|
|
9
10
|
fx: Optional[str] = None
|
|
10
11
|
|
|
@@ -16,6 +17,7 @@ class MarketCurrencies:
|
|
|
16
17
|
@modelclass
|
|
17
18
|
class MarketExchanges:
|
|
18
19
|
"Contains exchange market status data."
|
|
20
|
+
|
|
19
21
|
nasdaq: Optional[str] = None
|
|
20
22
|
nyse: Optional[str] = None
|
|
21
23
|
otc: Optional[str] = None
|
|
@@ -28,6 +30,7 @@ class MarketExchanges:
|
|
|
28
30
|
@modelclass
|
|
29
31
|
class MarketIndices:
|
|
30
32
|
"Contains indices market status data."
|
|
33
|
+
|
|
31
34
|
s_and_p: Optional[str] = None
|
|
32
35
|
societe_generale: Optional[str] = None
|
|
33
36
|
cgi: Optional[str] = None
|
|
@@ -47,6 +50,7 @@ class MarketIndices:
|
|
|
47
50
|
@modelclass
|
|
48
51
|
class MarketHoliday:
|
|
49
52
|
"MarketHoliday contains data for upcoming market holidays and their open/close times."
|
|
53
|
+
|
|
50
54
|
close: Optional[str] = None
|
|
51
55
|
date: Optional[str] = None
|
|
52
56
|
exchange: Optional[str] = None
|
|
@@ -62,6 +66,7 @@ class MarketHoliday:
|
|
|
62
66
|
@modelclass
|
|
63
67
|
class MarketStatus:
|
|
64
68
|
"MarketStatus contains data for the current trading status of the exchanges and overall financial markets."
|
|
69
|
+
|
|
65
70
|
after_hours: Optional[bool] = None
|
|
66
71
|
currencies: Optional[MarketCurrencies] = None
|
|
67
72
|
early_hours: Optional[bool] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class Quote:
|
|
7
7
|
"Quote contains quote data for a specified ticker symbol."
|
|
8
|
+
|
|
8
9
|
ask_exchange: Optional[int] = None
|
|
9
10
|
ask_price: Optional[float] = None
|
|
10
11
|
ask_size: Optional[float] = None
|
|
@@ -27,6 +28,7 @@ class Quote:
|
|
|
27
28
|
@modelclass
|
|
28
29
|
class LastQuote:
|
|
29
30
|
"LastQuote contains data for the most recent NBBO (Quote) tick for a given stock."
|
|
31
|
+
|
|
30
32
|
ticker: Optional[str] = None
|
|
31
33
|
trf_timestamp: Optional[int] = None
|
|
32
34
|
sequence_number: Optional[int] = None
|
|
@@ -65,6 +67,7 @@ class LastQuote:
|
|
|
65
67
|
@modelclass
|
|
66
68
|
class ForexQuote:
|
|
67
69
|
"Contains data for a forex quote."
|
|
70
|
+
|
|
68
71
|
ask: Optional[float] = None
|
|
69
72
|
bid: Optional[float] = None
|
|
70
73
|
exchange: Optional[int] = None
|
|
@@ -78,6 +81,7 @@ class ForexQuote:
|
|
|
78
81
|
@modelclass
|
|
79
82
|
class LastForexQuote:
|
|
80
83
|
"ForexLastQuote contains data for the last quote tick for a forex currency pair."
|
|
84
|
+
|
|
81
85
|
last: Optional[ForexQuote] = None
|
|
82
86
|
symbol: Optional[str] = None
|
|
83
87
|
|
|
@@ -92,6 +96,7 @@ class LastForexQuote:
|
|
|
92
96
|
@modelclass
|
|
93
97
|
class RealTimeCurrencyConversion:
|
|
94
98
|
"RealTimeCurrencyConversion contains data for currency conversions using the latest market conversion rates."
|
|
99
|
+
|
|
95
100
|
converted: Optional[float] = None
|
|
96
101
|
from_: Optional[str] = None
|
|
97
102
|
initial_amount: Optional[float] = None
|
|
@@ -43,6 +43,7 @@ class MinuteSnapshot:
|
|
|
43
43
|
@modelclass
|
|
44
44
|
class IndicesSession:
|
|
45
45
|
"Contains data for the most recent daily bar in an options contract."
|
|
46
|
+
|
|
46
47
|
change: Optional[float] = None
|
|
47
48
|
change_percent: Optional[float] = None
|
|
48
49
|
close: Optional[float] = None
|
|
@@ -66,6 +67,8 @@ class IndicesSnapshot:
|
|
|
66
67
|
session: Optional[IndicesSession] = None
|
|
67
68
|
error: Optional[str] = None
|
|
68
69
|
message: Optional[str] = None
|
|
70
|
+
last_updated: Optional[int] = None
|
|
71
|
+
timeframe: Optional[str] = None
|
|
69
72
|
|
|
70
73
|
@staticmethod
|
|
71
74
|
def from_dict(d):
|
|
@@ -80,12 +83,15 @@ class IndicesSnapshot:
|
|
|
80
83
|
),
|
|
81
84
|
error=d.get("error", None),
|
|
82
85
|
message=d.get("message", None),
|
|
86
|
+
last_updated=d.get("last_updated", None),
|
|
87
|
+
timeframe=d.get("timeframe", None),
|
|
83
88
|
)
|
|
84
89
|
|
|
85
90
|
|
|
86
91
|
@modelclass
|
|
87
92
|
class TickerSnapshot:
|
|
88
93
|
"Contains the most up-to-date market data for all traded ticker symbols."
|
|
94
|
+
|
|
89
95
|
day: Optional[Agg] = None
|
|
90
96
|
last_quote: Optional[LastQuote] = None
|
|
91
97
|
last_trade: Optional[LastTrade] = None
|
|
@@ -120,6 +126,7 @@ class TickerSnapshot:
|
|
|
120
126
|
@modelclass
|
|
121
127
|
class DayOptionContractSnapshot:
|
|
122
128
|
"Contains data for the most recent daily bar in an options contract."
|
|
129
|
+
|
|
123
130
|
change: Optional[float] = None
|
|
124
131
|
change_percent: Optional[float] = None
|
|
125
132
|
close: Optional[float] = None
|
|
@@ -139,6 +146,7 @@ class DayOptionContractSnapshot:
|
|
|
139
146
|
@modelclass
|
|
140
147
|
class OptionDetails:
|
|
141
148
|
"Contains details for an options contract."
|
|
149
|
+
|
|
142
150
|
contract_type: Optional[str] = None
|
|
143
151
|
exercise_style: Optional[str] = None
|
|
144
152
|
expiration_date: Optional[str] = None
|
|
@@ -154,6 +162,7 @@ class OptionDetails:
|
|
|
154
162
|
@modelclass
|
|
155
163
|
class LastQuoteOptionContractSnapshot:
|
|
156
164
|
"Contains data for the most recent quote in an options contract."
|
|
165
|
+
|
|
157
166
|
ask: Optional[float] = None
|
|
158
167
|
ask_size: Optional[float] = None
|
|
159
168
|
bid: Optional[float] = None
|
|
@@ -170,6 +179,7 @@ class LastQuoteOptionContractSnapshot:
|
|
|
170
179
|
@modelclass
|
|
171
180
|
class LastTradeOptionContractSnapshot:
|
|
172
181
|
"Contains data for the most recent trade for an options contract."
|
|
182
|
+
|
|
173
183
|
price: Optional[float] = None
|
|
174
184
|
sip_timestamp: Optional[int] = None
|
|
175
185
|
size: Optional[int] = None
|
|
@@ -185,6 +195,7 @@ class LastTradeOptionContractSnapshot:
|
|
|
185
195
|
@modelclass
|
|
186
196
|
class Greeks:
|
|
187
197
|
"Contains data for the greeks in an options contract."
|
|
198
|
+
|
|
188
199
|
delta: Optional[float] = None
|
|
189
200
|
gamma: Optional[float] = None
|
|
190
201
|
theta: Optional[float] = None
|
|
@@ -198,6 +209,7 @@ class Greeks:
|
|
|
198
209
|
@modelclass
|
|
199
210
|
class UnderlyingAsset:
|
|
200
211
|
"Contains data for the underlying stock in an options contract."
|
|
212
|
+
|
|
201
213
|
change_to_break_even: Optional[float] = None
|
|
202
214
|
last_updated: Optional[int] = None
|
|
203
215
|
price: Optional[float] = None
|
|
@@ -213,6 +225,7 @@ class UnderlyingAsset:
|
|
|
213
225
|
@modelclass
|
|
214
226
|
class OptionContractSnapshot:
|
|
215
227
|
"Contains data for the snapshot of an option contract of a stock equity."
|
|
228
|
+
|
|
216
229
|
break_even_price: Optional[float] = None
|
|
217
230
|
day: Optional[DayOptionContractSnapshot] = None
|
|
218
231
|
details: Optional[OptionDetails] = None
|
|
@@ -261,6 +274,7 @@ class OptionContractSnapshot:
|
|
|
261
274
|
@modelclass
|
|
262
275
|
class OrderBookQuote:
|
|
263
276
|
"Contains data for a book bid or ask."
|
|
277
|
+
|
|
264
278
|
price: Optional[float] = None
|
|
265
279
|
exchange_shares: Optional[Dict[str, float]] = None
|
|
266
280
|
|
|
@@ -275,6 +289,7 @@ class OrderBookQuote:
|
|
|
275
289
|
@modelclass
|
|
276
290
|
class SnapshotTickerFullBook:
|
|
277
291
|
"Contains the current level 2 book of a single ticker. This is the combined book from all of the exchanges."
|
|
292
|
+
|
|
278
293
|
ticker: Optional[str] = None
|
|
279
294
|
bids: Optional[List[OrderBookQuote]] = None
|
|
280
295
|
asks: Optional[List[OrderBookQuote]] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class Split:
|
|
7
7
|
"Split contains data for a historical stock split, including the ticker symbol, the execution date, and the factors of the split ratio."
|
|
8
|
+
|
|
8
9
|
id: Optional[int] = None
|
|
9
10
|
execution_date: Optional[str] = None
|
|
10
11
|
split_from: Optional[int] = None
|
|
@@ -6,6 +6,7 @@ from .tickers import Branding
|
|
|
6
6
|
@modelclass
|
|
7
7
|
class Session:
|
|
8
8
|
"Contains Session data for the summaries endpoint."
|
|
9
|
+
|
|
9
10
|
change: Optional[float] = None
|
|
10
11
|
change_percent: Optional[float] = None
|
|
11
12
|
early_trading_change: Optional[float] = None
|
|
@@ -27,6 +28,7 @@ class Session:
|
|
|
27
28
|
@modelclass
|
|
28
29
|
class Options:
|
|
29
30
|
"Contains options data for the summaries endpoint"
|
|
31
|
+
|
|
30
32
|
contract_type: Optional[str] = None
|
|
31
33
|
exercise_style: Optional[str] = None
|
|
32
34
|
expiration_date: Optional[str] = None
|
|
@@ -42,6 +44,7 @@ class Options:
|
|
|
42
44
|
@modelclass
|
|
43
45
|
class SummaryResult:
|
|
44
46
|
"Contains summary result data for a list of tickers"
|
|
47
|
+
|
|
45
48
|
price: Optional[float] = None
|
|
46
49
|
name: Optional[str] = None
|
|
47
50
|
ticker: Optional[str] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class CompanyAddress:
|
|
7
7
|
"Contains address data for a ticker detail."
|
|
8
|
+
|
|
8
9
|
address1: Optional[str] = None
|
|
9
10
|
address2: Optional[str] = None
|
|
10
11
|
city: Optional[str] = None
|
|
@@ -20,6 +21,7 @@ class CompanyAddress:
|
|
|
20
21
|
@modelclass
|
|
21
22
|
class Branding:
|
|
22
23
|
"Contains branding data for a ticker detail."
|
|
24
|
+
|
|
23
25
|
icon_url: Optional[str] = None
|
|
24
26
|
logo_url: Optional[str] = None
|
|
25
27
|
accent_color: Optional[str] = None
|
|
@@ -34,6 +36,7 @@ class Branding:
|
|
|
34
36
|
@modelclass
|
|
35
37
|
class Insight:
|
|
36
38
|
"Contains the insights related to the article."
|
|
39
|
+
|
|
37
40
|
sentiment: Optional[str] = None
|
|
38
41
|
sentiment_reasoning: Optional[str] = None
|
|
39
42
|
ticker: Optional[str] = None
|
|
@@ -46,6 +49,7 @@ class Insight:
|
|
|
46
49
|
@modelclass
|
|
47
50
|
class Publisher:
|
|
48
51
|
"Contains publisher data for ticker news."
|
|
52
|
+
|
|
49
53
|
favicon_url: Optional[str] = None
|
|
50
54
|
homepage_url: Optional[str] = None
|
|
51
55
|
logo_url: Optional[str] = None
|
|
@@ -59,6 +63,7 @@ class Publisher:
|
|
|
59
63
|
@modelclass
|
|
60
64
|
class Ticker:
|
|
61
65
|
"Ticker contains data for a specified ticker symbol."
|
|
66
|
+
|
|
62
67
|
active: Optional[bool] = None
|
|
63
68
|
cik: Optional[str] = None
|
|
64
69
|
composite_figi: Optional[str] = None
|
|
@@ -85,6 +90,7 @@ class Ticker:
|
|
|
85
90
|
@modelclass
|
|
86
91
|
class TickerDetails:
|
|
87
92
|
"TickerDetails contains data for a specified ticker symbol."
|
|
93
|
+
|
|
88
94
|
active: Optional[bool] = None
|
|
89
95
|
address: Optional[CompanyAddress] = None
|
|
90
96
|
branding: Optional[Branding] = None
|
|
@@ -157,6 +163,7 @@ class TickerDetails:
|
|
|
157
163
|
@modelclass
|
|
158
164
|
class TickerNews:
|
|
159
165
|
"TickerDetails contains data for news articles relating to a stock ticker symbol."
|
|
166
|
+
|
|
160
167
|
amp_url: Optional[str] = None
|
|
161
168
|
article_url: Optional[str] = None
|
|
162
169
|
author: Optional[str] = None
|
|
@@ -197,6 +204,7 @@ class TickerNews:
|
|
|
197
204
|
@modelclass
|
|
198
205
|
class TickerTypes:
|
|
199
206
|
"TickerTypes contains data for ticker types."
|
|
207
|
+
|
|
200
208
|
asset_class: Optional[str] = None
|
|
201
209
|
code: Optional[str] = None
|
|
202
210
|
description: Optional[str] = None
|
|
@@ -5,6 +5,7 @@ from ...modelclass import modelclass
|
|
|
5
5
|
@modelclass
|
|
6
6
|
class Trade:
|
|
7
7
|
"Trade contains trade data for a specified ticker symbol."
|
|
8
|
+
|
|
8
9
|
conditions: Optional[List[int]] = None
|
|
9
10
|
correction: Optional[int] = None
|
|
10
11
|
exchange: Optional[int] = None
|
|
@@ -66,6 +67,7 @@ class LastTrade:
|
|
|
66
67
|
@modelclass
|
|
67
68
|
class CryptoTrade:
|
|
68
69
|
"Contains data for a crypto trade."
|
|
70
|
+
|
|
69
71
|
conditions: Optional[List[int]] = None
|
|
70
72
|
exchange: Optional[int] = None
|
|
71
73
|
price: Optional[float] = None
|
|
@@ -28,6 +28,8 @@ from .models import (
|
|
|
28
28
|
ShortVolume,
|
|
29
29
|
RiskFactor,
|
|
30
30
|
RiskFactorTaxonomy,
|
|
31
|
+
Disclosure,
|
|
32
|
+
DisclosureTaxonomy,
|
|
31
33
|
FilingSection,
|
|
32
34
|
Filing8K,
|
|
33
35
|
Filing13F,
|
|
@@ -862,6 +864,88 @@ class ContractsClient(BaseClient):
|
|
|
862
864
|
options=options,
|
|
863
865
|
)
|
|
864
866
|
|
|
867
|
+
def list_stocks_filings_8k_disclosures(
|
|
868
|
+
self,
|
|
869
|
+
cik: Optional[str] = None,
|
|
870
|
+
cik_any_of: Optional[str] = None,
|
|
871
|
+
tickers: Optional[str] = None,
|
|
872
|
+
tickers_all_of: Optional[str] = None,
|
|
873
|
+
tickers_any_of: Optional[str] = None,
|
|
874
|
+
filing_date: Optional[Union[str, date]] = None,
|
|
875
|
+
filing_date_any_of: Optional[str] = None,
|
|
876
|
+
filing_date_gt: Optional[Union[str, date]] = None,
|
|
877
|
+
filing_date_gte: Optional[Union[str, date]] = None,
|
|
878
|
+
filing_date_lt: Optional[Union[str, date]] = None,
|
|
879
|
+
filing_date_lte: Optional[Union[str, date]] = None,
|
|
880
|
+
tertiary_category: Optional[str] = None,
|
|
881
|
+
limit: Optional[int] = 100,
|
|
882
|
+
sort: Optional[Union[str, Sort]] = "filing_date.desc",
|
|
883
|
+
params: Optional[Dict[str, Any]] = None,
|
|
884
|
+
raw: bool = False,
|
|
885
|
+
options: Optional[RequestOptionBuilder] = None,
|
|
886
|
+
) -> Union[Iterator[Disclosure], HTTPResponse]:
|
|
887
|
+
"""
|
|
888
|
+
SEC 8-K filing disclosure categorization. A single 8-K filing can produce
|
|
889
|
+
multiple rows when it covers multiple disclosure types, each classified into
|
|
890
|
+
primary, secondary, and tertiary categories with a supporting text excerpt.
|
|
891
|
+
The full classification list is available at /stocks/taxonomies/vX/disclosures.
|
|
892
|
+
"""
|
|
893
|
+
url = "/stocks/filings/8-K/vX/disclosures"
|
|
894
|
+
return self._paginate(
|
|
895
|
+
path=url,
|
|
896
|
+
params=self._get_params(self.list_stocks_filings_8k_disclosures, locals()),
|
|
897
|
+
result_key="results",
|
|
898
|
+
deserializer=Disclosure.from_dict,
|
|
899
|
+
raw=raw,
|
|
900
|
+
options=options,
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
def list_stocks_taxonomies_disclosures(
|
|
904
|
+
self,
|
|
905
|
+
taxonomy: Optional[str] = None,
|
|
906
|
+
taxonomy_any_of: Optional[str] = None,
|
|
907
|
+
taxonomy_gt: Optional[str] = None,
|
|
908
|
+
taxonomy_gte: Optional[str] = None,
|
|
909
|
+
taxonomy_lt: Optional[str] = None,
|
|
910
|
+
taxonomy_lte: Optional[str] = None,
|
|
911
|
+
primary_category: Optional[str] = None,
|
|
912
|
+
primary_category_any_of: Optional[str] = None,
|
|
913
|
+
primary_category_gt: Optional[str] = None,
|
|
914
|
+
primary_category_gte: Optional[str] = None,
|
|
915
|
+
primary_category_lt: Optional[str] = None,
|
|
916
|
+
primary_category_lte: Optional[str] = None,
|
|
917
|
+
secondary_category: Optional[str] = None,
|
|
918
|
+
secondary_category_any_of: Optional[str] = None,
|
|
919
|
+
secondary_category_gt: Optional[str] = None,
|
|
920
|
+
secondary_category_gte: Optional[str] = None,
|
|
921
|
+
secondary_category_lt: Optional[str] = None,
|
|
922
|
+
secondary_category_lte: Optional[str] = None,
|
|
923
|
+
tertiary_category: Optional[str] = None,
|
|
924
|
+
tertiary_category_any_of: Optional[str] = None,
|
|
925
|
+
tertiary_category_gt: Optional[str] = None,
|
|
926
|
+
tertiary_category_gte: Optional[str] = None,
|
|
927
|
+
tertiary_category_lt: Optional[str] = None,
|
|
928
|
+
tertiary_category_lte: Optional[str] = None,
|
|
929
|
+
limit: Optional[int] = 200,
|
|
930
|
+
sort: Optional[Union[str, Sort]] = "taxonomy.desc",
|
|
931
|
+
params: Optional[Dict[str, Any]] = None,
|
|
932
|
+
raw: bool = False,
|
|
933
|
+
options: Optional[RequestOptionBuilder] = None,
|
|
934
|
+
) -> Union[Iterator[DisclosureTaxonomy], HTTPResponse]:
|
|
935
|
+
"""
|
|
936
|
+
The complete list of 8-K disclosure classifications used in the 8-K
|
|
937
|
+
disclosures endpoint.
|
|
938
|
+
"""
|
|
939
|
+
url = "/stocks/taxonomies/vX/disclosures"
|
|
940
|
+
return self._paginate(
|
|
941
|
+
path=url,
|
|
942
|
+
params=self._get_params(self.list_stocks_taxonomies_disclosures, locals()),
|
|
943
|
+
result_key="results",
|
|
944
|
+
deserializer=DisclosureTaxonomy.from_dict,
|
|
945
|
+
raw=raw,
|
|
946
|
+
options=options,
|
|
947
|
+
)
|
|
948
|
+
|
|
865
949
|
def list_stocks_filings_10k_sections(
|
|
866
950
|
self,
|
|
867
951
|
cik: Optional[str] = None,
|
|
@@ -244,6 +244,17 @@ class SnapshotClient(BaseClient):
|
|
|
244
244
|
raw: bool = False,
|
|
245
245
|
options: Optional[RequestOptionBuilder] = None,
|
|
246
246
|
) -> Union[List[IndicesSnapshot], HTTPResponse]:
|
|
247
|
+
"""
|
|
248
|
+
Get snapshots for indices. Returns a single page of results; use
|
|
249
|
+
list_snapshot_indices to paginate through all of them.
|
|
250
|
+
- https://massive.com/docs/indices/get_v3_snapshot_indices
|
|
251
|
+
|
|
252
|
+
:param ticker_any_of: Comma-separated list of tickers, up to a maximum of 250.
|
|
253
|
+
:param params: Any additional query params
|
|
254
|
+
:param raw: returns the raw HTTP response if true, else the response is deserialized into a structured object
|
|
255
|
+
:param options: request options
|
|
256
|
+
:return: list of IndicesSnapshot
|
|
257
|
+
"""
|
|
247
258
|
url = f"/v3/snapshot/indices"
|
|
248
259
|
|
|
249
260
|
return self._get(
|
|
@@ -254,3 +265,47 @@ class SnapshotClient(BaseClient):
|
|
|
254
265
|
result_key="results",
|
|
255
266
|
options=options,
|
|
256
267
|
)
|
|
268
|
+
|
|
269
|
+
def list_snapshot_indices(
|
|
270
|
+
self,
|
|
271
|
+
ticker: Optional[str] = None,
|
|
272
|
+
ticker_any_of: Optional[Union[str, List[str]]] = None,
|
|
273
|
+
ticker_lt: Optional[str] = None,
|
|
274
|
+
ticker_lte: Optional[str] = None,
|
|
275
|
+
ticker_gt: Optional[str] = None,
|
|
276
|
+
ticker_gte: Optional[str] = None,
|
|
277
|
+
order: Optional[Union[str, Order]] = None,
|
|
278
|
+
limit: Optional[int] = None,
|
|
279
|
+
sort: Optional[str] = None,
|
|
280
|
+
params: Optional[Dict[str, Any]] = None,
|
|
281
|
+
raw: bool = False,
|
|
282
|
+
options: Optional[RequestOptionBuilder] = None,
|
|
283
|
+
) -> Union[Iterator[IndicesSnapshot], HTTPResponse]:
|
|
284
|
+
"""
|
|
285
|
+
Get snapshots for indices, following pagination.
|
|
286
|
+
- https://massive.com/docs/indices/get_v3_snapshot_indices
|
|
287
|
+
|
|
288
|
+
:param ticker: A single index ticker, e.g. I:SPX.
|
|
289
|
+
:param ticker_any_of: Comma-separated list of tickers, up to a maximum of 250. If no tickers are passed then all
|
|
290
|
+
results will be returned in a paginated manner.
|
|
291
|
+
:param ticker_lt: search for tickers less than
|
|
292
|
+
:param ticker_lte: search for tickers less than or equal to
|
|
293
|
+
:param ticker_gt: search for tickers greater than
|
|
294
|
+
:param ticker_gte: search for tickers greater than or equal to
|
|
295
|
+
:param order: Order results based on the sort field.
|
|
296
|
+
:param limit: Limit the size of the response per-page, max is 250.
|
|
297
|
+
:param sort: Sort field used for ordering. Default is ticker.
|
|
298
|
+
:param params: Any additional query params
|
|
299
|
+
:param raw: returns the raw HTTP response if true, else the response is deserialized into a structured object
|
|
300
|
+
:param options: request options
|
|
301
|
+
:return: iterator of IndicesSnapshot
|
|
302
|
+
"""
|
|
303
|
+
url = f"/v3/snapshot/indices"
|
|
304
|
+
return self._paginate(
|
|
305
|
+
path=url,
|
|
306
|
+
params=self._get_params(self.list_snapshot_indices, locals()),
|
|
307
|
+
result_key="results",
|
|
308
|
+
deserializer=IndicesSnapshot.from_dict,
|
|
309
|
+
raw=raw,
|
|
310
|
+
options=options,
|
|
311
|
+
)
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from collections import Counter
|
|
2
3
|
from enum import Enum
|
|
3
4
|
from typing import Optional, Union, List, Set, Callable, Awaitable, Any
|
|
4
5
|
import logging
|
|
5
6
|
import json
|
|
6
7
|
import asyncio
|
|
7
8
|
import ssl
|
|
8
|
-
import
|
|
9
|
+
import time
|
|
9
10
|
from .models import *
|
|
10
11
|
from websockets.asyncio.client import connect, ClientConnection
|
|
11
12
|
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
|
|
13
|
+
from ..certs import ca_bundle_path
|
|
12
14
|
from ..logging import get_logger
|
|
13
15
|
import logging
|
|
14
16
|
from ..exceptions import AuthError
|
|
@@ -16,6 +18,10 @@ from ..exceptions import AuthError
|
|
|
16
18
|
env_key = "MASSIVE_API_KEY"
|
|
17
19
|
logger = get_logger("WebSocketClient")
|
|
18
20
|
|
|
21
|
+
# Status messages that mean things are working. Any other status (e.g. "error",
|
|
22
|
+
# "max_connections", "slow_consumer", "force_disconnect") is logged as a warning.
|
|
23
|
+
OK_STATUSES = {"connected", "auth_success", "success"}
|
|
24
|
+
|
|
19
25
|
|
|
20
26
|
class WebSocketClient:
|
|
21
27
|
def __init__(
|
|
@@ -29,6 +35,7 @@ class WebSocketClient:
|
|
|
29
35
|
max_reconnects: Optional[int] = 5,
|
|
30
36
|
secure: bool = True,
|
|
31
37
|
custom_json: Optional[Any] = None,
|
|
38
|
+
ca_certs: Optional[str] = None,
|
|
32
39
|
**kwargs,
|
|
33
40
|
):
|
|
34
41
|
"""
|
|
@@ -41,6 +48,7 @@ class WebSocketClient:
|
|
|
41
48
|
:param subscriptions: List of subscription parameters.
|
|
42
49
|
:param max_reconnects: How many times to reconnect on network outage before ending .connect event loop.
|
|
43
50
|
:param custom_json: Optional module exposing loads/dumps functions (similar to Python's json module) to be used for JSON conversions.
|
|
51
|
+
:param ca_certs: Path to a CA bundle to verify TLS with. Defaults to the SSL_CERT_FILE or REQUESTS_CA_BUNDLE environment variable, then certifi.
|
|
44
52
|
:return: A client.
|
|
45
53
|
"""
|
|
46
54
|
if api_key is None:
|
|
@@ -70,6 +78,9 @@ class WebSocketClient:
|
|
|
70
78
|
subscriptions = []
|
|
71
79
|
self.scheduled_subs: Set[str] = set(subscriptions)
|
|
72
80
|
self.schedule_resub = True
|
|
81
|
+
self._server_errors: Counter = Counter()
|
|
82
|
+
self._server_errors_since = 0.0
|
|
83
|
+
self.ca_certs = ca_certs
|
|
73
84
|
if custom_json:
|
|
74
85
|
self.json = custom_json
|
|
75
86
|
else:
|
|
@@ -94,11 +105,7 @@ class WebSocketClient:
|
|
|
94
105
|
"""
|
|
95
106
|
reconnects = 0
|
|
96
107
|
logger.debug("connect: %s", self.url)
|
|
97
|
-
|
|
98
|
-
ssl_context = None
|
|
99
|
-
if self.url.startswith("wss://"):
|
|
100
|
-
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
101
|
-
ssl_context.load_verify_locations(certifi.where())
|
|
108
|
+
ssl_context = self._ssl_context()
|
|
102
109
|
|
|
103
110
|
last_exc = None
|
|
104
111
|
async for s in connect(
|
|
@@ -112,6 +119,7 @@ class WebSocketClient:
|
|
|
112
119
|
try:
|
|
113
120
|
msg = await s.recv()
|
|
114
121
|
logger.debug("connected: %s", msg)
|
|
122
|
+
self._log_server_statuses(self.json.loads(msg))
|
|
115
123
|
logger.debug("authing...")
|
|
116
124
|
await s.send(
|
|
117
125
|
self.json.dumps({"action": "auth", "params": self.api_key})
|
|
@@ -121,6 +129,9 @@ class WebSocketClient:
|
|
|
121
129
|
logger.debug("authed: %s", auth_msg)
|
|
122
130
|
if auth_msg_parsed[0]["status"] == "auth_failed":
|
|
123
131
|
raise AuthError(auth_msg_parsed[0]["message"])
|
|
132
|
+
# e.g. max_connections, which the server sends before closing
|
|
133
|
+
self._log_server_statuses(auth_msg_parsed)
|
|
134
|
+
self._flush_server_errors(force=True)
|
|
124
135
|
while True:
|
|
125
136
|
if self.schedule_resub:
|
|
126
137
|
logger.debug(
|
|
@@ -139,6 +150,7 @@ class WebSocketClient:
|
|
|
139
150
|
await asyncio.wait_for(s.recv(), timeout=1)
|
|
140
151
|
)
|
|
141
152
|
except asyncio.TimeoutError:
|
|
153
|
+
self._flush_server_errors()
|
|
142
154
|
continue
|
|
143
155
|
|
|
144
156
|
if not self.raw:
|
|
@@ -147,17 +159,21 @@ class WebSocketClient:
|
|
|
147
159
|
for m in msgJson:
|
|
148
160
|
if m["ev"] == "status":
|
|
149
161
|
logger.debug("status: %s", m["message"])
|
|
150
|
-
|
|
162
|
+
self._log_server_statuses(msgJson)
|
|
151
163
|
cmsg = parse(msgJson, logger, self.market)
|
|
164
|
+
self._flush_server_errors()
|
|
152
165
|
|
|
153
166
|
if len(cmsg) > 0:
|
|
154
167
|
await processor(cmsg) # type: ignore
|
|
155
168
|
except ConnectionClosedOK as e:
|
|
156
169
|
logger.debug("connection closed (OK): %s", e)
|
|
170
|
+
# the server sends a status (e.g. slow_consumer) before closing
|
|
171
|
+
self._flush_server_errors(force=True)
|
|
157
172
|
return
|
|
158
173
|
except ConnectionClosedError as e:
|
|
159
174
|
last_exc = e
|
|
160
175
|
logger.debug("connection closed (ERR): %s", e)
|
|
176
|
+
self._flush_server_errors(force=True)
|
|
161
177
|
reconnects += 1
|
|
162
178
|
self.scheduled_subs = set(self.subs)
|
|
163
179
|
self.subs = set()
|
|
@@ -173,6 +189,14 @@ class WebSocketClient:
|
|
|
173
189
|
):
|
|
174
190
|
raise last_exc
|
|
175
191
|
|
|
192
|
+
def _ssl_context(self) -> Optional[ssl.SSLContext]:
|
|
193
|
+
# darwin needs some extra <3
|
|
194
|
+
if not self.url.startswith("wss://"):
|
|
195
|
+
return None
|
|
196
|
+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
197
|
+
context.load_verify_locations(self.ca_certs or ca_bundle_path(logger))
|
|
198
|
+
return context
|
|
199
|
+
|
|
176
200
|
def run(
|
|
177
201
|
self,
|
|
178
202
|
handle_msg: Union[
|
|
@@ -195,6 +219,29 @@ class WebSocketClient:
|
|
|
195
219
|
|
|
196
220
|
asyncio.run(self.connect(handle_msg_wrapper, close_timeout, **kwargs))
|
|
197
221
|
|
|
222
|
+
def _log_server_statuses(self, msgs: List[Any]):
|
|
223
|
+
# status messages are dropped by parse(), so problems (e.g. subscriptions
|
|
224
|
+
# the server rejected, or why it disconnected) are logged here. The server
|
|
225
|
+
# can send one error per channel, so identical ones are counted and logged
|
|
226
|
+
# once a second.
|
|
227
|
+
for m in msgs:
|
|
228
|
+
if m.get("ev") != "status" or m.get("status") in OK_STATUSES:
|
|
229
|
+
continue
|
|
230
|
+
if not self._server_errors:
|
|
231
|
+
self._server_errors_since = time.monotonic()
|
|
232
|
+
self._server_errors[(m.get("status"), m.get("message"))] += 1
|
|
233
|
+
|
|
234
|
+
def _flush_server_errors(self, force: bool = False):
|
|
235
|
+
if not self._server_errors:
|
|
236
|
+
return
|
|
237
|
+
if not force and time.monotonic() - self._server_errors_since < 1:
|
|
238
|
+
return
|
|
239
|
+
for (status, message), count in self._server_errors.items():
|
|
240
|
+
logger.warning(
|
|
241
|
+
"server %s: %s%s", status, message, f" (x{count})" if count > 1 else ""
|
|
242
|
+
)
|
|
243
|
+
self._server_errors = Counter()
|
|
244
|
+
|
|
198
245
|
async def _subscribe(self, topics: Union[List[str], Set[str]]):
|
|
199
246
|
if self.websocket is None or len(topics) == 0:
|
|
200
247
|
return
|
|
@@ -276,6 +323,7 @@ class WebSocketClient:
|
|
|
276
323
|
Close the websocket connection.
|
|
277
324
|
"""
|
|
278
325
|
logger.debug("closing")
|
|
326
|
+
self._flush_server_errors(force=True)
|
|
279
327
|
|
|
280
328
|
if self.websocket:
|
|
281
329
|
await self.websocket.close()
|
|
@@ -51,6 +51,7 @@ class EquityAgg:
|
|
|
51
51
|
@modelclass
|
|
52
52
|
class CurrencyAgg:
|
|
53
53
|
"CurrencyAgg contains aggregate data for either forex currency pairs or crypto pairs."
|
|
54
|
+
|
|
54
55
|
event_type: Optional[Union[str, EventType]] = None
|
|
55
56
|
pair: Optional[str] = None
|
|
56
57
|
open: Optional[float] = None
|
|
@@ -83,6 +84,7 @@ class CurrencyAgg:
|
|
|
83
84
|
@modelclass
|
|
84
85
|
class EquityTrade:
|
|
85
86
|
"EquityTrade contains trade data for either stock tickers or option contracts."
|
|
87
|
+
|
|
86
88
|
event_type: Optional[Union[str, EventType]] = None
|
|
87
89
|
symbol: Optional[str] = None
|
|
88
90
|
exchange: Optional[int] = None
|
|
@@ -119,6 +121,7 @@ class EquityTrade:
|
|
|
119
121
|
@modelclass
|
|
120
122
|
class CryptoTrade:
|
|
121
123
|
"CryptoTrade contains trade data for a crypto pair."
|
|
124
|
+
|
|
122
125
|
event_type: Optional[Union[str, EventType]] = None
|
|
123
126
|
pair: Optional[str] = None
|
|
124
127
|
exchange: Optional[int] = None
|
|
@@ -147,6 +150,7 @@ class CryptoTrade:
|
|
|
147
150
|
@modelclass
|
|
148
151
|
class EquityQuote:
|
|
149
152
|
"EquityQuote contains quote data for either stock tickers or option contracts."
|
|
153
|
+
|
|
150
154
|
event_type: Optional[Union[str, EventType]] = None
|
|
151
155
|
symbol: Optional[str] = None
|
|
152
156
|
bid_exchange_id: Optional[int] = None
|
|
@@ -187,6 +191,7 @@ class EquityQuote:
|
|
|
187
191
|
@modelclass
|
|
188
192
|
class ForexQuote:
|
|
189
193
|
"ForexQuote contains quote data for a forex currency pair."
|
|
194
|
+
|
|
190
195
|
event_type: Optional[Union[str, EventType]] = None
|
|
191
196
|
pair: Optional[str] = None
|
|
192
197
|
exchange_id: Optional[int] = None
|
|
@@ -209,6 +214,7 @@ class ForexQuote:
|
|
|
209
214
|
@modelclass
|
|
210
215
|
class CryptoQuote:
|
|
211
216
|
"CryptoQuote contains quote data for a crypto pair."
|
|
217
|
+
|
|
212
218
|
event_type: Optional[Union[str, EventType]] = None
|
|
213
219
|
pair: Optional[str] = None
|
|
214
220
|
bid_price: Optional[int] = None
|
|
@@ -237,6 +243,7 @@ class CryptoQuote:
|
|
|
237
243
|
@modelclass
|
|
238
244
|
class Imbalance:
|
|
239
245
|
"Imbalance contains imbalance event data for a given stock ticker symbol."
|
|
246
|
+
|
|
240
247
|
event_type: Optional[Union[str, EventType]] = None
|
|
241
248
|
symbol: Optional[str] = None
|
|
242
249
|
time_stamp: Optional[int] = None
|
|
@@ -267,6 +274,7 @@ class Imbalance:
|
|
|
267
274
|
@modelclass
|
|
268
275
|
class LimitUpLimitDown:
|
|
269
276
|
"LimitUpLimitDown contains LULD event data for a given stock ticker symbol."
|
|
277
|
+
|
|
270
278
|
event_type: Optional[Union[str, EventType]] = None
|
|
271
279
|
symbol: Optional[str] = None
|
|
272
280
|
high_price: Optional[float] = None
|
|
@@ -293,6 +301,7 @@ class LimitUpLimitDown:
|
|
|
293
301
|
@modelclass
|
|
294
302
|
class Level2Book:
|
|
295
303
|
"Level2Book contains level 2 book data for a given crypto pair."
|
|
304
|
+
|
|
296
305
|
event_type: Optional[Union[str, EventType]] = None
|
|
297
306
|
pair: Optional[str] = None
|
|
298
307
|
bid_prices: Optional[float] = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "massive"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.9.0"
|
|
4
4
|
description = "Official Massive (formerly Polygon.io) REST and Websocket client."
|
|
5
5
|
authors = ["massive.com"]
|
|
6
6
|
license = "MIT"
|
|
@@ -24,13 +24,13 @@ packages = [
|
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[tool.poetry.dependencies]
|
|
27
|
-
python = "^3.
|
|
27
|
+
python = "^3.10"
|
|
28
28
|
urllib3 = ">=1.26.9"
|
|
29
29
|
websockets = ">=14.0"
|
|
30
30
|
certifi = ">=2022.5.18,<2027.0.0"
|
|
31
31
|
|
|
32
32
|
[tool.poetry.dev-dependencies]
|
|
33
|
-
black = "^
|
|
33
|
+
black = "^26.3.1"
|
|
34
34
|
mypy = "^1.19"
|
|
35
35
|
types-urllib3 = "^1.26.25"
|
|
36
36
|
Sphinx = "^7.4.7"
|
|
@@ -40,7 +40,7 @@ sphinx-autodoc-typehints = "^2.3.0"
|
|
|
40
40
|
types-certifi = "^2021.10.8"
|
|
41
41
|
types-setuptools = "^81.0.0"
|
|
42
42
|
pook = "^2.1.4"
|
|
43
|
-
orjson = "^3.11.
|
|
43
|
+
orjson = "^3.11.6"
|
|
44
44
|
|
|
45
45
|
[build-system]
|
|
46
46
|
requires = ["poetry-core>=1.0.0"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|