CiscsoPonce commited on
Commit
084eeb2
·
1 Parent(s): 95aba55

Feat: Sprint 4 - Update with smarter versions

Browse files
Files changed (2) hide show
  1. src/agent.py +3 -3
  2. src/finance_tools.py +19 -16
src/agent.py CHANGED
@@ -91,9 +91,9 @@ async def analyze_stock(state: AgentState):
91
  HARD DATA INPUTS:
92
  - Sector: {sector}
93
  - Current Price: ${metrics.get('current_price')}
94
- - Graham Intrinsic Value: ${graham_val}
95
- - Margin of Safety (Calculated): {safety_margin}% (Target > 15%)
96
- - Debt/Equity: {metrics.get('debt_to_equity')}
97
 
98
  QUALITATIVE INPUTS (News & Search):
99
  {market_news}
 
91
  HARD DATA INPUTS:
92
  - Sector: {sector}
93
  - Current Price: ${metrics.get('current_price')}
94
+ - Graham Intrinsic Value: ${graham_val} (Conservative)
95
+ - PEG Ratio (Growth Value): {metrics.get('peg_ratio')} (Target < 2.0 for Tech)
96
+ - Debt/Equity: {metrics.get('debt_to_equity')} (Note: Low % is Good)
97
 
98
  QUALITATIVE INPUTS (News & Search):
99
  {market_news}
src/finance_tools.py CHANGED
@@ -45,31 +45,35 @@ def calculate_graham_number(info):
45
 
46
  def check_financial_health(ticker):
47
  """
48
- The 'Graham & Buffett' Gatekeeper.
49
  """
50
  try:
51
  stock = yf.Ticker(ticker)
52
- # Fast info is faster, but 'info' has the deep balance sheet data we need
53
  info = stock.info
54
 
55
  sector = info.get('sector', 'Default')
56
  config = SECTOR_CONFIG.get(sector, SECTOR_CONFIG['Default'])
57
 
58
- reasons = []
 
 
 
59
 
60
- # --- 1. GRAHAM'S SOLVENCY CHECK (The Safety Net) ---
61
- # Current Ratio > 1.0 (Can they pay short-term bills?)
 
 
 
 
62
  current_ratio = info.get('currentRatio')
63
  if current_ratio and current_ratio < 1.0:
64
  return {"status": "FAIL", "reason": f"Graham Reject: Liquidity Crisis (Current Ratio {current_ratio} < 1.0)"}
65
 
66
  # --- 2. SECTOR SPECIFIC DEBT CHECK ---
67
- # If it's NOT a bank, we check Debt/EBITDA
68
  if not config['exclude_ebitda']:
69
  ebitda = info.get('ebitda')
70
  debt = info.get('totalDebt')
71
  cash = info.get('totalCash')
72
-
73
  if ebitda and debt and ebitda > 0:
74
  net_debt_ebitda = (debt - cash) / ebitda
75
  if net_debt_ebitda > config['debt_max']:
@@ -79,27 +83,26 @@ def check_financial_health(ticker):
79
  intrinsic_val = calculate_graham_number(info)
80
  current_price = info.get('currentPrice', 0)
81
 
82
- margin_of_safety = 0
83
  if intrinsic_val > 0 and current_price > 0:
84
- margin_of_safety = (intrinsic_val - current_price) / intrinsic_val * 100
 
85
 
86
- # Pass specific metrics to the Agent for the final report
87
  metrics = {
88
  "sector": sector,
89
  "current_price": current_price,
90
  "intrinsic_value": round(intrinsic_val, 2),
91
- "margin_of_safety": round(margin_of_safety, 1),
92
- "debt_to_equity": info.get('debtToEquity', 'N/A'),
93
- "return_on_equity": info.get('returnOnEquity', 'N/A'),
94
- "free_cash_flow": info.get('freeCashflow', 'N/A')
95
  }
96
 
97
  return {
98
  "status": "PASS",
99
- "reason": f"Solvent. Sector: {sector}. Margin of Safety: {metrics['margin_of_safety']}%",
100
  "metrics": metrics
101
  }
102
 
103
  except Exception as e:
104
- # If data fails, we default to PASS but warn the agent
105
  return {"status": "PASS", "reason": f"Data Warning: {str(e)}", "metrics": {}}
 
45
 
46
  def check_financial_health(ticker):
47
  """
48
+ The 'Graham & Buffett' Gatekeeper (Fixed for Percentage Bugs).
49
  """
50
  try:
51
  stock = yf.Ticker(ticker)
 
52
  info = stock.info
53
 
54
  sector = info.get('sector', 'Default')
55
  config = SECTOR_CONFIG.get(sector, SECTOR_CONFIG['Default'])
56
 
57
+ # --- FIX 1: FORMAT DEBT CORRECTLY ---
58
+ # yfinance returns 15.5 for 15.5%. We convert to decimal for math, but string for LLM.
59
+ debt_equity_raw = info.get('debtToEquity', 0)
60
+ debt_equity_str = f"{debt_equity_raw}%" if debt_equity_raw else "N/A"
61
 
62
+ # --- FIX 2: ADD GROWTH VALUATION (PEG RATIO) ---
63
+ # For Tech, we look at PEG (Price/Earnings to Growth).
64
+ # < 1.0 is Undervalued, < 2.0 is Fair for Quality.
65
+ peg_ratio = info.get('pegRatio', 'N/A')
66
+
67
+ # --- 1. GRAHAM'S SOLVENCY CHECK ---
68
  current_ratio = info.get('currentRatio')
69
  if current_ratio and current_ratio < 1.0:
70
  return {"status": "FAIL", "reason": f"Graham Reject: Liquidity Crisis (Current Ratio {current_ratio} < 1.0)"}
71
 
72
  # --- 2. SECTOR SPECIFIC DEBT CHECK ---
 
73
  if not config['exclude_ebitda']:
74
  ebitda = info.get('ebitda')
75
  debt = info.get('totalDebt')
76
  cash = info.get('totalCash')
 
77
  if ebitda and debt and ebitda > 0:
78
  net_debt_ebitda = (debt - cash) / ebitda
79
  if net_debt_ebitda > config['debt_max']:
 
83
  intrinsic_val = calculate_graham_number(info)
84
  current_price = info.get('currentPrice', 0)
85
 
86
+ margin_of_safety = "N/A"
87
  if intrinsic_val > 0 and current_price > 0:
88
+ margin = (intrinsic_val - current_price) / intrinsic_val * 100
89
+ margin_of_safety = f"{round(margin, 1)}%"
90
 
 
91
  metrics = {
92
  "sector": sector,
93
  "current_price": current_price,
94
  "intrinsic_value": round(intrinsic_val, 2),
95
+ "margin_of_safety": margin_of_safety,
96
+ "debt_to_equity": debt_equity_str, # Now has '%' symbol
97
+ "peg_ratio": peg_ratio, # New Metric for Tech
98
+ "return_on_equity": f"{info.get('returnOnEquity', 0)*100:.2f}%" # Format as %
99
  }
100
 
101
  return {
102
  "status": "PASS",
103
+ "reason": f"Solvent. Sector: {sector}. Safety: {margin_of_safety}",
104
  "metrics": metrics
105
  }
106
 
107
  except Exception as e:
 
108
  return {"status": "PASS", "reason": f"Data Warning: {str(e)}", "metrics": {}}