Blending Risk Scores Image

Unlock the Methodology of how the Centers for Medicare & Medicaid Services (CMS) ensures Medicare Advantage (MA) health plans receive accurate payments that truly reflect the health risks of their enrollees.

The CMS-Hierarchical Condition Categories (CMS-HCC) risk adjustment model is central to this process, utilizing advanced algorithms to calculate each enrollee's relative risk based on crucial demographic information and diagnosis codes, ultimately producing a critical raw risk score.

As we look ahead to calendar year (CY) 2025, CMS is continuing the phased implementation of its recent 2024 CMS-HCC model (V28). By blending 67% of risk scores from the latest V28 model with 33% from the previous V24 model, CMS is phasing in V28 as a more accurate model for current risk score calculations. This approach allows health plans to adapt to the new V28 model, as the impact of diagnoses has been revised. In this article, we delve into the latest updates to the CMS-HCC model and demonstate how to leverage MScore® and Python to process these risk scores seamlessly. We'll also walk you through a hands-on example to appling the normalization factor with MA coding pattern adjustment factor to raw risk scores as well as blending the risk scores according to CMS's guidelines.

Leveraging the power of MScore® and Python, we'll guide you through processing a sample input Person and Diagnosis file to apply both the 2024 CMS-HCC (V28) and 2020 CMS-HCC (V24) models. The continuing script will then process the normalization factor along with the coding adjustment factor of the raw risk scores before blending the risk scores to a single dataframe that accurately reflects CMS 2025 payment approach.

The V28 model will be gradually phased in over three years starting in 2024, with V28 model weighted alongside V24 model.

Year V24 V28
2024 67% 33%
2025 33% 67%
2026 0% 100%

Source: Centers for Medicare & Medicaid Services 2024 announcement [1]

Two adjustment factors are applied to the raw risk score before being used in health plan payment calculations:

  1. Normalization Factor - The purpose of this factor is to adjust risk scores for growth between the denominator year and payment year keeping the average risk score to 1.0.
  2. Medicare Coding Pattern Adjustment Factor - The purpose of this factor is to account for differences in diagnostic coding between MA and Medicare Fee-For-Service (FFS) plans.

Below are the adjustment factors for 2025:

Plans Models Normalization Coding Pattern Adjustment
MAOs and other non-PACE Plans 2024 CMS-HCC Model (V28) 1.045 5.9%
2020 CMS-HCC Model (V24) 1.153 5.9%
2023 ESRD Model Dialysis 1.044 5.9%
2023 ESRD Model Functioning Graft 1.074 5.9%
2025 RxHCC Model for MA-PD plans 1.073 5.9%
2025 RxHCC Model for PDPs 0.955 5.9%
PACE Plans 2017 CMS-HCC Model (V22) 1.157 5.9%
2019 ESRD Model Dialysis 1.103 5.9%
2019 ESRD Model Functioning Graft 1.159 5.9%
2025 RxHCC Model for PACE organizations 1.163 5.9%

Source: Centers for Medicare & Medicaid Services 2025 announcement [2]

The equations highlighted in this article focus on the process of calculating risk scores from raw risk scores then blending risk scores across the weighted CMS-HCC V24 and V28 models. The Normalization Factor and Coding Patterm Adjustment Factor adjusts raw risk scores by dividing them by a model-specific normalization factor, and then applying a reduction to account for the Medicare Advantage (MA) coding pattern adjustment factor:

Risk Score = (Raw Risk Score / Normalization Factor) * (1 - MA Coding Pattern Adjustment Factor)

The Blending Risk Score equation combines the risk scores of V24 and V28 models, weighting them to reflect the phased transition, where the V24 score is weighted at 33% and the V28 score at 67%:

Blended Risk Score = ((0.33 * V24 Risk Score) + (0.67 * V28 Risk Score))

Together, these equations create a harmonized score that integrates both versions, ensuring a smooth transition to the updated CMS-HCC model.



Prerequisites

Before proceeding, ensure you have the following:

In this article, we recommend starting with, "Three Steps to Visualizing Your Risk Scores with MScore® & Python" to understand how to run the CMS-HCC model with MScore® and visualizing the results. The Python script provided below builds upon these topics, offering a method to execute MScore® for both the V24 and V28 models for CY 2025 and save the output as a CSV file in your working directory.

Note:
To run the script, you'll need Person and Diagnosis data files along with a license key, which can be obtained by registering Here

import sourcedefender # This package is required at the top
from mscore import AuthorizeLicense, MScore
import pandas as pd

# Define the staging key for authorization
staging_key = 'your_license_key_here'

# Validate the authorization using the staging key
auth = AuthorizeLicense(staging_key).validate()

# Define model versions and years
versions_years = [
    {'version': 'V24', 'year': '2025'},
    {'version': 'V28', 'year': '2025'}
]

# Loop through each version and year
for item in versions_years:
    version = item['version']
    year = item['year']

    # Read person and diagnosis data from CSV files
    person_df = pd.read_csv('person.csv')
    diag_df = pd.read_csv('diag.csv')

    # Initialize the MScore model
    model = MScore(
        authorizer=auth,
        year=year,
        version=version,
        model='CMS-HCC',
        person_data=person_df,
        diag_data=diag_df,
        columns='all-fields',
        rf_output=False,
    )

    # Calculate the risk scores
    scores_data = model.score_mscore()

    # Create DataFrames
    scores_df = pd.DataFrame(scores_data.risk_scores)

    # Get the number of beneficiaries
    num_bene = len(scores_df)

    # Create filename
    filename = f"{version}_{year}_scores_{num_bene}.csv"

    # Save DataFrame to CSV
    scores_df.to_csv(filename, index=False)

    # Print confirmation
    print(f"Saved: {filename}")



Normalization Factor with Coding Pattern Adjustment Factor and Blending of V24 and V28

The Python script showcases the process of applying the normalization factor with the coding adjustment factor then blending of the weighted risk scores from the V24 and V28 models. It begins by reading raw risk scores from V24 and V28 created previously in the prerequisite section, followed by applying the normalization factor with coding adjustment factor then blending the results according to the CMS guidelines for the weighted versions of V24 and V28 of calender year (CY) 2025. This guide breaks down the execution of the script that previously utlized MScore® into three straightforward steps: reading the V24 and V28 model CSV data files and defining constant values, processing normalization factor with coding adjustment factor, and finally blending V24 and V28 models for export as a CSV data file.

Note:
We round the results to a maximum of three decimal places, as required by the CMS-HCC model.

Step 1: Reading the V24 and V28 Model CSV Files from MScore® and adding Constants

The initial code snippet sets up the environment for processing risk score data by importing key libraries and defining essential variables. It brings in numpy and pandas for data manipulation and uses Path from pathlib to manage file paths. The code specifies paths to CSV files containing risk scores for two model versions, V24 and V28, and loads these files into DataFrames. Additionally, it defines constants for normalization factors, coding pattern adjustment, and blending weight, which will be crucial for adjusting and merging the risk scores.

import numpy as np
import pandas as pd
from pathlib import Path

# Load data from the generated CSV files
score_file_v24 = Path.cwd().joinpath('V24_2025_scores_10.csv')
score_file_v28 = Path.cwd().joinpath('V28_2025_scores_10.csv')

# Load the CSV data
df_24 = pd.read_csv(score_file_v24)
df_28 = pd.read_csv(score_file_v28)

# Ensure both DataFrames are sorted by 'MBI' and aligned
common_mbis = np.intersect1d(df_24['MBI'], df_28['MBI'])
df_24 = df_24[df_24['MBI'].isin(common_mbis)].sort_values(by='MBI', ascending=True).reset_index(drop=True)
df_28 = df_28[df_28['MBI'].isin(common_mbis)].sort_values(by='MBI', ascending=True).reset_index(drop=True)

# Define constants
NORMALIZATION_FACTORS = {
    'V24': 1.153,
    'V28': 1.045
}
CODING_PATTERN_ADJUSTMENT = round(5.9/100, 3)
BLENDING_FACTORS = {
    'V24': round(33/100, 3),
    'V28': round(67/100, 3)
}


Step 2: Adding Normalization factor and Coding Pattern Adjustment Factor

The adding_factors function applies normalization factor and coding pattern adjustment factor to risk scores generated from MScore® to align them with CMS guidelines. It first identifies columns containing '_SCORE', such as CNA_SCORE, and generates two new sets of columns: normalized scores prefixed with 'normalized_' and payment risk scores prefixed with 'payment_'. The function applies these factors scores by dividing them by the provided normalization factor, rounding to three decimal places for consistency. It then applies the coding pattern adjustment factor by multiplying the normalized scores by *(1 - CODING_PATTERN_ADJUSTMENT). Finally, it outputs the results—including MBI, CNA_SCORE, normalized_CNA_SCORE, and payment_CNA_SCORE—into a CSV file. This process is executed for both the V24 and V28 datasets to prepare them for further blending and analysis.

# Function to add in normalization factor and coding adjustment factor
def adding_factors (df, version, normalization_factor, output_file):
    # Identify relevant score columns
    scores = [col for col in df.columns if '_SCORE' in col]
    normalized_scores = ['normalized_' + col for col in scores]
    payment_risk_scores = ['payment_' + col for col in scores]

    # Apply normalization and coding adjustment
    df[normalized_scores] = np.round(df[scores].to_numpy() / normalization_factor, 3)
    df[payment_risk_scores] = np.round(df[normalized_scores].to_numpy() * (1 - CODING_PATTERN_ADJUSTMENT), 3)

    # Save the processed DataFrame to CSV
    df[['MBI', 'CNA_SCORE', 'normalized_CNA_SCORE', 'payment_CNA_SCORE']].round(3).to_csv(output_file, index=False)
    print(f"{version} scores saved to {output_file}")

adding_factors(df_24, 'V24', NORMALIZATION_FACTORS['V24'], 'processed_factors_v24.csv')
adding_factors(df_28, 'V28', NORMALIZATION_FACTORS['V28'], 'processed_factors_v28.csv')

The chart snippet below shows the processed risk scores for the CMS-HCC model versions V24 and V28 after applying the normalization factor and coding pattern adjustment factor.

processed_factors_v24

MBI CNA_SCORE normalized_CNA_SCORE payment_CNA_SCORE
1001 0.817 0.709 0.667
1002 1.380 1.197 1.126
1003 0.355 0.308 0.290

processed_factors_v28

MBI CNA_SCORE normalized_CNA_SCORE payment_CNA_SCORE
1001 0.812 0.777 0.731
1002 0.952 0.911 0.857
1003 1.506 1.441 1.356

Step 3: Blending V24 with V28

The blend_v24_v28 function combines the payment risk scores from two versions—V24 and V28—using specified blending factors. It loads the processed data from CSV files for each version, applies the corresponding blending factor to the payment_CNA_SCORE column in each DataFrame, and retains only the MBI column for identification. The function then creates a new DataFrame with a blended score by adding the adjusted payment scores from both versions. The final blended results are saved into a new CSV file, ensuring the data is ready for further analysis or reporting.

# Function to blend the two versions for blended payment score
def blend_v24_v28 (v24_file, v28_file, blended_output_file):
    # Load V24 and V28 processed data
    df_v24 = pd.read_csv(v24_file)
    df_v28 = pd.read_csv(v28_file)

    # Apply blending factors to V24 and V28 scores
    df_v24['payment_CNA_SCORE']*= BLENDING_FACTORS['V24']
    df_v28['payment_CNA_SCORE']*= BLENDING_FACTORS['V28']

    # Combine payment scores into single DataFrame
    blended_df = df_v24[['MBI']].copy() # Keep only the 'MBI' column
    blended_df['blended_payment_CNA_SCORE'] = df_v24['payment_CNA_SCORE'] + df_v28['payment_CNA_SCORE']

    # Save the blended DataFrame to CSV
    blended_df.round(3).to_csv(blended_output_file, index=False)
    print(f"Blended scores saved to {blended_output_file}")

# Run functions and print results
blend_v24_v28('processed_factors_v24.csv', 'processed_factors_v28.csv', 'blended_scores.csv')

The table presents the calculated risk scores for ten individuals, identified by their Medicare Beneficiary Identifier (MBI). It highlights the blended_payment_CNA_SCORE column, which combines the scores from the V24 and V28 models using their respective blending weights. These weighted scores are then merged with the payment_CNA_SCORE values, resulting in the final blended_payment_CNA_SCORE.

Blended Payment Risk Scores

MBI blended_payment_CNA_SCORE
1001 0.710
1002 0.946
1003 1.005
1004 0.860
1005 1.068
1006 2.183
1007 0.723
1008 0.553
1009 0.505
1010 3.396

Conclusion

By following the steps outlined in this guide, you’ve learned how to apply normalization and coding pattern adjustments and blending to calculate a payment risk score. This approach ensures that your risk adjustment calculations align with the latest CMS methodologies, enabling you to estimate risk adjustment payments, validate scores from CMS reports and deliver actionable insights.


Disclaimer: The code examples above are for illustrative purposes and may require proper environment setup, license keys, or additional parameters to work effectively in your specific context. Always refer to MScore® documentation or reach out to support@riskadjustmentmodel.com if you have questions.

[1] Centers for Medicare & Medicaid Services. (2024). *CY 2024 announcement*. CMS.gov. https://www.cms.gov/files/document/2024-announcement-pdf.pdf

[2] Centers for Medicare & Medicaid Services. (2024). *CY 2025 announcement*. CMS.gov. https://www.cms.gov/files/document/2025-announcement.pdf

[3] Centers for Medicare & Medicaid Services. (2021). *Risk Adjustment Methodology*. CSSC Operations. https://csscoperations.com/internet/csscw3.nsf/RiskAdjustmentMethodologyTranscript.pdf