diff --git a/polygon/modelclass.py b/polygon/modelclass.py new file mode 100644 index 00000000..179e2689 --- /dev/null +++ b/polygon/modelclass.py @@ -0,0 +1,23 @@ +import inspect +from dataclasses import dataclass + + +def modelclass(cls): + cls = dataclass(cls) + attributes = [ + a + for a in cls.__dict__["__annotations__"].keys() + if not a.startswith("__") and not inspect.isroutine(a) + ] + + def init(self, *args, **kwargs): + for (i, a) in enumerate(args): + if i < len(attributes): + self.__dict__[attributes[i]] = a + for (k, v) in kwargs.items(): + if k in attributes: + self.__dict__[k] = v + + cls.__init__ = init + + return cls diff --git a/polygon/rest/models/aggs.py b/polygon/rest/models/aggs.py index b14d4969..72aa0db2 100644 --- a/polygon/rest/models/aggs.py +++ b/polygon/rest/models/aggs.py @@ -1,8 +1,8 @@ -from dataclasses import dataclass from typing import Optional +from ...modelclass import modelclass -@dataclass +@modelclass class Agg: "Contains aggregate data for a given ticker symbol over a given date range in a custom time window size." open: Optional[float] = None @@ -28,7 +28,7 @@ def from_dict(d): ) -@dataclass +@modelclass class GroupedDailyAgg: "Contains daily open, high, low, and close (OHLC) data for a given date." ticker: Optional[str] = None @@ -56,7 +56,7 @@ def from_dict(d): ) -@dataclass +@modelclass class DailyOpenCloseAgg: "Contains data for open, close and afterhours prices of a ticker symbol on a specified date." after_hours: Optional[float] = None @@ -86,7 +86,7 @@ def from_dict(d): ) -@dataclass +@modelclass class PreviousCloseAgg: "Contains data for the previous day's open, high, low, and close (OHLC) of the specified stock ticker." ticker: Optional[str] = None diff --git a/polygon/rest/models/conditions.py b/polygon/rest/models/conditions.py index e94984a8..3fc0d776 100644 --- a/polygon/rest/models/conditions.py +++ b/polygon/rest/models/conditions.py @@ -1,8 +1,8 @@ from typing import Optional, List -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class SipMapping: "Contains data for a mapping to a symbol for each SIP that has a given condition." CTA: Optional[str] = None @@ -14,7 +14,7 @@ def from_dict(d): return SipMapping(**d) -@dataclass +@modelclass class Consolidated: "Contains data for aggregation rules on a consolidated (all exchanges) basis." updates_high_low: Optional[bool] = None @@ -26,7 +26,7 @@ def from_dict(d): return Consolidated(**d) -@dataclass +@modelclass class MarketCenter: "Contains data for aggregation rules on a per-market-center basis." updates_high_low: Optional[bool] = None @@ -38,7 +38,7 @@ def from_dict(d): return MarketCenter(**d) -@dataclass +@modelclass class UpdateRules: "Contains data for a list of aggregation rules." consolidated: Optional[Consolidated] = None @@ -56,7 +56,7 @@ def from_dict(d): ) -@dataclass +@modelclass class Condition: "Condition contains data for a condition that Polygon.io uses." abbreviation: Optional[str] = None diff --git a/polygon/rest/models/dividends.py b/polygon/rest/models/dividends.py index 2ae3ba80..1abe5dc5 100644 --- a/polygon/rest/models/dividends.py +++ b/polygon/rest/models/dividends.py @@ -1,8 +1,8 @@ from typing import Optional -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class Dividend: "Dividend contains data for a historical cash dividend, including the ticker symbol, declaration date, ex-dividend date, record date, pay date, frequency, and amount." cash_amount: Optional[float] = None diff --git a/polygon/rest/models/exchanges.py b/polygon/rest/models/exchanges.py index ef0254fe..cd93a7d9 100644 --- a/polygon/rest/models/exchanges.py +++ b/polygon/rest/models/exchanges.py @@ -1,8 +1,8 @@ from typing import Optional -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class Exchange: "Exchange contains data for a condition that Polygon.io uses." acronym: Optional[str] = None diff --git a/polygon/rest/models/financials.py b/polygon/rest/models/financials.py index 6f578187..85a63e37 100644 --- a/polygon/rest/models/financials.py +++ b/polygon/rest/models/financials.py @@ -1,8 +1,8 @@ from typing import Optional, Dict -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class DataPoint: "An individual financial data point." formula: Optional[str] = None @@ -17,7 +17,7 @@ def from_dict(d): return DataPoint(**d) -@dataclass +@modelclass class ExchangeGainsLosses: "Contains exchange gains losses data for a cash flow statement." formula: Optional[str] = None @@ -32,7 +32,7 @@ def from_dict(d): return ExchangeGainsLosses(**d) -@dataclass +@modelclass class NetCashFlow: "Contains net cash flow data for a cash flow statement." formula: Optional[str] = None @@ -47,7 +47,7 @@ def from_dict(d): return NetCashFlow(**d) -@dataclass +@modelclass class NetCashFlowFromFinancingActivities: "Contains net cash flow from financing activities data for a cash flow statement." formula: Optional[str] = None @@ -62,7 +62,7 @@ def from_dict(d): return NetCashFlowFromFinancingActivities(**d) -@dataclass +@modelclass class CashFlowStatement: "Contains cash flow statement data." exchange_gains_losses: Optional[ExchangeGainsLosses] = None @@ -88,7 +88,7 @@ def from_dict(d): ) -@dataclass +@modelclass class ComprehensiveIncomeLoss: "Contains comprehensive income loss data for comprehensive income." formula: Optional[str] = None @@ -103,7 +103,7 @@ def from_dict(d): return ComprehensiveIncomeLoss(**d) -@dataclass +@modelclass class ComprehensiveIncomeLossAttributableToParent: "Contains comprehensive income loss attributable to parent data for comprehensive income." formula: Optional[str] = None @@ -118,7 +118,7 @@ def from_dict(d): return ComprehensiveIncomeLossAttributableToParent(**d) -@dataclass +@modelclass class OtherComprehensiveIncomeLoss: "Contains other comprehensive income loss data for comprehensive income." formula: Optional[str] = None @@ -133,7 +133,7 @@ def from_dict(d): return OtherComprehensiveIncomeLoss(**d) -@dataclass +@modelclass class ComprehensiveIncome: "Contains comprehensive income data." comprehensive_income_loss: Optional[ComprehensiveIncomeLoss] = None @@ -161,7 +161,7 @@ def from_dict(d): ) -@dataclass +@modelclass class BasicEarningsPerShare: "Contains basic earning per share data for an income statement." formula: Optional[str] = None @@ -176,7 +176,7 @@ def from_dict(d): return BasicEarningsPerShare(**d) -@dataclass +@modelclass class CostOfRevenue: "Contains cost of revenue data for an income statement." formula: Optional[str] = None @@ -191,7 +191,7 @@ def from_dict(d): return CostOfRevenue(**d) -@dataclass +@modelclass class GrossProfit: "Contains gross profit data for an income statement." formula: Optional[str] = None @@ -206,7 +206,7 @@ def from_dict(d): return GrossProfit(**d) -@dataclass +@modelclass class OperatingExpenses: "Contains operating expenses data for an income statement." formula: Optional[str] = None @@ -221,7 +221,7 @@ def from_dict(d): return OperatingExpenses(**d) -@dataclass +@modelclass class Revenues: "Contains revenues data for an income statement." formula: Optional[str] = None @@ -236,7 +236,7 @@ def from_dict(d): return Revenues(**d) -@dataclass +@modelclass class IncomeStatement: "Contains income statement data." basic_earnings_per_share: Optional[BasicEarningsPerShare] = None @@ -264,7 +264,7 @@ def from_dict(d): ) -@dataclass +@modelclass class Financials: "Contains financial data." balance_sheet: Optional[Dict[str, DataPoint]] = None @@ -290,7 +290,7 @@ def from_dict(d): ) -@dataclass +@modelclass class StockFinancial: "StockFinancial contains historical financial data for a stock ticker." cik: Optional[str] = None diff --git a/polygon/rest/models/markets.py b/polygon/rest/models/markets.py index 3a3bbbc2..0ef769be 100644 --- a/polygon/rest/models/markets.py +++ b/polygon/rest/models/markets.py @@ -1,8 +1,8 @@ from typing import Optional -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class MarketCurrencies: "Contains currency market status data." crypto: Optional[str] = None @@ -13,7 +13,7 @@ def from_dict(d): return MarketCurrencies(**d) -@dataclass +@modelclass class MarketExchanges: "Contains exchange market status data." nasdaq: Optional[str] = None @@ -25,7 +25,7 @@ def from_dict(d): return MarketExchanges(**d) -@dataclass +@modelclass class MarketHoliday: "MarketHoliday contains data for upcoming market holidays and their open/close times." close: Optional[str] = None @@ -40,7 +40,7 @@ def from_dict(d): return MarketHoliday(**d) -@dataclass +@modelclass class MarketStatus: "MarketStatus contains data for the current trading status of the exchanges and overall financial markets." after_hours: Optional[bool] = None diff --git a/polygon/rest/models/quotes.py b/polygon/rest/models/quotes.py index c19e711a..c4f2c00f 100644 --- a/polygon/rest/models/quotes.py +++ b/polygon/rest/models/quotes.py @@ -1,8 +1,8 @@ from typing import Optional, List -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class Quote: "Quote contains quote data for a specified ticker symbol." ask_exchange: Optional[int] = None @@ -24,7 +24,7 @@ def from_dict(d): return Quote(**d) -@dataclass +@modelclass class LastQuote: "LastQuote contains data for the most recent NBBO (Quote) tick for a given stock." ticker: Optional[str] = None @@ -62,7 +62,7 @@ def from_dict(d): ) -@dataclass +@modelclass class ForexQuote: "Contains data for a forex quote." ask: Optional[float] = None @@ -75,7 +75,7 @@ def from_dict(d): return ForexQuote(**d) -@dataclass +@modelclass class LastForexQuote: "ForexLastQuote contains data for the last quote tick for a forex currency pair." last: Optional[ForexQuote] = None @@ -89,7 +89,7 @@ def from_dict(d): ) -@dataclass +@modelclass class RealTimeCurrencyConversion: "RealTimeCurrencyConversion contains data for currency conversions using the latest market conversion rates." converted: Optional[float] = None diff --git a/polygon/rest/models/snapshot.py b/polygon/rest/models/snapshot.py index db2e3366..072b8421 100644 --- a/polygon/rest/models/snapshot.py +++ b/polygon/rest/models/snapshot.py @@ -1,11 +1,11 @@ -from dataclasses import dataclass from typing import Optional, List, Dict from .aggs import Agg from .quotes import LastQuote from .trades import LastTrade +from ...modelclass import modelclass -@dataclass +@modelclass class MinuteSnapshot: "Most recent minute bar." accumulated_volume: Optional[float] = None @@ -29,7 +29,7 @@ def from_dict(d): ) -@dataclass +@modelclass class TickerSnapshot: "Contains the most up-to-date market data for all traded ticker symbols." day: Optional[Agg] = None @@ -61,7 +61,7 @@ def from_dict(d): ) -@dataclass +@modelclass class DayOptionContractSnapshot: "Contains data for the most recent daily bar in an options contract." change: Optional[float] = None @@ -80,7 +80,7 @@ def from_dict(d): return DayOptionContractSnapshot(**d) -@dataclass +@modelclass class OptionDetails: "Contains details for an options contract." contract_type: Optional[str] = None @@ -95,7 +95,7 @@ def from_dict(d): return OptionDetails(**d) -@dataclass +@modelclass class LastQuoteOptionContractSnapshot: "Contains data for the most recent quote in an options contract." ask: Optional[float] = None @@ -111,7 +111,7 @@ def from_dict(d): return LastQuoteOptionContractSnapshot(**d) -@dataclass +@modelclass class Greeks: "Contains data for the greeks in an options contract." delta: Optional[float] = None @@ -124,7 +124,7 @@ def from_dict(d): return Greeks(**d) -@dataclass +@modelclass class UnderlyingAsset: "Contains data for the underlying stock in an options contract." change_to_break_even: Optional[float] = None @@ -138,7 +138,7 @@ def from_dict(d): return UnderlyingAsset(**d) -@dataclass +@modelclass class OptionContractSnapshot: "Contains data for the snapshot of an option contract of a stock equity." break_even_price: Optional[float] = None @@ -172,7 +172,7 @@ def from_dict(d): ) -@dataclass +@modelclass class OrderBookQuote: "Contains data for a book bid or ask." price: Optional[float] = None @@ -186,7 +186,7 @@ def from_dict(d): ) -@dataclass +@modelclass class SnapshotTickerFullBook: "Contains the current level 2 book of a single ticker. This is the combined book from all of the exchanges." ticker: Optional[str] = None diff --git a/polygon/rest/models/splits.py b/polygon/rest/models/splits.py index 037a3e1e..93244c50 100644 --- a/polygon/rest/models/splits.py +++ b/polygon/rest/models/splits.py @@ -1,8 +1,8 @@ from typing import Optional -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class Split: "Split contains data for a historical stock split, including the ticker symbol, the execution date, and the factors of the split ratio." execution_date: Optional[str] = None diff --git a/polygon/rest/models/tickers.py b/polygon/rest/models/tickers.py index 4f650baa..0b5e11f0 100644 --- a/polygon/rest/models/tickers.py +++ b/polygon/rest/models/tickers.py @@ -1,8 +1,8 @@ from typing import Optional, List -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class CompanyAddress: "Contains address data for a ticker detail." address1: Optional[str] = None @@ -17,7 +17,7 @@ def from_dict(d): return CompanyAddress(**d) -@dataclass +@modelclass class Branding: "Contains branding data for a ticker detail." icon_url: Optional[str] = None @@ -31,7 +31,7 @@ def from_dict(d): return Branding(**d) -@dataclass +@modelclass class Publisher: "Contains publisher data for ticker news." favicon_url: Optional[str] = None @@ -44,7 +44,7 @@ def from_dict(d): return Publisher(**d) -@dataclass +@modelclass class Ticker: "Ticker contains data for a specified ticker symbol." active: Optional[bool] = None @@ -69,7 +69,7 @@ def from_dict(d): return Ticker(**d) -@dataclass +@modelclass class TickerDetails: "TickerDetails contains data for a specified ticker symbol." active: Optional[bool] = None @@ -133,7 +133,7 @@ def from_dict(d): ) -@dataclass +@modelclass class TickerNews: "TickerDetails contains data for news articles relating to a stock ticker symbol." amp_url: Optional[str] = None @@ -167,7 +167,7 @@ def from_dict(d): ) -@dataclass +@modelclass class TickerTypes: "TickerTypes contains data for ticker types." asset_class: Optional[str] = None diff --git a/polygon/rest/models/trades.py b/polygon/rest/models/trades.py index 09825097..25b9adc7 100644 --- a/polygon/rest/models/trades.py +++ b/polygon/rest/models/trades.py @@ -1,8 +1,8 @@ from typing import Optional, List -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class Trade: "Trade contains trade data for a specified ticker symbol." conditions: Optional[List[int]] = None @@ -23,7 +23,7 @@ def from_dict(d): return Trade(**d) -@dataclass +@modelclass class LastTrade: "Contains data for the most recent trade for a given ticker symbol." ticker: Optional[str] = None @@ -59,7 +59,7 @@ def from_dict(d): ) -@dataclass +@modelclass class CryptoTrade: "Contains data for a crypto trade." conditions: Optional[List[int]] = None diff --git a/polygon/websocket/models/models.py b/polygon/websocket/models/models.py index 53f7743d..75232007 100644 --- a/polygon/websocket/models/models.py +++ b/polygon/websocket/models/models.py @@ -1,9 +1,9 @@ from typing import Optional, List, Union, NewType from .common import EventType -from dataclasses import dataclass +from ...modelclass import modelclass -@dataclass +@modelclass class EquityAgg: "EquityAgg contains aggregate data for either stock tickers or option contracts." event_type: Optional[Union[str, EventType]] = None @@ -41,7 +41,7 @@ def from_dict(d): ) -@dataclass +@modelclass class CurrencyAgg: "CurrencyAgg contains aggregate data for either forex currency pairs or crypto pairs." event_type: Optional[Union[str, EventType]] = None @@ -73,7 +73,7 @@ def from_dict(d): ) -@dataclass +@modelclass class EquityTrade: "EquityTrade contains trade data for either stock tickers or option contracts." event_type: Optional[Union[str, EventType]] = None @@ -103,7 +103,7 @@ def from_dict(d): ) -@dataclass +@modelclass class CryptoTrade: "CryptoTrade contains trade data for a crypto pair." event_type: Optional[Union[str, EventType]] = None @@ -131,7 +131,7 @@ def from_dict(d): ) -@dataclass +@modelclass class EquityQuote: "EquityQuote contains quote data for either stock tickers or option contracts." event_type: Optional[Union[str, EventType]] = None @@ -165,7 +165,7 @@ def from_dict(d): ) -@dataclass +@modelclass class ForexQuote: "ForexQuote contains quote data for a forex currency pair." event_type: Optional[Union[str, EventType]] = None @@ -187,7 +187,7 @@ def from_dict(d): ) -@dataclass +@modelclass class CryptoQuote: "CryptoQuote contains quote data for a crypto pair." event_type: Optional[Union[str, EventType]] = None @@ -215,7 +215,7 @@ def from_dict(d): ) -@dataclass +@modelclass class Imbalance: "Imbalance contains imbalance event data for a given stock ticker symbol." event_type: Optional[Union[str, EventType]] = None @@ -245,7 +245,7 @@ def from_dict(d): ) -@dataclass +@modelclass class LimitUpLimitDown: "LimitUpLimitDown contains LULD event data for a given stock ticker symbol." event_type: Optional[Union[str, EventType]] = None @@ -271,7 +271,7 @@ def from_dict(d): ) -@dataclass +@modelclass class Level2Book: "Level2Book contains level 2 book data for a given crypto pair." event_type: Optional[Union[str, EventType]] = None diff --git a/test_rest/test_modelclass.py b/test_rest/test_modelclass.py new file mode 100644 index 00000000..651a6393 --- /dev/null +++ b/test_rest/test_modelclass.py @@ -0,0 +1,51 @@ +from polygon.rest.models import Agg +from base import BaseTest + + +class ModelclassTest(BaseTest): + def test_extra_field(self): + a = Agg( + open=1.5032, + high=1.5064, + low=1.4489, + close=1.4604, + volume=642646396.0, + vwap=1.469, + timestamp=1112331600000, + transactions=82132, + ) + b = Agg( + open=1.5032, + high=1.5064, + low=1.4489, + close=1.4604, + volume=642646396.0, + vwap=1.469, + timestamp=1112331600000, + transactions=82132, + extra_field=22, + ) + self.assertEqual(a, b) + + def test_init_order(self): + a = Agg( + open=1.5032, + high=1.5064, + low=1.4489, + close=1.4604, + volume=642646396.0, + vwap=1.469, + timestamp=1112331600000, + transactions=82132, + ) + b = Agg( + 1.5032, + 1.5064, + 1.4489, + 1.4604, + 642646396.0, + 1.469, + 1112331600000, + 82132, + ) + self.assertEqual(a, b)