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:
- 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.
- 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:
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%:
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:
- MScore® License Key
- MScore® Python Package
- Python 3.10 to Python 3.11
- Person Data File
- Diagnosis Data File
- Three Steps to Visualizing Your Risk Scores with MScore® & Python
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.
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.
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 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.
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.
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