#!/usr/bin/env python3 """ 15년 재무재표 데이터 가져오기 종합 테스트 다양한 방법으로 API를 테스트하고 문제점을 찾아 수정합니다. """ import requests import yfinance_plus as yf from datetime import datetime, timedelta import json import time from typing import Dict, Any, List # API 설정 API_URL = "http://localhost:18001/api/v1" def print_section(title: str): """섹션 헤더 출력""" print(f"\n{'='*80}") print(f"🔍 {title}") print(f"{'='*80}") def print_test(test_name: str): """테스트 헤더 출력""" print(f"\n📋 {test_name}") print("-" * 60) def test_yfinance_plus_availability(): """YFinance Plus 사용 가능성 테스트""" print_test("YFinance Plus 모듈 테스트") try: # 모듈 임포트 테스트 print("✅ yfinance_plus 모듈 임포트 성공") # 기본 연결 테스트 stock = yf.Ticker("AAPL") info = stock.info print(f"✅ AAPL 기본 정보 가져오기 성공: {info.get('longName', 'N/A')}") # 재무재표 가져오기 테스트 quarterly_income = stock.quarterly_income_stmt print(f"✅ 분기별 손익계산서: {quarterly_income.shape}") annual_income = stock.income_stmt print(f"✅ 연간 손익계산서: {annual_income.shape}") # 사용 가능한 기간 확인 if not quarterly_income.empty: earliest = quarterly_income.columns[-1] latest = quarterly_income.columns[0] print(f"📅 사용 가능한 분기 데이터 기간: {earliest} ~ {latest}") if not annual_income.empty: earliest_annual = annual_income.columns[-1] latest_annual = annual_income.columns[0] print(f"📅 사용 가능한 연간 데이터 기간: {earliest_annual} ~ {latest_annual}") return True except Exception as e: print(f"❌ YFinance Plus 테스트 실패: {str(e)}") return False def test_api_basic_connection(): """API 기본 연결 테스트""" print_test("API 기본 연결 테스트") try: # Health check response = requests.get(f"{API_URL}/health", timeout=10) if response.status_code == 200: print("✅ Health check 성공") else: print(f"⚠️ Health check 응답: {response.status_code}") # Database stats response = requests.get(f"{API_URL}/database/stats", timeout=10) if response.status_code == 200: stats = response.json() print(f"✅ DB 통계 조회 성공") print(f" - 총 회사 수: {stats.get('companies', {}).get('total', 0)}") print(f" - 재무 데이터: {stats.get('financial_data', {}).get('total_records', 0)}") print(f" - 주가 데이터: {stats.get('price_data', {}).get('total_records', 0)}") else: print(f"❌ DB 통계 조회 실패: {response.status_code}") return True except Exception as e: print(f"❌ API 연결 테스트 실패: {str(e)}") return False def test_current_financial_data(): """현재 재무 데이터 조회 테스트""" print_test("현재 재무 데이터 조회 테스트") tickers = ["AAPL", "MSFT", "GOOGL"] for ticker in tickers: print(f"\n📊 {ticker} 테스트:") # 최근 2년 데이터 테스트 request_data = { "ticker": ticker, "start_date": "2023-01-01", "end_date": "2024-12-31", "period_type": "all", "include_metrics": True, "force_refresh": False } try: response = requests.post( f"{API_URL}/financial/data", json=request_data, timeout=30 ) if response.status_code == 200: data = response.json() financial_data = data.get('financial_data', []) print(f" ✅ 성공: {len(financial_data)}개 기간 데이터") if financial_data: real_data = sum(1 for d in financial_data if not d.get('is_estimated', True)) estimated_data = len(financial_data) - real_data print(f" 📈 실제 데이터: {real_data}, 추정 데이터: {estimated_data}") # 최신 데이터 확인 latest = financial_data[-1] print(f" 📅 최신 기간: {latest.get('period_date')}") print(f" 💰 매출: ${latest.get('revenue', 0):,.0f}") print(f" 📊 데이터 소스: {latest.get('data_source')}") else: print(" ⚠️ 재무 데이터가 비어있음") else: print(f" ❌ 실패: {response.status_code}") print(f" Error: {response.text[:200]}") except Exception as e: print(f" ❌ 예외: {str(e)}") def test_15year_historical_data(): """15년 과거 데이터 조회 테스트""" print_test("15년 과거 재무 데이터 조회 테스트") # 15년 전부터 현재까지 start_date = "2009-01-01" end_date = "2024-12-31" tickers = ["AAPL", "MSFT"] for ticker in tickers: print(f"\n🕰️ {ticker} 15년 데이터 테스트 ({start_date} ~ {end_date}):") # 연간 데이터로 테스트 request_data = { "ticker": ticker, "start_date": start_date, "end_date": end_date, "period_type": "annual", "include_metrics": True, "force_refresh": True # 강제 새로고침으로 실제 데이터 가져오기 } try: print(f" ⏳ 요청 시작... (시간이 오래 걸릴 수 있습니다)") start_time = time.time() response = requests.post( f"{API_URL}/financial/data", json=request_data, timeout=120 # 2분 타임아웃 ) end_time = time.time() duration = end_time - start_time if response.status_code == 200: data = response.json() financial_data = data.get('financial_data', []) print(f" ✅ 성공: {len(financial_data)}개 연간 데이터 ({duration:.1f}초)") if financial_data: # 데이터 품질 분석 years = set() real_count = 0 estimated_count = 0 for item in financial_data: year = item.get('period_date', '')[:4] years.add(year) if item.get('is_estimated', True): estimated_count += 1 else: real_count += 1 print(f" 📊 커버리지: {len(years)}년 ({min(years)} ~ {max(years)})") print(f" 📈 실제 데이터: {real_count}, 추정 데이터: {estimated_count}") # 최신 및 가장 오래된 데이터 확인 oldest = financial_data[0] newest = financial_data[-1] print(f" 📅 가장 오래된 데이터: {oldest.get('period_date')} (매출: ${oldest.get('revenue', 0):,.0f})") print(f" 📅 가장 최신 데이터: {newest.get('period_date')} (매출: ${newest.get('revenue', 0):,.0f})") # 데이터 소스 분포 sources = {} for item in financial_data: source = item.get('data_source', 'Unknown') sources[source] = sources.get(source, 0) + 1 print(f" 🔍 데이터 소스: {sources}") else: print(" ⚠️ 15년 데이터가 비어있음") else: print(f" ❌ 실패: {response.status_code} ({duration:.1f}초)") print(f" Error: {response.text[:500]}") except requests.exceptions.Timeout: print(f" ⏰ 타임아웃: 2분 초과") except Exception as e: print(f" ❌ 예외: {str(e)}") def test_quarterly_vs_annual(): """분기별 vs 연간 데이터 비교 테스트""" print_test("분기별 vs 연간 데이터 비교 테스트") ticker = "AAPL" period_range = { "start_date": "2020-01-01", "end_date": "2024-12-31" } results = {} # 분기별 및 연간 데이터 테스트 for period_type in ["quarterly", "annual"]: print(f"\n📊 {ticker} {period_type} 데이터:") request_data = { "ticker": ticker, "period_type": period_type, "include_metrics": True, "force_refresh": False, **period_range } try: response = requests.post( f"{API_URL}/financial/data", json=request_data, timeout=60 ) if response.status_code == 200: data = response.json() financial_data = data.get('financial_data', []) results[period_type] = financial_data print(f" ✅ 성공: {len(financial_data)}개 데이터") if financial_data: real_count = sum(1 for d in financial_data if not d.get('is_estimated', True)) print(f" 📈 실제 데이터: {real_count}/{len(financial_data)}") # 날짜 범위 확인 dates = [d.get('period_date') for d in financial_data] print(f" 📅 기간: {min(dates)} ~ {max(dates)}") else: print(f" ❌ 실패: {response.status_code}") results[period_type] = [] except Exception as e: print(f" ❌ 예외: {str(e)}") results[period_type] = [] # 비교 분석 print(f"\n🔍 비교 분석:") quarterly_count = len(results.get('quarterly', [])) annual_count = len(results.get('annual', [])) print(f" 분기별 데이터: {quarterly_count}개") print(f" 연간 데이터: {annual_count}개") print(f" 예상 비율: {quarterly_count/4:.1f} ≈ {annual_count} (이론적으로 4:1)") def test_different_error_scenarios(): """다양한 에러 시나리오 테스트""" print_test("에러 시나리오 테스트") error_tests = [ { "name": "존재하지 않는 종목", "data": { "ticker": "NONEXISTENT", "start_date": "2023-01-01", "end_date": "2023-12-31", "period_type": "quarterly" } }, { "name": "잘못된 날짜 형식", "data": { "ticker": "AAPL", "start_date": "invalid-date", "end_date": "2023-12-31", "period_type": "quarterly" } }, { "name": "미래 날짜", "data": { "ticker": "AAPL", "start_date": "2030-01-01", "end_date": "2030-12-31", "period_type": "quarterly" } }, { "name": "극도로 오래된 날짜", "data": { "ticker": "AAPL", "start_date": "1990-01-01", "end_date": "1990-12-31", "period_type": "quarterly" } } ] for test in error_tests: print(f"\n🚨 {test['name']} 테스트:") try: response = requests.post( f"{API_URL}/financial/data", json=test['data'], timeout=30 ) print(f" 응답 코드: {response.status_code}") if response.status_code == 200: data = response.json() financial_data = data.get('financial_data', []) print(f" ✅ 성공: {len(financial_data)}개 데이터") else: print(f" ⚠️ 에러 응답: {response.text[:200]}") except Exception as e: print(f" ❌ 예외: {str(e)}") def test_force_refresh_vs_cache(): """강제 새로고침 vs 캐시 비교 테스트""" print_test("강제 새로고침 vs 캐시 비교 테스트") ticker = "AAPL" request_base = { "ticker": ticker, "start_date": "2023-01-01", "end_date": "2024-12-31", "period_type": "quarterly", "include_metrics": True } # 캐시 사용 테스트 print(f"\n🔄 캐시 사용 테스트:") start_time = time.time() response1 = requests.post( f"{API_URL}/financial/data", json={**request_base, "force_refresh": False}, timeout=60 ) cache_time = time.time() - start_time if response1.status_code == 200: data1 = response1.json() print(f" ✅ 캐시 요청 성공: {len(data1.get('financial_data', []))}개 데이터 ({cache_time:.1f}초)") else: print(f" ❌ 캐시 요청 실패: {response1.status_code}") # 강제 새로고침 테스트 print(f"\n🔄 강제 새로고침 테스트:") start_time = time.time() response2 = requests.post( f"{API_URL}/financial/data", json={**request_base, "force_refresh": True}, timeout=120 ) refresh_time = time.time() - start_time if response2.status_code == 200: data2 = response2.json() print(f" ✅ 강제 새로고침 성공: {len(data2.get('financial_data', []))}개 데이터 ({refresh_time:.1f}초)") # 성능 비교 print(f"\n⚡ 성능 비교:") print(f" 캐시 사용: {cache_time:.1f}초") print(f" 강제 새로고침: {refresh_time:.1f}초") print(f" 속도 차이: {refresh_time/cache_time:.1f}배 느림") else: print(f" ❌ 강제 새로고침 실패: {response2.status_code}") def main(): """메인 테스트 실행""" print_section("Stock Oracle 15년 재무재표 데이터 종합 테스트") print(f"🚀 테스트 시작 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f"🎯 API URL: {API_URL}") # 테스트 실행 tests = [ ("YFinance Plus 사용 가능성", test_yfinance_plus_availability), ("API 기본 연결", test_api_basic_connection), ("현재 재무 데이터 조회", test_current_financial_data), ("15년 과거 데이터 조회", test_15year_historical_data), ("분기별 vs 연간 데이터 비교", test_quarterly_vs_annual), ("에러 시나리오", test_different_error_scenarios), ("강제 새로고침 vs 캐시", test_force_refresh_vs_cache), ] results = [] for test_name, test_func in tests: print_section(test_name) try: result = test_func() results.append((test_name, "성공" if result else "실패")) except Exception as e: print(f"❌ {test_name} 실행 중 예외: {str(e)}") results.append((test_name, "예외")) # 최종 결과 요약 print_section("테스트 결과 요약") for test_name, result in results: status_icon = "✅" if result == "성공" else "❌" print(f"{status_icon} {test_name}: {result}") success_count = sum(1 for _, result in results if result == "성공") total_count = len(results) print(f"\n📊 전체 결과: {success_count}/{total_count} 성공 ({success_count/total_count*100:.1f}%)") print(f"🕐 테스트 완료 시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") if __name__ == "__main__": main()