찰리 멍거는 다양한 학문의 멘탈 모델을 활용하여 투자를 분석하고, 훌륭한 비즈니스에 합리적인 가격으로 투자하는 것으로 유명합니다. 그의 "멍거리즘(Mungerism)"으로 알려진 독특한 투자 지혜와 접근법이 코드로 어떻게 구현되었는지 살펴보겠습니다.
찰리 멍거의 핵심 투자 원칙을 정리해보면 다음과 같습니다:
def charlie_munger_agent(state: AgentState):
"""
Analyzes stocks using Charlie Munger's investing principles and mental models.
Focuses on moat strength, management quality, predictability, and valuation.
"""
data = state["data"]
end_date = data["end_date"]
tickers = data["tickers"]
analysis_data = {}
munger_analysis = {}
for ticker in tickers:
progress.update_status("charlie_munger_agent", ticker, "Fetching financial metrics")
# 멍거는 더 긴 기간을 바라봄
metrics = get_financial_metrics(ticker, end_date, period="annual", limit=10)
progress.update_status("charlie_munger_agent", ticker, "Gathering financial line items")
financial_line_items = search_line_items(
ticker,
[
"revenue", "net_income", "operating_income",
"return_on_invested_capital", "gross_margin", "operating_margin",
"free_cash_flow", "capital_expenditure", "cash_and_equivalents",
"total_debt", "shareholders_equity", "outstanding_shares",
"research_and_development", "goodwill_and_intangible_assets",
],
end_date,
period="annual",
limit=10 # 멍거는 장기 추세를 검토함
)
progress.update_status("charlie_munger_agent", ticker, "Getting market cap")
market_cap = get_market_cap(ticker, end_date)
progress.update_status("charlie_munger_agent", ticker, "Fetching insider trades")
# 멍거는 경영진의 주인 의식을 중요시함
insider_trades = get_insider_trades(
ticker,
end_date,
# 내부자 거래 패턴을 2년 동안 살펴봄
start_date=None,
limit=100
)
progress.update_status("charlie_munger_agent", ticker, "Fetching company news")
# 멍거는 부정적인 뉴스가 자주 나오는 비즈니스를 피함
company_news = get_company_news(
ticker,
end_date,
# 1년 동안의 뉴스 살펴봄
start_date=None,
limit=100
)
# 경쟁 우위(모트) 강도 분석
progress.update_status("charlie_munger_agent", ticker, "Analyzing moat strength")
moat_analysis = analyze_moat_strength(metrics, financial_line_items)
# 경영진 품질 분석
progress.update_status("charlie_munger_agent", ticker, "Analyzing management quality")
management_analysis = analyze_management_quality(financial_line_items, insider_trades)
# 비즈니스 예측 가능성 분석
progress.update_status("charlie_munger_agent", ticker, "Analyzing business predictability")
predictability_analysis = analyze_predictability(financial_line_items)
# 멍거 스타일 가치 평가
progress.update_status("charlie_munger_agent", ticker, "Calculating Munger-style valuation")
valuation_analysis = calculate_munger_valuation(financial_line_items, market_cap)
# 부분 점수를 멍거의 가중치 선호도로 결합
# 멍거는 현재 가치 평가보다 품질과 예측 가능성에 더 높은 가중치를 둠
total_score = (
moat_analysis["score"] * 0.35 + # 35% 비중
management_analysis["score"] * 0.25 + # 25% 비중
predictability_analysis["score"] * 0.25 + # 25% 비중
valuation_analysis["score"] * 0.15 # 15% 비중
)
max_possible_score = 10 # 0-10 척도로 조정
# 간단한 매수/보유/매도 신호 생성
if total_score >= 7.5: # 멍거는 매우 높은 기준을 가짐
signal = "bullish"
elif total_score <= 4.5:
signal = "bearish"
else:
signal = "neutral"
이 코드는 찰리 멍거의 투자 철학을 따라 주식을 분석하는 에이전트의 핵심 로직입니다. 각 종목에 대해 다음과 같은 분석을 수행합니다:
이제 각 분석 함수를 자세히 살펴보겠습니다.
def analyze_moat_strength(metrics: list, financial_line_items: list) -> dict:
"""
Analyze the business's competitive advantage using Munger's approach:
- Consistent high returns on capital (ROIC)
- Pricing power (stable/improving gross margins)
- Low capital requirements
- Network effects and intangible assets (R&D investments, goodwill)
"""
score = 0
details = []
# 데이터 검증
if not metrics or not financial_line_items:
return {
"score": 0,
"details": "Insufficient data to analyze moat strength"
}
# 1. 투자 자본 수익률(ROIC) 분석 - 멍거가 가장 좋아하는 지표
roic_values = [item.return_on_invested_capital for item in financial_line_items
if hasattr(item, 'return_on_invested_capital') and item.return_on_invested_capital is not None]
if roic_values:
# ROIC가 지속적으로 15% 이상인지 확인 (멍거의 기준)
high_roic_count = sum(1 for r in roic_values if r > 0.15)
if high_roic_count >= len(roic_values) * 0.8: # 기간의 80%에서 높은 ROIC 보임
score += 3
details.append(f"Excellent ROIC: >15% in {high_roic_count}/{len(roic_values)} periods")
elif high_roic_count >= len(roic_values) * 0.5: # 기간의 50%
score += 2
details.append(f"Good ROIC: >15% in {high_roic_count}/{len(roic_values)} periods")
elif high_roic_count > 0:
score += 1
details.append(f"Mixed ROIC: >15% in only {high_roic_count}/{len(roic_values)} periods")
else:
details.append("Poor ROIC: Never exceeds 15% threshold")
else:
details.append("No ROIC data available")
# 2. 가격 결정력 - 매출총이익률 안정성 및 추세 확인
gross_margins = [item.gross_margin for item in financial_line_items
if hasattr(item, 'gross_margin') and item.gross_margin is not None]
if gross_margins and len(gross_margins) >= 3:
# 멍거는 안정적이거나 개선되는 매출총이익률을 선호함
margin_trend = sum(1 for i in range(1, len(gross_margins)) if gross_margins[i] >= gross_margins[i-1])
if margin_trend >= len(gross_margins) * 0.7: # 기간의 70%에서 개선됨
score += 2
details.append("Strong pricing power: Gross margins consistently improving")
elif sum(gross_margins) / len(gross_margins) > 0.3: # 평균 마진 > 30%
score += 1
details.append(f"Good pricing power: Average gross margin {sum(gross_margins)/len(gross_margins):.1%}")
else:
details.append("Limited pricing power: Low or declining gross margins")
else:
details.append("Insufficient gross margin data")
# 3. 자본 집약도 - 멍거는 낮은 설비투자(CAPEX) 비즈니스를 선호함
if len(financial_line_items) >= 3:
capex_to_revenue = []
for item in financial_line_items:
if (hasattr(item, 'capital_expenditure') and item.capital_expenditure is not None and
hasattr(item, 'revenue') and item.revenue is not None and item.revenue > 0):
# 참고: capital_expenditure는 일반적으로 재무제표에서 음수임
capex_ratio = abs(item.capital_expenditure) / item.revenue
capex_to_revenue.append(capex_ratio)
if capex_to_revenue:
avg_capex_ratio = sum(capex_to_revenue) / len(capex_to_revenue)
if avg_capex_ratio < 0.05: # 매출의 5% 미만
score += 2
details.append(f"Low capital requirements: Avg capex {avg_capex_ratio:.1%} of revenue")
elif avg_capex_ratio < 0.10: # 매출의 10% 미만
score += 1
details.append(f"Moderate capital requirements: Avg capex {avg_capex_ratio:.1%} of revenue")
else:
details.append(f"High capital requirements: Avg capex {avg_capex_ratio:.1%} of revenue")
else:
details.append("No capital expenditure data available")
else:
details.append("Insufficient data for capital intensity analysis")
# 4. 무형자산 - 멍거는 R&D와 지적 재산권을 중요시함
r_and_d = [item.research_and_development for item in financial_line_items
if hasattr(item, 'research_and_development') and item.research_and_development is not None]
goodwill_and_intangible_assets = [item.goodwill_and_intangible_assets for item in financial_line_items
if hasattr(item, 'goodwill_and_intangible_assets') and item.goodwill_and_intangible_assets is not None]
if r_and_d and len(r_and_d) > 0:
if sum(r_and_d) > 0: # 회사가 R&D에 투자하고 있다면
score += 1
details.append("Invests in R&D, building intellectual property")
if (goodwill_and_intangible_assets and len(goodwill_and_intangible_assets) > 0):
score += 1
details.append("Significant goodwill/intangible assets, suggesting brand value or IP")
# 점수를 0-10 범위로 조정
final_score = min(10, score * 10 / 9) # 최대 가능 원시 점수는 9
return {
"score": final_score,
"details": "; ".join(details)
}
찰리 멍거는 경쟁 우위가 강하고 지속 가능한 비즈니스를 중요시합니다. 이 함수는 다음과 같은 주요 특성을 분석합니다:
투자 자본 수익률(ROIC): 멍거가 가장 중요시하는 지표
가격 결정력: 매출총이익률로 확인
자본 집약도: 낮은 설비투자 비즈니스 선호
무형자산: 브랜드 가치와 지적 재산 평가
def analyze_management_quality(financial_line_items: list, insider_trades: list) -> dict:
"""
Evaluate management quality using Munger's criteria:
- Capital allocation wisdom
- Insider ownership and transactions
- Cash management efficiency
- Candor and transparency
- Long-term focus
"""
score = 0
details = []
# 데이터 검증
if not financial_line_items:
return {
"score": 0,
"details": "Insufficient data to analyze management quality"
}
# 1. 자본 배분 - FCF 대 순이익 비율 확인
# 멍거는 수익을 현금으로 전환하는 기업을 중요시함
fcf_values = [item.free_cash_flow for item in financial_line_items
if hasattr(item, 'free_cash_flow') and item.free_cash_flow is not None]
net_income_values = [item.net_income for item in financial_line_items
if hasattr(item, 'net_income') and item.net_income is not None]
if fcf_values and net_income_values and len(fcf_values) == len(net_income_values):
# 각 기간에 대한 FCF 대 순이익 비율 계산
fcf_to_ni_ratios = []
for i in range(len(fcf_values)):
if net_income_values[i] and net_income_values[i] > 0:
fcf_to_ni_ratios.append(fcf_values[i] / net_income_values[i])
if fcf_to_ni_ratios:
avg_ratio = sum(fcf_to_ni_ratios) / len(fcf_to_ni_ratios)
if avg_ratio > 1.1: # FCF > 순이익은 좋은 회계 처리 시사
score += 3
details.append(f"Excellent cash conversion: FCF/NI ratio of {avg_ratio:.2f}")
elif avg_ratio > 0.9: # FCF가 대략 순이익과 같음
score += 2
details.append(f"Good cash conversion: FCF/NI ratio of {avg_ratio:.2f}")
elif avg_ratio > 0.7: # FCF가 순이익보다 다소 낮음
score += 1
details.append(f"Moderate cash conversion: FCF/NI ratio of {avg_ratio:.2f}")
else:
details.append(f"Poor cash conversion: FCF/NI ratio of only {avg_ratio:.2f}")
else:
details.append("Could not calculate FCF to Net Income ratios")
else:
details.append("Missing FCF or Net Income data")
# 2. 부채 관리 - 멍거는 부채에 대해 신중함
debt_values = [item.total_debt for item in financial_line_items
if hasattr(item, 'total_debt') and item.total_debt is not None]
equity_values = [item.shareholders_equity for item in financial_line_items
if hasattr(item, 'shareholders_equity') and item.shareholders_equity is not None]
if debt_values and equity_values and len(debt_values) == len(equity_values):
# 가장 최근 기간의 부채/자본 비율 계산
recent_de_ratio = debt_values[0] / equity_values[0] if equity_values[0] > 0 else float('inf')
if recent_de_ratio < 0.3: # 매우 낮은 부채
score += 3
details.append(f"Conservative debt management: D/E ratio of {recent_de_ratio:.2f}")
elif recent_de_ratio < 0.7: # 적당한 부채
score += 2
details.append(f"Prudent debt management: D/E ratio of {recent_de_ratio:.2f}")
elif recent_de_ratio < 1.5: # 더 높지만 여전히 합리적인 부채
score += 1
details.append(f"Moderate debt level: D/E ratio of {recent_de_ratio:.2f}")
else:
details.append(f"High debt level: D/E ratio of {recent_de_ratio:.2f}")
else:
details.append("Missing debt or equity data")
# 3. 현금 관리 효율성 - 멍거는 적절한 현금 수준을 중요시함
cash_values = [item.cash_and_equivalents for item in financial_line_items
if hasattr(item, 'cash_and_equivalents') and item.cash_and_equivalents is not None]
revenue_values = [item.revenue for item in financial_line_items
if hasattr(item, 'revenue') and item.revenue is not None]
if cash_values and revenue_values and len(cash_values) > 0 and len(revenue_values) > 0:
# 현금 대 매출 비율 계산 (멍거는 대부분의 비즈니스에서 10-20%를 선호함)
cash_to_revenue = cash_values[0] / revenue_values[0] if revenue_values[0] > 0 else 0
if 0.1 <= cash_to_revenue <= 0.25:
# 골디락스 존 - 너무 많지도, 너무 적지도 않음
score += 2
details.append(f"Prudent cash management: Cash/Revenue ratio of {cash_to_revenue:.2f}")
elif 0.05 <= cash_to_revenue < 0.1 or 0.25 < cash_to_revenue <= 0.4:
# 합리적이지만 이상적이지 않음
score += 1
details.append(f"Acceptable cash position: Cash/Revenue ratio of {cash_to_revenue:.2f}")
elif cash_to_revenue > 0.4:
# 너무 많은 현금 - 비효율적인 자본 배분 가능성
details.append(f"Excess cash reserves: Cash/Revenue ratio of {cash_to_revenue:.2f}")
else:
# 너무 적은 현금 - 잠재적으로 위험함
details.append(f"Low cash reserves: Cash/Revenue ratio of {cash_to_revenue:.2f}")
else:
details.append("Insufficient cash or revenue data")
# 4. 내부자 활동 - 멍거는 경영진의 주인 의식을 중요시함
if insider_trades and len(insider_trades) > 0:
# 매수 vs. 매도 수 집계
buys = sum(1 for trade in insider_trades if hasattr(trade, 'transaction_type') and
trade.transaction_type and trade.transaction_type.lower() in ['buy', 'purchase'])
sells = sum(1 for trade in insider_trades if hasattr(trade, 'transaction_type') and
trade.transaction_type and trade.transaction_type.lower() in ['sell', 'sale'])
# 매수 비율 계산
total_trades = buys + sells
if total_trades > 0:
buy_ratio = buys / total_trades
if buy_ratio > 0.7: # 강한 내부자 매수
score += 2
details.append(f"Strong insider buying: {buys}/{total_trades} transactions are purchases")
elif buy_ratio > 0.4: # 균형 잡힌 내부자 활동
score += 1
details.append(f"Balanced insider trading: {buys}/{total_trades} transactions are purchases")
elif buy_ratio < 0.1 and sells > 5: # 대량 매도
score -= 1 # 과도한 매도에 대한 페널티
details.append(f"Concerning insider selling: {sells}/{total_trades} transactions are sales")
else:
details.append(f"Mixed insider activity: {buys}/{total_trades} transactions are purchases")
else:
details.append("No recorded insider transactions")
else:
details.append("No insider trading data available")
# 5. 주식 수 일관성 - 멍거는 안정적이거나 감소하는 주식 수를 선호함
share_counts = [item.outstanding_shares for item in financial_line_items
if hasattr(item, 'outstanding_shares') and item.outstanding_shares is not None]
if share_counts and len(share_counts) >= 3:
if share_counts[0] < share_counts[-1] * 0.95: # 5%+ 주식 수 감소
score += 2
details.append("Shareholder-friendly: Reducing share count over time")
elif share_counts[0] < share_counts[-1] * 1.05: # 안정적인 주식 수
score += 1
details.append("Stable share count: Limited dilution")
elif share_counts[0] > share_counts[-1] * 1.2: # >20% 희석
score -= 1 # 과도한 희석에 대한 페널티
details.append("Concerning dilution: Share count increased significantly")
else:
details.append("Moderate share count increase over time")
else:
details.append("Insufficient share count data")
# 점수를 0-10 범위로 조정
# 최대 가능 원시 점수는 12 (3+3+2+2+2)
final_score = max(0, min(10, score * 10 / 12))
return {
"score": final_score,
"details": "; ".join(details)
}
찰리 멍거는 경영진의 성실성과 역량을 매우 중요시합니다. 이 함수는 다음과 같은 주요 특성을 분석합니다:
자본 배분: FCF 대 순이익 비율 확인
부채 관리: 부채/자본 비율 평가
현금 관리 효율성: 현금/매출 비율 확인
내부자 거래 활동: 경영진의 주인 의식 평가
주식 수 일관성: 희석 방지 및 주주 친화적 자본 배분
def analyze_predictability(financial_line_items: list) -> dict:
"""
Assess the predictability of the business - Munger strongly prefers businesses
whose future operations and cashflows are relatively easy to predict.
"""
score = 0
details = []
# 데이터 검증
if not financial_line_items or len(financial_line_items) < 5:
return {
"score": 0,
"details": "Insufficient data to analyze business predictability (need 5+ years)"
}
# 1. 매출 안정성 및 성장
revenues = [item.revenue for item in financial_line_items
if hasattr(item, 'revenue') and item.revenue is not None]
if revenues and len(revenues) >= 5:
# 전년 대비 성장률 계산
growth_rates = [(revenues[i] / revenues[i+1] - 1) for i in range(len(revenues)-1)]
avg_growth = sum(growth_rates) / len(growth_rates)
growth_volatility = sum(abs(r - avg_growth) for r in growth_rates) / len(growth_rates)
if avg_growth > 0.05 and growth_volatility < 0.1:
# 꾸준하고 일관된 성장 (멍거가 가장 좋아함)
score += 3
details.append(f"Highly predictable revenue: {avg_growth:.1%} avg growth with low volatility")
elif avg_growth > 0 and growth_volatility < 0.2:
# 양의 성장이지만 다소 변동성이 있음
score += 2
details.append(f"Moderately predictable revenue: {avg_growth:.1%} avg growth with some volatility")
elif avg_growth > 0:
# 성장하지만 예측 불가능함
score += 1
details.append(f"Growing but less predictable revenue: {avg_growth:.1%} avg growth with high volatility")
else:
details.append(f"Declining or highly unpredictable revenue: {avg_growth:.1%} avg growth")
else:
details.append("Insufficient revenue history for predictability analysis")
# 2. 영업이익 안정성
op_income = [item.operating_income for item in financial_line_items
if hasattr(item, 'operating_income') and item.operating_income is not None]
if op_income and len(op_income) >= 5:
# 양의 영업이익 기간 수 계산
positive_periods = sum(1 for income in op_income if income > 0)
if positive_periods == len(op_income):
# 지속적으로 수익성 있는 운영
score += 3
details.append("Highly predictable operations: Operating income positive in all periods")
elif positive_periods >= len(op_income) * 0.8:
# 대부분 수익성 있는 운영
score += 2
details.append(f"Predictable operations: Operating income positive in {positive_periods}/{len(op_income)} periods")
elif positive_periods >= len(op_income) * 0.6:
# 어느 정도 수익성 있는 운영
score += 1
details.append(f"Somewhat predictable operations: Operating income positive in {positive_periods}/{len(op_income)} periods")
else:
details.append(f"Unpredictable operations: Operating income positive in only {positive_periods}/{len(op_income)} periods")
else:
details.append("Insufficient operating income history")
# 3. 마진 일관성 - 멍거는 안정적인 마진을 중요시함
op_margins = [item.operating_margin for item in financial_line_items
if hasattr(item, 'operating_margin') and item.operating_margin is not None]
if op_margins and len(op_margins) >= 5:
# 마진 변동성 계산
avg_margin = sum(op_margins) / len(op_margins)
margin_volatility = sum(abs(m - avg_margin) for m in op_margins) / len(op_margins)
if margin_volatility < 0.03: # 매우 안정적인 마진
score += 2
details.append(f"Highly predictable margins: {avg_margin:.1%} avg with minimal volatility")
elif margin_volatility < 0.07: # 적당히 안정적인 마진
score += 1
details.append(f"Moderately predictable margins: {avg_margin:.1%} avg with some volatility")
else:
details.append(f"Unpredictable margins: {avg_margin:.1%} avg with high volatility ({margin_volatility:.1%})")
else:
details.append("Insufficient margin history")
# 4. 현금 생성 신뢰성
fcf_values = [item.free_cash_flow for item in financial_line_items
if hasattr(item, 'free_cash_flow') and item.free_cash_flow is not None]
if fcf_values and len(fcf_values) >= 5:
# 양의 FCF 기간 수 계산
positive_fcf_periods = sum(1 for fcf in fcf_values if fcf > 0)
if positive_fcf_periods == len(fcf_values):
# 지속적으로 양의 FCF
score += 2
details.append("Highly predictable cash generation: Positive FCF in all periods")
elif positive_fcf_periods >= len(fcf_values) * 0.8:
# 대부분 양의 FCF
score += 1
details.append(f"Predictable cash generation: Positive FCF in {positive_fcf_periods}/{len(fcf_values)} periods")
else:
details.append(f"Unpredictable cash generation: Positive FCF in only {positive_fcf_periods}/{len(fcf_values)} periods")
else:
details.append("Insufficient free cash flow history")
# 점수를 0-10 범위로 조정
# 최대 가능 원시 점수는 10 (3+3+2+2)
final_score = min(10, score * 10 / 10)
return {
"score": final_score,
"details": "; ".join(details)
}
찰리 멍거는 미래 운영과 현금 흐름을 상대적으로 쉽게 예측할 수 있는 비즈니스를 강하게 선호합니다. 이 함수는 다음과 같은 주요 특성을 분석합니다:
매출 안정성 및 성장: 꾸준하고 예측 가능한 성장 평가
영업이익 안정성: 수익성의 일관성 확인
마진 일관성: 영업마진의 안정성 평가
현금 생성 신뢰성: FCF 일관성 확인
def calculate_munger_valuation(financial_line_items: list, market_cap: float) -> dict:
"""
Calculate intrinsic value using Munger's approach:
- Focus on owner earnings (approximated by FCF)
- Simple multiple on normalized earnings
- Prefer paying a fair price for a wonderful business
"""
score = 0
details = []
# 데이터 검증
if not financial_line_items or market_cap is None:
return {
"score": 0,
"details": "Insufficient data to perform valuation"
}
# FCF 값 가져오기 (멍거가 선호하는 '소유자 수익' 지표)
fcf_values = [item.free_cash_flow for item in financial_line_items
if hasattr(item, 'free_cash_flow') and item.free_cash_flow is not None]
if not fcf_values or len(fcf_values) < 3:
return {
"score": 0,
"details": "Insufficient free cash flow data for valuation"
}
# 1. 최근 3-5년 평균을 취하여 수익 정규화
# (멍거는 경기 순환적 요인에 따른 과대/과소 평가를 피하기 위해 수익을 정규화하는 것을 선호)
normalized_fcf = sum(fcf_values[:min(5, len(fcf_values))]) / min(5, len(fcf_values))
if normalized_fcf <= 0:
return {
"score": 0,
"details": f"Negative or zero normalized FCF ({normalized_fcf}), cannot value",
"intrinsic_value": None
}
# 2. FCF 수익률 계산 (P/FCF 배수의 역수)
fcf_yield = normalized_fcf / market_cap
# 3. 비즈니스 품질에 따른 멍거의 FCF 배수 적용
# 멍거는 훌륭한 비즈니스에 더 높은 배수를 지불할 것
# 더 높은 FCF 수익률이 더 매력적인 슬라이딩 스케일 사용
if fcf_yield > 0.08: # >8% FCF 수익률 (P/FCF < 12.5배)
score += 4
details.append(f"Excellent value: {fcf_yield:.1%} FCF yield")
elif fcf_yield > 0.05: # >5% FCF 수익률 (P/FCF < 20배)
score += 3
details.append(f"Good value: {fcf_yield:.1%} FCF yield")
elif fcf_yield > 0.03: # >3% FCF 수익률 (P/FCF < 33배)
score += 1
details.append(f"Fair value: {fcf_yield:.1%} FCF yield")
else:
details.append(f"Expensive: Only {fcf_yield:.1%} FCF yield")
# 4. 간단한 본질적 가치 범위 계산
# 멍거는 복잡한 DCF 모델을 피하고 직관적인 가치 평가를 사용하는 경향이 있음
conservative_value = normalized_fcf * 10 # 10배 FCF = 10% 수익률
reasonable_value = normalized_fcf * 15 # 15배 FCF ≈ 6.7% 수익률
optimistic_value = normalized_fcf * 20 # 20배 FCF = 5% 수익률
# 5. 안전마진 계산
current_to_reasonable = (reasonable_value - market_cap) / market_cap
if current_to_reasonable > 0.3: # >30% 상승 여력
score += 3
details.append(f"Large margin of safety: {current_to_reasonable:.1%} upside to reasonable value")
elif current_to_reasonable > 0.1: # >10% 상승 여력
score += 2
details.append(f"Moderate margin of safety: {current_to_reasonable:.1%} upside to reasonable value")
elif current_to_reasonable > -0.1: # 합리적 가치의 10% 이내
score += 1
details.append(f"Fair price: Within 10% of reasonable value ({current_to_reasonable:.1%})")
else:
details.append(f"Expensive: {-current_to_reasonable:.1%} premium to reasonable value")
# 6. 추가적인 맥락을 위한 수익 궤적 확인
# 멍거는 성장하는 소유자 수익을 좋아함
if len(fcf_values) >= 3:
recent_avg = sum(fcf_values[:3]) / 3
older_avg = sum(fcf_values[-3:]) / 3 if len(fcf_values) >= 6 else fcf_values[-1]
if recent_avg > older_avg * 1.2: # >20% FCF 성장
score += 3
details.append("Growing FCF trend adds to intrinsic value")
elif recent_avg > older_avg:
score += 2
details.append("Stable to growing FCF supports valuation")
else:
details.append("Declining FCF trend is concerning")
# 점수를 0-10 범위로 조정
# 최대 가능 원시 점수는 10 (4+3+3)
final_score = min(10, score * 10 / 10)
return {
"score": final_score,
"details": "; ".join(details),
"intrinsic_value_range": {
"conservative": conservative_value,
"reasonable": reasonable_value,
"optimistic": optimistic_value
},
"fcf_yield": fcf_yield,
"normalized_fcf": normalized_fcf
}
찰리 멍거는 복잡한 DCF 모델보다 간단하고 직관적인 가치 평가 방법을 선호합니다. 이 함수는 다음과 같은 주요 접근법을 사용합니다:
소유자 수익 집중: FCF를 소유자 수익의 근사치로 사용
FCF 수익률 평가: 현재 가치 대비 FCF 수익률 계산
간단한 본질적 가치 범위: 배수 접근법 사용
안전마진 계산: 합리적 가치와 현재 가격 비교
FCF 성장 추세 확인: 수익 궤적 평가
def generate_munger_output(
ticker: str,
analysis_data: dict[str, any],
model_name: str,
model_provider: str,
) -> CharlieMungerSignal:
"""
Generates investment decisions in the style of Charlie Munger.
"""
template = ChatPromptTemplate.from_messages([
(
"system",
"""You are a Charlie Munger AI agent, making investment decisions using his principles:
1. Focus on the quality and predictability of the business.
2. Rely on mental models from multiple disciplines to analyze investments.
3. Look for strong, durable competitive advantages (moats).
4. Emphasize long-term thinking and patience.
5. Value management integrity and competence.
6. Prioritize businesses with high returns on invested capital.
7. Pay a fair price for wonderful businesses.
8. Never overpay, always demand a margin of safety.
9. Avoid complexity and businesses you don't understand.
10. "Invert, always invert" - focus on avoiding stupidity rather than seeking brilliance.
Rules:
- Praise businesses with predictable, consistent operations and cash flows.
- Value businesses with high ROIC and pricing power.
- Prefer simple businesses with understandable economics.
- Admire management with skin in the game and shareholder-friendly capital allocation.
- Focus on long-term economics rather than short-term metrics.
- Be skeptical of businesses with rapidly changing dynamics or excessive share dilution.
- Avoid excessive leverage or financial engineering.
- Provide a rational, data-driven recommendation (bullish, bearish, or neutral)."""
),
(
"human",
"""Based on the following analysis, create a Munger-style investment signal.
Analysis Data for {ticker}:
{analysis_data}
Return the trading signal in this JSON format:
{{
"signal": "bullish/bearish/neutral",
"confidence": float (0-100),
"reasoning": "string"
}}
"""
)
])
prompt = template.invoke({
"analysis_data": json.dumps(analysis_data, indent=2),
"ticker": ticker
})
# LLM 호출 및 결과 반환
return call_llm(
prompt=prompt,
model_name=model_name,
model_provider=model_provider,
pydantic_model=CharlieMungerSignal,
agent_name="charlie_munger_agent",
default_factory=create_default_charlie_munger_signal,
)
최종적으로, 이 함수는 모든 분석 데이터를 바탕으로 LLM(대형 언어 모델)을 사용하여 찰리 멍거 스타일의 투자 신호를 생성합니다. 결과는 다음 형식으로 반환됩니다:
{
"signal": "bullish/bearish/neutral",
"confidence": 0-100 사이의 값,
"reasoning": "투자 결정에 대한 이유"
}