Alpha Vantage API에서 미국 주식 분석기 만들기 5)

Tasker_Jang·2025년 4월 3일
0

1. 재무제표 분석의 핵심 요소

기업의 재무 상태를 종합적으로 이해하기 위해서는 대차대조표(Balance Sheet), 손익계산서(Income Statement), 현금흐름표(Cash Flow Statement)의 세 가지 주요 재무제표를 분석해야 합니다. AlphaVantageAPIWrapper 클래스는 이 세 가지 재무제표를 Alpha Vantage API를 통해 가져오고, 이를 종합적으로 분석하는 기능을 제공합니다.

def analyze_financial_statements(self, ticker: str) -> Dict:
    """Analyze financial statements for a stock."""
    result = {
        "ticker": ticker,
        "timestamp": time.time()
    }

    # Get company overview
    try:
        overview = self.get_company_overview(ticker)
        if "error" not in overview:
            result["profile"] = overview
            result["company_name"] = overview.get("Name", "")
        else:
            result["profile_error"] = overview.get("error", "Unknown error")
    except Exception as e:
        result["profile_error"] = str(e)

    # Balance sheet data
    try:
        balance_sheet = self.get_balance_sheet(ticker)
        if "error" not in balance_sheet:
            result["balance_sheet"] = balance_sheet
        else:
            result["balance_sheet_error"] = balance_sheet.get("error", "Unknown error")
    except Exception as e:
        result["balance_sheet_error"] = str(e)

    # Income statement data
    try:
        income_statement = self.get_income_statement(ticker)
        if "error" not in income_statement:
            result["income_statement"] = income_statement
        else:
            result["income_statement_error"] = income_statement.get("error", "Unknown error")
    except Exception as e:
        result["income_statement_error"] = str(e)

    # Cash flow data
    try:
        cash_flow = self.get_cash_flow(ticker)
        if "error" not in cash_flow:
            result["cash_flow"] = cash_flow
        else:
            result["cash_flow_error"] = cash_flow.get("error", "Unknown error")
    except Exception as e:
        result["cash_flow_error"] = str(e)

    # Add analysis results
    result["analysis"] = self._analyze_financial_data(result)

    return result

이 메서드는 다음 단계로 작동합니다:
1. 기업 개요 정보 가져오기
2. 대차대조표 데이터 가져오기
3. 손익계산서 데이터 가져오기
4. 현금흐름표 데이터 가져오기
5. 수집된 데이터에 대한 종합 분석 수행

각 단계에서 예외 처리가 이루어져 API 응답 오류나 예상치 못한 예외에도 강건하게 작동합니다.

2. 재무제표 데이터 구조화 전략

2.1. Alpha Vantage API 응답 구조 활용

Alpha Vantage API는 재무제표 데이터를 연간(annualReports)과 분기(quarterlyReports) 형태로 제공합니다. 이 구조를 활용하여 가장 최근의 데이터와 이전 기간 데이터를 비교 분석할 수 있습니다.

# Balance sheet analysis
balance_sheet_data = data.get("balance_sheet", {})
if balance_sheet_data and "annualReports" in balance_sheet_data and len(balance_sheet_data["annualReports"]) > 0:
    try:
        recent = balance_sheet_data["annualReports"][0]  # 가장 최근 데이터

        # Extract basic metrics
        total_assets = float(recent.get("totalAssets", 0))
        total_liabilities = float(recent.get("totalLiabilities", 0))
        total_equity = float(recent.get("totalShareholderEquity", 0))
        
        # ... 기타 분석 로직
    except Exception as e:
        analysis["balance_sheet_analysis_error"] = str(e)

이 코드에서는 annualReports의 첫 번째 항목을 가장 최근 데이터로 사용하고 있습니다. 이는 Alpha Vantage API가 날짜 순서대로 데이터를 제공하기 때문입니다.

2.2. 다양한 재무제표 항목 분석

기업의 재무 상태를 종합적으로 이해하기 위해 대차대조표의 자산, 부채, 자본 항목부터 손익계산서의 매출, 비용, 이익 항목, 그리고 현금흐름표의 다양한 항목들을 분석합니다.

예를 들어, 손익계산서 분석에서는 다음과 같은 항목들을 추출하고 비교합니다:

# Income statement analysis
income_statement_data = data.get("income_statement", {})
if income_statement_data and "annualReports" in income_statement_data and len(income_statement_data["annualReports"]) >= 2:
    try:
        recent = income_statement_data["annualReports"][0]
        previous = income_statement_data["annualReports"][1]

        # Extract metrics
        recent_revenue = float(recent.get("totalRevenue", 0))
        recent_gross_profit = float(recent.get("grossProfit", 0))
        recent_operating_income = float(recent.get("operatingIncome", 0))
        recent_net_income = float(recent.get("netIncome", 0))

        previous_revenue = float(previous.get("totalRevenue", 0))
        previous_net_income = float(previous.get("netIncome", 0))
        
        # ... 추가 분석 로직
    except Exception as e:
        analysis["income_statement_analysis_error"] = str(e)

이 코드에서는 최근 회계연도와 이전 회계연도의 데이터를 비교하여 성장률을 계산합니다.

2.3. 주요 재무 지표 계산

재무제표 항목들로부터 다양한 재무 지표를 계산하여 기업의 건전성, 수익성, 성장성을 평가합니다. 다음은 수익성 지표 중 하나인 이익률 계산의 예시입니다:

# Profitability
if recent_revenue > 0:
    gross_margin = (recent_gross_profit / recent_revenue) * 100
    operating_margin = (recent_operating_income / recent_revenue) * 100
    net_margin = (recent_net_income / recent_revenue) * 100

    analysis["gross_margin"] = f"{gross_margin:.2f}%"
    if "operating_margin" not in analysis:  # Only add if not already added from profile
        analysis["operating_margin"] = f"{operating_margin:.2f}%"
        if operating_margin > 15:
            analysis["profitability_evaluation"] = "Excellent profitability"
        elif operating_margin > 10:
            analysis["profitability_evaluation"] = "Good profitability"
        elif operating_margin > 5:
            analysis["profitability_evaluation"] = "Average profitability"
        else:
            analysis["profitability_evaluation"] = "Below average profitability"
    analysis["net_margin"] = f"{net_margin:.2f}%"

이 코드에서는 매출 대비 이익률(총이익률, 영업이익률, 순이익률)을 계산하고, 특히 영업이익률에 대해서는 정성적인 평가도 함께 제공합니다.

3. 분석 로직 설계와 구현

3.1. 유동성 분석

기업의 단기 부채 상환 능력을 나타내는 유동성 지표는 기업의 재무 건전성을 평가하는 중요한 요소입니다. 대표적인 유동성 지표인 유동비율(Current Ratio)은 다음과 같이 계산합니다:

# Liquidity analysis
current_assets = float(recent.get("totalCurrentAssets", 0))
current_liabilities = float(recent.get("totalCurrentLiabilities", 0))

analysis["current_assets"] = f"${current_assets:,.2f}"
analysis["current_liabilities"] = f"${current_liabilities:,.2f}"

if current_liabilities > 0:
    current_ratio = (current_assets / current_liabilities)
    analysis["current_ratio"] = f"{current_ratio:.2f}"

    if current_ratio > 2:
        analysis["liquidity_evaluation"] = "Excellent liquidity"
    elif current_ratio > 1.5:
        analysis["liquidity_evaluation"] = "Good liquidity"
    elif current_ratio > 1:
        analysis["liquidity_evaluation"] = "Adequate liquidity"
    else:
        analysis["liquidity_evaluation"] = "Potential liquidity risk"

유동비율이 2 이상이면 우수한 유동성, 1.5 이상이면 양호한 유동성, 1 이상이면 적절한 유동성, 1 미만이면 유동성 위험이 있다고 평가합니다.

3.2. 부채 비율 분석

기업의 재무 구조를 평가하는 지표 중 하나인 부채비율(Debt-to-Equity Ratio)은 다음과 같이 계산합니다:

# Debt ratio
if total_equity > 0:
    debt_to_equity = (total_liabilities / total_equity) * 100
    analysis["debt_to_equity"] = f"{debt_to_equity:.2f}%"

    if debt_to_equity < 50:
        analysis["debt_evaluation"] = "Very low debt (conservative)"
    elif debt_to_equity < 100:
        analysis["debt_evaluation"] = "Moderate debt"
    elif debt_to_equity < 200:
        analysis["debt_evaluation"] = "High debt (aggressive)"
    else:
        analysis["debt_evaluation"] = "Very high debt (risky)"

부채비율이 50% 미만이면 매우 낮은 부채, 100% 미만이면 적정 부채, 200% 미만이면 높은 부채, 200% 이상이면 매우 높은 부채로 평가합니다.

3.3. 수익성 분석

기업의 이익 창출 능력을 평가하는 수익성 지표는 다양하게 계산됩니다. 다음은 자기자본이익률(ROE)의 계산 예시입니다:

if "ReturnOnEquityTTM" in profile:
    roe = float(profile['ReturnOnEquityTTM']) * 100
    analysis["roe"] = f"{roe:,.2f}%"
    if roe > 15:
        analysis["roe_evaluation"] = "Excellent ROE"
    elif roe > 10:
        analysis["roe_evaluation"] = "Good ROE"
    elif roe > 5:
        analysis["roe_evaluation"] = "Average ROE"
    else:
        analysis["roe_evaluation"] = "Below average ROE"

자기자본이익률이 15% 이상이면 우수, 10% 이상이면 양호, 5% 이상이면 평균, 5% 미만이면 평균 이하로 평가합니다.

3.4. 성장성 분석

기업의 성장 가능성을 평가하는 성장성 지표는 이전 기간과의 비교를 통해 계산됩니다:

# Growth rates
if previous_revenue > 0:
    revenue_growth = ((recent_revenue - previous_revenue) / previous_revenue) * 100
    analysis["revenue_growth"] = f"{revenue_growth:.2f}%"

    if revenue_growth > 20:
        analysis["revenue_growth_evaluation"] = "Strong revenue growth"
    elif revenue_growth > 5:
        analysis["revenue_growth_evaluation"] = "Good revenue growth"
    elif revenue_growth > 0:
        analysis["revenue_growth_evaluation"] = "Modest revenue growth"
    else:
        analysis["revenue_growth_evaluation"] = "Declining revenue"

매출 성장률이 20% 이상이면 강한 성장, 5% 이상이면 양호한 성장, 0% 이상이면 완만한 성장, 0% 미만이면 매출 감소로 평가합니다.

4. 결과 포맷팅: 가독성과 정보성의 균형

재무 분석 결과를 사용자가 이해하기 쉽게 포맷팅하는 것은 매우 중요합니다. _format_financial_analysis 메서드는 분석 결과를 마크다운 형식으로 구조화하여 제공합니다.

4.1. 마크다운 형식 활용

마크다운은 텍스트 기반 문서를 구조화하는 간결한 방법을 제공합니다. 아래 구현에서는 제목, 부제목, 목록을 활용하여 정보를 계층적으로 구성합니다:

def _format_financial_analysis(self, analysis_data: Dict) -> str:
    """Format analysis data into readable text."""
    output = []

    ticker = analysis_data.get("ticker", "Unknown")
    company_name = analysis_data.get("company_name", "")

    header = f"# Financial Statement Analysis for {company_name} (Ticker: {ticker})"
    output.append(header)

    # Company profile information
    output.append("\n## Company Profile")
    # ... 프로필 정보 추가
    
    # Balance sheet information
    output.append("\n## Balance Sheet Information")
    # ... 대차대조표 정보 추가
    
    # ... 기타 섹션 추가

4.2. 분석 결과의 섹션별 구성

분석 결과는 다음과 같은 주요 섹션으로 구성됩니다:

  1. 회사 프로필: 섹터, 산업, 시가총액, 직원 수 등 기본 정보
  2. 대차대조표 정보: 자산, 부채, 자본 등의 항목
  3. 손익계산서 정보: 매출, 비용, 이익 등의 항목
  4. 현금흐름표 정보: 영업, 투자, 재무 활동의 현금흐름
  5. 재무 비율: P/E, PEG, 자기자본이익률 등 주요 재무 비율
  6. 재무 분석: 유동성, 부채, 수익성, 성장성에 대한 종합 평가

각 섹션은 관련 정보를 논리적으로 그룹화하여 사용자가 원하는 정보를 쉽게 찾을 수 있도록 합니다.

4.3. 정량적 수치와 정성적 평가 결합

단순히 숫자 데이터를 나열하는 것을 넘어, 각 지표에 대한 정성적 평가를 함께 제공하여 사용자의 이해를 돕습니다:

# Financial Analysis section
output.append("\n## Financial Analysis")

# Liquidity analysis
if "current_ratio" in analysis:
    output.append(f"- Current Ratio: {analysis['current_ratio']}")
    if "liquidity_evaluation" in analysis:
        output.append(f"  - Evaluation: {analysis['liquidity_evaluation']}")

# Debt analysis
if "debt_to_equity" in analysis:
    output.append(f"- Debt to Equity Ratio: {analysis['debt_to_equity']}")
    if "debt_evaluation" in analysis:
        output.append(f"  - Evaluation: {analysis['debt_evaluation']}")

이와 같이 각 지표 값과 함께 해당 값에 대한 평가("우수", "양호", "적정", "위험" 등)를 제공함으로써 사용자는 숫자의 의미를 직관적으로 이해할 수 있습니다.

5. 코드 리팩토링과 최적화

5.1. 에러 처리 전략

API 호출 및 데이터 처리 과정에서 발생할 수 있는 다양한 오류에 대한 견고한 처리 전략을 구현했습니다:

# API 요청 오류 처리
def make_request(self, function: str, symbol: str, **kwargs) -> Dict:
    """Make API request to Alpha Vantage."""
    try:
        # ... API 요청 코드
        
        if "Error Message" in data:
            return {"error": data["Error Message"]}

        if "Note" in data and "API call frequency" in data["Note"]:
            return {"error": f"API call frequency exceeded: {data['Note']}"}

        return data
    except Exception as e:
        return {"error": f"Request failed: {str(e)}"}

# 분석 과정의 오류 처리
try:
    # 분석 코드
except Exception as e:
    analysis["balance_sheet_analysis_error"] = str(e)

이러한 에러 처리 전략은 API 한도 초과, 네트워크 오류, 데이터 형식 오류 등 다양한 상황에서도 애플리케이션이 안정적으로 작동하도록 보장합니다.

5.2. 코드 모듈화

코드를 기능별로 모듈화하여 유지보수성과 가독성을 높였습니다:

  1. API 래퍼 클래스: Alpha Vantage API 호출 관련 기능
  2. 분석 도구 클래스: 재무제표 분석 및 결과 포맷팅
  3. 노드 클래스: 사용자 쿼리 처리 및 분석 결과 반환

각 모듈은 단일 책임 원칙(Single Responsibility Principle)에 따라 설계되어 있어, 코드의 이해와 수정이 용이합니다.

5.3. 재활용 가능한 컴포넌트

캐싱, 결과 포맷팅 등의 기능은 재활용 가능한 컴포넌트로 설계되어 있습니다:

def _get_cached_or_fetch(self, endpoint: str, func, *args, **kwargs) -> Dict:
    """Get data from cache or fetch from API."""
    cache_key = endpoint
    current_time = time.time()

    # Return from cache if available and not expired
    if cache_key in self.cache and current_time - self.cache_timestamp.get(cache_key, 0) < self.base_cache_time:
        return self.cache[cache_key]

    # Fetch new data
    result = func(*args, **kwargs)

    # Cache the result
    self.cache[cache_key] = result
    self.cache_timestamp[cache_key] = current_time

    return result

이러한 재활용 가능한 컴포넌트는 코드 중복을 줄이고, 일관된 동작을 보장합니다.

6. 결론

이번 글에서는 Alpha Vantage API를 활용한 재무제표 분석 기능의 구현 세부 사항을 살펴보았습니다. 복잡한 재무 데이터를 가져와 분석하고, 그 결과를 사용자가 이해하기 쉬운 형태로 제공하는 과정을 코드 수준에서 상세히 설명했습니다.

주요 구현 포인트는 다음과 같습니다:

  1. 종합적인 재무 데이터 수집: 기업 개요, 대차대조표, 손익계산서, 현금흐름표의 다양한 데이터 수집
  2. 다양한 재무 지표 계산: 유동성, 부채, 수익성, 성장성 등 주요 재무 지표 계산
  3. 정성적 평가 제공: 단순 수치를 넘어 직관적인 평가 기준 제공
  4. 사용자 친화적 결과 포맷팅: 마크다운 형식의 계층적 결과 구조
  5. 견고한 오류 처리: API 호출 및 데이터 처리 과정의 다양한 오류 상황 대응
  6. 효율적인 캐싱: API 호출 횟수 최소화 및 응답 시간 개선

이러한 요소들이 조화롭게 결합되어 사용자는 자연어 쿼리만으로 기업의 재무 상태에 대한 종합적이고 이해하기 쉬운 분석 결과를 얻을 수 있습니다.

profile
ML Engineer 🧠 | AI 모델 개발과 최적화 경험을 기록하며 성장하는 개발자 🚀 The light that burns twice as bright burns half as long ✨

0개의 댓글