cardinal1 / agents /portfolio_analyzer.py
don-unagi's picture
add app
c90e00d
import yfinance as yf
import numpy as np
from typing import Dict, Any
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser
from .state import AgentState
import os
from dotenv import load_dotenv
load_dotenv()
def get_portfolio_data(portfolio):
"""Fetch historical data for portfolio assets."""
tickers = list(portfolio.keys())
# Get historical data for portfolio assets
historical_data = {}
for ticker in tickers:
try:
ticker_obj = yf.Ticker(ticker)
data = ticker_obj.history(period="1y")
historical_data[ticker] = data
except Exception as e:
print(f"Error fetching data for {ticker}: {e}")
return historical_data
def calculate_portfolio_metrics(portfolio, historical_data):
"""Calculate portfolio metrics."""
# Calculate portfolio metrics
total_value = sum(portfolio[ticker]["value"] for ticker in portfolio)
allocations = {ticker: portfolio[ticker]["value"] / total_value for ticker in portfolio}
# Calculate volatility and returns
returns = {}
volatility = {}
for ticker in historical_data:
if not historical_data[ticker].empty:
price_data = historical_data[ticker]['Close']
daily_returns = price_data.pct_change().dropna()
returns[ticker] = daily_returns.mean() * 252 # Annualized return
volatility[ticker] = daily_returns.std() * np.sqrt(252) # Annualized volatility
return {
"total_value": total_value,
"allocations": allocations,
"returns": returns,
"volatility": volatility
}
def create_portfolio_analyzer_chain():
"""Create a chain for portfolio analysis using LLM."""
# Define prompt template
prompt = ChatPromptTemplate.from_template("""
You are a portfolio analysis expert. Analyze the following portfolio information and provide insights.
Portfolio Data:
{portfolio_data}
Portfolio Metrics:
{portfolio_metrics}
Fundamental Data:
{fundamental_data}
Risk Level: {risk_level}
Investment Goals: {investment_goals}
Provide a comprehensive analysis of the portfolio including:
1. Overall portfolio composition and diversification
2. Risk assessment based on volatility and beta
3. Valuation assessment based on fundamental metrics
4. Alignment with the user's risk level and investment goals
5. Areas of concern or potential improvement
Your analysis should be detailed, insightful, and actionable.
""")
openai_api_key = os.getenv("OPENAI_API_KEY")
# Create LLM
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0.2, api_key=openai_api_key)
# Create chain
chain = prompt | llm | StrOutputParser()
return chain
# Initialize portfolio analyzer chain
portfolio_analyzer_chain = create_portfolio_analyzer_chain()
def portfolio_analyzer(state: AgentState) -> AgentState:
"""Analyzes the current portfolio composition using LLM."""
portfolio = state["portfolio_data"]
risk_level = state["risk_level"]
investment_goals = state["investment_goals"]
# Get portfolio data
historical_data = get_portfolio_data(portfolio)
# Calculate portfolio metrics
portfolio_metrics = calculate_portfolio_metrics(portfolio, historical_data)
# Format data for LLM
portfolio_data_str = ""
for ticker, data in portfolio.items():
portfolio_data_str += f"{ticker}: {data['shares']} shares at ${data['purchase_price']:.2f}, "
portfolio_data_str += f"current value: ${data['value']:.2f}, "
portfolio_data_str += f"gain/loss: {data['gain_loss_pct']:.2f}%\n"
# Get fundamental data from technical analysis if available
fundamental_data = {}
if "technical_analysis" in state:
for ticker, analysis in state["technical_analysis"].items():
if "fundamental_data" in analysis:
fundamental_data[ticker] = analysis["fundamental_data"]
# Format fundamental data for LLM
fundamental_data_str = ""
for ticker, data in fundamental_data.items():
fundamental_data_str += f"{ticker}:\n"
for key, value in data.items():
if value is not None:
fundamental_data_str += f" {key}: {value}\n"
fundamental_data_str += "\n"
# Format portfolio metrics for LLM
portfolio_metrics_str = ""
portfolio_metrics_str += f"Total Value: ${portfolio_metrics['total_value']:.2f}\n\n"
portfolio_metrics_str += "Allocations:\n"
for ticker, allocation in portfolio_metrics['allocations'].items():
portfolio_metrics_str += f" {ticker}: {allocation*100:.2f}%\n"
portfolio_metrics_str += "\nAnnualized Returns:\n"
for ticker, ret in portfolio_metrics['returns'].items():
portfolio_metrics_str += f" {ticker}: {ret*100:.2f}%\n"
portfolio_metrics_str += "\nAnnualized Volatility:\n"
for ticker, vol in portfolio_metrics['volatility'].items():
portfolio_metrics_str += f" {ticker}: {vol*100:.2f}%\n"
# Get LLM analysis
analysis = portfolio_analyzer_chain.invoke({
"portfolio_data": portfolio_data_str,
"portfolio_metrics": portfolio_metrics_str,
"fundamental_data": fundamental_data_str,
"risk_level": risk_level,
"investment_goals": investment_goals
})
# Update state
state["portfolio_analysis"] = {
"total_value": portfolio_metrics["total_value"],
"allocations": portfolio_metrics["allocations"],
"returns": portfolio_metrics["returns"],
"volatility": portfolio_metrics["volatility"],
"fundamental_data": fundamental_data,
"llm_analysis": analysis
}
# Add message to communication
state["messages"] = state.get("messages", []) + [{
"role": "ai",
"content": f"[PortfolioAnalyzer] I've analyzed your portfolio. Here are my findings:\n\n{analysis}"
}]
return state