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
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() |