You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
10 KiB
Python

"""
Comprehensive test for 15-year SEC EDGAR financial data fetching
Tests the new SEC-only implementation without yfinance_plus via API
"""
import requests
import asyncio
from datetime import datetime, timezone, timedelta
import time
import json
class TestSECEdgar15Year:
"""Test 15-year SEC EDGAR financial data fetching via API"""
def setup_method(self):
"""Setup test environment"""
self.api_url = "http://localhost:18001/api/v1"
def test_sec_only_financial_data_aapl(self):
"""Test that AAPL financial data comes from SEC only"""
print(f"\n=== Testing SEC EDGAR 15-year data for AAPL ===")
# Test 15-year range
end_date = datetime.now()
start_date = end_date - timedelta(days=15*365)
print(f"Date range: {start_date.date()} to {end_date.date()}")
request_data = {
"ticker": "AAPL",
"start_date": start_date.strftime('%Y-%m-%d'),
"end_date": end_date.strftime('%Y-%m-%d'),
"period_type": "all",
"include_metrics": True,
"force_refresh": True
}
try:
print("Requesting financial data...")
start_time = time.time()
response = requests.post(
f"{self.api_url}/financial/data",
json=request_data,
timeout=120
)
end_time = time.time()
duration = end_time - start_time
assert response.status_code == 200, f"API request failed: {response.status_code}"
data = response.json()
financial_data = data.get('financial_data', [])
print(f"Found {len(financial_data)} financial records in {duration:.1f}s")
# Verify data source is SEC
sec_records = [
record for record in financial_data
if record.get('data_source') == 'SEC_EDGAR'
]
print(f"SEC EDGAR records: {len(sec_records)}")
# Verify we have historical data (should go back multiple years)
if financial_data:
dates = [datetime.fromisoformat(record['period_date'].replace('Z', '+00:00')) for record in financial_data]
dates.sort()
oldest_date = dates[0]
newest_date = dates[-1]
print(f"Date range in data: {oldest_date.date()} to {newest_date.date()}")
years_span = (newest_date - oldest_date).days / 365.25
print(f"Years span: {years_span:.1f} years")
# Should have at least 3 years of data
assert years_span >= 3, f"Expected at least 3 years, got {years_span:.1f}"
# Verify data quality
for i, record in enumerate(financial_data[:3]):
period_date = record['period_date']
print(f"\nRecord {i+1} for {period_date[:10]}:")
print(f" Revenue: ${record.get('revenue', 0):,.0f}")
print(f" Net Income: ${record.get('net_income', 0):,.0f}")
print(f" Total Assets: ${record.get('total_assets', 0):,.0f}")
print(f" Data Source: {record.get('data_source')}")
print(f" Is Estimated: {record.get('is_estimated')}")
# Verify it's real SEC data
assert record.get('data_source') == 'SEC_EDGAR'
assert record.get('is_estimated') == False
assert len(financial_data) > 0, "Should have financial data from SEC"
print("✅ Test passed: SEC EDGAR data retrieved successfully")
except Exception as e:
print(f"❌ Test failed: {e}")
raise
def test_multiple_tickers_sec_data(self):
"""Test SEC data fetching for multiple tickers"""
print(f"\n=== Testing Multiple Tickers SEC Data ===")
tickers = ["AAPL", "MSFT"]
end_date = datetime.now()
start_date = end_date - timedelta(days=5*365) # 5 years
for ticker in tickers:
print(f"\nTesting {ticker}...")
request_data = {
"ticker": ticker,
"start_date": start_date.strftime('%Y-%m-%d'),
"end_date": end_date.strftime('%Y-%m-%d'),
"period_type": "all",
"include_metrics": True,
"force_refresh": False
}
try:
response = requests.post(
f"{self.api_url}/financial/data",
json=request_data,
timeout=60
)
if response.status_code == 200:
data = response.json()
financial_data = data.get('financial_data', [])
print(f" Records found: {len(financial_data)}")
if financial_data:
# Show latest record
latest = max(financial_data, key=lambda x: x['period_date'])
print(f" Latest period: {latest['period_date'][:10]}")
print(f" Revenue: ${latest.get('revenue', 0):,.0f}")
print(f" Data source: {latest.get('data_source')}")
# Verify all are SEC data
sec_count = sum(1 for r in financial_data if r.get('data_source') == 'SEC_EDGAR')
print(f" SEC records: {sec_count}/{len(financial_data)}")
assert sec_count == len(financial_data), f"Expected all SEC data, got {sec_count}/{len(financial_data)}"
else:
print(f" ❌ Failed: {response.status_code}")
except Exception as e:
print(f" ❌ Error: {e}")
def test_no_yfinance_plus_dependency(self):
"""Verify yfinance_plus is not used for financial data"""
print(f"\n=== Testing No yfinance_plus Dependency ===")
# Test that yfinance_plus is not used
try:
import yfinance_plus
print("WARNING: yfinance_plus is still available in environment")
except ImportError:
print("✓ yfinance_plus correctly not available")
# Test financial data request to ensure SEC is used
end_date = datetime.now()
start_date = end_date - timedelta(days=365)
request_data = {
"ticker": "AAPL",
"start_date": start_date.strftime('%Y-%m-%d'),
"end_date": end_date.strftime('%Y-%m-%d'),
"period_type": "quarterly",
"include_metrics": True,
"force_refresh": False
}
try:
response = requests.post(
f"{self.api_url}/financial/data",
json=request_data,
timeout=60
)
assert response.status_code == 200, f"API request failed: {response.status_code}"
data = response.json()
financial_data = data.get('financial_data', [])
# All financial data should be from SEC
for record in financial_data:
assert record.get('data_source') == 'SEC_EDGAR', f"Expected SEC_EDGAR, got {record.get('data_source')}"
assert record.get('is_estimated') == False, "Should not be estimated data"
print(f"✓ All {len(financial_data)} financial records are from SEC EDGAR")
except Exception as e:
print(f"❌ Error testing financial data: {e}")
raise
def test_price_data_separation(self):
"""Test that price data still works with yfinance"""
print(f"\n=== Testing Price Data Separation ===")
end_date = datetime.now()
start_date = end_date - timedelta(days=30) # 30 days
request_data = {
"ticker": "AAPL",
"start_date": start_date.strftime('%Y-%m-%d'),
"end_date": end_date.strftime('%Y-%m-%d'),
"interval": "1d"
}
try:
response = requests.post(
f"{self.api_url}/financial/price-data",
json=request_data,
timeout=60
)
if response.status_code == 200:
data = response.json()
price_data = data.get('price_data', [])
print(f"Price records found: {len(price_data)}")
if price_data:
latest_price = max(price_data, key=lambda x: x['date'])
print(f"Latest price date: {latest_price['date'][:10]}")
print(f"Latest close: ${latest_price.get('close', 0):.2f}")
print(f"Price data source: {latest_price.get('data_source')}")
# Price data should be from Yahoo Finance
yahoo_count = sum(1 for p in price_data if p.get('data_source') == 'YAHOO_FINANCE')
print(f"Yahoo Finance price records: {yahoo_count}/{len(price_data)}")
assert yahoo_count > 0, "Should have price data from Yahoo Finance"
else:
print(f"Price data request failed: {response.status_code}")
except Exception as e:
print(f"❌ Error testing price data: {e}")
if __name__ == "__main__":
def run_tests():
test_instance = TestSECEdgar15Year()
test_instance.setup_method()
try:
test_instance.test_sec_only_financial_data_aapl()
test_instance.test_multiple_tickers_sec_data()
test_instance.test_no_yfinance_plus_dependency()
test_instance.test_price_data_separation()
print("\n✅ All tests completed successfully!")
except Exception as e:
print(f"\n❌ Test failed: {e}")
raise
# Run the tests
run_tests()