File size: 6,129 Bytes
c90e00d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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