Skip to main content

A comprehensive, open-source rating system for Valorant player performance analysis

Project description

Open Soup Rating (OSR)

A comprehensive, open-source rating system for Valorant player performance analysis. OSR provides accurate player ratings based on situational impact, economic factors, and statistical performance in competitive matches.

Table of Contents

Overview

Open Soup Rating is designed to evaluate Valorant player performance beyond simple kill/death statistics. The system considers:

  • Situational Impact: How kills and deaths affect round outcomes based on player counts (XvX situations)
  • Economic Factors: The relative value of eliminations based on equipment loadouts
  • Performance Consistency: Individual performance metrics like damage efficiency and assist rates
  • Round Context: Whether the spike is planted and team positioning

Key Features

  • Modular Design: Easy to customize and extend with new components
  • Data-Driven: Built using analysis of 44,080 player-match performances from North American Premier competitive play
  • Comprehensive: Accounts for situational context that traditional stats miss
  • Open Source: Fully transparent methodology and implementation

The Rating Formula

The Open Soup Rating combines four main components through a weighted formula:

Final Rating = Σ(Component_i × Weight_i × Normalization_i) × Scaling_Factor

Core Components

  1. Kill Contribution (KillContrib) - Weight: 43.14%

    • Measures positive impact from eliminations
    • Based on XvX win probability changes and economic modifiers
    • Accounts for situational context (spike planted, player counts)
  2. Death Contribution (DeathContrib) - Weight: 43.14%

    • Measures negative impact from deaths
    • Uses same XvX methodology as kills but inverted
    • Balanced with kill contribution for net impact assessment
  3. Average Performance Rating (APR) - Weight: 5.20%

    • Assists per round played
    • Measures teamwork and supporting play
  4. Average Damage per Round Adjusted (ADRa) - Weight: 8.51%

    • Damage dealt above expected damage from kills
    • Accounts for armor values when calculating expected damage
    • Measures damage efficiency beyond securing eliminations

XvX Impact Methodology

The core innovation of OSR is using actual win probabilities for different player count scenarios:

  • 5v5 (no spike): 49.5% attacker win rate
  • 5v4 (no spike): 71.0% attacker win rate
  • 4v4 (spike planted): 62.1% attacker win rate
  • 3v2 (spike planted): 84.4% attacker win rate

When a player gets a kill, the system:

  1. Determines the before/after XvX situation
  2. Calculates the win probability change for their team
  3. Applies economic modifiers based on equipment values
  4. Weights the impact by the specific round context

Normalization and Weights

Statistical Foundation

The Open Soup Rating system was calibrated using comprehensive data from North American Premier competitive matches spanning Episode 9 Act 1 through Episode V25 Act 4. This dataset includes:

  • Total Matches: 4,408 competitive matches
  • Player Performances: 44,080 individual player-match combinations
  • Competition Levels: Contender (East/West) and Invite divisions
  • Time Period: Multiple competitive seasons ensuring diverse meta representation

Component Weights

The component weights were derived through statistical analysis to optimize rating accuracy:

{
  "KillContrib": 0.4314443970577043,    // 43.14%
  "DeathContrib": 0.4314443970577043,   // 43.14%  
  "APR": 0.05202608006591386,           // 5.20%
  "ADRa": 0.08508512581867748           // 8.51%
}

Weight Rationale:

  • Kill/Death Contribution (86.28% combined): These components capture the most impactful moments in rounds - eliminations that directly change win probabilities
  • APR (5.20%): Supporting play through assists, while valuable, has less direct impact on round outcomes
  • ADRa (8.51%): Damage efficiency beyond kills provides context for consistent performance but is secondary to actual eliminations

Normalization Parameters

All components are normalized to achieve a standard distribution (mean = 0, standard deviation = 1) based on the competitive dataset:

{
  "KillContrib": {"mean": 2.916, "std": 3.614},
  "DeathContrib": {"mean": -2.916, "std": 2.129},
  "APR": {"mean": 0.282, "std": 0.517},
  "ADRa": {"mean": 66.121, "std": 69.760}
}

Normalization Process:

  1. Calculate each component's raw value
  2. Apply Z-score normalization: (value - mean) / std
  3. Apply component weights to normalized values
  4. Sum weighted components and apply final scaling

Final Rating Scaling

The final rating undergoes additional scaling to produce interpretable values:

{
  "base_rating": 1.0,
  "scaling_factor": 0.56,
  "mean": 1.0354,
  "std": 0.0898
}

This ensures that:

  • Average performance equals 0.0 (by Z-score normalization design)
  • Standard deviation equals 1.0 for meaningful statistical interpretation
  • Exceptional performances achieve positive ratings (typically +1.5 to +6.4 range)
  • Poor performances receive negative ratings (typically -1.5 to -3.3 range)
  • 68% of players fall within ±1.0 (one standard deviation)
  • 95% of players fall within ±2.0 (two standard deviations)

Installation

Requirements

  • Python 3.9+
  • JSON match data from Riot Games API

Quick Installation (Recommended)

Install directly from PyPI:

pip install open-soup-rating

Development Setup

For development or if you want to modify the source code:

  1. Clone the repository:
git clone https://github.com/zachleolewis/OpenSoupRating.git
cd OpenSoupRating
  1. Install in development mode:
pip install -e .
  1. Test the installation:
python main.py

Quick Start

Command Line Interface

Using the OSR command (after pip install):

# Calculate ratings from match data
osr calculate match_data.json --output ratings.json

# List available rating components
osr list-components

# Get version information
osr version

# See all options
osr --help

Using the interactive calculator (development):

python main.py

Options include:

  • Calculate ratings from match directories
  • Extract individual components
  • List available components
  • Save results to JSON files

Python Library Usage

import open_soup_rating as osr

# Calculate ratings for a single match
ratings = osr.calculate_rating("path/to/match.json")

# Calculate ratings with player information
detailed_ratings = osr.calculate_rating_with_player_info("path/to/match_directory/")

# Get individual component values
components = osr.get_components("path/to/match.json", ["KillContrib", "DeathContrib"])

print(f"Available components: {osr.list_components()}")

Example Output

{
  "player_puuid_match_id": {
    "rating": 0.845,
    "player_info": {
      "name": "PlayerName",
      "tagline": "TAG",
      "kills": 18,
      "deaths": 12,
      "assists": 7,
      "rounds_played": 24
    },
    "components": {
      "KillContrib": 3.24,
      "DeathContrib": -2.18,
      "APR": 0.29,
      "ADRa": 74.5
    },
    "normalized_components": {
      "KillContrib_norm": 0.492,
      "DeathContrib_norm": 0.492,
      "APR_norm": 0.015,
      "ADRa_norm": 0.120
    }
  }
}

Understanding the Components

The Open Soup Rating system evaluates player performance through four distinct components, each capturing different aspects of competitive play. These components work together to provide a comprehensive assessment that goes far beyond traditional kill/death ratios.

Kill Contribution (KillContrib)

Purpose: Measures the positive impact of eliminations based on situational context and economic factors.

Methodology: The system evaluates each kill by analyzing how it changes the round's win probability for the player's team. This goes beyond simply counting kills to assess their actual value in winning rounds.

Calculation Process:

  1. Pre-Kill Analysis:

    • Determine alive player counts for both teams (e.g., 4v3, 5v2)
    • Check if spike is planted
    • Record round timing and context
  2. XvX Impact Calculation:

    • Look up win probability for the pre-kill situation
    • Look up win probability for the post-kill situation
    • Calculate the probability change for the killer's team

    Example: A kill that changes 3v3 (49% win rate) to 3v2 (74.6% win rate) provides +25.6% win probability

  3. Economic Modifiers:

    • Calculate the loadout value difference between killer and victim
    • Apply economic multipliers based on relative equipment investment
    • Higher-value eliminations (killing an expensive loadout with a cheap one) receive bonuses
  4. Final Component Value:

    KillContrib = base_impact × economic_modifier
    

Key Insights:

  • Eliminating an enemy with superior equipment provides additional value
  • Spike planted status significantly affects the impact calculation

Example Values:

  • Typical range: 0.0-9.1 points
  • Median performance: ~2.8 points
  • 75th percentile: ~3.7 points

Death Contribution (DeathContrib)

Purpose: Measures the negative impact when a player dies, using the same sophisticated methodology as kill contribution.

Methodology: Death Contribution is essentially the inverse of Kill Contribution, calculating how much a player's death hurts their team's chances of winning the round.

Calculation Process:

  1. Pre-Death Analysis:

    • Record the XvX situation before the player dies
    • Note spike planted status and round timing
    • Assess the player's equipment loadout
  2. XvX Impact Calculation:

    • Calculate win probability before death (e.g., 4v4 = 50.5%)
    • Calculate win probability after death (e.g., 3v4 = 27.0%)
    • Determine probability loss for the player's team (-23.5%)
  3. Economic Impact:

    • Factor in the value of equipment lost upon death
    • Consider the relative investment compared to the killer
    • Losing expensive equipment while the enemy has cheap gear increases the negative impact
  4. Final Component Value:

    DeathContrib = -(base_impact × economic_modifier)
    

Key Characteristics: Key Characteristics:

  • Always produces negative values (deaths always hurt your team)
  • Deaths with expensive equipment carry additional negative weight
  • Balanced with KillContrib to show net round impact

Example Values:

  • Typical range: -5.6 to 0.0 points
  • Median performance: ~-2.3 points
  • 25th percentile: ~-2.8 points

Average Performance Rating (APR)

Purpose: Captures supporting play and teamwork through assist contributions.

Simple but Meaningful: While APR appears straightforward, it provides crucial context about a player's role and impact beyond direct eliminations.

Calculation:

APR = total_assists / rounds_played

Why Assists Matter:

  • Team Play Indicator: High assist rates suggest good positioning and team coordination
  • Support Role Recognition: Players who enable teammates get appropriate credit
  • Consistency Metric: Shows reliable contribution across all rounds

Important Notes:

  • Assists include both damage contributions and ability assists within the elimination time window
  • Normalized across all competitive data to ensure fair weighting
  • Lower weight (5.20%) reflects supportive rather than primary impact

Example Values:

  • Typical range: 0.0-1.4 assists per round
  • Median performance: ~0.2 assists per round
  • 75th percentile: ~0.4 assists per round

Average Damage per Round Adjusted (ADRa)

Purpose: Measures damage efficiency beyond what's expected from kills, indicating consistent performance and finishing ability.

The Problem with Raw ADR: Traditional Average Damage per Round doesn't account for the fact that players with more kills naturally deal more damage. ADRa solves this by calculating damage efficiency relative to eliminations achieved.

Calculation Process:

  1. Expected Damage Calculation: For each kill a player achieves, calculate expected damage based on victim's armor:

    expected_damage_per_kill = {
        'No Armor': 100,
        'Light Armor': 125,  # 100 + 25
        'Heavy Armor': 150,  # 100 + 50
        'Regen Armor': 125   # 100 + 25
    }
    
  2. Total Expected Damage:

    total_expected = sum(expected_damage for each kill)
    
  3. ADRa Calculation:

    ADRa = max(0, (total_damage_dealt - total_expected) / rounds_played)
    
  4. Floor at Zero: Negative values are set to 0 to ensure ADRa only measures positive damage efficiency

What ADRa Reveals:

  • High ADRa: Player consistently damages multiple enemies, weakens targets for teammates, or finishes wounded opponents efficiently
  • Low ADRa: Player tends to secure kills with minimal excess damage, potentially indicating good target selection or cleanup kills
  • Consistent High ADRa: Indicates strong aim, positioning, and ability to apply pressure beyond just securing eliminations

Example Values:

  • Typical range: 0.0-272.6 points
  • Median performance: ~63.5 points
  • 75th percentile: ~79.1 points

Component Synergy

Balanced Assessment: The four components work together to provide a complete picture:

  • Kill/Death Contribution: Measures round impact through eliminations
  • APR: Recognizes teamwork and supporting play
  • ADRa: Assesses consistent damage pressure and efficiency

This multi-faceted approach ensures that different playstyles and roles are fairly evaluated within the competitive context.

Project Structure

The Open Soup Rating system is organized into a clean, modular architecture that separates concerns and makes the codebase easy to understand and extend.

OpenSoupRating/
├── main.py                          # Command-line interface and entry point
├── README.md                        # This documentation
├── 
├── open_soup_rating/                # Main package directory
│   ├── __init__.py                  # Package initialization and public API
│   │
│   ├── core/                        # Core calculation engine
│   │   ├── __init__.py
│   │   ├── rating_calculator.py     # Main rating calculation logic
│   │   └── component_registry.py    # Component management system
│   │
│   ├── components/                  # Individual rating components
│   │   ├── __init__.py
│   │   ├── kill_contrib.py          # Kill contribution calculator
│   │   ├── death_contrib.py         # Death contribution calculator
│   │   ├── apr.py                   # Average Performance Rating
│   │   └── adra.py                  # Average Damage per Round Adjusted
│   │
│   ├── data/                        # Data loading and management
│   │   ├── __init__.py
│   │   └── loader.py                # Data loading utilities
│   │
│   └── utils/                       # Utility functions
│       ├── __init__.py
│       ├── config.py                # Configuration management
│       └── validation.py            # Data validation functions
│
├── data/                            # Configuration and reference data
│   ├── weights.json                 # Component weights
│   ├── normalization_params.json    # Statistical parameters
│   ├── xvx_data.json               # XvX win probabilities
│   └── loadout_cost_analysis.json  # Economic data
│
└── docs/                           # Additional documentation
    ├── LICENSE.md                  # Full license text
    └── TRADEMARK.md               # Trademark policy

Key Directories Explained

open_soup_rating/ - Main Package

The core library that can be imported and used in other projects. Contains all the calculation logic and utilities needed to compute ratings.

open_soup_rating/core/ - Calculation Engine

  • rating_calculator.py: Contains the main functions (calculate_rating, get_components) that orchestrate the rating calculation process
  • component_registry.py: Manages the available rating components, handles dynamic loading and registration

open_soup_rating/components/ - Rating Components

Each file contains the calculation logic for one rating component:

  • kill_contrib.py: XvX-based kill impact calculation with economic modifiers
  • death_contrib.py: XvX-based death impact calculation (inverse of kills)
  • apr.py: Simple assists-per-round calculation
  • adra.py: Damage efficiency calculation with expected damage adjustment

open_soup_rating/data/ - Data Management

  • loader.py: Functions to load match data from files, directories, or data structures

open_soup_rating/utils/ - Utilities

  • config.py: Configuration loading and default values
  • validation.py: Match data format validation

data/ - Configuration Files

Contains the calibrated parameters derived from competitive match analysis:

  • weights.json: The optimized weights for each component
  • normalization_params.json: Statistical parameters for standardization
  • xvx_data.json: Win probability data for all player count scenarios
  • loadout_cost_analysis.json: Economic impact data for equipment valuations

Architecture Benefits

Modularity: Each component is self-contained and can be easily modified or replaced Extensibility: New components can be added without changing existing code Testability: Individual components can be tested in isolation Maintainability: Clear separation of concerns makes the codebase easy to understand Reusability: Core library can be imported and used in other projects

Adding Custom Components

The modular architecture makes it easy to add new rating components:

  1. Create a new file in open_soup_rating/components/
  2. Implement a function following the standard signature
  3. The component will be automatically discovered and registered
  4. Use in calculations by including it in the components_to_use parameter

This structure ensures that the Open Soup Rating system remains flexible and extensible while maintaining clean, understandable code.

Shows consistent damage output and finishing ability.

Library Usage

Core Functions

calculate_rating(match_data, **kwargs)

Calculates OSR ratings from match data.

Parameters:

  • match_data: File path, directory path, or loaded match data
  • weights: Custom component weights (optional)
  • normalization_params: Custom normalization parameters (optional)
  • components_to_use: List of components to include (optional)

calculate_rating_with_player_info(match_data, **kwargs)

Same as above but includes detailed player information in output.

get_components(match_data, component_names=None, **kwargs)

Extract individual component values without final rating calculation.

Custom Components

Add your own rating components:

def calculate_headshot_rate(player, match, damage_data, economic_data, xvx_data, **kwargs):
    """Calculate headshot percentage component."""
    stats = player.get('stats', {})
    total_kills = stats.get('kills', 0)
    headshots = stats.get('headshotKills', 0)
    
    if total_kills == 0:
        return 0.0
    
    return headshots / total_kills

# Register the component
osr.add_component('HeadshotRate', calculate_headshot_rate)

# Use in calculations  
ratings = osr.calculate_rating("match.json", components_to_use=['KillContrib', 'HeadshotRate'])

Data Requirements

Match Data Format

OSR requires JSON match data from the Riot Games API v2. Key required fields:

{
  "matchInfo": {
    "matchId": "uuid",
    "mapId": "/Game/Maps/...",
    "gameVersion": "..."
  },
  "players": [
    {
      "puuid": "player_uuid", 
      "gameName": "PlayerName",
      "tagLine": "TAG",
      "teamId": "Blue",
      "stats": {
        "kills": 18,
        "deaths": 12, 
        "assists": 7,
        "roundsPlayed": 24
      }
    }
  ],
  "roundResults": [
    {
      "roundNum": 1,
      "playerStats": [
        {
          "puuid": "player_uuid",
          "kills": [{"victim": "victim_puuid", "timeSinceRoundStartMillis": 15000}],
          "damage": [{"receiver": "victim_puuid", "damage": 150}],
          "economy": {"loadoutValue": 4500, "armor": "Heavy"}
        }
      ]
    }
  ]
}

Data Sources

The system works with:

  • Riot Games API: Official tournament match data
  • Community APIs: Third-party match data providers
  • Local Files: Saved match data in JSON format

Configuration

Default Configuration

The system uses optimized weights and normalization parameters derived from tournament data:

{
  "weights": {
    "KillContrib": 0.4314,
    "DeathContrib": 0.4314, 
    "APR": 0.0520,
    "ADRa": 0.0851
  },
  "normalization_params": {
    "KillContrib": {"mean": 2.889, "std": 3.614},
    "DeathContrib": {"mean": -2.360, "std": 2.129},
    "APR": {"mean": 0.282, "std": 0.517},
    "ADRa": {"mean": 66.121, "std": 69.760}
  }
}

Custom Configuration

Override defaults by providing custom parameters:

custom_weights = {
    'KillContrib': 0.40,
    'DeathContrib': 0.40,
    'APR': 0.10, 
    'ADRa': 0.10
}

ratings = osr.calculate_rating("match.json", weights=custom_weights)

Advanced Features

Batch Processing

Process multiple matches efficiently:

# Directory of match files
ratings = osr.calculate_rating("tournament_matches/")

# List of match data
matches = [match1_data, match2_data, match3_data]
ratings = osr.calculate_rating(matches)

Component Analysis

Analyze individual performance aspects:

# Get only kill/death impact
kd_components = osr.get_components("match.json", ["KillContrib", "DeathContrib"])

# Analyze damage efficiency
damage_components = osr.get_components("match_dir/", "ADRa")

# Get all components
all_components = osr.get_components("match.json")

Tournament Analysis

Example workflow for tournament evaluation:

import open_soup_rating as osr

# Calculate all player ratings
tournament_ratings = osr.calculate_rating_with_player_info("tournament_data/")

# Find top performers
top_players = sorted(tournament_ratings.items(), 
                    key=lambda x: x[1]['rating'], reverse=True)

print("Top 10 Players:")
for i, (player_key, data) in enumerate(top_players[:10]):
    player_info = data['player_info']
    print(f"{i+1}. {player_info['name']}#{player_info['tagline']}: {data['rating']:.3f}")

# Save results
with open("tournament_results.json", "w") as f:
    json.dump(tournament_ratings, f, indent=2)

Contributing

We welcome contributions to improve the Open Soup Rating system!

Development Setup

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure all tests pass
  5. Submit a pull request

Code Style

  • Follow PEP 8 guidelines
  • Use type hints where possible
  • Write docstrings for all functions
  • Include unit tests for new features

Adding Components

To add a new rating component:

  1. Create a function following the component interface
  2. Place it in open_soup_rating/components/
  3. Add tests in the tests/ directory
  4. Update documentation

Component function signature:

def calculate_component_name(
    player: Dict[str, Any],
    match: Dict[str, Any], 
    damage_data: Dict[str, int],
    economic_data: Dict[str, Any],
    xvx_data: Dict[str, Any],
    **kwargs
) -> float:
    """Component calculation logic here"""
    return component_value

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).

The AGPL-3.0 license ensures that:

  • The source code remains open and free
  • Any modifications or improvements must be shared back to the community
  • Network use of the software requires providing source code to users
  • Commercial use is allowed with proper attribution and source code sharing

See LICENSE.md for the full license text.

Acknowledgments

  • Built using match data from North American Premier Contender and Invite divisions
  • Tournament data spanning Episode 9 Act 1 through Episode V25 Act 4
  • XvX probability data derived from 44,080 competitive match performances
  • Normalization parameters trained on comprehensive competitive tournament statistics

Open Soup Rating - Transparent, data-driven Valorant performance analysis.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opensouprating-1.1.1.tar.gz (38.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

opensouprating-1.1.1-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file opensouprating-1.1.1.tar.gz.

File metadata

  • Download URL: opensouprating-1.1.1.tar.gz
  • Upload date:
  • Size: 38.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opensouprating-1.1.1.tar.gz
Algorithm Hash digest
SHA256 b6839b803fc606d10450876f971d7c444c8517a5844dace4ec4367b921434a13
MD5 e225418d02f2a2e0098236ce30177e2c
BLAKE2b-256 8f80f13d07e00cffe9cd22090024a562f7de4c0e87b67ef2b45d56c4421c0f72

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensouprating-1.1.1.tar.gz:

Publisher: release.yml on zachleolewis/OpenSoupRating

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opensouprating-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: opensouprating-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opensouprating-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d61179707e99db0474d6e57ae1be092df559ecdef248824a7f9725a2c1894256
MD5 ac207b8b46ca786f94e944892a87534d
BLAKE2b-256 6d7c31d5217d0b2acd69d32a76c50c2f4578dd92d0c584db0758c31c730c2d28

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensouprating-1.1.1-py3-none-any.whl:

Publisher: release.yml on zachleolewis/OpenSoupRating

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page