Skip to main content
Glama

Grants Search MCP Server

roi_metrics.py12.8 kB
"""ROI Score calculations based on research funding efficiency metrics.""" import logging from typing import Dict, List, Optional, Tuple import numpy as np from mcp_server.models.grants_schemas import OpportunityV1 from mcp_server.models.analytics_schemas import ScoreBreakdown, IndustryConstants logger = logging.getLogger(__name__) class ROICalculator: """ Calculate Return on Investment (ROI) scores for grant opportunities. Evaluates the financial and strategic value of pursuing a grant application relative to the effort and resources required. """ def __init__(self): self.constants = IndustryConstants() def estimate_application_cost( self, award_ceiling: Optional[float], award_floor: Optional[float], agency_code: str, complexity_factors: Optional[Dict] = None ) -> Tuple[float, int]: """ Estimate the cost (in dollars and hours) to prepare an application. Args: award_ceiling: Maximum award amount award_floor: Minimum award amount agency_code: Agency code complexity_factors: Additional complexity factors Returns: Tuple of (cost_dollars, hours_required) """ # Base hours by award size (empirical data from academic researchers) award_amount = award_ceiling or award_floor or 100000 if award_amount < 50000: base_hours = 40 # Small grants elif award_amount < 100000: base_hours = 60 # Standard grants elif award_amount < 500000: base_hours = 100 # Medium grants elif award_amount < 1000000: base_hours = 150 # Large grants else: base_hours = 200 # Very large grants # Agency-specific complexity multipliers agency_multipliers = { 'NIH': 1.5, # Complex requirements, detailed budgets 'NSF': 1.3, # Moderate complexity 'DOE': 1.4, # Technical complexity 'DOD': 1.6, # High security/compliance requirements 'NASA': 1.4, # Technical complexity 'EPA': 1.2, # Moderate requirements 'USDA': 1.1, # Simpler applications } main_agency = agency_code.split('-')[0] if agency_code else 'OTHER' complexity_multiplier = agency_multipliers.get(main_agency, 1.2) # Additional complexity factors if complexity_factors: if complexity_factors.get('requires_partnerships', False): complexity_multiplier *= 1.2 if complexity_factors.get('requires_preliminary_data', False): complexity_multiplier *= 1.1 if complexity_factors.get('first_time_agency', False): complexity_multiplier *= 1.3 total_hours = int(base_hours * complexity_multiplier) # Calculate dollar cost (using academic hourly rate) cost_dollars = total_hours * self.constants.ACADEMIC_HOURLY_RATE return cost_dollars, total_hours def calculate_basic_roi( self, award_amount: float, application_cost: float ) -> float: """ Calculate basic ROI percentage. Formula: Grant_ROI = ((Award_Amount - Application_Cost) / Application_Cost) * 100 Args: award_amount: Expected award amount application_cost: Cost to prepare application Returns: ROI percentage """ if application_cost <= 0: return 0.0 return ((award_amount - application_cost) / application_cost) * 100 def calculate_effort_adjusted_roi( self, grant_roi: float, hours_required: int, hourly_opportunity_cost: float ) -> float: """ Calculate effort-adjusted ROI. Formula: Effort_ROI = Grant_ROI / (Hours_Required * Opportunity_Cost_Per_Hour) Args: grant_roi: Basic grant ROI hours_required: Hours required for application hourly_opportunity_cost: Opportunity cost per hour Returns: Effort-adjusted ROI """ total_opportunity_cost = hours_required * hourly_opportunity_cost if total_opportunity_cost <= 0: return grant_roi return grant_roi / total_opportunity_cost * 1000 # Scale for readability def calculate_risk_adjusted_roi( self, grant_roi: float, success_probability: float, risk_factors: Optional[Dict] = None ) -> float: """ Calculate risk-adjusted ROI. Formula: Risk_Adjusted_ROI = Grant_ROI × Success_Probability × (1 - Risk_Factor) Args: grant_roi: Basic grant ROI success_probability: Probability of success (0.0-1.0) risk_factors: Additional risk factors Returns: Risk-adjusted ROI """ # Base risk factor base_risk = 0.1 # 10% base risk # Additional risk factors additional_risk = 0.0 if risk_factors: if risk_factors.get('new_agency', False): additional_risk += 0.1 if risk_factors.get('tight_deadline', False): additional_risk += 0.05 if risk_factors.get('high_competition', False): additional_risk += 0.1 if risk_factors.get('complex_requirements', False): additional_risk += 0.05 total_risk_factor = min(0.5, base_risk + additional_risk) # Cap at 50% return grant_roi * success_probability * (1 - total_risk_factor) def calculate_strategic_value_multiplier( self, opportunity: OpportunityV1, user_profile: Optional[Dict] = None ) -> float: """ Calculate strategic value multiplier beyond pure financial ROI. Args: opportunity: Grant opportunity user_profile: User research profile Returns: Strategic value multiplier (0.8-2.0) """ multiplier = 1.0 # Agency prestige factor prestige_multipliers = { 'NIH': 1.3, # High prestige 'NSF': 1.2, # High prestige 'DOE': 1.1, # Good prestige 'NASA': 1.2, # High prestige 'DOD': 1.1, # Good prestige 'EPA': 1.0, # Standard prestige 'USDA': 1.0, # Standard prestige } main_agency = opportunity.agency_code.split('-')[0] if opportunity.agency_code else 'OTHER' multiplier *= prestige_multipliers.get(main_agency, 1.0) # Career stage considerations if user_profile: career_stage = user_profile.get('career_stage', 'mid-career') if career_stage == 'early-career': # Early career researchers benefit more from prestigious grants multiplier *= 1.2 elif career_stage == 'senior': # Senior researchers may value efficiency more multiplier *= 0.9 # Multi-year funding bonus if opportunity.summary.award_ceiling and opportunity.summary.award_ceiling > 500000: multiplier *= 1.1 # Assume larger grants are multi-year # Collaboration opportunities description = opportunity.summary.summary_description or "" if any(word in description.lower() for word in ['collaboration', 'partnership', 'consortium']): multiplier *= 1.1 return max(0.8, min(2.0, multiplier)) def calculate_roi_score( self, opportunity: OpportunityV1, success_probability: float, user_profile: Optional[Dict] = None ) -> ScoreBreakdown: """ Calculate comprehensive ROI score. Args: opportunity: Grant opportunity to score success_probability: Success probability (0-100) user_profile: User research profile (optional) Returns: ScoreBreakdown with transparent calculation """ try: # Extract key information award_ceiling = opportunity.summary.award_ceiling or 100000 award_floor = opportunity.summary.award_floor or 0 agency = opportunity.agency_code # Use average award amount for calculations award_amount = (award_ceiling + award_floor) / 2 if award_floor else award_ceiling # Estimate application costs application_cost, hours_required = self.estimate_application_cost( award_ceiling, award_floor, agency ) # Calculate basic ROI basic_roi = self.calculate_basic_roi(award_amount, application_cost) # Calculate effort-adjusted ROI hourly_rate = user_profile.get('hourly_opportunity_cost', self.constants.ACADEMIC_HOURLY_RATE) if user_profile else self.constants.ACADEMIC_HOURLY_RATE effort_roi = self.calculate_effort_adjusted_roi(basic_roi, hours_required, hourly_rate) # Risk factors assessment risk_factors = { 'tight_deadline': False, # Would need deadline analysis 'high_competition': False, # Would need competition data 'new_agency': not user_profile.get('familiar_agencies', [agency]) if user_profile else False, 'complex_requirements': agency.startswith(('NIH', 'DOD')) # Heuristic } # Calculate risk-adjusted ROI risk_adjusted_roi = self.calculate_risk_adjusted_roi( basic_roi, success_probability / 100, risk_factors ) # Apply strategic value multiplier strategic_multiplier = self.calculate_strategic_value_multiplier(opportunity, user_profile) final_roi = risk_adjusted_roi * strategic_multiplier # Convert to 0-100 score scale # Normalize based on typical ROI ranges (0-1000% basic ROI) roi_score = min(100, max(0, (final_roi / 1000) * 100)) # Build calculation explanation calculation = f"ROI: ${award_amount:,.0f} award / ${application_cost:,.0f} cost = {basic_roi:.0f}%" # Interpret the score if roi_score >= 80: interpretation = "Excellent ROI (high value opportunity)" elif roi_score >= 60: interpretation = "Good ROI (worthwhile investment)" elif roi_score >= 40: interpretation = "Moderate ROI (consider other factors)" elif roi_score >= 20: interpretation = "Low ROI (high cost relative to benefit)" else: interpretation = "Poor ROI (not cost-effective)" # Calculate percentile percentile = roi_score # Use score directly as percentile approximation # Industry benchmark benchmark = f"Typical academic grant ROI: 300-800%" return ScoreBreakdown( value=roi_score, calculation=calculation, components={ "award_amount": award_amount, "application_cost": application_cost, "hours_required": hours_required, "basic_roi": basic_roi, "effort_adjusted_roi": effort_roi, "risk_adjusted_roi": risk_adjusted_roi, "strategic_multiplier": strategic_multiplier, "final_roi": final_roi, "formula": "Risk_Adjusted_ROI × Strategic_Value_Multiplier" }, interpretation=interpretation, percentile=percentile, industry_benchmark=benchmark ) except Exception as e: logger.error(f"Error calculating ROI score: {e}") return ScoreBreakdown( value=50.0, calculation="Error in calculation", components={"error": str(e)}, interpretation="Unable to calculate ROI", percentile=None, industry_benchmark=None )

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/Tar-ive/grants-mcp'

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