ai-hedge-fund 9) stanley druckenmiller

Tasker_Jang·2025년 3월 18일
0

드러켄밀러는 소로스 펀드에서 퀀텀 펀드의 포트폴리오 매니저로 일하며 명성을 쌓았고, 이후 자신의 회사인 두큐즌 캐피털 매니지먼트를 설립했습니다. 그는 30년 경력 중 단 한 해도 손실을 기록하지 않았다는 놀라운 기록을 가지고 있습니다.

스탠리 드러켄밀러의 투자 철학

드러켄밀러의 투자 방식은 다음과 같은 특징을 가집니다:

  1. 비대칭적 리스크-리워드 기회 추구: 큰 상승 가능성과 제한된 하락 위험
  2. 성장, 모멘텀, 시장 심리 강조: 강한 성장과 주가 모멘텀을 중시
  3. 자본 보존: 주요 하락장에서 자본 손실 방지
  4. 진정한 성장 기업에 높은 가격 지불 의향: 기존 가치 투자자보다 높은 밸류에이션 수용
  5. 높은 확신이 있을 때 공격적 배팅: 확신이 높을 때 집중 투자
  6. 논리가 틀렸을 때 빠르게 손절: 투자 논리가 무너지면 즉시 철수

스탠리 드러켄밀러 에이전트 분석

에이전트의 주요 구성 요소

드러켄밀러 에이전트는 다음 5가지 핵심 분석을 수행합니다:

  1. 성장 및 모멘텀 분석 (35% 가중치)
  2. 리스크-리워드 분석 (20% 가중치)
  3. 밸류에이션 분석 (20% 가중치)
  4. 감성 분석 (15% 가중치)
  5. 내부자 거래 분석 (10% 가중치)

각 분석은 0-10점 척도로 평가되며, 가중치를 적용하여 최종 투자 신호를 생성합니다.

데이터 수집 과정

def stanley_druckenmiller_agent(state: AgentState):
    data = state["data"]
    end_date = data["end_date"]
    tickers = data["tickers"]

    analysis_data = {}
    druck_analysis = {}

    for ticker in tickers:
        progress.update_status("stanley_druckenmiller_agent", ticker, "Fetching financial metrics")
        metrics = get_financial_metrics(ticker, end_date, period="annual", limit=5)

        progress.update_status("stanley_druckenmiller_agent", ticker, "Gathering financial line items")
        financial_line_items = search_line_items(
            ticker,
            [
                "revenue",
                "earnings_per_share",
                "net_income",
                "operating_income",
                "gross_margin",
                "operating_margin",
                "free_cash_flow",
                "capital_expenditure",
                "cash_and_equivalents",
                "total_debt",
                "shareholders_equity",
                "outstanding_shares",
                "ebit",
                "ebitda",
            ],
            end_date,
            period="annual",
            limit=5,
        )

        # 추가 데이터 수집: 시가총액, 내부자 거래, 뉴스, 가격 데이터
        market_cap = get_market_cap(ticker, end_date)
        insider_trades = get_insider_trades(ticker, end_date, start_date=None, limit=50)
        company_news = get_company_news(ticker, end_date, start_date=None, limit=50)
        prices = get_prices(ticker, start_date="2022-01-01", end_date=end_date)
        
        # 각 분석 실행
        # ... 분석 코드 ...

이 에이전트는 5년간의 연간 재무 데이터, 내부자 거래, 기업 뉴스, 약 1년간의 일별 가격 데이터 등 다양한 정보를 수집합니다.

1. 성장 및 모멘텀 분석 (35%)

드러켄밀러의 투자 철학에서 가장 중요한 요소는 성장과 모멘텀입니다. 그는 강한 성장세와 모멘텀을 보이는 기업에 투자하는 것을 선호합니다.

def analyze_growth_and_momentum(financial_line_items: list, prices: list) -> dict:
    """
    Evaluate:
      - Revenue Growth (YoY)
      - EPS Growth (YoY)
      - Price Momentum
    """
    if not financial_line_items or len(financial_line_items) < 2:
        return {"score": 0, "details": "Insufficient financial data for growth analysis"}

    details = []
    raw_score = 0  # We'll sum up a maximum of 9 raw points, then scale to 0–10

    # 1. Revenue Growth
    revenues = [fi.revenue for fi in financial_line_items if fi.revenue is not None]
    if len(revenues) >= 2:
        latest_rev = revenues[0]
        older_rev = revenues[-1]
        if older_rev > 0:
            rev_growth = (latest_rev - older_rev) / abs(older_rev)
            if rev_growth > 0.30:  # 30% 이상의 매출 성장
                raw_score += 3
                details.append(f"Strong revenue growth: {rev_growth:.1%}")
            elif rev_growth > 0.15:  # 15-30% 매출 성장
                raw_score += 2
                details.append(f"Moderate revenue growth: {rev_growth:.1%}")
            elif rev_growth > 0.05:  # 5-15% 매출 성장
                raw_score += 1
                details.append(f"Slight revenue growth: {rev_growth:.1%}")
            else:
                details.append(f"Minimal/negative revenue growth: {rev_growth:.1%}")
    
    # 2. EPS Growth
    # EPS 성장률 분석 (유사한 로직)
    
    # 3. Price Momentum
    if prices and len(prices) > 30:
        sorted_prices = sorted(prices, key=lambda p: p.time)
        close_prices = [p.close for p in sorted_prices if p.close is not None]
        if len(close_prices) >= 2:
            start_price = close_prices[0]
            end_price = close_prices[-1]
            if start_price > 0:
                pct_change = (end_price - start_price) / start_price
                if pct_change > 0.50:  # 50% 이상의 가격 상승
                    raw_score += 3
                    details.append(f"Very strong price momentum: {pct_change:.1%}")
                elif pct_change > 0.20:  # 20-50% 가격 상승
                    raw_score += 2
                    details.append(f"Moderate price momentum: {pct_change:.1%}")
                elif pct_change > 0:  # 0-20% 가격 상승
                    raw_score += 1
                    details.append(f"Slight positive momentum: {pct_change:.1%}")
    
    # 점수 변환 (9점 만점에서 10점 만점으로)
    final_score = min(10, (raw_score / 9) * 10)
    return {"score": final_score, "details": "; ".join(details)}

이 함수는 세 가지 핵심 측면을 분석합니다:

  1. 매출 성장률: 30% 이상이면 '강한 성장', 15-30%면 '중간 성장', 5-15%면 '약한 성장'으로 평가
  2. EPS(주당순이익) 성장률: 매출 성장과 유사한 기준으로 평가
  3. 가격 모멘텀: 50% 이상의 가격 상승이면 '매우 강한 모멘텀', 20-50%면 '중간 모멘텀', 0-20%면 '약한 모멘텀'으로 평가

각 측면에서 최대 3점씩, 총 9점을 획득할 수 있고, 이를 10점 만점으로 변환합니다.

2. 리스크-리워드 분석 (20%)

드러켄밀러는 높은 상승 잠재력과 제한된 하락 위험을 가진 비대칭적 기회를 추구합니다. 이 분석은 기업의 재무적 안정성과 주가 변동성을 평가합니다.

def analyze_risk_reward(financial_line_items: list, market_cap: float | None, prices: list) -> dict:
    """
    Assesses risk via:
      - Debt-to-Equity
      - Price Volatility
    Aims for strong upside with contained downside.
    """
    if not financial_line_items or not prices:
        return {"score": 0, "details": "Insufficient data for risk-reward analysis"}

    details = []
    raw_score = 0  # We'll accumulate up to 6 raw points, then scale to 0-10

    # 1. Debt-to-Equity
    debt_values = [fi.total_debt for fi in financial_line_items if fi.total_debt is not None]
    equity_values = [fi.shareholders_equity for fi in financial_line_items if fi.shareholders_equity is not None]

    if debt_values and equity_values and len(debt_values) == len(equity_values) and len(debt_values) > 0:
        recent_debt = debt_values[0]
        recent_equity = equity_values[0] if equity_values[0] else 1e-9
        de_ratio = recent_debt / recent_equity
        if de_ratio < 0.3:  # 낮은 부채비율
            raw_score += 3
            details.append(f"Low debt-to-equity: {de_ratio:.2f}")
        elif de_ratio < 0.7:  # 중간 부채비율
            raw_score += 2
            details.append(f"Moderate debt-to-equity: {de_ratio:.2f}")
        elif de_ratio < 1.5:  # 높은 부채비율
            raw_score += 1
            details.append(f"Somewhat high debt-to-equity: {de_ratio:.2f}")
        else:  # 매우 높은 부채비율
            details.append(f"High debt-to-equity: {de_ratio:.2f}")
    
    # 2. Price Volatility
    if len(prices) > 10:
        # 가격의 일일 수익률 표준 편차 계산
        # 변동성이 낮을수록 더 높은 점수 (낮은 위험)
        sorted_prices = sorted(prices, key=lambda p: p.time)
        close_prices = [p.close for p in sorted_prices if p.close is not None]
        if len(close_prices) > 10:
            daily_returns = []
            for i in range(1, len(close_prices)):
                prev_close = close_prices[i - 1]
                if prev_close > 0:
                    daily_returns.append((close_prices[i] - prev_close) / prev_close)
            if daily_returns:
                stdev = statistics.pstdev(daily_returns)  # 모집단 표준편차
                if stdev < 0.01:  # 매우 낮은 변동성
                    raw_score += 3
                    details.append(f"Low volatility: daily returns stdev {stdev:.2%}")
                elif stdev < 0.02:  # 중간 변동성
                    raw_score += 2
                    details.append(f"Moderate volatility: daily returns stdev {stdev:.2%}")
                elif stdev < 0.04:  # 높은 변동성
                    raw_score += 1
                    details.append(f"High volatility: daily returns stdev {stdev:.2%}")
                else:  # 매우 높은 변동성
                    details.append(f"Very high volatility: daily returns stdev {stdev:.2%}")
    
    # 점수 변환 (6점 만점에서 10점 만점으로)
    final_score = min(10, (raw_score / 6) * 10)
    return {"score": final_score, "details": "; ".join(details)}

이 분석은 두 가지 핵심 측면을 평가합니다:

  1. 부채-자기자본 비율(D/E): 0.3 미만이면 '낮은 부채비율', 0.3-0.7이면 '중간 부채비율', 0.7-1.5면 '다소 높은 부채비율', 1.5 이상이면 '높은 부채비율'로 평가
  2. 가격 변동성: 일일 수익률의 표준편차를 계산하여 1% 미만이면 '낮은 변동성', 1-2%면 '중간 변동성', 2-4%면 '높은 변동성', 4% 이상이면 '매우 높은 변동성'으로 평가

3. 드러켄밀러 스타일 밸류에이션 분석 (20%)

드러켄밀러는 성장 기업에 높은 가격을 지불할 의향이 있지만, 여전히 기본적인 밸류에이션 지표를 확인합니다.

def analyze_druckenmiller_valuation(financial_line_items: list, market_cap: float | None) -> dict:
    """
    Druckenmiller is willing to pay up for growth, but still checks:
      - P/E
      - P/FCF
      - EV/EBIT
      - EV/EBITDA
    Each can yield up to 2 points => max 8 raw points => scale to 0–10.
    """
    if not financial_line_items or market_cap is None:
        return {"score": 0, "details": "Insufficient data to perform valuation"}

    details = []
    raw_score = 0

    # 필요한 데이터 수집
    net_incomes = [fi.net_income for fi in financial_line_items if fi.net_income is not None]
    fcf_values = [fi.free_cash_flow for fi in financial_line_items if fi.free_cash_flow is not None]
    ebit_values = [fi.ebit for fi in financial_line_items if fi.ebit is not None]
    ebitda_values = [fi.ebitda for fi in financial_line_items if fi.ebitda is not None]

    # EV 계산을 위한 최신 부채와 현금
    debt_values = [fi.total_debt for fi in financial_line_items if fi.total_debt is not None]
    cash_values = [fi.cash_and_equivalents for fi in financial_line_items if fi.cash_and_equivalents is not None]
    recent_debt = debt_values[0] if debt_values else 0
    recent_cash = cash_values[0] if cash_values else 0

    enterprise_value = market_cap + recent_debt - recent_cash

    # 1) P/E (주가수익비율)
    recent_net_income = net_incomes[0] if net_incomes else None
    if recent_net_income and recent_net_income > 0:
        pe = market_cap / recent_net_income
        pe_points = 0
        if pe < 15:  # 매력적인 P/E
            pe_points = 2
            details.append(f"Attractive P/E: {pe:.2f}")
        elif pe < 25:  # 적정한 P/E
            pe_points = 1
            details.append(f"Fair P/E: {pe:.2f}")
        else:  # 높은 P/E
            details.append(f"High or Very high P/E: {pe:.2f}")
        raw_score += pe_points
    
    # 2) P/FCF (주가현금흐름비율)
    # 3) EV/EBIT (기업가치/영업이익)
    # 4) EV/EBITDA (기업가치/EBITDA)
    # (위와 유사한 로직으로 각각 분석)
    
    # 점수 변환 (8점 만점에서 10점 만점으로)
    final_score = min(10, (raw_score / 8) * 10)
    return {"score": final_score, "details": "; ".join(details)}

이 분석은 네 가지 밸류에이션 지표를 확인합니다:

  1. P/E (주가수익비율): 15 미만이면 '매력적', 15-25면 '적정', 25 이상이면 '높음'으로 평가
  2. P/FCF (주가현금흐름비율): 15 미만이면 '매력적', 15-25면 '적정', 25 이상이면 '높음'으로 평가
  3. EV/EBIT (기업가치/영업이익): 15 미만이면 '매력적', 15-25면 '적정', 25 이상이면 '높음'으로 평가
  4. EV/EBITDA (기업가치/EBITDA): 10 미만이면 '매력적', 10-18이면 '적정', 18 이상이면 '높음'으로 평가

각 지표에서 최대 2점씩, 총 8점을 획득할 수 있고, 이를 10점 만점으로 변환합니다.

4. 감성 분석 (15%)

시장 심리와 뉴스 흐름은 드러켄밀러의 투자 결정에 중요한 요소입니다.

def analyze_sentiment(news_items: list) -> dict:
    """
    Basic news sentiment: negative keyword check vs. overall volume.
    """
    if not news_items:
        return {"score": 5, "details": "No news data; defaulting to neutral sentiment"}

    negative_keywords = ["lawsuit", "fraud", "negative", "downturn", "decline", "investigation", "recall"]
    negative_count = 0
    for news in news_items:
        title_lower = (news.title or "").lower()
        if any(word in title_lower for word in negative_keywords):
            negative_count += 1

    details = []
    if negative_count > len(news_items) * 0.3:
        # 30% 이상 부정적 → 3점 (약세)
        score = 3
        details.append(f"High proportion of negative headlines: {negative_count}/{len(news_items)}")
    elif negative_count > 0:
        # 일부 부정적 → 6점 (중립/약한 강세)
        score = 6
        details.append(f"Some negative headlines: {negative_count}/{len(news_items)}")
    else:
        # 대부분 긍정적 → 8점 (강세)
        score = 8
        details.append("Mostly positive/neutral headlines")

    return {"score": score, "details": "; ".join(details)}

이 함수는 뉴스 제목에서 부정적인 키워드를 찾아 부정적인 뉴스의 비율을 계산합니다:

  • 뉴스의 30% 이상이 부정적이면 약세 신호 (3점)
  • 일부 부정적 뉴스가 있으면 약한 강세 신호 (6점)
  • 대부분 긍정적이거나 중립적이면 강세 신호 (8점)

5. 내부자 거래 분석 (10%)

내부자들의 매수/매도 패턴은 회사의 미래에 대한 경영진의 견해를 반영합니다.

def analyze_insider_activity(insider_trades: list) -> dict:
    """
    Simple insider-trade analysis:
      - If there's heavy insider buying, we nudge the score up.
      - If there's mostly selling, we reduce it.
      - Otherwise, neutral.
    """
    # 기본값은 중립 (5/10)
    score = 5
    details = []

    if not insider_trades:
        details.append("No insider trades data; defaulting to neutral")
        return {"score": score, "details": "; ".join(details)}

    buys, sells = 0, 0
    for trade in insider_trades:
        # 거래 주식 수로 매수/매도 판단
        # 음수 = 매도, 양수 = 매수
        if trade.transaction_shares is not None:
            if trade.transaction_shares > 0:
                buys += 1
            elif trade.transaction_shares < 0:
                sells += 1

    total = buys + sells
    if total == 0:
        details.append("No buy/sell transactions found; neutral")
        return {"score": score, "details": "; ".join(details)}

    buy_ratio = buys / total
    if buy_ratio > 0.7:
        # 대규모 매수 → 8점
        score = 8
        details.append(f"Heavy insider buying: {buys} buys vs. {sells} sells")
    elif buy_ratio > 0.4:
        # 중간 규모 매수 → 6점
        score = 6
        details.append(f"Moderate insider buying: {buys} buys vs. {sells} sells")
    else:
        # 적은 매수 (대부분 매도) → 4점
        score = 4
        details.append(f"Mostly insider selling: {buys} buys vs. {sells} sells")

    return {"score": score, "details": "; ".join(details)}

이 함수는 내부자 거래를 매수와 매도로 분류하고 매수 비율을 계산합니다:

  • 매수 비율이 70% 이상이면 강한 긍정 신호 (8점)
  • 매수 비율이 40-70%면 중간 긍정 신호 (6점)
  • 매수 비율이 40% 미만이면 부정 신호 (4점)

종합 신호 생성 및 가중치 적용

모든 분석이 완료되면, 각 분석에 가중치를 적용하여 최종 점수를 계산합니다:

# Combine partial scores with weights typical for Druckenmiller:
#   35% Growth/Momentum, 20% Risk/Reward, 20% Valuation,
#   15% Sentiment, 10% Insider Activity = 100%
total_score = (
    growth_momentum_analysis["score"] * 0.35
    + risk_reward_analysis["score"] * 0.20
    + valuation_analysis["score"] * 0.20
    + sentiment_analysis["score"] * 0.15
    + insider_activity["score"] * 0.10
)

max_possible_score = 10

# Simple bullish/neutral/bearish signal
if total_score >= 7.5:
    signal = "bullish"
elif total_score <= 4.5:
    signal = "bearish"
else:
    signal = "neutral"

가중치는 드러켄밀러의 투자 철학을 반영합니다:

  • 성장 및 모멘텀: 35% (가장 중요)
  • 리스크-리워드: 20%
  • 밸류에이션: 20%
  • 감성 분석: 15%
  • 내부자 거래: 10%

최종 점수가 7.5 이상이면 "bullish", 4.5 이하면 "bearish", 그 사이면 "neutral" 신호를 생성합니다.

LLM 기반 최종 신호 생성

마지막으로, 모든 분석 데이터를 바탕으로 대형 언어 모델(LLM)이 스탠리 드러켄밀러 스타일의 최종 투자 신호를 생성합니다:

def generate_druckenmiller_output(
    ticker: str,
    analysis_data: dict[str, any],
    model_name: str,
    model_provider: str,
) -> StanleyDruckenmillerSignal:
    """
    Generates a JSON signal in the style of Stanley Druckenmiller.
    """
    template = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                """You are a Stanley Druckenmiller AI agent, making investment decisions using his principles:
            
            1. Seek asymmetric risk-reward opportunities (large upside, limited downside).
            2. Emphasize growth, momentum, and market sentiment.
            3. Preserve capital by avoiding major drawdowns.
            4. Willing to pay higher valuations for true growth leaders.
            5. Be aggressive when conviction is high.
            6. Cut losses quickly if the thesis changes.
            
            Rules:
            - Reward companies showing strong revenue/earnings growth and positive stock momentum.
            - Evaluate sentiment and insider activity as supportive or contradictory signals.
            - Watch out for high leverage or extreme volatility that threatens capital.
            - Output a JSON object with signal, confidence, and a reasoning string.
            """,
            ),
            (
                "human",
                """Based on the following analysis, create a Druckenmiller-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})
    return call_llm(
        prompt=prompt,
        model_name=model_name,
        model_provider=model_provider,
        pydantic_model=StanleyDruckenmillerSignal,
        agent_name="stanley_druckenmiller_agent",
        default_factory=create_default_signal,
    )

이 함수는 모든 분석 데이터를 LLM에 전달하고, 드러켄밀러 스타일의 투자 신호, 신뢰도, 그리고 근거를 포함한 응답을 생성합니다.

드러켄밀러 에이전트의 장단점

장점

  1. 성장과 모멘텀 중시: 빠르게 성장하는 기업과 강한 모멘텀을 가진 주식을 식별
  2. 비대칭적 리스크-리워드 추구: 위험 대비 높은 수익 가능성에 초점
  3. 종합적 분석: 재무적 요소와 비재무적 요소(뉴스, 내부자 거래)를 모두 고려
  4. 유연한 밸류에이션: 성장 기업에 대해 높은 밸류에이션을 수용하는 유연성

단점

  1. 모멘텀 의존: 강한 모멘텀은 때때로 갑자기 역전될 수 있음
  2. 주관적 요소: 모멘텀과 감성 평가에는 주관적 요소가 많음
  3. 시장 타이밍 의존: 거시 트렌드와 시장 타이밍에 의존하는 전략이 항상 적중하지는 않음
  4. 단순화된 분석: 실제 드러켄밀러의 분석보다 단순화된 접근 방식
profile
터널을 지나고 있을 뿐, 길은 여전히 열려 있다.

0개의 댓글

관련 채용 정보