Financial Scenario Analyzer - Model different business scenarios and their financial impact
from typing import Dict, List, Tuple
class FinancialScenarioAnalyzer:
'revenue', 'gross_margin', 'operating_expenses',
'ebitda', 'cash_flow', 'runway', 'valuation'
'linear': lambda base, rate, period: base * (1 + rate * period),
'exponential': lambda base, rate, period: base * math.pow(1 + rate, period),
'logarithmic': lambda base, rate, period: base * (1 + rate * math.log(period + 1)),
's_curve': lambda base, rate, period: base * (2 / (1 + math.exp(-rate * period)))
def analyze_scenarios(self, base_case: Dict, scenarios: List[Dict]) -> Dict:
"""Analyze multiple financial scenarios"""
'base_case_summary': self._summarize_financials(base_case),
'sensitivity_analysis': {},
for scenario in scenarios:
scenario_result = self._analyze_scenario(base_case, scenario)
results['scenario_analysis'].append(scenario_result)
results['sensitivity_analysis'] = self._perform_sensitivity_analysis(
results['risk_adjusted_view'] = self._calculate_risk_adjusted_returns(
results['scenario_analysis']
# Generate recommendation
results['recommendation'] = self._generate_recommendation(
results['scenario_analysis'],
results['risk_adjusted_view']
def _summarize_financials(self, financials: Dict) -> Dict:
"""Summarize key financial metrics"""
revenue = financials.get('revenue', 0)
cogs = financials.get('cogs', 0)
opex = financials.get('operating_expenses', 0)
gross_profit = revenue - cogs
gross_margin = (gross_profit / revenue * 100) if revenue > 0 else 0
ebitda = gross_profit - opex
ebitda_margin = (ebitda / revenue * 100) if revenue > 0 else 0
'gross_profit': gross_profit,
'gross_margin': gross_margin,
'operating_expenses': opex,
'ebitda_margin': ebitda_margin,
'cash': financials.get('cash', 0),
'burn_rate': financials.get('burn_rate', 0),
'runway_months': self._calculate_runway(
financials.get('cash', 0),
financials.get('burn_rate', 0)
def _calculate_runway(self, cash: float, burn_rate: float) -> float:
"""Calculate months of runway"""
def _analyze_scenario(self, base_case: Dict, scenario: Dict) -> Dict:
"""Analyze a single scenario"""
name = scenario.get('name', 'Unnamed Scenario')
probability = scenario.get('probability', 0.5)
projected_financials = self._apply_scenario_changes(base_case, scenario)
# Calculate metrics for each year
current_state = projected_financials.copy()
for year in range(1, 4): # 3-year projection
year_projection = self._project_year(
projections.append(year_projection)
current_state = year_projection
cash_flows = [p['free_cash_flow'] for p in projections]
npv = self._calculate_npv(cash_flows, scenario.get('discount_rate', 0.1))
irr = self._calculate_irr(cash_flows, base_case.get('initial_investment', 0))
'probability': probability,
'projections': projections,
'break_even_month': self._find_break_even(projections),
'total_return': self._calculate_total_return(projections, base_case),
'key_assumptions': scenario.get('assumptions', [])
def _apply_scenario_changes(self, base_case: Dict, scenario: Dict) -> Dict:
"""Apply scenario changes to base case"""
result = base_case.copy()
changes = scenario.get('changes', {})
for key, change in changes.items():
if isinstance(change, dict):
result[key] *= change['multiply']
result[key] += change['add']
def _project_year(self, current_state: Dict, scenario: Dict, year: int) -> Dict:
"""Project financials for a specific year"""
growth_model = scenario.get('growth_model', 'exponential')
growth_rate = scenario.get('growth_rate', 0.3)
model_func = self.growth_models.get(growth_model, self.growth_models['linear'])
current_state.get('revenue', 0),
cogs = revenue * scenario.get('cogs_ratio', 0.3)
opex = current_state.get('operating_expenses', 0) * (1 + scenario.get('opex_growth', 0.15))
gross_profit = revenue - cogs
ebitda = gross_profit - opex
# Calculate free cash flow (simplified)
capex = revenue * scenario.get('capex_ratio', 0.05)
working_capital_change = (revenue - current_state.get('revenue', 0)) * 0.1
free_cash_flow = ebitda - capex - working_capital_change
'gross_profit': gross_profit,
'gross_margin': (gross_profit / revenue * 100) if revenue > 0 else 0,
'operating_expenses': opex,
'ebitda_margin': (ebitda / revenue * 100) if revenue > 0 else 0,
'free_cash_flow': free_cash_flow,
'cumulative_cash_flow': current_state.get('cumulative_cash_flow', 0) + free_cash_flow
def _calculate_npv(self, cash_flows: List[float], discount_rate: float) -> float:
"""Calculate Net Present Value"""
for i, cf in enumerate(cash_flows):
npv += cf / math.pow(1 + discount_rate, i + 1)
def _calculate_irr(self, cash_flows: List[float], initial_investment: float) -> float:
"""Calculate Internal Rate of Return (simplified)"""
if not cash_flows or initial_investment == 0:
# Simple IRR approximation
total_return = sum(cash_flows)
if initial_investment > 0:
return math.pow(total_return / initial_investment, 1/years) - 1
def _find_break_even(self, projections: List[Dict]) -> int:
"""Find break-even month"""
for projection in projections:
if projection.get('ebitda', 0) > 0:
# Interpolate to find exact month
prev_ebitda = projections[projection['year']-2].get('ebitda', 0) if projection['year'] > 1 else 0
monthly_improvement = (projection['ebitda'] - prev_ebitda) / 12
if monthly_improvement > 0:
months_to_breakeven = abs(prev_ebitda) / monthly_improvement
return int(months - 12 + months_to_breakeven)
def _calculate_total_return(self, projections: List[Dict], base_case: Dict) -> float:
"""Calculate total return multiple"""
initial = base_case.get('valuation', 1000000)
# Simple valuation at end (10x revenue multiple for SaaS)
final_revenue = projections[-1]['revenue'] if projections else 0
final_valuation = final_revenue * 10
return (final_valuation / initial) if initial > 0 else 0
def _perform_sensitivity_analysis(self, base_case: Dict, scenarios: List[Dict]) -> Dict:
"""Perform sensitivity analysis on key variables"""
key_variables = ['growth_rate', 'gross_margin', 'customer_acquisition_cost']
for variable in key_variables:
sensitivity[variable] = {
'low': self._calculate_variable_impact(base_case, variable, -0.2),
'base': self._calculate_variable_impact(base_case, variable, 0),
'high': self._calculate_variable_impact(base_case, variable, 0.2)
def _calculate_variable_impact(self, base_case: Dict, variable: str, change: float) -> float:
"""Calculate impact of variable change on valuation"""
# Simplified impact calculation
'growth_rate': 2.5, # 2.5x multiplier on valuation
'gross_margin': 1.8, # 1.8x multiplier
'customer_acquisition_cost': -1.2 # Negative impact
base_value = 10000000 # Base valuation
impact_multiplier = impacts.get(variable, 1.0)
return base_value * (1 + change * impact_multiplier)
def _calculate_risk_adjusted_returns(self, scenarios: List[Dict]) -> Dict:
"""Calculate risk-adjusted returns"""
for scenario in scenarios:
probability = scenario['probability']
expected_value += probability * npv
if best_case is None or npv > best_case['npv']:
if worst_case is None or npv < worst_case['npv']:
# Calculate standard deviation (simplified)
scenario['probability'] * math.pow(scenario['npv'] - expected_value, 2)
for scenario in scenarios
std_dev = math.sqrt(variance)
'expected_value': expected_value,
'best_case': best_case['name'] if best_case else 'None',
'best_case_npv': best_case['npv'] if best_case else 0,
'worst_case': worst_case['name'] if worst_case else 'None',
'worst_case_npv': worst_case['npv'] if worst_case else 0,
'standard_deviation': std_dev,
'sharpe_ratio': (expected_value / std_dev) if std_dev > 0 else 0
def _generate_recommendation(self, scenarios: List[Dict], risk_adjusted: Dict) -> Dict:
"""Generate recommendation based on analysis"""
'recommended_scenario': '',
best_risk_adjusted = max(scenarios, key=lambda s: s['npv'] * s['probability'])
recommendation['recommended_scenario'] = best_risk_adjusted['name']
if best_risk_adjusted['npv'] > 0:
recommendation['rationale'].append(f"Positive NPV of ${best_risk_adjusted['npv']:,.0f}")
if best_risk_adjusted['irr'] > 0.15:
recommendation['rationale'].append(f"Strong IRR of {best_risk_adjusted['irr']:.1%}")
if best_risk_adjusted['break_even_month'] > 0 and best_risk_adjusted['break_even_month'] < 24:
recommendation['rationale'].append(f"Quick path to profitability ({best_risk_adjusted['break_even_month']} months)")
recommendation['key_actions'] = [
'Secure funding for growth initiatives',
'Build scalable operational infrastructure',
'Invest in customer acquisition channels',
'Strengthen unit economics',
'Establish financial controls'
if risk_adjusted['standard_deviation'] > risk_adjusted['expected_value'] * 0.5:
recommendation['risk_mitigation'].append('High variability - consider hedging strategies')
recommendation['risk_mitigation'].extend([
'Maintain 12+ months runway',
'Diversify revenue streams',
'Build contingency plans for downside scenarios'
def analyze_financial_scenarios(base_case: Dict, scenarios: List[Dict]) -> str:
"""Main function to analyze financial scenarios"""
analyzer = FinancialScenarioAnalyzer()
results = analyzer.analyze_scenarios(base_case, scenarios)
"=== Financial Scenario Analysis ===",
f" Revenue: ${results['base_case_summary']['revenue']:,.0f}",
f" Gross Margin: {results['base_case_summary']['gross_margin']:.1f}%",
f" EBITDA: ${results['base_case_summary']['ebitda']:,.0f}",
f" Runway: {results['base_case_summary']['runway_months']:.1f} months",
for scenario in results['scenario_analysis']:
output.append(f"\n{scenario['name']} (Probability: {scenario['probability']:.0%})")
output.append(f" NPV: ${scenario['npv']:,.0f}")
output.append(f" IRR: {scenario['irr']:.1%}")
output.append(f" Break-even: {scenario['break_even_month']} months")
output.append(f" Return Multiple: {scenario['total_return']:.1f}x")
if scenario['projections']:
year3 = scenario['projections'][-1]
output.append(f" Year 3 Revenue: ${year3['revenue']:,.0f}")
output.append(f" Year 3 EBITDA Margin: {year3['ebitda_margin']:.1f}%")
"Risk-Adjusted Analysis:",
f" Expected Value: ${results['risk_adjusted_view']['expected_value']:,.0f}",
f" Best Case: {results['risk_adjusted_view']['best_case']} (${results['risk_adjusted_view']['best_case_npv']:,.0f})",
f" Worst Case: {results['risk_adjusted_view']['worst_case']} (${results['risk_adjusted_view']['worst_case_npv']:,.0f})",
f" Risk (Std Dev): ${results['risk_adjusted_view']['standard_deviation']:,.0f}",
f" Sharpe Ratio: {results['risk_adjusted_view']['sharpe_ratio']:.2f}",
f"RECOMMENDATION: {results['recommendation']['recommended_scenario']}",
for reason in results['recommendation']['rationale']:
output.append(f" • {reason}")
for action in results['recommendation']['key_actions'][:3]:
output.append(f" • {action}")
if __name__ == "__main__":
'operating_expenses': 3000000,
'initial_investment': 5000000
'name': 'Aggressive Growth',
'growth_model': 'exponential',
'operating_expenses': {'multiply': 1.3}
'assumptions': ['Market expansion successful', 'Product-market fit achieved'],
'name': 'Moderate Growth',
'growth_model': 'exponential',
'assumptions': ['Steady market growth', 'Competition remains stable'],
'growth_model': 'linear',
'operating_expenses': {'multiply': 0.9}
'assumptions': ['Market headwinds', 'Focus on profitability'],
print(analyze_financial_scenarios(example_base_case, example_scenarios))