4
4
5
5
@modelclass
6
6
class DataPoint :
7
- "An individual financial data point."
8
- formula : Optional [str ] = None
7
+ "Represents a single financial data point."
9
8
label : Optional [str ] = None
10
9
order : Optional [int ] = None
11
10
unit : Optional [str ] = None
12
11
value : Optional [float ] = None
12
+ derived_from : Optional [list ] = None
13
+ formula : Optional [str ] = None
14
+ source : Optional [dict ] = None
13
15
xpath : Optional [str ] = None
14
16
15
17
@staticmethod
16
18
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
+ )
18
29
19
30
20
31
@modelclass
@@ -286,43 +297,44 @@ def from_dict(d):
286
297
287
298
@modelclass
288
299
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
294
313
295
314
@staticmethod
296
315
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
+
297
321
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" )),
320
326
)
321
327
322
328
323
329
@modelclass
324
330
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):
326
338
cik : Optional [str ] = None
327
339
company_name : Optional [str ] = None
328
340
end_date : Optional [str ] = None
@@ -333,20 +345,50 @@ class StockFinancial:
333
345
source_filing_file_url : Optional [str ] = None
334
346
source_filing_url : Optional [str ] = None
335
347
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
336
351
337
352
@staticmethod
338
353
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" ),
344
364
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
346
366
),
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" ),
352
372
)
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