Skip to content

Commit 55bd5ce

Browse files
committed
Update StockFinancial model (net income, EPS, and parent net income loss)
1 parent d4e6205 commit 55bd5ce

File tree

1 file changed

+84
-42
lines changed

1 file changed

+84
-42
lines changed

polygon/rest/models/financials.py

Lines changed: 84 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,28 @@
44

55
@modelclass
66
class DataPoint:
7-
"An individual financial data point."
8-
formula: Optional[str] = None
7+
"Represents a single financial data point."
98
label: Optional[str] = None
109
order: Optional[int] = None
1110
unit: Optional[str] = None
1211
value: Optional[float] = None
12+
derived_from: Optional[list] = None
13+
formula: Optional[str] = None
14+
source: Optional[dict] = None
1315
xpath: Optional[str] = None
1416

1517
@staticmethod
1618
def from_dict(d):
17-
return DataPoint(**d)
19+
return DataPoint(
20+
label=d.get("label"),
21+
order=d.get("order"),
22+
unit=d.get("unit"),
23+
value=d.get("value"),
24+
derived_from=d.get("derived_from"),
25+
formula=d.get("formula"),
26+
source=d.get("source"),
27+
xpath=d.get("xpath"),
28+
)
1829

1930

2031
@modelclass
@@ -286,43 +297,44 @@ def from_dict(d):
286297

287298
@modelclass
288299
class Financials:
289-
"Contains financial data."
290-
balance_sheet: Optional[Dict[str, DataPoint]] = None
291-
cash_flow_statement: Optional[CashFlowStatement] = None
292-
comprehensive_income: Optional[ComprehensiveIncome] = None
293-
income_statement: Optional[IncomeStatement] = None
300+
"""
301+
Contains data for:
302+
- balance_sheet
303+
- cash_flow_statement
304+
- comprehensive_income
305+
- income_statement
306+
Each is a dict of { 'SomeTag': DataPoint }, e.g. { 'NetIncomeLoss': DataPoint(...) }
307+
"""
308+
309+
balance_sheet: Optional[dict] = None
310+
cash_flow_statement: Optional[dict] = None
311+
comprehensive_income: Optional[dict] = None
312+
income_statement: Optional[dict] = None
294313

295314
@staticmethod
296315
def from_dict(d):
316+
def parse_statement(x):
317+
if not x or not isinstance(x, dict):
318+
return None
319+
return {k: DataPoint.from_dict(v) for k, v in x.items()}
320+
297321
return Financials(
298-
balance_sheet=(
299-
None
300-
if "balance_sheet" not in d
301-
else {
302-
k: DataPoint.from_dict(v) for (k, v) in d["balance_sheet"].items()
303-
}
304-
),
305-
cash_flow_statement=(
306-
None
307-
if "cash_flow_statement" not in d
308-
else CashFlowStatement.from_dict(d["cash_flow_statement"])
309-
),
310-
comprehensive_income=(
311-
None
312-
if "comprehensive_income" not in d
313-
else ComprehensiveIncome.from_dict(d["comprehensive_income"])
314-
),
315-
income_statement=(
316-
None
317-
if "income_statement" not in d
318-
else IncomeStatement.from_dict(d["income_statement"])
319-
),
322+
balance_sheet=parse_statement(d.get("balance_sheet")),
323+
cash_flow_statement=parse_statement(d.get("cash_flow_statement")),
324+
comprehensive_income=parse_statement(d.get("comprehensive_income")),
325+
income_statement=parse_statement(d.get("income_statement")),
320326
)
321327

322328

323329
@modelclass
324330
class StockFinancial:
325-
"StockFinancial contains historical financial data for a stock ticker."
331+
"""
332+
StockFinancial contains historical financial data for a stock ticker.
333+
Augmented with new fields such as net_income_loss, diluted_earnings_per_share, etc.
334+
to avoid repeated dictionary lookups into 'financials' for common data.
335+
"""
336+
337+
# Existing fields (unchanged):
326338
cik: Optional[str] = None
327339
company_name: Optional[str] = None
328340
end_date: Optional[str] = None
@@ -333,20 +345,50 @@ class StockFinancial:
333345
source_filing_file_url: Optional[str] = None
334346
source_filing_url: Optional[str] = None
335347
start_date: Optional[str] = None
348+
net_income_loss: Optional[float] = None
349+
net_income_loss_attributable_to_parent: Optional[float] = None
350+
diluted_earnings_per_share: Optional[float] = None
336351

337352
@staticmethod
338353
def from_dict(d):
339-
return StockFinancial(
340-
cik=d.get("cik", None),
341-
company_name=d.get("company_name", None),
342-
end_date=d.get("end_date", None),
343-
filing_date=d.get("filing_date", None),
354+
"""
355+
Create a StockFinancial, preserving all old behavior, but also pulling out
356+
a few commonly used fields from the income_statement and comprehensive_income
357+
so they can be accessed directly at the top level.
358+
"""
359+
sf = StockFinancial(
360+
cik=d.get("cik"),
361+
company_name=d.get("company_name"),
362+
end_date=d.get("end_date"),
363+
filing_date=d.get("filing_date"),
344364
financials=(
345-
None if "financials" not in d else Financials.from_dict(d["financials"])
365+
Financials.from_dict(d["financials"]) if "financials" in d else None
346366
),
347-
fiscal_period=d.get("fiscal_period", None),
348-
fiscal_year=d.get("fiscal_year", None),
349-
source_filing_file_url=d.get("source_filing_file_url", None),
350-
source_filing_url=d.get("source_filing_url", None),
351-
start_date=d.get("start_date", None),
367+
fiscal_period=d.get("fiscal_period"),
368+
fiscal_year=d.get("fiscal_year"),
369+
source_filing_file_url=d.get("source_filing_file_url"),
370+
source_filing_url=d.get("source_filing_url"),
371+
start_date=d.get("start_date"),
352372
)
373+
374+
# If we have an income_statement, try to pull out new fields
375+
if sf.financials and sf.financials.income_statement:
376+
income_stmt = sf.financials.income_statement
377+
net_income_dp = income_stmt.get("net_income_loss")
378+
if net_income_dp and net_income_dp.value is not None:
379+
sf.net_income_loss = net_income_dp.value
380+
381+
diluted_eps_dp = income_stmt.get("diluted_earnings_per_share")
382+
if diluted_eps_dp and diluted_eps_dp.value is not None:
383+
sf.diluted_earnings_per_share = diluted_eps_dp.value
384+
385+
# If we have a comprehensive_income, pull out net income (parent)
386+
if sf.financials and sf.financials.comprehensive_income:
387+
comp_inc = sf.financials.comprehensive_income
388+
net_income_parent_dp = comp_inc.get(
389+
"net_income_loss_attributable_to_parent"
390+
)
391+
if net_income_parent_dp and net_income_parent_dp.value is not None:
392+
sf.net_income_loss_attributable_to_parent = net_income_parent_dp.value
393+
394+
return sf

0 commit comments

Comments
 (0)