financial_calculator.pyβ’11.9 kB
"""
Financial calculation utilities
TDD Green Phase: Implement minimum code to pass tests
"""
import math
from typing import Dict, Any, List, Optional, Union
class FinancialCalculator:
"""Utility class for financial calculations and ratio analysis"""
def __init__(self):
"""Initialize financial calculator"""
pass
def calculate_profitability_ratios(self, financial_data: Dict[str, Any]) -> Dict[str, float]:
"""Calculate profitability ratios"""
revenue = financial_data.get("revenue", 1)
operating_profit = financial_data.get("operating_profit", 0)
net_profit = financial_data.get("net_profit", 0)
total_assets = financial_data.get("total_assets", 1)
total_equity = financial_data.get("total_equity", total_assets * 0.75) # Default assumption
# Avoid division by zero
revenue = max(revenue, 1)
total_assets = max(total_assets, 1)
total_equity = max(total_equity, 1)
operating_margin = (operating_profit / revenue) * 100
net_margin = (net_profit / revenue) * 100
roe = (net_profit / total_equity) * 100
roa = (net_profit / total_assets) * 100
# ROIC calculation (simplified)
invested_capital = total_equity + financial_data.get("total_debt", total_assets * 0.25)
roic = (operating_profit / invested_capital) * 100 if invested_capital > 0 else 0
return {
"operating_margin": round(operating_margin, 2),
"net_margin": round(net_margin, 2),
"roe": round(roe, 2),
"roa": round(roa, 2),
"roic": round(roic, 2)
}
def calculate_liquidity_ratios(self, financial_data: Dict[str, Any]) -> Dict[str, float]:
"""Calculate liquidity ratios"""
current_assets = financial_data.get("current_assets", 0)
current_liabilities = financial_data.get("current_liabilities", 1)
inventory = financial_data.get("inventory", current_assets * 0.3) # Default assumption
cash = financial_data.get("cash", current_assets * 0.2) # Default assumption
# Avoid division by zero
current_liabilities = max(current_liabilities, 1)
current_ratio = current_assets / current_liabilities
quick_ratio = (current_assets - inventory) / current_liabilities
cash_ratio = cash / current_liabilities
return {
"current_ratio": round(current_ratio, 2),
"quick_ratio": round(quick_ratio, 2),
"cash_ratio": round(cash_ratio, 2)
}
def calculate_leverage_ratios(self, financial_data: Dict[str, Any]) -> Dict[str, float]:
"""Calculate leverage ratios"""
total_debt = financial_data.get("total_debt", 0)
total_equity = financial_data.get("total_equity", 1)
total_assets = financial_data.get("total_assets", 1)
operating_profit = financial_data.get("operating_profit", 0)
interest_expense = financial_data.get("interest_expense", total_debt * 0.03) # Assume 3% interest rate
# Avoid division by zero
total_equity = max(total_equity, 1)
total_assets = max(total_assets, 1)
interest_expense = max(interest_expense, 0.001) # Small positive value
debt_to_equity = total_debt / total_equity
debt_to_assets = total_debt / total_assets
equity_ratio = (total_equity / total_assets) * 100
interest_coverage = operating_profit / interest_expense if interest_expense > 0 else 999.99
return {
"debt_to_equity": round(debt_to_equity, 2),
"debt_to_assets": round(debt_to_assets, 2),
"equity_ratio": round(equity_ratio, 2),
"interest_coverage": round(interest_coverage, 2)
}
def calculate_market_ratios(self, financial_data: Dict[str, Any]) -> Dict[str, Union[float, int]]:
"""Calculate market valuation ratios"""
shares_outstanding = financial_data.get("shares_outstanding", 1)
stock_price = financial_data.get("stock_price", 0)
net_profit = financial_data.get("net_profit", 0)
total_equity = financial_data.get("total_equity", 1)
revenue = financial_data.get("revenue", 1)
total_debt = financial_data.get("total_debt", 0)
cash = financial_data.get("cash", 0)
# Avoid division by zero
shares_outstanding = max(shares_outstanding, 1)
market_cap = shares_outstanding * stock_price
# Calculate ratios
eps = net_profit / shares_outstanding
book_value_per_share = total_equity / shares_outstanding
sales_per_share = revenue / shares_outstanding
per = stock_price / eps if eps > 0 else 0
pbr = stock_price / book_value_per_share if book_value_per_share > 0 else 0
psr = stock_price / sales_per_share if sales_per_share > 0 else 0
enterprise_value = market_cap + total_debt - cash
return {
"per": round(per, 2),
"pbr": round(pbr, 2),
"psr": round(psr, 2),
"market_cap": int(market_cap),
"enterprise_value": int(enterprise_value)
}
def calculate_growth_rates(self, historical_data: List[Dict[str, Any]], metric: str) -> Dict[str, Any]:
"""Calculate growth rates for a specific metric"""
if len(historical_data) < 2:
return {"yoy_growth": [], "cagr": 0, "average_growth": 0}
# Sort by year
sorted_data = sorted(historical_data, key=lambda x: x.get("year", 0))
yoy_growth = []
values = []
for i in range(1, len(sorted_data)):
prev_value = sorted_data[i-1].get(metric, 0)
curr_value = sorted_data[i].get(metric, 0)
if prev_value > 0:
growth_rate = ((curr_value - prev_value) / prev_value) * 100
yoy_growth.append(round(growth_rate, 2))
values.append(curr_value)
# Calculate CAGR
if len(values) >= 2 and values[0] > 0:
start_value = values[0]
end_value = values[-1]
years = len(values) - 1
cagr = self.calculate_cagr(start_value, end_value, years)
else:
cagr = 0
# Calculate average growth
average_growth = sum(yoy_growth) / len(yoy_growth) if yoy_growth else 0
return {
"yoy_growth": yoy_growth,
"cagr": round(cagr, 2),
"average_growth": round(average_growth, 2)
}
def calculate_cagr(self, start_value: float, end_value: float, years: int) -> float:
"""Calculate Compound Annual Growth Rate"""
if start_value <= 0 or end_value <= 0 or years <= 0:
return 0.0
cagr = ((end_value / start_value) ** (1 / years) - 1) * 100
return round(cagr, 2)
def calculate_percentile_ranking(self, company_value: float, industry_values: List[float]) -> float:
"""Calculate percentile ranking of company value within industry"""
if not industry_values:
return 50.0 # Default to median if no data
# Include company value in the list for proper ranking
all_values = industry_values + [company_value]
sorted_values = sorted(all_values)
# Find position of company value
company_position = sorted_values.index(company_value)
# Calculate percentile (position / (total - 1) * 100)
percentile = (company_position / (len(sorted_values) - 1)) * 100
return round(percentile, 1)
def calculate_statistical_measures(self, values: List[float]) -> Dict[str, float]:
"""Calculate basic statistical measures"""
if not values:
return {"mean": 0, "median": 0, "std_dev": 0, "min": 0, "max": 0}
sorted_values = sorted(values)
n = len(values)
# Mean
mean = sum(values) / n
# Median
if n % 2 == 0:
median = (sorted_values[n//2 - 1] + sorted_values[n//2]) / 2
else:
median = sorted_values[n//2]
# Standard deviation
variance = sum((x - mean) ** 2 for x in values) / n
std_dev = math.sqrt(variance)
return {
"mean": round(mean, 2),
"median": round(median, 2),
"std_dev": round(std_dev, 2),
"min": round(min(values), 2),
"max": round(max(values), 2)
}
def calculate_financial_score(self, ratios: Dict[str, Dict[str, float]]) -> Dict[str, Any]:
"""Calculate overall financial health score"""
# Scoring weights
weights = {
"profitability": 0.3,
"liquidity": 0.2,
"leverage": 0.25,
"efficiency": 0.15,
"market": 0.1
}
category_scores = {}
# Score each category (simplified scoring)
for category, metrics in ratios.items():
if category == "profitability":
score = min(100, max(0, (metrics.get("roe", 0) + metrics.get("roa", 0)) * 2))
elif category == "liquidity":
score = min(100, max(0, metrics.get("current_ratio", 0) * 30))
elif category == "leverage":
debt_ratio = metrics.get("debt_to_assets", 0)
score = max(0, 100 - debt_ratio * 2) # Lower debt is better
elif category == "efficiency":
score = min(100, max(0, metrics.get("asset_turnover", 0) * 100))
else: # market
score = 75 # Default market score
category_scores[category] = min(100, max(0, score))
# Calculate weighted overall score
overall_score = sum(
category_scores.get(category, 0) * weight
for category, weight in weights.items()
)
# Determine rating
if overall_score >= 80:
rating = "A"
elif overall_score >= 70:
rating = "B"
elif overall_score >= 60:
rating = "C"
elif overall_score >= 50:
rating = "D"
else:
rating = "F"
return {
"overall_score": round(overall_score, 1),
"rating": rating,
"category_scores": category_scores,
"recommendations": self._generate_recommendations(category_scores)
}
def _generate_recommendations(self, category_scores: Dict[str, float]) -> List[str]:
"""Generate recommendations based on category scores"""
recommendations = []
for category, score in category_scores.items():
if score < 50:
if category == "profitability":
recommendations.append("Focus on improving operational efficiency and cost management")
elif category == "liquidity":
recommendations.append("Consider improving cash management and working capital")
elif category == "leverage":
recommendations.append("Consider reducing debt levels to improve financial stability")
elif category == "efficiency":
recommendations.append("Focus on improving asset utilization and operational efficiency")
if not recommendations:
recommendations.append("Maintain strong financial performance")
return recommendations