Spaces:
Sleeping
Sleeping
File size: 10,318 Bytes
ce180e5 |
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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
"""
CFA AI Agent - Finance Calculation Tools
This module contains various financial calculation functions for CFA analysis.
"""
import numpy as np
import pandas as pd
from typing import List, Dict, Union, Optional
import yfinance as yf
from langchain.tools import tool
@tool
def calculate_dcf(
cash_flows: List[float],
terminal_value: float,
discount_rate: float
) -> Dict[str, float]:
"""
Calculate Discounted Cash Flow (DCF) valuation.
Args:
cash_flows: List of projected free cash flows
terminal_value: Terminal value at end of projection period
discount_rate: Weighted average cost of capital (WACC) as decimal
Returns:
Dictionary with NPV, terminal value present value, and total enterprise value
"""
try:
if not cash_flows:
raise ValueError("Cash flows list cannot be empty")
if discount_rate <= 0:
raise ValueError("Discount rate must be positive")
# Calculate present value of cash flows
pv_cash_flows = []
for i, cf in enumerate(cash_flows, 1):
pv = cf / ((1 + discount_rate) ** i)
pv_cash_flows.append(pv)
# Calculate present value of terminal value
years = len(cash_flows)
pv_terminal = terminal_value / ((1 + discount_rate) ** years)
# Total enterprise value
enterprise_value = sum(pv_cash_flows) + pv_terminal
return {
"pv_cash_flows": sum(pv_cash_flows),
"pv_terminal_value": pv_terminal,
"enterprise_value": enterprise_value,
"cash_flow_details": pv_cash_flows
}
except Exception as e:
return {"error": f"DCF calculation failed: {str(e)}"}
@tool
def calculate_sharpe_ratio(
returns: List[float],
risk_free_rate: float
) -> Dict[str, float]:
"""
Calculate Sharpe Ratio for risk-adjusted returns.
Args:
returns: List of periodic returns (as decimals)
risk_free_rate: Risk-free rate (as decimal)
Returns:
Dictionary with Sharpe ratio, average return, and standard deviation
"""
try:
if not returns:
raise ValueError("Returns list cannot be empty")
if len(returns) < 2:
raise ValueError("Need at least 2 return observations")
returns_array = np.array(returns)
# Calculate metrics
avg_return = np.mean(returns_array)
std_dev = np.std(returns_array, ddof=1) # Sample standard deviation
excess_return = avg_return - risk_free_rate
if std_dev == 0:
raise ValueError("Standard deviation cannot be zero")
sharpe_ratio = excess_return / std_dev
return {
"sharpe_ratio": sharpe_ratio,
"average_return": avg_return,
"standard_deviation": std_dev,
"excess_return": excess_return,
"risk_free_rate": risk_free_rate
}
except Exception as e:
return {"error": f"Sharpe ratio calculation failed: {str(e)}"}
@tool
def compare_pe_ratios(ticker1: str, ticker2: str) -> Dict[str, Union[float, str]]:
"""
Compare P/E ratios of two stocks using real-time data.
Args:
ticker1: First stock ticker symbol
ticker2: Second stock ticker symbol
Returns:
Dictionary with P/E ratios and comparison analysis
"""
try:
# Fetch stock data
stock1 = yf.Ticker(ticker1.upper())
stock2 = yf.Ticker(ticker2.upper())
# Get info
info1 = stock1.info
info2 = stock2.info
# Extract P/E ratios
pe1 = info1.get('trailingPE') or info1.get('forwardPE')
pe2 = info2.get('trailingPE') or info2.get('forwardPE')
if pe1 is None or pe2 is None:
return {"error": f"Could not retrieve P/E ratios for {ticker1} or {ticker2}"}
# Calculate comparison metrics
pe_difference = pe1 - pe2
pe_ratio = pe1 / pe2 if pe2 != 0 else None
# Determine which is more expensive
comparison = "equal"
if pe1 > pe2:
comparison = f"{ticker1} is more expensive"
elif pe1 < pe2:
comparison = f"{ticker2} is more expensive"
return {
f"{ticker1}_pe": pe1,
f"{ticker2}_pe": pe2,
"pe_difference": pe_difference,
"pe_ratio": pe_ratio,
"comparison": comparison,
f"{ticker1}_name": info1.get('longName', ticker1),
f"{ticker2}_name": info2.get('longName', ticker2)
}
except Exception as e:
return {"error": f"P/E comparison failed: {str(e)}"}
@tool
def calculate_beta(ticker: str, market_ticker: str = "^GSPC", period: str = "2y") -> Dict[str, float]:
"""
Calculate beta coefficient for a stock relative to market.
Args:
ticker: Stock ticker symbol
market_ticker: Market index ticker (default S&P 500)
period: Time period for calculation
Returns:
Dictionary with beta, correlation, and other metrics
"""
try:
# Fetch data
stock = yf.Ticker(ticker.upper())
market = yf.Ticker(market_ticker)
# Get historical data
stock_data = stock.history(period=period)
market_data = market.history(period=period)
if stock_data.empty or market_data.empty:
raise ValueError("Could not fetch historical data")
# Calculate returns
stock_returns = stock_data['Close'].pct_change().dropna()
market_returns = market_data['Close'].pct_change().dropna()
# Align data
aligned_data = pd.concat([stock_returns, market_returns], axis=1, join='inner')
aligned_data.columns = ['stock', 'market']
aligned_data = aligned_data.dropna()
if len(aligned_data) < 20:
raise ValueError("Insufficient data points for beta calculation")
# Calculate beta
covariance = np.cov(aligned_data['stock'], aligned_data['market'])[0][1]
market_variance = np.var(aligned_data['market'], ddof=1)
beta = covariance / market_variance
# Calculate correlation
correlation = np.corrcoef(aligned_data['stock'], aligned_data['market'])[0][1]
return {
"beta": beta,
"correlation": correlation,
"stock_volatility": np.std(aligned_data['stock'], ddof=1),
"market_volatility": np.std(aligned_data['market'], ddof=1),
"data_points": len(aligned_data),
"period": period
}
except Exception as e:
return {"error": f"Beta calculation failed: {str(e)}"}
@tool
def calculate_wacc(
cost_of_equity: float,
cost_of_debt: float,
tax_rate: float,
market_value_equity: float,
market_value_debt: float
) -> Dict[str, float]:
"""
Calculate Weighted Average Cost of Capital (WACC).
Args:
cost_of_equity: Cost of equity as decimal
cost_of_debt: Cost of debt as decimal
tax_rate: Corporate tax rate as decimal
market_value_equity: Market value of equity
market_value_debt: Market value of debt
Returns:
Dictionary with WACC and component calculations
"""
try:
total_value = market_value_equity + market_value_debt
if total_value <= 0:
raise ValueError("Total market value must be positive")
# Calculate weights
weight_equity = market_value_equity / total_value
weight_debt = market_value_debt / total_value
# Calculate after-tax cost of debt
after_tax_cost_debt = cost_of_debt * (1 - tax_rate)
# Calculate WACC
wacc = (weight_equity * cost_of_equity) + (weight_debt * after_tax_cost_debt)
return {
"wacc": wacc,
"weight_equity": weight_equity,
"weight_debt": weight_debt,
"after_tax_cost_debt": after_tax_cost_debt,
"cost_of_equity": cost_of_equity,
"cost_of_debt": cost_of_debt,
"tax_rate": tax_rate
}
except Exception as e:
return {"error": f"WACC calculation failed: {str(e)}"}
@tool
def financial_ratios_analysis(ticker: str) -> Dict[str, Union[float, str]]:
"""
Perform comprehensive financial ratios analysis for a stock.
Args:
ticker: Stock ticker symbol
Returns:
Dictionary with various financial ratios and metrics
"""
try:
stock = yf.Ticker(ticker.upper())
info = stock.info
# Extract key metrics
ratios = {
"ticker": ticker.upper(),
"company_name": info.get('longName', 'N/A'),
# Valuation ratios
"pe_ratio": info.get('trailingPE'),
"forward_pe": info.get('forwardPE'),
"price_to_book": info.get('priceToBook'),
"price_to_sales": info.get('priceToSalesTrailing12Months'),
"peg_ratio": info.get('pegRatio'),
# Profitability ratios
"profit_margin": info.get('profitMargins'),
"operating_margin": info.get('operatingMargins'),
"roe": info.get('returnOnEquity'),
"roa": info.get('returnOnAssets'),
# Financial health
"current_ratio": info.get('currentRatio'),
"quick_ratio": info.get('quickRatio'),
"debt_to_equity": info.get('debtToEquity'),
"total_debt": info.get('totalDebt'),
"total_cash": info.get('totalCash'),
# Market data
"market_cap": info.get('marketCap'),
"enterprise_value": info.get('enterpriseValue'),
"beta": info.get('beta'),
"52_week_high": info.get('fiftyTwoWeekHigh'),
"52_week_low": info.get('fiftyTwoWeekLow'),
# Dividend info
"dividend_yield": info.get('dividendYield'),
"payout_ratio": info.get('payoutRatio'),
# Growth metrics
"earnings_growth": info.get('earningsGrowth'),
"revenue_growth": info.get('revenueGrowth')
}
# Remove None values
ratios = {k: v for k, v in ratios.items() if v is not None}
return ratios
except Exception as e:
return {"error": f"Financial ratios analysis failed: {str(e)}"} |