JC321 commited on
Commit
edc6df9
·
verified ·
1 Parent(s): 05f9632

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +1 -1
  2. edgar_client.py +44 -20
app.py CHANGED
@@ -289,7 +289,7 @@ async def mcp_http_endpoint(request: Request):
289
  )
290
 
291
  # Create Gradio interface
292
- with gr.Blocks(title="SEC Financial Data MCP Server", theme=gr.themes.Soft()) as demo:
293
  gr.Markdown("""
294
  # 📊 SEC Financial Data MCP Server
295
 
 
289
  )
290
 
291
  # Create Gradio interface
292
+ with gr.Blocks(title="SEC Financial Data MCP Server") as demo:
293
  gr.Markdown("""
294
  # 📊 SEC Financial Data MCP Server
295
 
edgar_client.py CHANGED
@@ -3,6 +3,7 @@
3
  import requests
4
  from requests.adapters import HTTPAdapter
5
  from urllib3.util.retry import Retry
 
6
  try:
7
  from sec_edgar_api.EdgarClient import EdgarClient
8
  except ImportError:
@@ -14,6 +15,7 @@ from functools import lru_cache
14
  from datetime import datetime, timedelta
15
  import re
16
  import difflib
 
17
 
18
 
19
  class EdgarDataClient:
@@ -65,12 +67,16 @@ class EdgarDataClient:
65
  # Configure requests session with connection pooling
66
  self.session = requests.Session()
67
 
68
- # Configure retry strategy
 
 
 
69
  retry_strategy = Retry(
70
- total=3,
71
  backoff_factor=1,
72
  status_forcelist=[429, 500, 502, 503, 504],
73
- allowed_methods=["HEAD", "GET", "OPTIONS"]
 
74
  )
75
 
76
  adapter = HTTPAdapter(
@@ -83,8 +89,9 @@ class EdgarDataClient:
83
  self.session.mount("http://", adapter)
84
  self.session.mount("https://", adapter)
85
 
86
- # Set default timeout
87
- self.timeout = 30 # 30 seconds timeout
 
88
 
89
  # Initialize sec_edgar_api client with timeout wrapper
90
  if EdgarClient:
@@ -116,10 +123,10 @@ class EdgarDataClient:
116
 
117
  thread = threading.Thread(target=wrapper, daemon=True)
118
  thread.start()
119
- thread.join(timeout=self.timeout)
120
 
121
  if thread.is_alive():
122
- raise TimeoutError(f"SEC API request timeout ({self.timeout}s)")
123
 
124
  if exception[0]:
125
  raise exception[0]
@@ -139,10 +146,10 @@ class EdgarDataClient:
139
 
140
  thread = threading.Thread(target=wrapper, daemon=True)
141
  thread.start()
142
- thread.join(timeout=self.timeout)
143
 
144
  if thread.is_alive():
145
- raise TimeoutError(f"SEC API request timeout ({self.timeout}s)")
146
 
147
  if exception[0]:
148
  raise exception[0]
@@ -544,16 +551,18 @@ class EdgarDataClient:
544
  # Extract year from filing_date (format: YYYY-MM-DD)
545
  file_year = int(filing_date[:4]) if len(filing_date) >= 4 else 0
546
 
547
- # Store filing if it matches the period year
548
- if file_year == year:
549
- key = f"{form_type}_{file_year}"
550
- if key not in filings_map:
551
- filings_map[key] = {
552
- "accession_number": accession_number,
553
- "primary_document": primary_document,
554
- "form_type": form_type,
555
- "filing_date": filing_date
556
- }
 
 
557
 
558
  # Iterate through each financial metric
559
  for metric_key, metric_tags in financial_metrics.items():
@@ -649,12 +658,27 @@ class EdgarDataClient:
649
 
650
  # Get form and accession info
651
  form_type = matched_entry.get("form", "")
 
652
  accn_from_facts = matched_entry.get('accn', '').replace('-', '')
653
 
654
- # Try to get accession_number and primary_document from filings
 
 
 
655
  filing_key = f"{form_type}_{year}"
656
  filing_info = filings_map.get(filing_key)
657
 
 
 
 
 
 
 
 
 
 
 
 
658
  if filing_info:
659
  # Use filing info from get_company_filings
660
  accession_number = filing_info["accession_number"].replace('-', '')
 
3
  import requests
4
  from requests.adapters import HTTPAdapter
5
  from urllib3.util.retry import Retry
6
+ import urllib3
7
  try:
8
  from sec_edgar_api.EdgarClient import EdgarClient
9
  except ImportError:
 
15
  from datetime import datetime, timedelta
16
  import re
17
  import difflib
18
+ import ssl
19
 
20
 
21
  class EdgarDataClient:
 
67
  # Configure requests session with connection pooling
68
  self.session = requests.Session()
69
 
70
+ # Disable SSL warnings for compatibility
71
+ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
72
+
73
+ # Configure retry strategy with more aggressive retry for SSL errors
74
  retry_strategy = Retry(
75
+ total=5, # Increased from 3 to 5
76
  backoff_factor=1,
77
  status_forcelist=[429, 500, 502, 503, 504],
78
+ allowed_methods=["HEAD", "GET", "OPTIONS"],
79
+ raise_on_status=False # Don't raise on retry-able status codes
80
  )
81
 
82
  adapter = HTTPAdapter(
 
89
  self.session.mount("http://", adapter)
90
  self.session.mount("https://", adapter)
91
 
92
+ # Set default timeout (connect timeout, read timeout)
93
+ self.timeout = (10, 30) # 10s connect, 30s read
94
+ self.thread_timeout = 40 # Total timeout for thread-based operations
95
 
96
  # Initialize sec_edgar_api client with timeout wrapper
97
  if EdgarClient:
 
123
 
124
  thread = threading.Thread(target=wrapper, daemon=True)
125
  thread.start()
126
+ thread.join(timeout=self.thread_timeout)
127
 
128
  if thread.is_alive():
129
+ raise TimeoutError(f"SEC API request timeout ({self.thread_timeout}s)")
130
 
131
  if exception[0]:
132
  raise exception[0]
 
146
 
147
  thread = threading.Thread(target=wrapper, daemon=True)
148
  thread.start()
149
+ thread.join(timeout=self.thread_timeout)
150
 
151
  if thread.is_alive():
152
+ raise TimeoutError(f"SEC API request timeout ({self.thread_timeout}s)")
153
 
154
  if exception[0]:
155
  raise exception[0]
 
551
  # Extract year from filing_date (format: YYYY-MM-DD)
552
  file_year = int(filing_date[:4]) if len(filing_date) >= 4 else 0
553
 
554
+ # Store filing - allow filing_date year to differ from fiscal year
555
+ # (20-F/10-K for FY2024 may be filed in 2025)
556
+ # We'll match by fiscal year in the facts data instead
557
+ key = f"{form_type}_{file_year}"
558
+ if key not in filings_map:
559
+ filings_map[key] = {
560
+ "accession_number": accession_number,
561
+ "primary_document": primary_document,
562
+ "form_type": form_type,
563
+ "filing_date": filing_date,
564
+ "file_year": file_year
565
+ }
566
 
567
  # Iterate through each financial metric
568
  for metric_key, metric_tags in financial_metrics.items():
 
658
 
659
  # Get form and accession info
660
  form_type = matched_entry.get("form", "")
661
+ filed_date = matched_entry.get('filed', '') # Filing date from facts
662
  accn_from_facts = matched_entry.get('accn', '').replace('-', '')
663
 
664
+ # Try to match filing by accession number OR by form and filed year
665
+ filing_info = None
666
+
667
+ # Strategy 1: Try exact filing_date year match
668
  filing_key = f"{form_type}_{year}"
669
  filing_info = filings_map.get(filing_key)
670
 
671
+ # Strategy 2: Try filed year from facts (20-F usually filed next year)
672
+ if not filing_info and filed_date and len(filed_date) >= 4:
673
+ filed_year = int(filed_date[:4])
674
+ filing_key = f"{form_type}_{filed_year}"
675
+ filing_info = filings_map.get(filing_key)
676
+
677
+ # Strategy 3: Try year+1 (for 20-F filed in following year)
678
+ if not filing_info:
679
+ filing_key = f"{form_type}_{year + 1}"
680
+ filing_info = filings_map.get(filing_key)
681
+
682
  if filing_info:
683
  # Use filing info from get_company_filings
684
  accession_number = filing_info["accession_number"].replace('-', '')