diff --git a/massive/rest/models/financials.py b/massive/rest/models/financials.py index 2e97eeba..a2989f15 100644 --- a/massive/rest/models/financials.py +++ b/massive/rest/models/financials.py @@ -920,3 +920,78 @@ def from_dict(d): taxonomy=d.get("taxonomy"), tertiary_category=d.get("tertiary_category"), ) + + +@modelclass +class FilingSection: + """SEC document text section from a 10-K/10-Q (raw text content).""" + + cik: Optional[str] = None + filing_date: Optional[str] = None + filing_url: Optional[str] = None + period_end: Optional[str] = None + section: Optional[str] = None + text: Optional[str] = None + ticker: Optional[str] = None + + @staticmethod + def from_dict(d): + return FilingSection( + cik=d.get("cik"), + filing_date=d.get("filing_date"), + filing_url=d.get("filing_url"), + period_end=d.get("period_end"), + section=d.get("section"), + text=d.get("text"), + ticker=d.get("ticker"), + ) + + +@modelclass +class Filing8K: + """Parsed 8-K filing with item-level text content.""" + + accession_number: Optional[str] = None + cik: Optional[str] = None + filing_date: Optional[str] = None + filing_url: Optional[str] = None + form_type: Optional[str] = None + items_text: Optional[str] = None + ticker: Optional[str] = None + + @staticmethod + def from_dict(d): + return Filing8K( + accession_number=d.get("accession_number"), + cik=d.get("cik"), + filing_date=d.get("filing_date"), + filing_url=d.get("filing_url"), + form_type=d.get("form_type"), + items_text=d.get("items_text"), + ticker=d.get("ticker"), + ) + + +@modelclass +class FilingIndex: + """Master index entry for any SEC filing (10-K, 8-K, 10-Q, etc.).""" + + accession_number: Optional[str] = None + cik: Optional[str] = None + filing_date: Optional[str] = None + filing_url: Optional[str] = None + form_type: Optional[str] = None + issuer_name: Optional[str] = None + ticker: Optional[str] = None + + @staticmethod + def from_dict(d): + return FilingIndex( + accession_number=d.get("accession_number"), + cik=d.get("cik"), + filing_date=d.get("filing_date"), + filing_url=d.get("filing_url"), + form_type=d.get("form_type"), + issuer_name=d.get("issuer_name"), + ticker=d.get("ticker"), + ) diff --git a/massive/rest/models/snapshot.py b/massive/rest/models/snapshot.py index 3d38abe2..0f6dd160 100644 --- a/massive/rest/models/snapshot.py +++ b/massive/rest/models/snapshot.py @@ -7,7 +7,8 @@ @modelclass class MinuteSnapshot: - "Most recent minute bar." + """Most recent minute bar.""" + accumulated_volume: Optional[float] = None open: Optional[float] = None high: Optional[float] = None @@ -18,20 +19,24 @@ class MinuteSnapshot: otc: Optional[bool] = None timestamp: Optional[int] = None transactions: Optional[int] = None + fractional_volume: Optional[str] = None + fractional_accumulated_volume: Optional[str] = None @staticmethod def from_dict(d): return MinuteSnapshot( - d.get("av", None), - d.get("o", None), - d.get("h", None), - d.get("l", None), - d.get("c", None), - d.get("v", None), - d.get("vw", None), - d.get("otc", None), - d.get("t", None), - d.get("n", None), + accumulated_volume=d.get("av"), + open=d.get("o"), + high=d.get("h"), + low=d.get("l"), + close=d.get("c"), + volume=d.get("v"), + vwap=d.get("vw"), + otc=d.get("otc"), + timestamp=d.get("t"), + transactions=d.get("n"), + fractional_volume=d.get("dv"), + fractional_accumulated_volume=d.get("dav"), ) @@ -318,10 +323,32 @@ class UniversalSnapshotSession: low: Optional[float] = None previous_close: Optional[float] = None volume: Optional[float] = None + vwap: Optional[float] = None + last_updated: Optional[int] = None + fractional_volume: Optional[str] = None @staticmethod def from_dict(d): - return UniversalSnapshotSession(**d) + return UniversalSnapshotSession( + price=d.get("price"), + change=d.get("change"), + change_percent=d.get("change_percent"), + early_trading_change=d.get("early_trading_change"), + early_trading_change_percent=d.get("early_trading_change_percent"), + regular_trading_change=d.get("regular_trading_change"), + regular_trading_change_percent=d.get("regular_trading_change_percent"), + late_trading_change=d.get("late_trading_change"), + late_trading_change_percent=d.get("late_trading_change_percent"), + open=d.get("open"), + close=d.get("close"), + high=d.get("high"), + low=d.get("low"), + previous_close=d.get("previous_close"), + volume=d.get("volume"), + vwap=d.get("vwap"), + last_updated=d.get("last_updated"), + fractional_volume=d.get("decimal_volume"), + ) @modelclass @@ -330,8 +357,10 @@ class UniversalSnapshotLastQuote: ask: Optional[float] = None ask_size: Optional[float] = None + ask_exchange: Optional[int] = None bid: Optional[float] = None bid_size: Optional[float] = None + bid_exchange: Optional[int] = None midpoint: Optional[float] = None exchange: Optional[int] = None timeframe: Optional[str] = None @@ -339,7 +368,18 @@ class UniversalSnapshotLastQuote: @staticmethod def from_dict(d): - return UniversalSnapshotLastQuote(**d) + return UniversalSnapshotLastQuote( + ask=d.get("ask"), + ask_size=d.get("ask_size"), + ask_exchange=d.get("ask_exchange"), + bid=d.get("bid"), + bid_size=d.get("bid_size"), + bid_exchange=d.get("bid_exchange"), + midpoint=d.get("midpoint"), + exchange=d.get("exchange"), + timeframe=d.get("timeframe"), + last_updated=d.get("last_updated"), + ) @modelclass @@ -355,10 +395,51 @@ class UniversalSnapshotLastTrade: last_updated: Optional[int] = None participant_timestamp: Optional[int] = None sip_timestamp: Optional[int] = None + fractional_size: Optional[str] = None + + @staticmethod + def from_dict(d): + return UniversalSnapshotLastTrade( + id=d.get("id"), + price=d.get("price"), + size=d.get("size"), + exchange=d.get("exchange"), + conditions=d.get("conditions"), + timeframe=d.get("timeframe"), + last_updated=d.get("last_updated"), + participant_timestamp=d.get("participant_timestamp"), + sip_timestamp=d.get("sip_timestamp"), + fractional_size=d.get("decimal_size"), + ) + + +@modelclass +class UniversalSnapshotLastMinute: + """Contains the most recent minute-level aggregate for the asset.""" + + open: Optional[float] = None + close: Optional[float] = None + high: Optional[float] = None + low: Optional[float] = None + volume: Optional[float] = None + vwap: Optional[float] = None + transactions: Optional[int] = None + last_updated: Optional[int] = None + fractional_volume: Optional[str] = None @staticmethod def from_dict(d): - return UniversalSnapshotLastTrade(**d) + return UniversalSnapshotLastMinute( + open=d.get("open"), + close=d.get("close"), + high=d.get("high"), + low=d.get("low"), + volume=d.get("volume"), + vwap=d.get("vwap"), + transactions=d.get("transactions"), + last_updated=d.get("last_updated"), + fractional_volume=d.get("decimal_volume"), + ) @modelclass @@ -374,7 +455,14 @@ class UniversalSnapshotUnderlyingAsset: @staticmethod def from_dict(d): - return UniversalSnapshotUnderlyingAsset(**d) + return UniversalSnapshotUnderlyingAsset( + ticker=d.get("ticker"), + price=d.get("price"), + value=d.get("value"), + change_to_break_even=d.get("change_to_break_even"), + timeframe=d.get("timeframe"), + last_updated=d.get("last_updated"), + ) @modelclass @@ -389,18 +477,25 @@ class UniversalSnapshotDetails: @staticmethod def from_dict(d): - return UniversalSnapshotDetails(**d) + return UniversalSnapshotDetails( + contract_type=d.get("contract_type"), + exercise_style=d.get("exercise_style"), + expiration_date=d.get("expiration_date"), + shares_per_contract=d.get("shares_per_contract"), + strike_price=d.get("strike_price"), + ) @modelclass class UniversalSnapshot: - """Contains snapshot data for an asset.""" + """Contains snapshot data for an asset (stocks, options, indices, fx, crypto).""" ticker: Optional[str] = None type: Optional[str] = None session: Optional[UniversalSnapshotSession] = None last_quote: Optional[UniversalSnapshotLastQuote] = None last_trade: Optional[UniversalSnapshotLastTrade] = None + last_minute: Optional[UniversalSnapshotLastMinute] = None greeks: Optional[Greeks] = None underlying_asset: Optional[UniversalSnapshotUnderlyingAsset] = None details: Optional[UniversalSnapshotDetails] = None @@ -412,12 +507,15 @@ class UniversalSnapshot: fair_market_value: Optional[float] = None error: Optional[str] = None message: Optional[str] = None + value: Optional[float] = None + last_updated: Optional[int] = None + timeframe: Optional[str] = None @staticmethod def from_dict(d): return UniversalSnapshot( - ticker=d.get("ticker", None), - type=d.get("type", None), + ticker=d.get("ticker"), + type=d.get("type"), session=( None if "session" not in d @@ -433,7 +531,12 @@ def from_dict(d): if "last_trade" not in d else UniversalSnapshotLastTrade.from_dict(d["last_trade"]) ), - greeks=None if "greeks" not in d else Greeks.from_dict(d["greeks"]), + last_minute=( + None + if "last_minute" not in d + else UniversalSnapshotLastMinute.from_dict(d["last_minute"]) + ), + greeks=(None if "greeks" not in d else Greeks.from_dict(d["greeks"])), underlying_asset=( None if "underlying_asset" not in d @@ -444,12 +547,15 @@ def from_dict(d): if "details" not in d else UniversalSnapshotDetails.from_dict(d["details"]) ), - break_even_price=d.get("break_even_price", None), - implied_volatility=d.get("implied_volatility", None), - open_interest=d.get("open_interest", None), - market_status=d.get("market_status", None), - name=d.get("name", None), - fair_market_value=d.get("fmv", None), - error=d.get("error", None), - message=d.get("message", None), + break_even_price=d.get("break_even_price"), + implied_volatility=d.get("implied_volatility"), + open_interest=d.get("open_interest"), + market_status=d.get("market_status"), + name=d.get("name"), + fair_market_value=d.get("fmv"), + error=d.get("error"), + message=d.get("message"), + value=d.get("value"), + last_updated=d.get("last_updated"), + timeframe=d.get("timeframe"), ) diff --git a/massive/rest/models/trades.py b/massive/rest/models/trades.py index f4746b1a..e298ed29 100644 --- a/massive/rest/models/trades.py +++ b/massive/rest/models/trades.py @@ -26,7 +26,8 @@ def from_dict(d): @modelclass class LastTrade: - "Contains data for the most recent trade for a given ticker symbol." + """Contains data for the most recent trade for a given ticker symbol.""" + ticker: Optional[str] = None trf_timestamp: Optional[int] = None sequence_number: Optional[float] = None @@ -40,25 +41,25 @@ class LastTrade: size: Optional[float] = None exchange: Optional[int] = None tape: Optional[int] = None - fractional_shares: Optional[str] = None + fractional_size: Optional[str] = None @staticmethod def from_dict(d): return LastTrade( - d.get("T", None), - d.get("f", None), - d.get("q", None), - d.get("t", None), - d.get("y", None), - d.get("c", None), - d.get("e", None), - d.get("i", None), - d.get("p", None), - d.get("r", None), - d.get("s", None), - d.get("x", None), - d.get("z", None), - d.get("ds", None), + ticker=d.get("T"), + trf_timestamp=d.get("f"), + sequence_number=d.get("q"), + sip_timestamp=d.get("t"), + participant_timestamp=d.get("y"), + conditions=d.get("c"), + correction=d.get("e"), + id=d.get("i"), + price=d.get("p"), + trf_id=d.get("r"), + size=d.get("s"), + exchange=d.get("x"), + tape=d.get("z"), + fractional_size=d.get("ds"), ) diff --git a/massive/rest/reference.py b/massive/rest/reference.py index 446d9f13..e1f7dd32 100644 --- a/massive/rest/reference.py +++ b/massive/rest/reference.py @@ -28,6 +28,9 @@ ShortVolume, RiskFactor, RiskFactorTaxonomy, + FilingSection, + Filing8K, + FilingIndex, ) from urllib3 import HTTPResponse from datetime import date @@ -773,7 +776,7 @@ def list_stocks_dividends( options=options, ) - def list_stocks_filings_risk_factors( + def list_stocks_taxonomies_risk_factors( self, filing_date: Optional[Union[str, date]] = None, filing_date_any_of: Optional[str] = None, @@ -793,63 +796,154 @@ def list_stocks_filings_risk_factors( cik_gte: Optional[str] = None, cik_lt: Optional[str] = None, cik_lte: Optional[str] = None, - limit: Optional[int] = None, - sort: Optional[Union[str, Sort]] = None, + limit: Optional[int] = 100, + sort: Optional[Union[str, Sort]] = "filing_date.desc", params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, ) -> Union[Iterator[RiskFactor], HTTPResponse]: """ - Endpoint: GET /stocks/filings/vX/risk-factors + Get categorized risk factors from 10-K filings. """ url = "/stocks/filings/vX/risk-factors" return self._paginate( path=url, - params=self._get_params(self.list_stocks_filings_risk_factors, locals()), - raw=raw, + params=self._get_params(self.list_stocks_taxonomies_risk_factors, locals()), + result_key="results", deserializer=RiskFactor.from_dict, + raw=raw, options=options, ) - def list_stocks_taxonomies_risk_factors( + def list_stocks_filings_10k_sections( self, - taxonomy: Optional[float] = None, - taxonomy_gt: Optional[float] = None, - taxonomy_gte: Optional[float] = None, - taxonomy_lt: Optional[float] = None, - taxonomy_lte: Optional[float] = None, - primary_category: Optional[str] = None, - primary_category_any_of: Optional[str] = None, - primary_category_gt: Optional[str] = None, - primary_category_gte: Optional[str] = None, - primary_category_lt: Optional[str] = None, - primary_category_lte: Optional[str] = None, - secondary_category: Optional[str] = None, - secondary_category_any_of: Optional[str] = None, - secondary_category_gt: Optional[str] = None, - secondary_category_gte: Optional[str] = None, - secondary_category_lt: Optional[str] = None, - secondary_category_lte: Optional[str] = None, - tertiary_category: Optional[str] = None, - tertiary_category_any_of: Optional[str] = None, - tertiary_category_gt: Optional[str] = None, - tertiary_category_gte: Optional[str] = None, - tertiary_category_lt: Optional[str] = None, - tertiary_category_lte: Optional[str] = None, - limit: Optional[int] = None, - sort: Optional[Union[str, Sort]] = None, + cik: Optional[str] = None, + cik_any_of: Optional[str] = None, + cik_gt: Optional[str] = None, + cik_gte: Optional[str] = None, + cik_lt: Optional[str] = None, + cik_lte: Optional[str] = None, + ticker: Optional[str] = None, + ticker_any_of: Optional[str] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + section: Optional[str] = None, + section_any_of: Optional[str] = None, + filing_date: Optional[Union[str, date]] = None, + filing_date_gt: Optional[Union[str, date]] = None, + filing_date_gte: Optional[Union[str, date]] = None, + filing_date_lt: Optional[Union[str, date]] = None, + filing_date_lte: Optional[Union[str, date]] = None, + period_end: Optional[Union[str, date]] = None, + period_end_gt: Optional[Union[str, date]] = None, + period_end_gte: Optional[Union[str, date]] = None, + period_end_lt: Optional[Union[str, date]] = None, + period_end_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = 100, + sort: Optional[Union[str, Sort]] = "period_end.desc", params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, - ) -> Union[Iterator[RiskFactorTaxonomy], HTTPResponse]: + ) -> Union[Iterator[FilingSection], HTTPResponse]: """ - Endpoint: GET /stocks/taxonomies/vX/risk-factors + Get raw text sections from 10-K/10-Q filings (business, risk_factors, etc.). """ - url = "/stocks/taxonomies/vX/risk-factors" + url = "/stocks/filings/10-K/vX/sections" return self._paginate( path=url, - params=self._get_params(self.list_stocks_taxonomies_risk_factors, locals()), + params=self._get_params(self.list_stocks_filings_10k_sections, locals()), + result_key="results", + deserializer=FilingSection.from_dict, + raw=raw, + options=options, + ) + + def list_stocks_filings_8k_text( + self, + cik: Optional[str] = None, + cik_any_of: Optional[str] = None, + cik_gt: Optional[str] = None, + cik_gte: Optional[str] = None, + cik_lt: Optional[str] = None, + cik_lte: Optional[str] = None, + ticker: Optional[str] = None, + ticker_any_of: Optional[str] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + form_type: Optional[str] = None, + form_type_any_of: Optional[str] = None, + form_type_gt: Optional[str] = None, + form_type_gte: Optional[str] = None, + form_type_lt: Optional[str] = None, + form_type_lte: Optional[str] = None, + filing_date: Optional[Union[str, date]] = None, + filing_date_gt: Optional[Union[str, date]] = None, + filing_date_gte: Optional[Union[str, date]] = None, + filing_date_lt: Optional[Union[str, date]] = None, + filing_date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = 100, + sort: Optional[Union[str, Sort]] = "filing_date.desc", + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[Filing8K], HTTPResponse]: + """ + Get parsed 8-K filings (earnings, acquisitions, executive changes, etc.). + """ + url = "/stocks/filings/8-K/vX/text" + return self._paginate( + path=url, + params=self._get_params(self.list_stocks_filings_8k_text, locals()), + result_key="results", + deserializer=Filing8K.from_dict, + raw=raw, + options=options, + ) + + def list_stocks_filings_index( + self, + cik: Optional[str] = None, + cik_any_of: Optional[str] = None, + cik_gt: Optional[str] = None, + cik_gte: Optional[str] = None, + cik_lt: Optional[str] = None, + cik_lte: Optional[str] = None, + ticker: Optional[str] = None, + ticker_any_of: Optional[str] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + form_type: Optional[str] = None, + form_type_any_of: Optional[str] = None, + form_type_gt: Optional[str] = None, + form_type_gte: Optional[str] = None, + form_type_lt: Optional[str] = None, + form_type_lte: Optional[str] = None, + filing_date: Optional[Union[str, date]] = None, + filing_date_gt: Optional[Union[str, date]] = None, + filing_date_gte: Optional[Union[str, date]] = None, + filing_date_lt: Optional[Union[str, date]] = None, + filing_date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = 1000, + sort: Optional[Union[str, Sort]] = "filing_date.desc", + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[FilingIndex], HTTPResponse]: + """ + Get the master index of all SEC filings (10-K, 8-K, 10-Q, S-1, 4, etc.). + """ + url = "/stocks/filings/vX/index" + return self._paginate( + path=url, + params=self._get_params(self.list_stocks_filings_index, locals()), + result_key="results", + deserializer=FilingIndex.from_dict, raw=raw, - deserializer=RiskFactorTaxonomy.from_dict, options=options, ) diff --git a/massive/websocket/models/models.py b/massive/websocket/models/models.py index e8ea3c0a..cc3d3c16 100644 --- a/massive/websocket/models/models.py +++ b/massive/websocket/models/models.py @@ -22,6 +22,8 @@ class EquityAgg: start_timestamp: Optional[int] = None end_timestamp: Optional[int] = None otc: Optional[bool] = None + fractional_volume: Optional[str] = None + fractional_accumulated_volume: Optional[str] = None @staticmethod def from_dict(d): @@ -41,6 +43,8 @@ def from_dict(d): d.get("s", None), d.get("e", None), d.get("otc", None), + d.get("dv", None), + d.get("dav", None), )