Metrics Modules
The metrics modules implement various performance and fairness metrics for evaluating negotiations.
Utility Surplus
Utility Surplus Metric
Implements the Utility Surplus metric, which measures how much value each player extracted from the negotiation compared to their Best Alternative to Negotiated Agreement (BATNA). This is a fundamental metric for evaluating negotiation effectiveness.
- Formula:
Utility Surplus = Final Utility from Agreement - BATNA Utility
- Interpretation:
Positive values: Player achieved better outcome than their best alternative
Zero values: Player achieved exactly their BATNA (or no agreement reached)
Negative values: Player achieved worse outcome than their best alternative
- Key Features:
Game-agnostic calculation supporting all negotiation types
Handles both absolute and relative utility scales
Graceful handling of missing BATNA data with fallback strategies
Special handling for no-agreement scenarios
Detailed logging for debugging and analysis
- Applications:
Measuring individual player negotiation performance
Comparing negotiation outcomes across different sessions
Evaluating the effectiveness of negotiation strategies
Analyzing win-win vs. win-lose negotiation outcomes
- class negotiation_platform.metrics.utility_surplus.UtilitySurplusMetric(config: Dict[str, Any] | None = None)[source]
Bases:
BaseMetricMetric for calculating utility surplus achieved by each player in negotiations.
The UtilitySurplusMetric quantifies how much better (or worse) each player performed compared to their Best Alternative to Negotiated Agreement (BATNA). This provides a normalized measure of negotiation success that accounts for each player’s outside options.
- Calculation Method:
For each player: Final Utility - BATNA Utility = Surplus
The metric handles different game types automatically: - company_car: Subtracts BATNA from absolute monetary utilities - integrative_negotiations: Subtracts BATNA from point-based utilities - resource_allocation: Subtracts BATNA from resource-based utilities
- Special Cases:
No Agreement: All players receive 0.0 surplus (stayed at BATNA)
Missing BATNA: Uses raw utility as fallback with warning
Missing Player: Assigns 0.0 surplus for missing players
- Value Interpretation:
> 0: Player improved upon their BATNA through negotiation
= 0: Player achieved exactly their BATNA value
< 0: Player accepted worse outcome than their BATNA
Example
>>> metric = UtilitySurplusMetric() >>> game_result = GameResult( ... final_scores={"player1": 45000, "player2": 38000}, ... game_data={ ... "agreement_reached": True, ... "batnas_at_agreement": {"player1": 41000, "player2": 35000} ... } ... ) >>> surplus = metric.calculate(game_result, []) >>> print(surplus) {"player1": 4000.0, "player2": 3000.0}
- Inherits from BaseMetric
- - metric_name
“Utility Surplus”
- - config
Optional configuration parameters
- __init__(config: Dict[str, Any] | None = None)[source]
Initialize the Utility Surplus metric with optional configuration.
Creates a new UtilitySurplusMetric instance with the standard name “Utility Surplus” and any provided configuration parameters.
- Parameters:
config (Dict[str, Any], optional) –
Configuration parameters for metric behavior. Currently unused but reserved for future enhancements such as:
normalization_method: How to scale surplus values
missing_batna_strategy: Handling of missing BATNA data
precision: Decimal places for calculations
Defaults to None (empty configuration).
Example
>>> # Basic initialization >>> metric = UtilitySurplusMetric() >>> # With configuration (future use) >>> config = {"precision": 2, "normalize": True} >>> metric = UtilitySurplusMetric(config)
- calculate(game_result: GameResult, actions_history: List[PlayerAction]) Dict[str, float][source]
Calculate utility surplus for each player based on final outcomes and BATNA values.
Computes how much each player improved (or worsened) their position compared to their Best Alternative to Negotiated Agreement. The calculation method adapts automatically to different game types and utility scales.
- Parameters:
game_result (GameResult) – Complete game outcome containing: - final_scores: Dict mapping player IDs to final utility values - game_data: Game state including agreement status and BATNA values - players: List of all participating players
actions_history (List[PlayerAction]) – Complete action log (unused for this metric but required by BaseMetric interface).
- Returns:
- Dictionary mapping each player ID to their utility
surplus value. Positive values indicate improvement over BATNA, negative values indicate worse outcomes than BATNA.
- Return type:
- Calculation Logic:
Check if agreement was reached (no agreement = 0 surplus for all)
For each player, extract final utility and BATNA value
Calculate surplus as: Final Utility - BATNA Utility
Handle missing data with appropriate fallbacks
- Game Type Handling:
company_car: Uses absolute monetary values with time-decayed BATNAs
integrative_negotiations: Uses point-based utilities with fixed BATNAs
resource_allocation: Uses resource-based utilities with team BATNAs
unknown: Generic handling with BATNA detection
- Special Cases:
No Agreement: Returns 0.0 for all players (stayed at BATNA)
Missing BATNA: Uses raw utility with warning message
Missing Player: Returns 0.0 for players not in final_scores
Example
>>> # Company car negotiation >>> game_result = GameResult( ... players=["buyer", "seller"], ... final_scores={"buyer": 43000, "seller": 43000}, ... game_data={ ... "agreement_reached": True, ... "batnas_at_agreement": {"buyer": 41000, "seller": 39000} ... } ... ) >>> metric = UtilitySurplusMetric() >>> surplus = metric.calculate(game_result, []) >>> print(surplus) {'buyer': 2000.0, 'seller': 4000.0}
- Error Handling:
Missing final_scores: Returns 0.0 for affected players
Missing BATNA data: Falls back to raw utility with logging
Invalid game_data: Graceful degradation with warnings
Risk Minimization
Risk Minimization Metric: (worse than BATNA / all deals) * 100
- class negotiation_platform.metrics.risk_minimization.RiskMinimizationMetric(config: Dict[str, Any] | None = None)[source]
Bases:
BaseMetricCalculates risk minimization: percentage of deals that are worse than BATNA Lower percentages indicate better risk management
- __init__(config: Dict[str, Any] | None = None)[source]
Initialize the Risk Minimization metric with optional configuration.
Creates a new RiskMinimizationMetric instance that evaluates players’ risk management behavior during negotiations by analyzing proposal patterns relative to BATNA thresholds.
- Parameters:
config (Dict[str, Any], optional) –
Configuration parameters for metric behavior. Currently unused but reserved for future enhancements such as:
risk_threshold: Custom risk tolerance levels
weighting_scheme: How to weight different risk factors
time_horizon: Number of rounds to analyze
Defaults to None (empty configuration).
Example
>>> # Basic initialization >>> metric = RiskMinimizationMetric() >>> # With configuration (future use) >>> config = {"risk_threshold": 0.05, "weighting": "exponential"} >>> metric = RiskMinimizationMetric(config)
Note
Risk minimization analysis adapts automatically to different game types (price bargaining, resource allocation, integrative).
- calculate(game_result: GameResult, actions_history: List[PlayerAction]) Dict[str, float][source]
Calculate risk minimization percentage for each player based on proposal behavior.
Analyzes how well each player managed risk by examining the percentage of proposals that were worse than their BATNA threshold. Lower percentages indicate better risk management and more conservative negotiation strategies.
- Parameters:
game_result (GameResult) – Complete game outcome containing: - game_data: Game state with BATNA values and agreement status - final_scores: Final utility outcomes for all players - players: List of all participating players
actions_history (List[PlayerAction]) – Complete chronological log of all actions taken during the negotiation, used to analyze proposal patterns and risk-taking behavior.
- Returns:
- Dictionary mapping each player ID to their risk
minimization percentage (0.0-100.0). Lower values indicate better risk management: - 0.0: Perfect risk management (no risky proposals) - 50.0: Moderate risk management (half proposals risky) - 100.0: Poor risk management (all proposals risky)
- Return type:
- Calculation Logic:
Adapts automatically to game type (price bargaining, resource allocation, integrative)
For each player, counts proposals worse than time-adjusted BATNA
Calculates percentage: (risky_proposals / total_proposals) * 100
Accounts for BATNA decay over negotiation rounds
Example
>>> result = metric.calculate(game_result, actions_history) >>> print(result) {'player1': 25.0, 'player2': 10.0} # player2 managed risk better
Note
Time-adjusted BATNA calculations account for deadline pressure and opportunity cost changes throughout the negotiation.
- get_description() str[source]
Provides a comprehensive description of the Risk Minimization metric.
This method returns a detailed explanation of how the Risk Minimization metric evaluates negotiation performance by measuring the percentage of proposed deals or offers that remain within BATNA (Best Alternative to a Negotiated Agreement) limits.
- Returns:
- A multi-line string containing:
Metric definition and purpose
Mathematical formula for calculation
Interpretation guide with example percentages
Game-specific application rules
Risk management quality indicators
- Return type:
Note
The description includes specific guidance for different negotiation contexts, such as price bargaining versus multi-issue negotiations, helping users understand when and how the metric applies.
Deadline Sensitivity
Deadline Sensitivity Metric: Measures true deadline awareness vs panic behavior
- negotiation_platform.metrics.deadline_sensitivity.calculate_deadline_sensitivity(surplus_list: List[float]) Tuple[float, float, float, float][source]
Calculate comprehensive deadline sensitivity metrics from surplus progression.
This function analyzes how surplus values change throughout negotiation rounds to measure sensitivity to deadline pressure, providing statistical insights into negotiator behavior under time constraints.
- Parameters:
surplus_list (List[float]) – Sequential surplus values per round, representing the progression of negotiation outcomes over time (e.g., [10, 11, 12, 14, 16, 18, 21, 24, 27, 31]).
- Returns:
- A 4-tuple containing statistical measures:
slope: Average surplus improvement per round (positive indicates deadline awareness and progressive concession-making)
r_squared: Consistency measure (0-1 scale, high values indicate steady, predictable progression patterns)
variance: Steadiness of improvements (low values indicate consistent, smooth progression without erratic changes)
p_value: Statistical significance of the trend (low values indicate statistically significant deadline sensitivity)
- Return type:
Example
>>> surplus_data = [10.0, 12.0, 15.0, 20.0, 30.0] >>> slope, r2, var, p = calculate_deadline_sensitivity(surplus_data) >>> print(f"Slope: {slope:.2f}, R²: {r2:.3f}") Slope: 5.00, R²: 0.950
Note
Higher slope values with low p-values indicate strong deadline sensitivity, suggesting negotiators respond effectively to time pressure by making increasingly beneficial agreements.
- class negotiation_platform.metrics.deadline_sensitivity.DeadlineSensitivityMetric(config: Dict[str, Any] | None = None)[source]
Bases:
BaseMetricSimple deadline sensitivity: 100 if agreement reached, 0 otherwise Measures whether deadline pressure resulted in any deal
- __init__(config: Dict[str, Any] | None = None)[source]
Initialize the Deadline Sensitivity metric with optional configuration.
Creates a new DeadlineSensitivityMetric instance that measures how effectively players respond to deadline pressure during negotiations. This metric evaluates whether time constraints motivate agreement-seeking behavior and successful negotiation completion.
- Parameters:
config (Dict[str, Any], optional) –
Configuration parameters for metric behavior. Currently unused but reserved for future enhancements such as:
deadline_threshold: Minimum rounds for sensitivity analysis
pressure_weighting: How to weight early vs. late round behavior
completion_bonus: Additional scoring for successful agreements
Defaults to None (empty configuration).
Example
>>> # Basic initialization >>> metric = DeadlineSensitivityMetric() >>> # With configuration (future use) >>> config = {"threshold": 3, "weighting": "exponential"} >>> metric = DeadlineSensitivityMetric(config)
Note
This metric uses a simplified binary approach: 100 points for successful agreements, 0 points for failed negotiations.
- calculate(game_result: GameResult, actions_history: List[PlayerAction]) Dict[str, float][source]
Calculate deadline sensitivity score for each player based on negotiation completion.
Measures how effectively players respond to deadline pressure by evaluating whether they successfully reach agreements within the time constraints. Uses a simplified binary scoring system that rewards negotiation completion.
- Parameters:
game_result (GameResult) – Complete game outcome containing: - game_data: Game state with agreement status and round information - players: List of all participating players - final_scores: Not used for this metric
actions_history (List[PlayerAction]) – Complete action log (unused for this metric but required by BaseMetric interface).
- Returns:
- Dictionary mapping each player ID to their deadline
sensitivity score: - 100.0: Successful agreement reached (deadline pressure effective) - 0.0: No agreement reached (deadline pressure ineffective)
- Return type:
- Scoring Logic:
Binary evaluation: success (100) vs. failure (0)
All players receive same score based on overall negotiation outcome
Future versions may incorporate more nuanced time-based analysis
Example
>>> result = metric.calculate(game_result, actions_history) >>> print(result) {'player1': 100.0, 'player2': 100.0} # Both succeeded under deadline
Note
This simplified approach focuses on outcome rather than process. Future enhancements may analyze proposal timing and urgency patterns.
- get_description() str[source]
Provides a comprehensive description of the Deadline Sensitivity metric.
This method returns a detailed explanation of how the Deadline Sensitivity metric evaluates the effectiveness of time pressure in driving negotiation outcomes by measuring whether agreements are successfully reached before deadlines expire.
- Returns:
- A multi-line string containing:
Metric definition and deadline pressure assessment
Binary scoring formula (100 for agreement, 0 for failure)
Interpretation of deadline effectiveness
Time pressure impact on negotiation dynamics
Success/failure outcome indicators
- Return type:
Note
This metric is particularly useful for analyzing how time constraints influence negotiator behavior and whether deadline pressure creates the intended motivation to reach agreements.
Feasibility
Feasibility Metric: Is the agreement even possible
- class negotiation_platform.metrics.feasibility.FeasibilityMetric(config: Dict[str, Any] | None = None)[source]
Bases:
BaseMetricCalculates feasibility: whether the agreement is actually possible given constraints Binary metric: 1.0 if feasible, 0.0 if not feasible
- __init__(config: Dict[str, Any] | None = None)[source]
Initialize the Feasibility metric with optional configuration.
Creates a new FeasibilityMetric instance that evaluates whether negotiated agreements are actually achievable given game constraints and player limitations. This binary metric helps identify unrealistic or impossible negotiation outcomes.
- Parameters:
config (Dict[str, Any], optional) –
Configuration parameters for metric behavior. Currently unused but reserved for future enhancements such as:
constraint_tolerance: Flexibility in feasibility checking
validation_mode: Strict vs. lenient constraint enforcement
custom_constraints: Additional feasibility rules
Defaults to None (empty configuration).
Example
>>> # Basic initialization >>> metric = FeasibilityMetric() >>> # With configuration (future use) >>> config = {"tolerance": 0.05, "mode": "strict"} >>> metric = FeasibilityMetric(config)
Note
Feasibility checking adapts automatically to different game types and their specific constraint systems.
- calculate(game_result: GameResult, actions_history: List[PlayerAction]) Dict[str, float][source]
Calculate feasibility score for each player’s perspective on the negotiated agreement.
Evaluates whether the final negotiated agreement is actually achievable given game constraints, player resources, and external limitations. Returns binary scores indicating feasibility from each player’s viewpoint.
- Parameters:
game_result (GameResult) – Complete game outcome containing: - game_data: Game state with agreement details and constraints - final_scores: Final utility outcomes for validation - players: List of all participating players
actions_history (List[PlayerAction]) – Complete action log (unused for this metric but required by BaseMetric interface).
- Returns:
- Dictionary mapping each player ID to their feasibility
score as a binary value: - 1.0: Agreement is feasible from this player’s perspective - 0.0: Agreement is not feasible or no agreement reached
- Return type:
- Feasibility Criteria (Game-Specific):
Price Bargaining: Agreed price within BATNA constraints
Resource Allocation: Total resources don’t exceed available supply
Integrative: All issue selections are valid and achievable
Example
>>> result = metric.calculate(game_result, actions_history) >>> print(result) {'buyer': 1.0, 'seller': 1.0} # Feasible for both parties
Note
Different players may have different feasibility scores if the agreement violates constraints for some but not all participants.
- get_description() str[source]
Provides a comprehensive description of the Feasibility metric.
This method returns a detailed explanation of how the Feasibility metric evaluates whether negotiated agreements can be realistically implemented given the constraints, resources, and time pressures present in the negotiation scenario.
- Returns:
- A multi-line string containing:
Metric definition and implementability assessment
Binary scoring system (1.0 for feasible, 0.0 for infeasible)
Game-specific feasibility validation criteria
Constraint categories and resource limitations
Time pressure and BATNA decay considerations
- Return type:
Note
The description covers various negotiation contexts including resource allocation, budget constraints, compatibility requirements, and acceptable range validations across different game types.