벤 그레이엄, 빌 액크만, 펀더멘털 에이전트 등의 분석 결과를 종합하여 실제 매매 결정을 내리는 이 에이전트는 AI 헤지펀드 시스템의 최종 실행 단계를 담당합니다.
포트폴리오 관리 에이전트는 다음과 같은 중요한 역할을 수행합니다:
이 에이전트는 실질적으로 헤지펀드 매니저의 역할을 수행하며, 다양한 정보를 종합하여 최적의 투자 결정을 내립니다.
먼저 이 에이전트가 사용하는 데이터 모델부터 살펴보겠습니다:
class PortfolioDecision(BaseModel):
action: Literal["buy", "sell", "short", "cover", "hold"]
quantity: int = Field(description="Number of shares to trade")
confidence: float = Field(description="Confidence in the decision, between 0.0 and 100.0")
reasoning: str = Field(description="Reasoning for the decision")
class PortfolioManagerOutput(BaseModel):
decisions: dict[str, PortfolioDecision] = Field(description="Dictionary of ticker to trading decisions")
PortfolioDecision
: 각 종목별 매매 결정(액션, 수량, 신뢰도, 근거)을 담는 모델PortfolioManagerOutput
: 전체 포트폴리오 결정을 담는 모델(모든 종목의 결정을 포함)포트폴리오 관리 에이전트는 다음과 같은 단계로 작동합니다:
# Get the portfolio and analyst signals
portfolio = state["data"]["portfolio"]
analyst_signals = state["data"]["analyst_signals"]
tickers = state["data"]["tickers"]
# Get position limits, current prices, and signals for every ticker
position_limits = {}
current_prices = {}
max_shares = {}
signals_by_ticker = {}
for ticker in tickers:
# Get position limits and current prices for the ticker
risk_data = analyst_signals.get("risk_management_agent", {}).get(ticker, {})
position_limits[ticker] = risk_data.get("remaining_position_limit", 0)
current_prices[ticker] = risk_data.get("current_price", 0)
# Calculate maximum shares allowed based on position limit and price
if current_prices[ticker] > 0:
max_shares[ticker] = int(position_limits[ticker] / current_prices[ticker])
else:
max_shares[ticker] = 0
# Get signals for the ticker
ticker_signals = {}
for agent, signals in analyst_signals.items():
if agent != "risk_management_agent" and ticker in signals:
ticker_signals[agent] = {"signal": signals[ticker]["signal"], "confidence": signals[ticker]["confidence"]}
signals_by_ticker[ticker] = ticker_signals
result = generate_trading_decision(
tickers=tickers,
signals_by_ticker=signals_by_ticker,
current_prices=current_prices,
max_shares=max_shares,
portfolio=portfolio,
model_name=state["metadata"]["model_name"],
model_provider=state["metadata"]["model_provider"],
)
message = HumanMessage(
content=json.dumps({ticker: decision.model_dump() for ticker, decision in result.decisions.items()}),
name="portfolio_management",
)
# 필요시 결정 내용 표시
if state["metadata"]["show_reasoning"]:
show_agent_reasoning({ticker: decision.model_dump() for ticker, decision in result.decisions.items()}, "Portfolio Management Agent")
가장 중요한 부분은 실제 매매 결정을 생성하는 generate_trading_decision
함수입니다:
def generate_trading_decision(
tickers: list[str],
signals_by_ticker: dict[str, dict],
current_prices: dict[str, float],
max_shares: dict[str, int],
portfolio: dict[str, float],
model_name: str,
model_provider: str,
) -> PortfolioManagerOutput:
"""Attempts to get a decision from the LLM with retry logic"""
# 프롬프트 템플릿 생성
template = ChatPromptTemplate.from_messages([...])
# 프롬프트 생성
prompt = template.invoke({...})
# LLM 호출 및 결과 반환
return call_llm(
prompt=prompt,
model_name=model_name,
model_provider=model_provider,
pydantic_model=PortfolioManagerOutput,
agent_name="portfolio_management_agent",
default_factory=create_default_portfolio_output
)
이 함수는 LLM(대형 언어 모델)을 사용하여 다양한 신호와 제약 조건을 고려한 최종 매매 결정을 생성합니다.
포트폴리오 관리 에이전트가 사용하는 프롬프트는 매우 상세하며 중요한 트레이딩 규칙을 포함하고 있습니다:
Trading Rules:
- For long positions:
* Only buy if you have available cash
* Only sell if you currently hold long shares of that ticker
* Sell quantity must be ≤ current long position shares
* Buy quantity must be ≤ max_shares for that ticker
- For short positions:
* Only short if you have available margin (50% of position value required)
* Only cover if you currently have short shares of that ticker
* Cover quantity must be ≤ current short position shares
* Short quantity must respect margin requirements
- The max_shares values are pre-calculated to respect position limits
- Consider both long and short opportunities based on signals
- Maintain appropriate risk management with both long and short exposure
Available Actions:
- "buy": Open or add to long position
- "sell": Close or reduce long position
- "short": Open or add to short position
- "cover": Close or reduce short position
- "hold": No action
이 규칙들은 실제 트레이딩 환경에서 반드시 지켜야 할 제약 조건들을 반영하고 있습니다:
롱 포지션(매수) 규칙:
숏 포지션(공매도) 규칙:
가능한 액션:
실제 매매 결정이 어떻게 이루어지는지 단계별로 살펴보겠습니다:
입력 데이터 수집:
종목별 제약 조건 계산:
LLM 기반 매매 결정:
결과 통합 및 반환:
실제 매매 결정은 다음과 같은 형태로 생성됩니다:
{
"decisions": {
"AAPL": {
"action": "buy",
"quantity": 10,
"confidence": 85.5,
"reasoning": "Strong bullish signals from Warren Buffett and fundamental analysis agents, with high ROE and reasonable P/E ratio."
},
"MSFT": {
"action": "hold",
"quantity": 0,
"confidence": 65.0,
"reasoning": "Mixed signals with slightly bullish bias, but current position already at optimal level."
},
"NVDA": {
"action": "sell",
"quantity": 5,
"confidence": 70.0,
"reasoning": "Bearish signals from valuation agent indicating overvaluation. Reducing position while maintaining some exposure."
}
}
}
각 종목에 대해: