From d34d575cb0224c109f30e75416d02c23e5c8db14 Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:45:19 -0700 Subject: [PATCH 1/7] Send *_all_of kwargs as .all_of query params _get_params converted _lt/_lte/_gt/_gte/_any_of suffixes to the dotted query form but not _all_of, so tickers_all_of=... went out as ?tickers_all_of=... and the API ignored it. Affects form-3/form-4, financials and benzinga news filters. Lists are joined like any_of. --- massive/rest/base.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/massive/rest/base.py b/massive/rest/base.py index 8e2c4ac7..4962007c 100644 --- a/massive/rest/base.py +++ b/massive/rest/base.py @@ -208,16 +208,18 @@ def _get_params( elif isinstance(val, datetime): val = int(val.timestamp() * self.time_mult(datetime_res)) if val is not None: - for ext in ["lt", "lte", "gt", "gte", "any_of"]: + for ext in ["lt", "lte", "gt", "gte", "any_of", "all_of"]: if argname.endswith(f"_{ext}"): # lop off ext, then rebuild argname with ext, # using ., and not _ (removesuffix would work) # but that is python 3.9+ argname = argname[: -len(f"_{ext}")] + f".{ext}" - # any_of accepts a comma-separated str (per the spec) or a - # list of values; only join the latter, since joining a str - # would split it into single characters - if argname.endswith("any_of") and not isinstance(val, str): + # any_of/all_of accept a comma-separated str (per the spec) + # or a list of values; only join the latter, since joining + # a str would split it into single characters + if argname.endswith(("any_of", "all_of")) and not isinstance( + val, str + ): val = ",".join(val) params[argname] = val From 96a0b38b540e8ae09f35196d662d95bcf67780da Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:45:44 -0700 Subject: [PATCH 2/7] Add endpoints documented on massive.com that the client was missing - list_options_open_interest GET /options/v1/open-interest - list_options_open_interest_by_underlying GET /options/vX/open-interest/underlying - list_options_unusual_trades GET /options/v1/unusual-trades - list_stocks_insider_activity GET /stocks/insiders/vX/activity - list_stocks_insider_holdings GET /stocks/insiders/vX/holdings - list_funding_conditions GET /fed/v1/funding-conditions - get_crypto_daily_open_close_agg GET /v1/open-close/crypto/{from}/{to}/{date} Kwargs mirror the OpenAPI spec params. --- massive/rest/aggs.py | 42 +++++++- massive/rest/economy.py | 31 ++++++ massive/rest/models/aggs.py | 58 ++++++++++- massive/rest/models/contracts.py | 56 ++++++++++ massive/rest/models/economy.py | 67 ++++++++++++ massive/rest/models/financials.py | 88 ++++++++++++++++ massive/rest/models/trades.py | 31 ++++++ massive/rest/reference.py | 166 ++++++++++++++++++++++++++++++ massive/rest/trades.py | 39 ++++++- 9 files changed, 574 insertions(+), 4 deletions(-) diff --git a/massive/rest/aggs.py b/massive/rest/aggs.py index cb874fac..226df3f5 100644 --- a/massive/rest/aggs.py +++ b/massive/rest/aggs.py @@ -1,6 +1,13 @@ from .base import BaseClient from typing import Optional, Any, Dict, List, Union, Iterator -from .models import Agg, GroupedDailyAgg, DailyOpenCloseAgg, PreviousCloseAgg, Sort +from .models import ( + Agg, + GroupedDailyAgg, + DailyOpenCloseAgg, + CryptoDailyOpenCloseAgg, + PreviousCloseAgg, + Sort, +) from urllib3 import HTTPResponse from datetime import datetime, date @@ -188,3 +195,36 @@ def get_previous_close_agg( raw=raw, options=options, ) + + def get_crypto_daily_open_close_agg( + self, + from_: str, + to: str, + date: Union[str, date], + adjusted: Optional[bool] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[CryptoDailyOpenCloseAgg, HTTPResponse]: + """ + Get the open and close prices of a crypto pair on a certain date, with the opening and closing trades. + + Endpoint: GET /v1/open-close/crypto/{from}/{to}/{date} + + :param from_: The "from" symbol of the pair (e.g. BTC). + :param to: The "to" symbol of the pair (e.g. USD). + :param date: The date of the requested open/close in the format YYYY-MM-DD. + :param adjusted: Whether or not the results are adjusted for splits. By default, results are adjusted. + :param params: Any additional query params + :param raw: Return raw object instead of results object + :return: Crypto daily open close aggregate + """ + url = f"/v1/open-close/crypto/{from_}/{to}/{date}" + + return self._get( + path=url, + params=self._get_params(self.get_crypto_daily_open_close_agg, locals()), + deserializer=CryptoDailyOpenCloseAgg.from_dict, + raw=raw, + options=options, + ) diff --git a/massive/rest/economy.py b/massive/rest/economy.py index 4c4b251f..dc010e8b 100644 --- a/massive/rest/economy.py +++ b/massive/rest/economy.py @@ -10,6 +10,7 @@ FedLaborMarket, EUMerchantAggregate, EUMerchantHierarchy, + FedFundingConditions, ) from .models.common import Sort, Order from .models.request import RequestOptionBuilder @@ -270,3 +271,33 @@ def list_eu_merchant_hierarchy( result_key="results", options=options, ) + + def list_funding_conditions( + self, + date: Optional[Union[str, date]] = None, + date_gt: Optional[Union[str, date]] = None, + date_gte: Optional[Union[str, date]] = None, + date_lt: Optional[Union[str, date]] = None, + date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[FedFundingConditions], HTTPResponse]: + """ + Daily U.S. money-market funding conditions from the Federal Reserve and the New York Fed: policy rates, + overnight reference rates (EFFR, OBFR, SOFR, TGCR) and repo operations. + + Endpoint: GET /fed/v1/funding-conditions + """ + url = "/fed/v1/funding-conditions" + + return self._paginate( + path=url, + params=self._get_params(self.list_funding_conditions, locals()), + result_key="results", + deserializer=FedFundingConditions.from_dict, + raw=raw, + options=options, + ) diff --git a/massive/rest/models/aggs.py b/massive/rest/models/aggs.py index 49bba680..2135f68d 100644 --- a/massive/rest/models/aggs.py +++ b/massive/rest/models/aggs.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, List from ...modelclass import modelclass @@ -120,3 +120,59 @@ def from_dict(d): d.get("v", None), d.get("vw", None), ) + + +@modelclass +class CryptoOpenCloseTrade: + "An opening or closing trade for a crypto pair on a given day." + + conditions: Optional[List[int]] = None + id: Optional[str] = None + price: Optional[float] = None + size: Optional[float] = None + timestamp: Optional[int] = None + exchange: Optional[int] = None + + @staticmethod + def from_dict(d): + return CryptoOpenCloseTrade( + conditions=d.get("c"), + id=d.get("i"), + price=d.get("p"), + size=d.get("s"), + timestamp=d.get("t"), + exchange=d.get("x"), + ) + + +@modelclass +class CryptoDailyOpenCloseAgg: + "Contains the open and close prices of a crypto pair on a given day, with the opening and closing trades." + + symbol: Optional[str] = None + is_utc: Optional[bool] = None + day: Optional[str] = None + open: Optional[float] = None + close: Optional[float] = None + open_trades: Optional[List[CryptoOpenCloseTrade]] = None + closing_trades: Optional[List[CryptoOpenCloseTrade]] = None + + @staticmethod + def from_dict(d): + return CryptoDailyOpenCloseAgg( + symbol=d.get("symbol"), + is_utc=d.get("isUTC"), + day=d.get("day"), + open=d.get("open"), + close=d.get("close"), + open_trades=( + [CryptoOpenCloseTrade.from_dict(x) for x in d["openTrades"]] + if d.get("openTrades") is not None + else None + ), + closing_trades=( + [CryptoOpenCloseTrade.from_dict(x) for x in d["closingTrades"]] + if d.get("closingTrades") is not None + else None + ), + ) diff --git a/massive/rest/models/contracts.py b/massive/rest/models/contracts.py index f0ca648e..8a1ee9fb 100644 --- a/massive/rest/models/contracts.py +++ b/massive/rest/models/contracts.py @@ -51,3 +51,59 @@ def from_dict(d): ticker=d.get("ticker", None), underlying_ticker=d.get("underlying_ticker", None), ) + + +@modelclass +class OptionsOpenInterest: + "Daily open interest for a single options contract." + + ticker: Optional[str] = None + underlying_ticker: Optional[str] = None + root: Optional[str] = None + contract_type: Optional[str] = None + expiration_date: Optional[str] = None + strike_price: Optional[float] = None + date: Optional[str] = None + open_interest: Optional[int] = None + + @staticmethod + def from_dict(d): + return OptionsOpenInterest( + ticker=d.get("ticker"), + underlying_ticker=d.get("underlying_ticker"), + root=d.get("root"), + contract_type=d.get("contract_type"), + expiration_date=d.get("expiration_date"), + strike_price=d.get("strike_price"), + date=d.get("date"), + open_interest=d.get("open_interest"), + ) + + +@modelclass +class OptionsUnderlyingOpenInterest: + "Daily options open interest rolled up across all contracts for an underlying ticker." + + underlying_ticker: Optional[str] = None + date: Optional[str] = None + open_interest: Optional[int] = None + open_interest_calls: Optional[int] = None + open_interest_puts: Optional[int] = None + previous_open_interest: Optional[int] = None + open_interest_change: Optional[int] = None + open_interest_change_calls: Optional[int] = None + open_interest_change_puts: Optional[int] = None + + @staticmethod + def from_dict(d): + return OptionsUnderlyingOpenInterest( + underlying_ticker=d.get("underlying_ticker"), + date=d.get("date"), + open_interest=d.get("open_interest"), + open_interest_calls=d.get("open_interest_calls"), + open_interest_puts=d.get("open_interest_puts"), + previous_open_interest=d.get("previous_open_interest"), + open_interest_change=d.get("open_interest_change"), + open_interest_change_calls=d.get("open_interest_change_calls"), + open_interest_change_puts=d.get("open_interest_change_puts"), + ) diff --git a/massive/rest/models/economy.py b/massive/rest/models/economy.py index 97386235..337b24ec 100644 --- a/massive/rest/models/economy.py +++ b/massive/rest/models/economy.py @@ -227,3 +227,70 @@ def from_dict(d): sub_industry=d.get("sub_industry"), ticker=d.get("ticker"), ) + + +@modelclass +class FedFundingConditions: + "Daily U.S. money-market funding conditions: policy rates, overnight reference rates and repo operations." + + date: Optional[str] = None + fed_funds_target_lower: Optional[float] = None + fed_funds_target_upper: Optional[float] = None + interest_on_reserve_balances: Optional[float] = None + effective_fed_funds_rate: Optional[float] = None + effective_fed_funds_volume: Optional[float] = None + overnight_bank_funding_rate: Optional[float] = None + obfr_25th_percentile: Optional[float] = None + obfr_75th_percentile: Optional[float] = None + obfr_volume: Optional[float] = None + secured_overnight_financing_rate: Optional[float] = None + sofr_25th_percentile: Optional[float] = None + sofr_75th_percentile: Optional[float] = None + sofr_volume: Optional[float] = None + tri_party_general_collateral_rate: Optional[float] = None + tgcr_25th_percentile: Optional[float] = None + tgcr_75th_percentile: Optional[float] = None + tri_party_general_collateral_volume: Optional[float] = None + fed_overnight_repo_treasury_amount: Optional[float] = None + fed_overnight_reverse_repo_treasury_amount: Optional[float] = None + financial_commercial_paper_90d_rate: Optional[float] = None + nonfinancial_commercial_paper_90d_rate: Optional[float] = None + + @staticmethod + def from_dict(d): + return FedFundingConditions( + date=d.get("date"), + fed_funds_target_lower=d.get("fed_funds_target_lower"), + fed_funds_target_upper=d.get("fed_funds_target_upper"), + interest_on_reserve_balances=d.get("interest_on_reserve_balances"), + effective_fed_funds_rate=d.get("effective_fed_funds_rate"), + effective_fed_funds_volume=d.get("effective_fed_funds_volume"), + overnight_bank_funding_rate=d.get("overnight_bank_funding_rate"), + obfr_25th_percentile=d.get("obfr_25th_percentile"), + obfr_75th_percentile=d.get("obfr_75th_percentile"), + obfr_volume=d.get("obfr_volume"), + secured_overnight_financing_rate=d.get("secured_overnight_financing_rate"), + sofr_25th_percentile=d.get("sofr_25th_percentile"), + sofr_75th_percentile=d.get("sofr_75th_percentile"), + sofr_volume=d.get("sofr_volume"), + tri_party_general_collateral_rate=d.get( + "tri_party_general_collateral_rate" + ), + tgcr_25th_percentile=d.get("tgcr_25th_percentile"), + tgcr_75th_percentile=d.get("tgcr_75th_percentile"), + tri_party_general_collateral_volume=d.get( + "tri_party_general_collateral_volume" + ), + fed_overnight_repo_treasury_amount=d.get( + "fed_overnight_repo_treasury_amount" + ), + fed_overnight_reverse_repo_treasury_amount=d.get( + "fed_overnight_reverse_repo_treasury_amount" + ), + financial_commercial_paper_90d_rate=d.get( + "financial_commercial_paper_90d_rate" + ), + nonfinancial_commercial_paper_90d_rate=d.get( + "nonfinancial_commercial_paper_90d_rate" + ), + ) diff --git a/massive/rest/models/financials.py b/massive/rest/models/financials.py index 3266da1d..f936acec 100644 --- a/massive/rest/models/financials.py +++ b/massive/rest/models/financials.py @@ -1306,3 +1306,91 @@ def from_dict(d): issuer_name=d.get("issuer_name"), ticker=d.get("ticker"), ) + + +@modelclass +class InsiderActivity: + "Daily insider trading activity for an issuer, aggregated across all of its insiders' open-market purchases and sales." + + issuer_cik: Optional[str] = None + issuer_name: Optional[str] = None + tickers: Optional[List[str]] = None + transaction_date: Optional[str] = None + last_filing_date: Optional[str] = None + purchase_count: Optional[int] = None + purchaser_count: Optional[int] = None + purchase_shares: Optional[float] = None + purchase_value: Optional[float] = None + sale_count: Optional[int] = None + seller_count: Optional[int] = None + sale_shares: Optional[float] = None + sale_value: Optional[float] = None + net_shares: Optional[float] = None + net_value: Optional[float] = None + planned_purchase_value: Optional[float] = None + planned_sale_value: Optional[float] = None + + @staticmethod + def from_dict(d): + return InsiderActivity( + issuer_cik=d.get("issuer_cik"), + issuer_name=d.get("issuer_name"), + tickers=d.get("tickers"), + transaction_date=d.get("transaction_date"), + last_filing_date=d.get("last_filing_date"), + purchase_count=d.get("purchase_count"), + purchaser_count=d.get("purchaser_count"), + purchase_shares=d.get("purchase_shares"), + purchase_value=d.get("purchase_value"), + sale_count=d.get("sale_count"), + seller_count=d.get("seller_count"), + sale_shares=d.get("sale_shares"), + sale_value=d.get("sale_value"), + net_shares=d.get("net_shares"), + net_value=d.get("net_value"), + planned_purchase_value=d.get("planned_purchase_value"), + planned_sale_value=d.get("planned_sale_value"), + ) + + +@modelclass +class InsiderHolding: + "An insider of an issuer (one reporting owner per issuer) with their current reported holdings." + + issuer_cik: Optional[str] = None + issuer_name: Optional[str] = None + tickers: Optional[List[str]] = None + owner_cik: Optional[str] = None + owner_name: Optional[str] = None + officer_title: Optional[str] = None + roles: Optional[List[str]] = None + shares_owned: Optional[float] = None + shares_owned_direct: Optional[float] = None + shares_owned_indirect: Optional[float] = None + ownership_pct: Optional[float] = None + position_count: Optional[int] = None + filing_count: Optional[int] = None + first_filing_date: Optional[str] = None + last_filing_date: Optional[str] = None + oldest_position_date: Optional[str] = None + + @staticmethod + def from_dict(d): + return InsiderHolding( + issuer_cik=d.get("issuer_cik"), + issuer_name=d.get("issuer_name"), + tickers=d.get("tickers"), + owner_cik=d.get("owner_cik"), + owner_name=d.get("owner_name"), + officer_title=d.get("officer_title"), + roles=d.get("roles"), + shares_owned=d.get("shares_owned"), + shares_owned_direct=d.get("shares_owned_direct"), + shares_owned_indirect=d.get("shares_owned_indirect"), + ownership_pct=d.get("ownership_pct"), + position_count=d.get("position_count"), + filing_count=d.get("filing_count"), + first_filing_date=d.get("first_filing_date"), + last_filing_date=d.get("last_filing_date"), + oldest_position_date=d.get("oldest_position_date"), + ) diff --git a/massive/rest/models/trades.py b/massive/rest/models/trades.py index 06cf8bd9..d4a41302 100644 --- a/massive/rest/models/trades.py +++ b/massive/rest/models/trades.py @@ -77,3 +77,34 @@ class CryptoTrade: @staticmethod def from_dict(d): return CryptoTrade(**d) + + +@modelclass +class OptionsUnusualTrade: + "An unusual trade for a US options contract." + + ticker: Optional[str] = None + underlying_ticker: Optional[str] = None + root: Optional[str] = None + sip_timestamp: Optional[int] = None + sequence_number: Optional[int] = None + price: Optional[float] = None + size: Optional[int] = None + exchange: Optional[int] = None + conditions: Optional[List[int]] = None + flags: Optional[List[str]] = None + + @staticmethod + def from_dict(d): + return OptionsUnusualTrade( + ticker=d.get("ticker"), + underlying_ticker=d.get("underlying_ticker"), + root=d.get("root"), + sip_timestamp=d.get("sip_timestamp"), + sequence_number=d.get("sequence_number"), + price=d.get("price"), + size=d.get("size"), + exchange=d.get("exchange"), + conditions=d.get("conditions"), + flags=d.get("flags"), + ) diff --git a/massive/rest/reference.py b/massive/rest/reference.py index b6886c55..f74124bb 100644 --- a/massive/rest/reference.py +++ b/massive/rest/reference.py @@ -36,6 +36,10 @@ FilingForm3, FilingForm4, FilingIndex, + OptionsOpenInterest, + OptionsUnderlyingOpenInterest, + InsiderActivity, + InsiderHolding, ) from urllib3 import HTTPResponse from datetime import date @@ -1206,3 +1210,165 @@ def list_stocks_filings_form_4( raw=raw, options=options, ) + + def list_options_open_interest( + self, + ticker: Optional[str] = None, + ticker_any_of: Optional[Union[str, List[str]]] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + root: Optional[str] = None, + root_any_of: Optional[Union[str, List[str]]] = None, + root_gt: Optional[str] = None, + root_gte: Optional[str] = None, + root_lt: Optional[str] = None, + root_lte: Optional[str] = None, + contract_type: Optional[str] = None, + contract_type_any_of: Optional[Union[str, List[str]]] = None, + expiration_date: Optional[Union[str, date]] = None, + expiration_date_gt: Optional[Union[str, date]] = None, + expiration_date_gte: Optional[Union[str, date]] = None, + expiration_date_lt: Optional[Union[str, date]] = None, + expiration_date_lte: Optional[Union[str, date]] = None, + date: Optional[Union[str, date]] = None, + date_gt: Optional[Union[str, date]] = None, + date_gte: Optional[Union[str, date]] = None, + date_lt: Optional[Union[str, date]] = None, + date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[OptionsOpenInterest], HTTPResponse]: + """ + Daily open interest for US options contracts, as reported by OPRA. + + Endpoint: GET /options/v1/open-interest + """ + url = "/options/v1/open-interest" + + return self._paginate( + path=url, + params=self._get_params(self.list_options_open_interest, locals()), + result_key="results", + deserializer=OptionsOpenInterest.from_dict, + raw=raw, + options=options, + ) + + def list_options_open_interest_by_underlying( + self, + underlying_ticker: Optional[str] = None, + underlying_ticker_any_of: Optional[Union[str, List[str]]] = None, + underlying_ticker_gt: Optional[str] = None, + underlying_ticker_gte: Optional[str] = None, + underlying_ticker_lt: Optional[str] = None, + underlying_ticker_lte: Optional[str] = None, + date: Optional[Union[str, date]] = None, + date_gt: Optional[Union[str, date]] = None, + date_gte: Optional[Union[str, date]] = None, + date_lt: Optional[Union[str, date]] = None, + date_lte: Optional[Union[str, date]] = None, + open_interest: Optional[int] = None, + open_interest_gt: Optional[int] = None, + open_interest_gte: Optional[int] = None, + open_interest_lt: Optional[int] = None, + open_interest_lte: Optional[int] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[OptionsUnderlyingOpenInterest], HTTPResponse]: + """ + Daily US options open interest rolled up across each underlying ticker's contracts, split into calls and puts. + + Endpoint: GET /options/vX/open-interest/underlying + """ + url = "/options/vX/open-interest/underlying" + + return self._paginate( + path=url, + params=self._get_params( + self.list_options_open_interest_by_underlying, locals() + ), + result_key="results", + deserializer=OptionsUnderlyingOpenInterest.from_dict, + raw=raw, + options=options, + ) + + def list_stocks_insider_activity( + self, + tickers: Optional[str] = None, + tickers_all_of: Optional[Union[str, List[str]]] = None, + tickers_any_of: Optional[Union[str, List[str]]] = None, + issuer_cik: Optional[str] = None, + issuer_cik_any_of: Optional[Union[str, List[str]]] = None, + transaction_date: Optional[Union[str, date]] = None, + transaction_date_gt: Optional[Union[str, date]] = None, + transaction_date_gte: Optional[Union[str, date]] = None, + transaction_date_lt: Optional[Union[str, date]] = None, + transaction_date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[InsiderActivity], HTTPResponse]: + """ + Daily insider trading activity per issuer: open-market purchases and sales by directors, officers and 10%+ + shareholders, aggregated from SEC Form 4 and Form 5 filings. + + Endpoint: GET /stocks/insiders/vX/activity + """ + url = "/stocks/insiders/vX/activity" + + return self._paginate( + path=url, + params=self._get_params(self.list_stocks_insider_activity, locals()), + result_key="results", + deserializer=InsiderActivity.from_dict, + raw=raw, + options=options, + ) + + def list_stocks_insider_holdings( + self, + tickers: Optional[str] = None, + tickers_all_of: Optional[Union[str, List[str]]] = None, + tickers_any_of: Optional[Union[str, List[str]]] = None, + issuer_cik: Optional[str] = None, + issuer_cik_any_of: Optional[Union[str, List[str]]] = None, + owner_cik: Optional[str] = None, + owner_cik_any_of: Optional[Union[str, List[str]]] = None, + last_filing_date: Optional[Union[str, date]] = None, + last_filing_date_gt: Optional[Union[str, date]] = None, + last_filing_date_gte: Optional[Union[str, date]] = None, + last_filing_date_lt: Optional[Union[str, date]] = None, + last_filing_date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[InsiderHolding], HTTPResponse]: + """ + Insiders of each issuer, one row per reporting owner, with their current reported holdings from SEC + Forms 3, 4 and 5. + + Endpoint: GET /stocks/insiders/vX/holdings + """ + url = "/stocks/insiders/vX/holdings" + + return self._paginate( + path=url, + params=self._get_params(self.list_stocks_insider_holdings, locals()), + result_key="results", + deserializer=InsiderHolding.from_dict, + raw=raw, + options=options, + ) diff --git a/massive/rest/trades.py b/massive/rest/trades.py index 3aad1d0e..34ab5d51 100644 --- a/massive/rest/trades.py +++ b/massive/rest/trades.py @@ -1,6 +1,6 @@ from .base import BaseClient -from typing import Optional, Any, Dict, Union, Iterator -from .models import Trade, LastTrade, CryptoTrade, Sort, Order +from typing import Optional, Any, Dict, List, Union, Iterator +from .models import Trade, LastTrade, CryptoTrade, OptionsUnusualTrade, Sort, Order from urllib3 import HTTPResponse from datetime import datetime, date @@ -102,3 +102,38 @@ def get_last_crypto_trade( raw=raw, options=options, ) + + def list_options_unusual_trades( + self, + ticker: Optional[str] = None, + ticker_any_of: Optional[Union[str, List[str]]] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + sip_timestamp: Optional[Union[str, int, datetime, date]] = None, + sip_timestamp_gt: Optional[Union[str, int, datetime, date]] = None, + sip_timestamp_gte: Optional[Union[str, int, datetime, date]] = None, + sip_timestamp_lt: Optional[Union[str, int, datetime, date]] = None, + sip_timestamp_lte: Optional[Union[str, int, datetime, date]] = None, + limit: Optional[int] = None, + sort: Optional[Union[str, Sort]] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[OptionsUnusualTrade], HTTPResponse]: + """ + Unusual trades for US options contracts, flagged from real-time OPRA trade data. + + Endpoint: GET /options/v1/unusual-trades + """ + url = "/options/v1/unusual-trades" + + return self._paginate( + path=url, + params=self._get_params(self.list_options_unusual_trades, locals()), + result_key="results", + deserializer=OptionsUnusualTrade.from_dict, + raw=raw, + options=options, + ) From cf8c40eab32883ae191f72fc6c1150d4fdf0368b Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:46:18 -0700 Subject: [PATCH 3/7] Fix forex snapshot last_quote ask/bid prices TickerSnapshot.last_quote reuses the stock LastQuote model, which reads ask/bid prices from P/p. Forex snapshot quotes send a/b instead, so get_snapshot_ticker/all/direction for forex returned ask_price and bid_price as None. Fall back to a/b when P/p are absent. --- massive/rest/models/quotes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/massive/rest/models/quotes.py b/massive/rest/models/quotes.py index 5f14d6b1..0c365cf5 100644 --- a/massive/rest/models/quotes.py +++ b/massive/rest/models/quotes.py @@ -52,12 +52,13 @@ def from_dict(d): sequence_number=d.get("q", None), sip_timestamp=d.get("t", None), participant_timestamp=d.get("y", None), - ask_price=d.get("P", None), + # stocks send P/p; forex snapshot quotes send a/b + ask_price=d.get("P", d.get("a")), ask_size=d.get("S", None), ask_exchange=d.get("X", None), conditions=d.get("c", None), indicators=d.get("i", None), - bid_price=d.get("p", None), + bid_price=d.get("p", d.get("b")), bid_size=d.get("s", None), bid_exchange=d.get("x", None), tape=d.get("z", None), From 329252e3ef2fb7043ef11b598fcfb64096be8ed6 Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:47:50 -0700 Subject: [PATCH 4/7] Expose response fields the models were dropping Found by comparing live API responses with the deserialized objects: - RealTimeCurrencyConversion.from_ read the key "from_", but the API sends "from", so it was always None; also add symbol - PreviousCloseAgg.transactions (n) - TickerDetails.round_lot - EtfGlobalConstituent.constituent_rank - LastQuoteOptionContractSnapshot.ask_exchange / bid_exchange Two existing tests asserted the old values (from_=None, no transactions) even though their fixtures carried the data; updated them. --- massive/rest/models/aggs.py | 2 ++ massive/rest/models/etf_global.py | 2 ++ massive/rest/models/quotes.py | 4 +++- massive/rest/models/snapshot.py | 2 ++ massive/rest/models/tickers.py | 2 ++ test_rest/test_aggs.py | 1 + test_rest/test_quotes.py | 3 ++- 7 files changed, 14 insertions(+), 2 deletions(-) diff --git a/massive/rest/models/aggs.py b/massive/rest/models/aggs.py index 2135f68d..10591ecb 100644 --- a/massive/rest/models/aggs.py +++ b/massive/rest/models/aggs.py @@ -107,6 +107,7 @@ class PreviousCloseAgg: timestamp: Optional[float] = None volume: Optional[float] = None vwap: Optional[float] = None + transactions: Optional[int] = None @staticmethod def from_dict(d): @@ -119,6 +120,7 @@ def from_dict(d): d.get("t", None), d.get("v", None), d.get("vw", None), + d.get("n", None), ) diff --git a/massive/rest/models/etf_global.py b/massive/rest/models/etf_global.py index 053babe6..f131a089 100644 --- a/massive/rest/models/etf_global.py +++ b/massive/rest/models/etf_global.py @@ -84,6 +84,7 @@ class EtfGlobalConstituent: asset_class: Optional[str] = None composite_ticker: Optional[str] = None constituent_name: Optional[str] = None + constituent_rank: Optional[int] = None constituent_ticker: Optional[str] = None country_of_exchange: Optional[str] = None currency_traded: Optional[str] = None @@ -105,6 +106,7 @@ def from_dict(d): asset_class=d.get("asset_class"), composite_ticker=d.get("composite_ticker"), constituent_name=d.get("constituent_name"), + constituent_rank=d.get("constituent_rank"), constituent_ticker=d.get("constituent_ticker"), country_of_exchange=d.get("country_of_exchange"), currency_traded=d.get("currency_traded"), diff --git a/massive/rest/models/quotes.py b/massive/rest/models/quotes.py index 0c365cf5..81940b9d 100644 --- a/massive/rest/models/quotes.py +++ b/massive/rest/models/quotes.py @@ -103,13 +103,15 @@ class RealTimeCurrencyConversion: initial_amount: Optional[float] = None last: Optional[ForexQuote] = None to: Optional[str] = None + symbol: Optional[str] = None @staticmethod def from_dict(d): return RealTimeCurrencyConversion( converted=d.get("converted", None), - from_=d.get("from_", None), + from_=d.get("from", d.get("from_")), initial_amount=d.get("initialAmount", None), last=None if "last" not in d else ForexQuote.from_dict(d["last"]), to=d.get("to", None), + symbol=d.get("symbol"), ) diff --git a/massive/rest/models/snapshot.py b/massive/rest/models/snapshot.py index 80b0bdb7..0e73894a 100644 --- a/massive/rest/models/snapshot.py +++ b/massive/rest/models/snapshot.py @@ -170,6 +170,8 @@ class LastQuoteOptionContractSnapshot: last_updated: Optional[int] = None midpoint: Optional[float] = None timeframe: Optional[str] = None + ask_exchange: Optional[int] = None + bid_exchange: Optional[int] = None @staticmethod def from_dict(d): diff --git a/massive/rest/models/tickers.py b/massive/rest/models/tickers.py index 2fda6c4f..775da553 100644 --- a/massive/rest/models/tickers.py +++ b/massive/rest/models/tickers.py @@ -112,6 +112,7 @@ class TickerDetails: name: Optional[str] = None phone_number: Optional[str] = None primary_exchange: Optional[str] = None + round_lot: Optional[int] = None share_class_figi: Optional[str] = None share_class_shares_outstanding: Optional[int] = None sic_code: Optional[str] = None @@ -147,6 +148,7 @@ def from_dict(d): name=d.get("name", None), phone_number=d.get("phone_number", None), primary_exchange=d.get("primary_exchange", None), + round_lot=d.get("round_lot", None), share_class_figi=d.get("share_class_figi", None), share_class_shares_outstanding=d.get( "share_class_shares_outstanding", None diff --git a/test_rest/test_aggs.py b/test_rest/test_aggs.py index 850a00da..b45ed7eb 100644 --- a/test_rest/test_aggs.py +++ b/test_rest/test_aggs.py @@ -108,6 +108,7 @@ def test_get_previous_close_agg(self): timestamp=1651003200000, volume=95595226.0, vwap=158.6074, + transactions=899965, ) ] self.assertEqual(aggs, expected) diff --git a/test_rest/test_quotes.py b/test_rest/test_quotes.py index 1086bdb5..c9028e1a 100644 --- a/test_rest/test_quotes.py +++ b/test_rest/test_quotes.py @@ -351,12 +351,13 @@ def test_get_real_time_currency_conversion(self): conversion = self.c.get_real_time_currency_conversion("AUD", "USD", 100, 2) expected = RealTimeCurrencyConversion( converted=69.31, - from_=None, + from_="AUD", initial_amount=100, last=ForexQuote( ask=1.4436264, bid=1.4427932, exchange=48, timestamp=1652195426000 ), to="USD", + symbol="USD/AUD", ) self.assertEqual(conversion, expected) From 51b19ff24162017e7cff21f5bb0016ac0f611ef6 Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:48:58 -0700 Subject: [PATCH 5/7] Support spec query params the client was missing All additive and inserted just before params=, so positional calls keep working: - list_short_interest: ticker_{any_of,gt,gte,lt,lte}, {days_to_cover,settlement_date,avg_daily_volume}_any_of - list_short_volume: ticker_{any_of,gt,gte,lt,lte}, {date,short_volume_ratio}_any_of - list_futures_contracts: provider_id, provider_product_id - list_futures_products: provider_id - list_benzinga_consensus_ratings: date_any_of - list_options_contracts: ticker - list_universal_snapshots: ticker Verified against the live API that each filter is applied. --- massive/rest/benzinga.py | 1 + massive/rest/futures.py | 3 +++ massive/rest/reference.py | 16 ++++++++++++++++ massive/rest/snapshot.py | 1 + 4 files changed, 21 insertions(+) diff --git a/massive/rest/benzinga.py b/massive/rest/benzinga.py index fdd9d0b0..64811eae 100644 --- a/massive/rest/benzinga.py +++ b/massive/rest/benzinga.py @@ -139,6 +139,7 @@ def list_benzinga_consensus_ratings( date_lt: Optional[Union[str, date]] = None, date_lte: Optional[Union[str, date]] = None, limit: Optional[int] = None, + date_any_of: Optional[Union[str, List[str]]] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, diff --git a/massive/rest/futures.py b/massive/rest/futures.py index 8973c7af..d8d77696 100644 --- a/massive/rest/futures.py +++ b/massive/rest/futures.py @@ -91,6 +91,8 @@ def list_futures_contracts( last_trade_date_lte: Optional[str] = None, limit: Optional[int] = None, sort: Optional[str] = None, + provider_id: Optional[str] = None, + provider_product_id: Optional[str] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, @@ -146,6 +148,7 @@ def list_futures_products( type_any_of: Optional[str] = None, limit: Optional[int] = None, sort: Optional[str] = None, + provider_id: Optional[str] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, diff --git a/massive/rest/reference.py b/massive/rest/reference.py index f74124bb..337b2d9e 100644 --- a/massive/rest/reference.py +++ b/massive/rest/reference.py @@ -556,6 +556,7 @@ def list_options_contracts( limit: Optional[int] = None, sort: Optional[Union[str, Sort]] = None, order: Optional[Union[str, Order]] = None, + ticker: Optional[str] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, @@ -607,6 +608,14 @@ def list_short_interest( limit: Optional[int] = None, sort: Optional[Union[str, Sort]] = None, order: Optional[Union[str, Order]] = None, + ticker_any_of: Optional[Union[str, List[str]]] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + days_to_cover_any_of: Optional[Union[str, List[str]]] = None, + settlement_date_any_of: Optional[Union[str, List[str]]] = None, + avg_daily_volume_any_of: Optional[Union[str, List[str]]] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, @@ -670,6 +679,13 @@ def list_short_volume( limit: Optional[int] = None, sort: Optional[Union[str, Sort]] = None, order: Optional[Union[str, Order]] = None, + ticker_any_of: Optional[Union[str, List[str]]] = None, + ticker_gt: Optional[str] = None, + ticker_gte: Optional[str] = None, + ticker_lt: Optional[str] = None, + ticker_lte: Optional[str] = None, + date_any_of: Optional[Union[str, List[str]]] = None, + short_volume_ratio_any_of: Optional[Union[str, List[str]]] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, diff --git a/massive/rest/snapshot.py b/massive/rest/snapshot.py index 1cf4e368..8e9e0c7b 100644 --- a/massive/rest/snapshot.py +++ b/massive/rest/snapshot.py @@ -35,6 +35,7 @@ def list_universal_snapshots( ticker_lte: Optional[str] = None, ticker_gt: Optional[str] = None, ticker_gte: Optional[str] = None, + ticker: Optional[str] = None, params: Optional[Dict[str, Any]] = None, raw: bool = False, options: Optional[RequestOptionBuilder] = None, From 5ef4e48e2588e74df5345a5a8db3d0e62468ef97 Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Wed, 11 Nov 2026 13:37:25 -0700 Subject: [PATCH 6/7] Remove endpoints that are not live yet Drop list_funding_conditions, get_crypto_daily_open_close_agg and list_options_open_interest_by_underlying. They are in the prod spec but not live yet. --- massive/rest/aggs.py | 42 +------------------- massive/rest/economy.py | 31 --------------- massive/rest/models/aggs.py | 58 +-------------------------- massive/rest/models/contracts.py | 29 -------------- massive/rest/models/economy.py | 67 -------------------------------- massive/rest/reference.py | 43 -------------------- 6 files changed, 2 insertions(+), 268 deletions(-) diff --git a/massive/rest/aggs.py b/massive/rest/aggs.py index 226df3f5..cb874fac 100644 --- a/massive/rest/aggs.py +++ b/massive/rest/aggs.py @@ -1,13 +1,6 @@ from .base import BaseClient from typing import Optional, Any, Dict, List, Union, Iterator -from .models import ( - Agg, - GroupedDailyAgg, - DailyOpenCloseAgg, - CryptoDailyOpenCloseAgg, - PreviousCloseAgg, - Sort, -) +from .models import Agg, GroupedDailyAgg, DailyOpenCloseAgg, PreviousCloseAgg, Sort from urllib3 import HTTPResponse from datetime import datetime, date @@ -195,36 +188,3 @@ def get_previous_close_agg( raw=raw, options=options, ) - - def get_crypto_daily_open_close_agg( - self, - from_: str, - to: str, - date: Union[str, date], - adjusted: Optional[bool] = None, - params: Optional[Dict[str, Any]] = None, - raw: bool = False, - options: Optional[RequestOptionBuilder] = None, - ) -> Union[CryptoDailyOpenCloseAgg, HTTPResponse]: - """ - Get the open and close prices of a crypto pair on a certain date, with the opening and closing trades. - - Endpoint: GET /v1/open-close/crypto/{from}/{to}/{date} - - :param from_: The "from" symbol of the pair (e.g. BTC). - :param to: The "to" symbol of the pair (e.g. USD). - :param date: The date of the requested open/close in the format YYYY-MM-DD. - :param adjusted: Whether or not the results are adjusted for splits. By default, results are adjusted. - :param params: Any additional query params - :param raw: Return raw object instead of results object - :return: Crypto daily open close aggregate - """ - url = f"/v1/open-close/crypto/{from_}/{to}/{date}" - - return self._get( - path=url, - params=self._get_params(self.get_crypto_daily_open_close_agg, locals()), - deserializer=CryptoDailyOpenCloseAgg.from_dict, - raw=raw, - options=options, - ) diff --git a/massive/rest/economy.py b/massive/rest/economy.py index dc010e8b..4c4b251f 100644 --- a/massive/rest/economy.py +++ b/massive/rest/economy.py @@ -10,7 +10,6 @@ FedLaborMarket, EUMerchantAggregate, EUMerchantHierarchy, - FedFundingConditions, ) from .models.common import Sort, Order from .models.request import RequestOptionBuilder @@ -271,33 +270,3 @@ def list_eu_merchant_hierarchy( result_key="results", options=options, ) - - def list_funding_conditions( - self, - date: Optional[Union[str, date]] = None, - date_gt: Optional[Union[str, date]] = None, - date_gte: Optional[Union[str, date]] = None, - date_lt: Optional[Union[str, date]] = None, - date_lte: Optional[Union[str, date]] = None, - limit: Optional[int] = None, - sort: Optional[Union[str, Sort]] = None, - params: Optional[Dict[str, Any]] = None, - raw: bool = False, - options: Optional[RequestOptionBuilder] = None, - ) -> Union[Iterator[FedFundingConditions], HTTPResponse]: - """ - Daily U.S. money-market funding conditions from the Federal Reserve and the New York Fed: policy rates, - overnight reference rates (EFFR, OBFR, SOFR, TGCR) and repo operations. - - Endpoint: GET /fed/v1/funding-conditions - """ - url = "/fed/v1/funding-conditions" - - return self._paginate( - path=url, - params=self._get_params(self.list_funding_conditions, locals()), - result_key="results", - deserializer=FedFundingConditions.from_dict, - raw=raw, - options=options, - ) diff --git a/massive/rest/models/aggs.py b/massive/rest/models/aggs.py index 10591ecb..c689e519 100644 --- a/massive/rest/models/aggs.py +++ b/massive/rest/models/aggs.py @@ -1,4 +1,4 @@ -from typing import Optional, List +from typing import Optional from ...modelclass import modelclass @@ -122,59 +122,3 @@ def from_dict(d): d.get("vw", None), d.get("n", None), ) - - -@modelclass -class CryptoOpenCloseTrade: - "An opening or closing trade for a crypto pair on a given day." - - conditions: Optional[List[int]] = None - id: Optional[str] = None - price: Optional[float] = None - size: Optional[float] = None - timestamp: Optional[int] = None - exchange: Optional[int] = None - - @staticmethod - def from_dict(d): - return CryptoOpenCloseTrade( - conditions=d.get("c"), - id=d.get("i"), - price=d.get("p"), - size=d.get("s"), - timestamp=d.get("t"), - exchange=d.get("x"), - ) - - -@modelclass -class CryptoDailyOpenCloseAgg: - "Contains the open and close prices of a crypto pair on a given day, with the opening and closing trades." - - symbol: Optional[str] = None - is_utc: Optional[bool] = None - day: Optional[str] = None - open: Optional[float] = None - close: Optional[float] = None - open_trades: Optional[List[CryptoOpenCloseTrade]] = None - closing_trades: Optional[List[CryptoOpenCloseTrade]] = None - - @staticmethod - def from_dict(d): - return CryptoDailyOpenCloseAgg( - symbol=d.get("symbol"), - is_utc=d.get("isUTC"), - day=d.get("day"), - open=d.get("open"), - close=d.get("close"), - open_trades=( - [CryptoOpenCloseTrade.from_dict(x) for x in d["openTrades"]] - if d.get("openTrades") is not None - else None - ), - closing_trades=( - [CryptoOpenCloseTrade.from_dict(x) for x in d["closingTrades"]] - if d.get("closingTrades") is not None - else None - ), - ) diff --git a/massive/rest/models/contracts.py b/massive/rest/models/contracts.py index 8a1ee9fb..31a7aaff 100644 --- a/massive/rest/models/contracts.py +++ b/massive/rest/models/contracts.py @@ -78,32 +78,3 @@ def from_dict(d): date=d.get("date"), open_interest=d.get("open_interest"), ) - - -@modelclass -class OptionsUnderlyingOpenInterest: - "Daily options open interest rolled up across all contracts for an underlying ticker." - - underlying_ticker: Optional[str] = None - date: Optional[str] = None - open_interest: Optional[int] = None - open_interest_calls: Optional[int] = None - open_interest_puts: Optional[int] = None - previous_open_interest: Optional[int] = None - open_interest_change: Optional[int] = None - open_interest_change_calls: Optional[int] = None - open_interest_change_puts: Optional[int] = None - - @staticmethod - def from_dict(d): - return OptionsUnderlyingOpenInterest( - underlying_ticker=d.get("underlying_ticker"), - date=d.get("date"), - open_interest=d.get("open_interest"), - open_interest_calls=d.get("open_interest_calls"), - open_interest_puts=d.get("open_interest_puts"), - previous_open_interest=d.get("previous_open_interest"), - open_interest_change=d.get("open_interest_change"), - open_interest_change_calls=d.get("open_interest_change_calls"), - open_interest_change_puts=d.get("open_interest_change_puts"), - ) diff --git a/massive/rest/models/economy.py b/massive/rest/models/economy.py index 337b24ec..97386235 100644 --- a/massive/rest/models/economy.py +++ b/massive/rest/models/economy.py @@ -227,70 +227,3 @@ def from_dict(d): sub_industry=d.get("sub_industry"), ticker=d.get("ticker"), ) - - -@modelclass -class FedFundingConditions: - "Daily U.S. money-market funding conditions: policy rates, overnight reference rates and repo operations." - - date: Optional[str] = None - fed_funds_target_lower: Optional[float] = None - fed_funds_target_upper: Optional[float] = None - interest_on_reserve_balances: Optional[float] = None - effective_fed_funds_rate: Optional[float] = None - effective_fed_funds_volume: Optional[float] = None - overnight_bank_funding_rate: Optional[float] = None - obfr_25th_percentile: Optional[float] = None - obfr_75th_percentile: Optional[float] = None - obfr_volume: Optional[float] = None - secured_overnight_financing_rate: Optional[float] = None - sofr_25th_percentile: Optional[float] = None - sofr_75th_percentile: Optional[float] = None - sofr_volume: Optional[float] = None - tri_party_general_collateral_rate: Optional[float] = None - tgcr_25th_percentile: Optional[float] = None - tgcr_75th_percentile: Optional[float] = None - tri_party_general_collateral_volume: Optional[float] = None - fed_overnight_repo_treasury_amount: Optional[float] = None - fed_overnight_reverse_repo_treasury_amount: Optional[float] = None - financial_commercial_paper_90d_rate: Optional[float] = None - nonfinancial_commercial_paper_90d_rate: Optional[float] = None - - @staticmethod - def from_dict(d): - return FedFundingConditions( - date=d.get("date"), - fed_funds_target_lower=d.get("fed_funds_target_lower"), - fed_funds_target_upper=d.get("fed_funds_target_upper"), - interest_on_reserve_balances=d.get("interest_on_reserve_balances"), - effective_fed_funds_rate=d.get("effective_fed_funds_rate"), - effective_fed_funds_volume=d.get("effective_fed_funds_volume"), - overnight_bank_funding_rate=d.get("overnight_bank_funding_rate"), - obfr_25th_percentile=d.get("obfr_25th_percentile"), - obfr_75th_percentile=d.get("obfr_75th_percentile"), - obfr_volume=d.get("obfr_volume"), - secured_overnight_financing_rate=d.get("secured_overnight_financing_rate"), - sofr_25th_percentile=d.get("sofr_25th_percentile"), - sofr_75th_percentile=d.get("sofr_75th_percentile"), - sofr_volume=d.get("sofr_volume"), - tri_party_general_collateral_rate=d.get( - "tri_party_general_collateral_rate" - ), - tgcr_25th_percentile=d.get("tgcr_25th_percentile"), - tgcr_75th_percentile=d.get("tgcr_75th_percentile"), - tri_party_general_collateral_volume=d.get( - "tri_party_general_collateral_volume" - ), - fed_overnight_repo_treasury_amount=d.get( - "fed_overnight_repo_treasury_amount" - ), - fed_overnight_reverse_repo_treasury_amount=d.get( - "fed_overnight_reverse_repo_treasury_amount" - ), - financial_commercial_paper_90d_rate=d.get( - "financial_commercial_paper_90d_rate" - ), - nonfinancial_commercial_paper_90d_rate=d.get( - "nonfinancial_commercial_paper_90d_rate" - ), - ) diff --git a/massive/rest/reference.py b/massive/rest/reference.py index 337b2d9e..babd7b7c 100644 --- a/massive/rest/reference.py +++ b/massive/rest/reference.py @@ -37,7 +37,6 @@ FilingForm4, FilingIndex, OptionsOpenInterest, - OptionsUnderlyingOpenInterest, InsiderActivity, InsiderHolding, ) @@ -1275,48 +1274,6 @@ def list_options_open_interest( options=options, ) - def list_options_open_interest_by_underlying( - self, - underlying_ticker: Optional[str] = None, - underlying_ticker_any_of: Optional[Union[str, List[str]]] = None, - underlying_ticker_gt: Optional[str] = None, - underlying_ticker_gte: Optional[str] = None, - underlying_ticker_lt: Optional[str] = None, - underlying_ticker_lte: Optional[str] = None, - date: Optional[Union[str, date]] = None, - date_gt: Optional[Union[str, date]] = None, - date_gte: Optional[Union[str, date]] = None, - date_lt: Optional[Union[str, date]] = None, - date_lte: Optional[Union[str, date]] = None, - open_interest: Optional[int] = None, - open_interest_gt: Optional[int] = None, - open_interest_gte: Optional[int] = None, - open_interest_lt: Optional[int] = None, - open_interest_lte: Optional[int] = None, - limit: Optional[int] = None, - sort: Optional[Union[str, Sort]] = None, - params: Optional[Dict[str, Any]] = None, - raw: bool = False, - options: Optional[RequestOptionBuilder] = None, - ) -> Union[Iterator[OptionsUnderlyingOpenInterest], HTTPResponse]: - """ - Daily US options open interest rolled up across each underlying ticker's contracts, split into calls and puts. - - Endpoint: GET /options/vX/open-interest/underlying - """ - url = "/options/vX/open-interest/underlying" - - return self._paginate( - path=url, - params=self._get_params( - self.list_options_open_interest_by_underlying, locals() - ), - result_key="results", - deserializer=OptionsUnderlyingOpenInterest.from_dict, - raw=raw, - options=options, - ) - def list_stocks_insider_activity( self, tickers: Optional[str] = None, From 010cc6ca2802989a119ea1f5dee3096684bb645b Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Wed, 11 Nov 2026 17:03:11 -0700 Subject: [PATCH 7/7] Retrigger CI