Skip to main content
Glama

MCP Stock Details Server

by whdghk1907
esg_tools.pyβ€’30.3 kB
""" ESG (Environmental, Social, Governance) analysis tools TDD Green Phase: Implement minimum code to pass tests """ import logging import asyncio import statistics from typing import Dict, Any, List, Optional, Union from datetime import datetime, date, timedelta import math from ..collectors.dart_collector import DARTCollector from ..exceptions import MCPStockDetailsError, InsufficientDataError from ..config import get_settings from ..utils.financial_calculator import FinancialCalculator class ESGAnalyzer: """Advanced ESG analysis with comprehensive metrics""" def __init__(self): """Initialize ESG analyzer""" self.settings = get_settings() self.logger = logging.getLogger("mcp_stock_details.esg_analyzer") self.dart_collector = None self.financial_calculator = FinancialCalculator() async def _get_dart_collector(self) -> DARTCollector: """Get or create DART collector instance""" if self.dart_collector is None: api_key = self.settings.dart_api_key or "test_api_key" self.dart_collector = DARTCollector(api_key=api_key) return self.dart_collector async def calculate_environmental_score( self, company_data: Dict[str, Any], environmental_data: Dict[str, Any] ) -> Dict[str, Any]: """Calculate environmental score based on various metrics""" # Extract environmental metrics carbon_emissions = environmental_data.get("carbon_emissions", 0) energy_consumption = environmental_data.get("energy_consumption", 0) renewable_energy = environmental_data.get("renewable_energy", 0) water_usage = environmental_data.get("water_usage", 0) waste_generated = environmental_data.get("waste_generated", 0) waste_recycled = environmental_data.get("waste_recycled", 0) revenue = company_data.get("revenue", 1) # Calculate environmental metrics carbon_intensity = (carbon_emissions / revenue) * 1_000_000_000 if revenue > 0 else 0 renewable_ratio = (renewable_energy / energy_consumption) * 100 if energy_consumption > 0 else 0 recycling_rate = (waste_recycled / waste_generated) * 100 if waste_generated > 0 else 0 # Calculate base scores (0-100) carbon_score = max(0, min(100, 100 - (carbon_intensity / 50))) # Lower is better renewable_score = min(100, renewable_ratio) # Higher is better recycling_score = min(100, recycling_rate) # Higher is better water_efficiency_score = 75 # Mock score based on industry standards # Weighted average environmental_score = ( carbon_score * 0.35 + renewable_score * 0.25 + recycling_score * 0.25 + water_efficiency_score * 0.15 ) # Determine grade if environmental_score >= 90: grade = "A+" elif environmental_score >= 85: grade = "A" elif environmental_score >= 80: grade = "A-" elif environmental_score >= 75: grade = "B+" elif environmental_score >= 70: grade = "B" elif environmental_score >= 65: grade = "B-" elif environmental_score >= 60: grade = "C+" elif environmental_score >= 55: grade = "C" elif environmental_score >= 50: grade = "C-" elif environmental_score >= 40: grade = "D" else: grade = "F" return { "score": round(environmental_score, 1), "grade": grade, "carbon_intensity": round(carbon_intensity, 2), "renewable_ratio": round(renewable_ratio, 1), "recycling_rate": round(recycling_rate, 1), "water_efficiency": water_efficiency_score, "components": { "carbon_score": round(carbon_score, 1), "renewable_score": round(renewable_score, 1), "recycling_score": round(recycling_score, 1), "water_score": water_efficiency_score } } async def calculate_social_score( self, company_data: Dict[str, Any], social_data: Dict[str, Any] ) -> Dict[str, Any]: """Calculate social score based on employee and community metrics""" # Extract social metrics employee_turnover = social_data.get("employee_turnover", 0.1) employee_satisfaction = social_data.get("employee_satisfaction", 3.0) gender_diversity = social_data.get("gender_diversity", {}) safety_incidents = social_data.get("safety_incidents", 0) training_hours = social_data.get("training_hours_per_employee", 40) community_investment = social_data.get("community_investment", 0) employees = company_data.get("employees", 1) revenue = company_data.get("revenue", 1) # Calculate component scores # Employee wellbeing (turnover + satisfaction) turnover_score = max(0, min(100, (0.15 - employee_turnover) / 0.15 * 100)) satisfaction_score = (employee_satisfaction / 5.0) * 100 employee_wellbeing_score = (turnover_score + satisfaction_score) / 2 # Diversity score total_diversity = gender_diversity.get("total", 0.3) management_diversity = gender_diversity.get("management", 0.2) board_diversity = gender_diversity.get("board", 0.15) diversity_score = (total_diversity * 50 + management_diversity * 100 + board_diversity * 150) / 3 diversity_score = min(100, diversity_score) # Safety score (incidents per 1000 employees) incident_rate = (safety_incidents / employees) * 1000 if employees > 0 else 0 safety_score = max(0, min(100, 100 - (incident_rate * 10))) # Community impact community_ratio = (community_investment / revenue) * 100 if revenue > 0 else 0 community_impact_score = min(100, community_ratio * 500) # Scale appropriately # Training score training_score = min(100, training_hours / 80 * 100) # Weighted social score social_score = ( employee_wellbeing_score * 0.30 + diversity_score * 0.25 + safety_score * 0.25 + community_impact_score * 0.15 + training_score * 0.05 ) # Determine grade if social_score >= 90: grade = "A+" elif social_score >= 85: grade = "A" elif social_score >= 80: grade = "A-" elif social_score >= 75: grade = "B+" elif social_score >= 70: grade = "B" elif social_score >= 65: grade = "B-" else: grade = "C+" return { "score": round(social_score, 1), "grade": grade, "diversity_score": round(diversity_score, 1), "safety_score": round(safety_score, 1), "employee_wellbeing_score": round(employee_wellbeing_score, 1), "community_impact_score": round(community_impact_score, 1), "training_score": round(training_score, 1) } async def calculate_governance_score( self, company_data: Dict[str, Any], governance_data: Dict[str, Any] ) -> Dict[str, Any]: """Calculate governance score based on board and management metrics""" # Extract governance metrics board_size = governance_data.get("board_size", 9) independent_directors = governance_data.get("independent_directors", 6) ethics_violations = governance_data.get("ethics_violations", 0) audit_issues = governance_data.get("audit_issues", 0) ceo_compensation = governance_data.get("ceo_compensation", 2000000000) median_employee_pay = governance_data.get("median_employee_pay", 50000000) shareholder_proposals_passed = governance_data.get("shareholder_proposals_passed", 3) shareholder_proposals_total = governance_data.get("shareholder_proposals_total", 5) # Calculate component scores # Board independence board_independence_ratio = independent_directors / board_size if board_size > 0 else 0 independence_score = min(100, board_independence_ratio * 150) # Target 67%+ # Ethics score ethics_score = max(0, 100 - (ethics_violations * 20)) # Audit score audit_score = max(0, 100 - (audit_issues * 30)) # Pay equity pay_ratio = ceo_compensation / median_employee_pay if median_employee_pay > 0 else 100 pay_equity_score = max(0, min(100, 100 - ((pay_ratio - 20) / 80 * 100))) # Shareholder rights shareholder_pass_rate = shareholder_proposals_passed / shareholder_proposals_total if shareholder_proposals_total > 0 else 0.6 shareholder_rights_score = shareholder_pass_rate * 100 # Weighted governance score governance_score = ( independence_score * 0.25 + ethics_score * 0.30 + audit_score * 0.20 + pay_equity_score * 0.15 + shareholder_rights_score * 0.10 ) # Determine grade if governance_score >= 90: grade = "A+" elif governance_score >= 85: grade = "A" elif governance_score >= 80: grade = "A-" elif governance_score >= 75: grade = "B+" else: grade = "B" return { "score": round(governance_score, 1), "grade": grade, "board_independence_ratio": round(board_independence_ratio, 2), "ethics_score": round(ethics_score, 1), "pay_equity_score": round(pay_equity_score, 1), "shareholder_rights_score": round(shareholder_rights_score, 1), "audit_score": round(audit_score, 1) } async def aggregate_esg_scores( self, individual_scores: Dict[str, Dict[str, Any]], weights: Dict[str, float] = None ) -> Dict[str, Any]: """Aggregate individual ESG scores into total score""" if weights is None: weights = {"environmental": 0.33, "social": 0.33, "governance": 0.34} total_score = 0 breakdown = {} for category, score_data in individual_scores.items(): score = score_data.get("score", 0) weight = weights.get(category, 0) total_score += score * weight breakdown[category] = { "score": score, "grade": score_data.get("grade", "N/A"), "weight": weight } # Determine total grade if total_score >= 90: total_grade = "A+" elif total_score >= 85: total_grade = "A" elif total_score >= 80: total_grade = "A-" elif total_score >= 75: total_grade = "B+" elif total_score >= 70: total_grade = "B" else: total_grade = "B-" return { "total_score": round(total_score, 1), "total_grade": total_grade, "breakdown": breakdown, "weights": weights } async def analyze_esg_trends( self, historical_scores: List[Dict[str, Any]], period: str = "3Y" ) -> Dict[str, Any]: """Analyze ESG trends over time""" if len(historical_scores) < 2: return {"overall_trend": "insufficient_data"} # Sort by date sorted_scores = sorted(historical_scores, key=lambda x: x["date"]) # Calculate trends for each category categories = ["environmental", "social", "governance", "total"] category_trends = {} for category in categories: scores = [score[category] for score in sorted_scores] # Simple linear trend if len(scores) >= 2: first_score = scores[0] last_score = scores[-1] change = last_score - first_score change_percent = (change / first_score) * 100 if first_score > 0 else 0 if change > 2: direction = "up" elif change < -2: direction = "down" else: direction = "stable" category_trends[category] = { "direction": direction, "change": round(change, 1), "change_percent": round(change_percent, 1), "first_score": first_score, "last_score": last_score } # Determine overall trend total_change = category_trends.get("total", {}).get("change", 0) if total_change > 2: overall_trend = "improving" elif total_change < -2: overall_trend = "declining" else: overall_trend = "stable" # Identify improvement areas improvement_areas = [] for category, trend in category_trends.items(): if category != "total" and trend["direction"] == "up": improvement_areas.append(category) # Year-over-year change if len(sorted_scores) >= 2: recent = sorted_scores[-1] previous = sorted_scores[-2] yoy_change = { "environmental": recent["environmental"] - previous["environmental"], "social": recent["social"] - previous["social"], "governance": recent["governance"] - previous["governance"], "total": recent["total"] - previous["total"] } else: yoy_change = {} return { "overall_trend": overall_trend, "category_trends": category_trends, "improvement_areas": improvement_areas, "year_over_year_change": yoy_change, "data_points": len(historical_scores), "analysis_period": period } async def compare_with_peers( self, company_code: str, company_score: Dict[str, float], industry_code: str = None ) -> Dict[str, Any]: """Compare ESG scores with industry peers""" # Mock industry averages (in real implementation, would fetch from database) industry_averages = { "environmental": 76.2, "social": 72.8, "governance": 79.5, "total": 76.2 } # Mock peer data peer_scores = [ {"environmental": 78.5, "social": 74.2, "governance": 81.3, "total": 78.0}, {"environmental": 72.1, "social": 69.8, "governance": 76.7, "total": 72.9}, {"environmental": 80.3, "social": 75.6, "governance": 83.2, "total": 79.7}, {"environmental": 74.8, "social": 71.4, "governance": 78.9, "total": 75.0} ] # Calculate percentile rankings percentile_rank = {} relative_performance = {} for category in ["environmental", "social", "governance", "total"]: company_value = company_score.get(category, 0) industry_avg = industry_averages[category] # Count peers with lower scores peer_values = [peer[category] for peer in peer_scores] peer_values.append(company_value) peer_values.sort() rank_position = peer_values.index(company_value) + 1 percentile = (rank_position / len(peer_values)) * 100 percentile_rank[category] = round(percentile, 1) # Relative performance if company_value > industry_avg: performance = "above_average" difference = company_value - industry_avg elif company_value < industry_avg: performance = "below_average" difference = industry_avg - company_value else: performance = "average" difference = 0 relative_performance[category] = { "status": performance, "difference": round(difference, 1), "industry_average": industry_avg } return { "company_score": company_score, "industry_average": industry_averages, "percentile_rank": percentile_rank, "relative_performance": relative_performance, "peer_count": len(peer_scores), "industry_code": industry_code or "26211" } async def identify_esg_risks( self, company_data: Dict[str, Any], esg_scores: Dict[str, Any] ) -> List[Dict[str, Any]]: """Identify ESG-related risks""" risks = [] # Environmental risks env_score = esg_scores.get("environmental", {}).get("score", 0) if env_score < 70: risks.append({ "type": "environmental", "description": "Below-average environmental performance", "severity": "medium", "impact": "Regulatory compliance issues, carbon pricing exposure", "mitigation_suggestions": [ "Implement renewable energy transition plan", "Set science-based emissions targets", "Improve energy efficiency programs" ] }) carbon_intensity = esg_scores.get("environmental", {}).get("carbon_intensity", 0) if carbon_intensity > 50: risks.append({ "type": "environmental", "description": "High carbon intensity operations", "severity": "high", "impact": "Carbon tax exposure, stranded assets risk", "mitigation_suggestions": [ "Develop decarbonization roadmap", "Invest in clean technology", "Carbon offset programs" ] }) # Social risks social_score = esg_scores.get("social", {}).get("score", 0) if social_score < 75: risks.append({ "type": "social", "description": "Social performance gaps", "severity": "medium", "impact": "Talent retention issues, reputation risk", "mitigation_suggestions": [ "Enhance employee engagement programs", "Improve diversity and inclusion initiatives", "Strengthen community relations" ] }) # Governance risks governance_score = esg_scores.get("governance", {}).get("score", 0) if governance_score < 80: risks.append({ "type": "governance", "description": "Governance structure weaknesses", "severity": "medium", "impact": "Investor confidence, regulatory scrutiny", "mitigation_suggestions": [ "Increase board independence", "Strengthen ethics and compliance programs", "Improve executive compensation alignment" ] }) return risks async def generate_esg_initiatives( self, company_data: Dict[str, Any], esg_analysis: Dict[str, Any] ) -> List[Dict[str, Any]]: """Generate ESG improvement initiatives""" initiatives = [] scores = esg_analysis.get("scores", {}) weak_areas = esg_analysis.get("weak_areas", []) # Environmental initiatives if scores.get("environmental", 0) < 80 or "carbon_emissions" in weak_areas: initiatives.append({ "category": "environmental", "title": "Carbon Neutrality Program", "description": "Comprehensive program to achieve carbon neutrality by 2030", "expected_impact": "50% reduction in Scope 1&2 emissions", "implementation_timeline": "24 months", "cost_estimate": "β‚©50-100 billion", "key_actions": [ "Renewable energy procurement", "Energy efficiency improvements", "Carbon offset investments" ] }) if "renewable_energy" in weak_areas: initiatives.append({ "category": "environmental", "title": "RE100 Implementation", "description": "Transition to 100% renewable electricity", "expected_impact": "100% renewable electricity by 2025", "implementation_timeline": "18 months", "cost_estimate": "β‚©30-60 billion", "key_actions": [ "Solar panel installations", "Wind power agreements", "Green electricity procurement" ] }) # Social initiatives if scores.get("social", 0) < 75 or "gender_diversity" in weak_areas: initiatives.append({ "category": "social", "title": "Diversity & Inclusion Program", "description": "Comprehensive D&I program targeting leadership representation", "expected_impact": "40% female representation in management by 2026", "implementation_timeline": "36 months", "cost_estimate": "β‚©10-20 billion", "key_actions": [ "Bias-free recruitment processes", "Leadership development programs", "Inclusive culture training" ] }) # Governance initiatives if scores.get("governance", 0) < 85: initiatives.append({ "category": "governance", "title": "Board Effectiveness Enhancement", "description": "Strengthen board independence and expertise", "expected_impact": "Improved governance rating to A grade", "implementation_timeline": "12 months", "cost_estimate": "β‚©5-10 billion", "key_actions": [ "Add independent directors", "Board skills assessment", "Enhanced committee structures" ] }) return initiatives async def generate_esg_report( self, company_data: Dict[str, Any], include_scores: bool = True, include_trends: bool = False, include_peer_comparison: bool = False, include_risks: bool = False, include_initiatives: bool = False ) -> Dict[str, Any]: """Generate comprehensive ESG report""" report = { "company_info": { "code": company_data.get("company_code", "Unknown"), "name": company_data.get("company_name", "Unknown Company"), "industry": company_data.get("industry", "Technology") }, "report_date": datetime.now().strftime("%Y-%m-%d"), "executive_summary": { "overall_rating": "B+", "key_strengths": [ "Strong governance framework", "Advanced environmental initiatives", "Comprehensive stakeholder engagement" ], "key_areas_for_improvement": [ "Gender diversity in leadership", "Supply chain sustainability", "Community investment programs" ] } } # Add sections based on parameters if include_scores: report["scores"] = { "environmental": {"score": 82.5, "grade": "A"}, "social": {"score": 78.3, "grade": "B+"}, "governance": {"score": 85.7, "grade": "A"}, "total": {"score": 82.2, "grade": "A-"} } if include_trends: report["trends"] = { "overall_trend": "improving", "3_year_improvement": 6.3, "best_performing_area": "environmental" } if include_peer_comparison: report["peer_comparison"] = { "industry_ranking": "Top 25%", "above_peer_average": True, "strongest_vs_peers": "governance" } if include_risks: report["risks"] = await self.identify_esg_risks(company_data, { "environmental": {"score": 82.5}, "social": {"score": 78.3}, "governance": {"score": 85.7} }) if include_initiatives: report["initiatives"] = await self.generate_esg_initiatives(company_data, { "scores": {"environmental": 82.5, "social": 78.3, "governance": 85.7}, "weak_areas": ["gender_diversity"] }) report["recommendations"] = [ "Continue leadership in environmental sustainability", "Accelerate diversity and inclusion programs", "Enhance supply chain ESG monitoring", "Increase community investment allocation" ] return report async def validate_esg_data(self, data: Dict[str, Any]) -> bool: """Validate ESG data inputs""" # Check for negative values where they shouldn't exist if data.get("carbon_emissions", 0) < 0: raise MCPStockDetailsError("Carbon emissions cannot be negative") # Check ranges for satisfaction scores satisfaction = data.get("employee_satisfaction", 3.0) if satisfaction < 0 or satisfaction > 5: raise MCPStockDetailsError("Employee satisfaction must be between 0 and 5") # Check percentage ratios independence_ratio = data.get("board_independence_ratio", 0.5) if independence_ratio < 0 or independence_ratio > 1: raise MCPStockDetailsError("Board independence ratio must be between 0 and 1") return True async def get_esg_data(self, company_code: str) -> Dict[str, Any]: """Get ESG data for a company (mock implementation)""" # Mock ESG data for testing if company_code == "005930": # Samsung Electronics return { "esg_scores": { "environmental": { "score": 82.5, "grade": "A", "carbon_emissions": 12500000, "renewable_energy_percent": 45.2, "water_usage": 152000000, "waste_recycling_rate": 92.3 }, "social": { "score": 78.3, "grade": "B+", "employee_satisfaction": 4.2, "gender_diversity_ratio": 0.35, "safety_incidents": 12, "community_investment": 125000000000 }, "governance": { "score": 85.7, "grade": "A", "board_independence_ratio": 0.67, "ethics_violations": 2, "ceo_pay_ratio": 52.3, "shareholder_rights_score": 88.5 }, "total_score": 82.2, "total_grade": "A-", "last_updated": "2024-01-15" }, "esg_initiatives": [ { "category": "environmental", "title": "RE100 Commitment", "description": "Target 100% renewable energy by 2025", "impact": "Reduce carbon emissions by 5M tons annually" }, { "category": "social", "title": "Digital Education Program", "description": "STEM education for 1M students", "impact": "Enhanced tech literacy in 50 countries" } ], "esg_risks": [ { "type": "environmental", "risk": "Supply chain carbon footprint", "severity": "medium", "mitigation": "Supplier sustainability program" }, { "type": "governance", "risk": "Regulatory compliance in emerging markets", "severity": "low", "mitigation": "Enhanced compliance monitoring system" } ] } else: # Default mock data for other companies return { "esg_scores": { "environmental": {"score": 75.0, "grade": "B+"}, "social": {"score": 72.0, "grade": "B"}, "governance": {"score": 78.0, "grade": "B+"}, "total_score": 75.0, "total_grade": "B+", "last_updated": "2024-01-15" }, "esg_initiatives": [], "esg_risks": [] }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/whdghk1907/mcp-stock-details'

If you have feedback or need assistance with the MCP directory API, please join our Discord server