def calculate_final_score(portfolio_metrics, benchmark_metrics, global_skipped_set=None):
    """
    Calculate the final score for a portfolio based on various metrics compared to the benchmark.
    
    Parameters:
    - portfolio_metrics (dict): Metrics of the portfolio.
    - benchmark_metrics (dict): Metrics of the benchmark.
    - global_skipped_set (set, optional): Set to store skipped metrics.
    
    Returns:
    - float: Final score out of 100.
    """
    try:
        score = 0.0
        total_weight = 0.0

        # Define metric weights
        metric_weights = {
            'Annualized Return (CAGR)': 20,
            'Sharpe Ratio': 20,
            'Maximum Drawdown': 15,
            'Sortino Ratio': 10,
            'Information Ratio': 10,
            'Calmar Ratio': 10,
            'Treynor Ratio': 10,
            'Alpha (annualized)': 5,
            'Modigliani–Modigliani Measure': 5,
            'Upside Capture Ratio': 5
        }

        for metric, weight in metric_weights.items():
            portfolio_value = portfolio_metrics.get(metric, None)
            benchmark_value = benchmark_metrics.get(metric, None)
            
            if portfolio_value is None or benchmark_value is None:
                if global_skipped_set is not None:
                    global_skipped_set.add(metric)
                continue
            
            # Normalize scores based on benchmark
            if metric in ['Maximum Drawdown']:
                # For drawdown, lower is better
                if benchmark_value > 0:
                    metric_score = max(0, (benchmark_value - portfolio_value) / benchmark_value) * weight
                else:
                    metric_score = 0
            elif metric in ['Annualized Return (CAGR)', 'Sharpe Ratio', 'Sortino Ratio',
                            'Information Ratio', 'Calmar Ratio', 'Treynor Ratio', 
                            'Alpha (annualized)', 'Modigliani–Modigliani Measure', 'Upside Capture Ratio']:
                # For these, higher is better
                if benchmark_value > 0:
                    metric_score = min(portfolio_value / benchmark_value, 2) * weight  # Cap at 200%
                else:
                    metric_score = 0
            else:
                metric_score = 0
            
            score += metric_score
            total_weight += weight

        # Normalize the score to be out of 100
        if total_weight > 0:
            final_score = (score / total_weight) * 100
        else:
            final_score = 0.0

        return min(final_score, 100.0)
    except Exception as e:
        if global_skipped_set is not None:
            global_skipped_set.add("Final Score Calculation Error")
        return 0.0

combine them:
def calculate_final_score(primary_metrics, comparison_metrics, global_skipped=None):
    """
    Calculate the final score based on selected metrics.

    Parameters:
    - primary_metrics (dict): Metrics of the primary portfolio.
    - comparison_metrics (dict): Metrics of the comparison portfolio.
    - global_skipped (set, optional): A set to accumulate skipped metrics.

    Returns:
    - float: Final score.
    """
    if global_skipped is None:
        global_skipped = set()
    
    metrics_to_compare = [
        'Annualized Return (CAGR)',
        'Sharpe Ratio',
        'Sortino Ratio',
        'Treynor Ratio',
        'Calmar Ratio',
        'Alpha (annualized)',
        'Information Ratio',
        'Modigliani–Modigliani Measure',
        'Upside Capture Ratio',
        'Gain/Loss Ratio'
    ]
    
    metric_weights = {
        'Annualized Return (CAGR)': 15,
        'Sharpe Ratio': 15,
        'Sortino Ratio': 10,
        'Treynor Ratio': 10,
        'Calmar Ratio': 10,
        'Alpha (annualized)': 10,
        'Information Ratio': 10,
        'Modigliani–Modigliani Measure': 10,
        'Upside Capture Ratio': 5,
        'Gain/Loss Ratio': 5
    }

    # Ensure metric weights sum to 100
    total_weight = sum(metric_weights.values())
    if not np.isclose(total_weight, 100):
        st.error(f"Total metric weights must sum to 100, but got {total_weight}. Please adjust the weights.")
        return 0

    score = 0
    total = 0
    skipped_metrics = set()

    for metric in metrics_to_compare:
        primary = primary_metrics.get(metric, "N/A")
        comparison = comparison_metrics.get(metric, "N/A")
        
        if primary != "N/A" and comparison != "N/A":
            try:
                # Convert primary metric to float
                if isinstance(primary, str) and '%' in primary:
                    primary_val = float(primary.strip('%'))
                else:
                    primary_val = float(primary)
                
                # Convert comparison metric to float
                if isinstance(comparison, str) and '%' in comparison:
                    comparison_val = float(comparison.strip('%'))
                else:
                    comparison_val = float(comparison)
                
                # Check if both values are finite numbers
                if not (np.isfinite(primary_val) and np.isfinite(comparison_val)):
                    skipped_metrics.add(metric)
                    continue
                
                # Avoid division by zero
                if comparison_val == 0:
                    skipped_metrics.add(metric)
                    continue
                
                weight = metric_weights.get(metric, 1)
                ratio = primary_val / comparison_val

                # Cap the ratio to prevent excessive scores
                ratio = min(ratio, 2)  # Adjust as needed
                
                # Avoid negative ratios
                ratio = max(ratio, 0)
                
                if primary_val > comparison_val:
                    score += weight * ratio
                else:
                    score += weight * ratio * 0.5  # Partial credit
                
                total += weight

            except (ValueError, AttributeError):
                # Skip metric if conversion fails
                skipped_metrics.add(metric)
                continue

    # Update global skipped metrics
    global_skipped.update(skipped_metrics)

    # Calculate final score safely
    if total > 0 and np.isfinite(score):
        final_score = (score / total) * 100
    else:
        final_score = 0

    # Ensure the final score is a finite number and cap it at 100
    final_score = min(max(final_score, 0), 100)

    return final_score