vn6295337 Claude Opus 4.5 commited on
Commit
34989f6
·
1 Parent(s): d66c6c9

Fix NoneType error for companies with incomplete SEC data

Browse files

Add defensive null checks to get_latest_value() and calculate_growth()
to handle cases where SEC EDGAR returns incomplete data for smaller
companies (e.g., VBNK). The us-gaap namespace may be missing or contain
incomplete concept data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

mcp-servers/financials-basket/server.py CHANGED
@@ -102,30 +102,45 @@ def get_latest_value(facts: dict, concept: str, unit: str = "USD") -> Optional[d
102
  Extract latest value for a concept from company facts.
103
  Returns dict with value, period end date, and fiscal year.
104
  """
 
 
 
 
105
  try:
106
- concept_data = facts.get("us-gaap", {}).get(concept, {})
107
- units = concept_data.get("units", {}).get(unit, [])
 
 
 
 
 
 
 
 
 
108
 
109
- if not units:
 
110
  return None
111
 
112
  # Filter for annual (10-K) filings and get most recent
113
- annual_facts = [f for f in units if f.get("form") == "10-K"]
 
 
 
114
  if not annual_facts:
115
- annual_facts = units # Fallback to all if no 10-K
116
 
117
  # Sort by end date descending
118
  annual_facts.sort(key=lambda x: x.get("end", ""), reverse=True)
119
 
120
- if annual_facts:
121
- latest = annual_facts[0]
122
- return {
123
- "value": latest.get("val"),
124
- "end_date": latest.get("end"),
125
- "fiscal_year": latest.get("fy"),
126
- "form": latest.get("form")
127
- }
128
- return None
129
  except Exception as e:
130
  logger.error(f"Error extracting {concept}: {e}")
131
  return None
@@ -133,18 +148,35 @@ def get_latest_value(facts: dict, concept: str, unit: str = "USD") -> Optional[d
133
 
134
  def calculate_growth(facts: dict, concept: str, years: int = 3) -> Optional[float]:
135
  """Calculate CAGR for a concept over specified years."""
 
 
 
 
136
  try:
137
- concept_data = facts.get("us-gaap", {}).get(concept, {})
138
- units = concept_data.get("units", {}).get("USD", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- annual_facts = [f for f in units if f.get("form") == "10-K"]
141
  annual_facts.sort(key=lambda x: x.get("end", ""), reverse=True)
142
 
143
  if len(annual_facts) < years + 1:
144
  return None
145
 
146
- latest_val = annual_facts[0].get("val", 0)
147
- older_val = annual_facts[years].get("val", 0)
148
 
149
  if older_val <= 0 or latest_val <= 0:
150
  return None
 
102
  Extract latest value for a concept from company facts.
103
  Returns dict with value, period end date, and fiscal year.
104
  """
105
+ # Defensive check for None or invalid facts
106
+ if not facts or not isinstance(facts, dict):
107
+ return None
108
+
109
  try:
110
+ us_gaap = facts.get("us-gaap")
111
+ if not us_gaap or not isinstance(us_gaap, dict):
112
+ return None
113
+
114
+ concept_data = us_gaap.get(concept)
115
+ if not concept_data or not isinstance(concept_data, dict):
116
+ return None
117
+
118
+ units_data = concept_data.get("units")
119
+ if not units_data or not isinstance(units_data, dict):
120
+ return None
121
 
122
+ units = units_data.get(unit, [])
123
+ if not units or not isinstance(units, list):
124
  return None
125
 
126
  # Filter for annual (10-K) filings and get most recent
127
+ annual_facts = [f for f in units if isinstance(f, dict) and f.get("form") == "10-K"]
128
+ if not annual_facts:
129
+ annual_facts = [f for f in units if isinstance(f, dict)] # Fallback to all if no 10-K
130
+
131
  if not annual_facts:
132
+ return None
133
 
134
  # Sort by end date descending
135
  annual_facts.sort(key=lambda x: x.get("end", ""), reverse=True)
136
 
137
+ latest = annual_facts[0]
138
+ return {
139
+ "value": latest.get("val"),
140
+ "end_date": latest.get("end"),
141
+ "fiscal_year": latest.get("fy"),
142
+ "form": latest.get("form")
143
+ }
 
 
144
  except Exception as e:
145
  logger.error(f"Error extracting {concept}: {e}")
146
  return None
 
148
 
149
  def calculate_growth(facts: dict, concept: str, years: int = 3) -> Optional[float]:
150
  """Calculate CAGR for a concept over specified years."""
151
+ # Defensive check for None or invalid facts
152
+ if not facts or not isinstance(facts, dict):
153
+ return None
154
+
155
  try:
156
+ us_gaap = facts.get("us-gaap")
157
+ if not us_gaap or not isinstance(us_gaap, dict):
158
+ return None
159
+
160
+ concept_data = us_gaap.get(concept)
161
+ if not concept_data or not isinstance(concept_data, dict):
162
+ return None
163
+
164
+ units_data = concept_data.get("units")
165
+ if not units_data or not isinstance(units_data, dict):
166
+ return None
167
+
168
+ units = units_data.get("USD", [])
169
+ if not units or not isinstance(units, list):
170
+ return None
171
 
172
+ annual_facts = [f for f in units if isinstance(f, dict) and f.get("form") == "10-K"]
173
  annual_facts.sort(key=lambda x: x.get("end", ""), reverse=True)
174
 
175
  if len(annual_facts) < years + 1:
176
  return None
177
 
178
+ latest_val = annual_facts[0].get("val", 0) or 0
179
+ older_val = annual_facts[years].get("val", 0) or 0
180
 
181
  if older_val <= 0 or latest_val <= 0:
182
  return None