File size: 7,127 Bytes
c90e00d
 
 
 
 
76b5330
c90e00d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76b5330
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
170
171
172
173
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser
from typing import Dict, List
from .state import AgentState, AgentState2

def create_portfolio_fit_evaluator():
    """Create a portfolio fit evaluator using LangChain."""
    # Define the function schema for structured output
    function_def = {
        "name": "evaluate_portfolio_fit",
        "description": "Evaluate how new stocks fit into the existing portfolio",
        "parameters": {
            "type": "object",
            "properties": {
                "portfolio_fit_summary": {
                    "type": "string",
                    "description": "Summary of how the new stocks fit into the existing portfolio"
                },
                "evaluated_stocks": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "ticker": {
                                "type": "string",
                                "description": "Stock ticker symbol"
                            },
                            "portfolio_fit_score": {
                                "type": "integer",
                                "description": "Score from 1-10 indicating how well the stock fits in the portfolio (10 being best)",
                                "minimum": 1,
                                "maximum": 10
                            },
                            "diversification_impact": {
                                "type": "string",
                                "description": "How this stock would impact portfolio diversification"
                            },
                            "risk_impact": {
                                "type": "string",
                                "description": "How this stock would impact portfolio risk"
                            },
                            "sector_balance": {
                                "type": "string",
                                "description": "How this stock would affect sector balance in the portfolio"
                            },
                            "recommendation": {
                                "type": "string",
                                "enum": ["STRONG_FIT", "MODERATE_FIT", "POOR_FIT"],
                                "description": "Overall recommendation for portfolio fit"
                            }
                        },
                        "required": ["ticker", "portfolio_fit_score", "diversification_impact", "risk_impact", "sector_balance", "recommendation"]
                    }
                }
            },
            "required": ["portfolio_fit_summary", "evaluated_stocks"]
        }
    }
    
    # Create the prompt template
    prompt = ChatPromptTemplate.from_messages([
        ("system", """You are an expert portfolio manager specializing in evaluating how new investments fit into existing portfolios.
        
Your task is to analyze new high-ranked stocks and evaluate how well they would fit into the user's existing portfolio.

Consider the following when evaluating portfolio fit:
1. Diversification across sectors, industries, and asset classes
2. Risk profile and how it aligns with the user's risk tolerance
3. Correlation with existing holdings
4. Impact on overall portfolio performance
5. Balance between growth and value investments
6. Geographic diversification if applicable

Provide a detailed evaluation of each stock's fit within the portfolio context.
"""),
        ("human", """
I need to evaluate how these new high-ranked stocks would fit into my existing portfolio:

User's Portfolio:
{portfolio_data}

User's Risk Tolerance: {risk_level}
User's Investment Goals: {investment_goals}

Technical Analysis of Existing Portfolio:
{technical_analysis}

New High-Ranked Stocks Analysis:
{new_stock_analysis}

Please evaluate how each new stock would fit into my existing portfolio.
""")
    ])
    
    # Create the LLM
    llm = ChatOpenAI(model="gpt-4-turbo", temperature=0.2, api_key=os.getenv("OPENAI_API_KEY"))
    
    # Create the structured output chain
    chain = prompt | llm.bind_functions(functions=[function_def], function_call={"name": "evaluate_portfolio_fit"}) | JsonOutputFunctionsParser()
    
    return chain

def portfolio_fit_evaluator(state: AgentState2) -> AgentState2:
    """Evaluates how new high-ranked stocks fit into the existing portfolio context."""
    try:
        # Get the portfolio data
        portfolio = state["portfolio_data"]
        
        # Get the technical analysis of the existing portfolio
        technical_analysis = state.get("technical_analysis", {})
        
        # Get the analysis of new stocks
        new_stock_analysis = state.get("new_stock_analysis", {})
        
        # If no new stocks to evaluate, return early
        if not new_stock_analysis:
            state["messages"] = state.get("messages", []) + [{
                "role": "ai",
                "content": "[PortfolioFitEvaluator] No new stocks to evaluate for portfolio fit."
            }]
            state["portfolio_fit"] = {
                "portfolio_fit_summary": "No new stocks to evaluate.",
                "evaluated_stocks": []
            }
            return state
        
        # Get user preferences
        risk_level = state.get("risk_level", 5)
        investment_goals = state.get("investment_goals", "Growth")
        
        # Create the evaluator
        evaluator = create_portfolio_fit_evaluator()
        
        # Generate evaluation
        result = evaluator.invoke({
            "portfolio_data": portfolio,
            "risk_level": risk_level,
            "investment_goals": investment_goals,
            "technical_analysis": technical_analysis,
            "new_stock_analysis": new_stock_analysis
        })
        
        # Ensure we have the required fields
        if "portfolio_fit_summary" not in result:
            result["portfolio_fit_summary"] = f"Evaluation of {len(new_stock_analysis)} new stocks for portfolio fit."
        
        if "evaluated_stocks" not in result:
            result["evaluated_stocks"] = []
            
    except Exception as e:
        # Handle any errors in the evaluation
        state["messages"] = state.get("messages", []) + [{
            "role": "ai",
            "content": f"[PortfolioFitEvaluator] Error evaluating portfolio fit: {str(e)}"
        }]
        
        result = {
            "portfolio_fit_summary": "Error in evaluation",
            "evaluated_stocks": []
        }
    
    # Update state with portfolio fit evaluation
    state["portfolio_fit"] = result
    
    # Add message to communication
    state["messages"] = state.get("messages", []) + [{
        "role": "ai",
        "content": f"[PortfolioFitEvaluator] I've evaluated how {len(result.get('evaluated_stocks', []))} new stocks would fit into your existing portfolio."
    }]
    
    return state