Calculating Credit Valuation Adjustment (CVA) for OTC derivatives portfolio

Authors:

Dr. Haykaz Aramyan
Developer Advocate Developer Advocate
Marc Laillat
Senior Product Manager Senior Product Manager

Introduction

Credit Valuation Adjustment (CVA) is an important metric in the financial industry, used to account for the counterparty credit risk in derivative transactions. Traditional methods of valuing derivatives often overlook the potential risk of a counterparty defaulting, which can lead to significant financial losses. CVA addresses this gap by adjusting the market value of derivative instruments to reflect the credit risk associated with the counterparties involved.

In recent years, the importance of accurately calculating CVA has grown, driven by regulatory requirements and the need for better risk management practices. Financial institutions are now required to report CVA as part of their risk management and regulatory compliance processes. This requires the development of models and tools to estimate CVA more accurately.

This article utilizes async-cva endpoint of LSEG data platform to calculate CVA and leverages a set of custom modules designed to simplify the process. We will detail the PortfolioLoader, CounterpartyManager, CVACalculator, PricingParameters, and CVAVisualizer modules, which together form a system for managing portfolios, counterparty information, and performing CVA calculations. Throughout the article, we will explain these modules in more detail and perform a CVA calculation on a sample portfolio created in MARVAL. MARVAL acts as a trade repository, but you can use your own trade repository and still utilize the async-cva endpoint. As for the CVA calculation methodology, please refer to CVA technical documentation here.

It is important to note that the current version of the CVA calculation library supports only Interest Rate Swaps (IRS) and Cross Currency Swaps (CCS).

Creating a Portfolio

Creating a portfolio is the first step in our CVA calculation process. In this article, we will create a sample portfolio using the MARVAL app available in LSEG Workspace. This section will guide through the steps to create a new portfolio using the provided Excel template. Note that using the MARVAL Trade Import is just one approach; you can also create trades manually from the MARVAL app.

1. Access MARVAL Trade Import:

  • Navigate to the MARVAL Trade Import page. This is the platform where you can upload and manage your portfolios.

2. Download the Excel Template:

  • Download the excel template included in this article to your local machine. This template is pre-configured with the necessary fields and formats required by MARVAL. It can be found on the right panel in this artcile and in the associated GitHub Repo.

3. Create a New Portfolio:

  • Go back to the MARVAL Trade Import page.
  • Choose "Swaps" as the instrument type. This is important because the current version of our CVA calculation system supports only Interest Rate Swaps (IRS) and Cross Currency Swaps (CCS).
  • Select "Create New Portfolio" from the available options or select a portfolio of your own.

4. Upload the Template:

  • Click on the "Browse" button and select the filled Excel template from your local machine. This will load the transactions to the portfolio.
  • Select the instruments.
  • Click on the Import to Portfolio.

The system will process the file, create a new portfolio (if not selected from the existing portfolios) and transactions based on the provided data.

5. Verify the Portfolio:

  • Once the upload is complete, verify that the portfolio has been created successfully. Below is the screenshot of created transactions:

By following these steps, you will have successfully created a new portfolio in MARVAL, which will be used in the subsequent sections for loading, managing, and performing CVA calculations. This step ensures that all necessary data is available for CVA computation.

Import required libraries

To start, we import the required modules from our project. The modules are explained throughout the article and the detailed implementation can be found in the GitHub repo.

    	
            

import pandas as pd

from modules.PortfolioLoader import PortfolioLoader

from modules.EntityManager import CounterpartyManager, CounterpartyParams, SelfEntity

from modules.CVACalculator import CVACalculator, PricingParameters

from modules.CVAVisualizer import CVAVisualizer

Loading Portfolios and Instruments

Once we have a portfolio we can load it along with the transactions and the instruments using PortfolioLoader module. To begin, we create an instance of PortfolioLoader to be used to interact with the portfolios.

    	
            portfolio_loader = PortfolioLoader()
        
        
    

The class is initialized with empty lists for portfolios, transactions, and instruments and defines several public and private functions which use set of LSEG Data Platform endpoints to show/load prortfolios and load transactions and instruments. Below is the part of the module just to demonstrate the structure, for detailed implementation of the module, including the used endpoints please refer to the GitHub repository.

    	
            

class PortfolioLoader:

    def __init__(self):

        pass

        

    def show_portfolios(self):

        # Code to fetch and return available portfolios

        pass

 

    def load_portfolio(self, portfolio_name):

        # Code to load a specific portfolio

        pass

 

    def load_transactions(self):

        # Code to load transactions for the loaded portfolio

        pass

 

    def load_instruments(self):

        # Code to load instruments for the loaded portfolio

        pass

To display the available portfolios, we use the show_portfolios function. The function uses the user's uuid to fetch the portfolios via user-data/portfolios/v1/common/{scope} endpoint.

    	
            

portfolios = portfolio_loader.show_portfolios()

portfolios


The load_portfolio method loads a specific portfolio by fetching its details from MARVAL. It requires the portfolio name as an argument:

    	
            portfolio = portfolio_loader.load_portfolio('CVASAMPLE')
        
        
    

The load_transactions method below loads the transactions associated with the loaded portfolio.

    	
            

transactions = portfolio.load_transactions()

transactions

Finally, we load the instruments associated with the portfolio using the load_instruments method. The instruments variable is a large dictionary with all the details about the instruments which we will be using for CVA calculation. For demonstration purposes, we simplify the response by providing the instrument type, tag, and the counterparty in a separate DataFrame below.

    	
            

instruments = portfolio.load_instruments()

 

instruments_brief = pd.DataFrame([{

    'instrumentType': instrument['instrumentType'],

    'csaTag': instrument['csaTag'],

    "instrumentTag": instrument['instrumentDefinition']['instrumentTag']

} for instrument in instruments])

instruments_brief

It should be noted that we can also load the portfolio instruments as of a date by passing the date to the load_instruments function. eg. load_instruments(effective_date = '2024-10-11')

At this stage, we have effectively loaded the instruments using the PortfolioLoader module to be used for CVA calculations. For detailed implementation of the module, please refer to the GitHub repository. The next step before proceeding with CVA calculation is to enhance the counterparty data.

Managing Counterparty Information

Managing counterparty information is a critical step in the CVA calculation process. We define and use the CounterpartyManager module to manage counterparty information. The module is responsible for enhancing counterparty data, including credit risk profiles and recovery rates, which are crucial for accurate CVA calculations.

Below, we create an instance of the CounterpartyManager class to be used to interact with the counterparty data.

    	
            counterparty_manager = CounterpartyManager()
        
        
    

The CounterpartyManager class defines several public and private functions to get and enhance counterparty data. For detailed implementation of the module, please refer to the GitHub repository.

    	
            

class CounterpartyManager:

    def __init__(self):

        self.counterparties = None

 

    def get_counterparties(self, counterparties, org_id=None):

        #Retrieve counterparty information based on provided org_id or return all counterparties if org_id is not provided.

        pass

 

    def enhance_counterparty_data(self, reference_entity, enhancement_data):

        #Enhances the data of a specified counterparty with additional information.

        pass

 

    def to_df(self):

        #Returns a DataFrame of the counterparties.

        pass

 

    def to_dict(self):

        #Returns a dictionary of the counterparties.

        pass

To display the available counterparties, we use the get_counterparties method:

    	
            

counterparties = counterparty_manager.get_counterparties(portfolio.counterparties)

counterparties.to_df()

Above we have the counterparties assigned to specific transactions. However, for CVA calculation, we require additional information about the counterparty risk profiles, including the risky curve used to imply the default probability of the counterparty, the currency in which the collateral is posted, the recovery rate of a counterparty, and the deal. We use the CounterpartyParams object to assign the required counterparty data to the counterparties.

This object is then used by the enhance_counterparty_data method to enhance the counterparty risk profile. The method enhances the counterparty data by adding information such as curve ID, collateral currency, and recovery rates. It requires the reference entity and enhancement data as arguments.

Below we use the function to enhance the counterparty details for the transactions in our portfolio.

    	
            

counterparties.enhance_counterparty_data(

            reference_entity="8589934316", 

            enhancement_data = CounterpartyParams(

                                    curve_id="0#BCSBEUAMRBMK=", 

                                    collateral_currency="EUR",

                                    recovery_deal=40, 

                                    recovery_instrument=40

                                    ))

 

counterparties.enhance_counterparty_data(

            reference_entity="8589934333", 

            enhancement_data = CounterpartyParams(

                                    curve_id="0#CUSAXRBMK=", 

                                    collateral_currency="USD",

                                    recovery_deal=40, 

                                    recovery_instrument=40))

 

counterparties.enhance_counterparty_data(

            reference_entity="4295875726",

            enhancement_data = CounterpartyParams(

                                    curve_id="0#UNICEUAMRBMK=", 

                                    collateral_currency="EUR",

                                    recovery_deal=40, 

                                    recovery_instrument=40,

                                    ))

Below, we show The CounterpartyParams class which was used to create an object that holds the risk profile information of the counterparty. The object is also open for providing additional extended parameters for the counterparty.

    	
            

class CounterpartyParams:

    def __init__(self, curve_id, recovery_instrument, recovery_deal, collateral_currency, extended_params=None):

        # initialize the counterparty parameters

        pass

 

    def to_dict(self):

        #Converts the object attributes to a dictionary.

        pass

After enhancing the counterparty data, let's verify the changed by calling get_counterparties function:

    	
            counterparties.to_df()
        
        
    

As we have the enhanced counterparties we set those to our portfolio transactions.

    	
            portfolio.set_counterparties(counterparties)
        
        
    

Now, when we have our transactions with enhanced counterparty data we can proceed with CVA Calculation.

Performing CVA Calculation

We create and use CVACalculator module to perform the CVA calculations. The module uses async-cva endpoint of LSEG data platform to calculate the CVA using the loaded instruments and counterparty data.

Below, we create an instance of the CVACalculator class to be used to perform the CVA calculation.

    	
            cva_calculator = CVACalculator()
        
        
    

CVACalculator defines several private and public functions to perform the CVA calculation. Below is the part of the module to demonstrate the structure, for detailed implementation of the module, please refer to the GitHub repository.

    	
            

class CVACalculator:

    def __init__(self):

        pass

 

    def calculate_cva(self, portfolio, pricing_parameters, entity):

        # Code to perform CVA calculation

        pass

We will use calculate_cva function accepting pricing parameters and self entity as an input to perform the calculation. To create the inputs we will use PricingParameters and PrimaryEntity objects.

Below, we define the pricing parameters using the PricingParameters class for our portfolio.

    	
            

pricing_parameters = PricingParameters(

        valuation_date="2024-12-24",

        simulation_count=1000,

        self_reference_entity="LSEG",

        self_recovery_rate_percent=40.0,

        report_ccy="GBP"

)


The PricingParameters class is used to define the parameters required for pricing and CVA calculations, such as valuation date, simulation count as well as self reference entity and recovery rates used for the Debt Valuation Adjustment (DVA). The object is also open for providing additional extended parameters for the pricing

    	
            

class PricingParameters:

    def __init__(self, valuation_date, simulation_count: int, 

                 self_reference_entity: str, self_recovery_rate_percent: float, 

                 report_ccy: str, extended_params=None):

        # initialize the pricing parameters

        pass

 

    def to_dict(self):

        #Converts the object attributes to a dictionary.

        pass

Below, we also define the primary entity involved in the CVA calculation using the SelfEntity class. Risky curve is assigned to the self reference entity.

    	
            

self_entity = SelfEntity(

        name="LSEG",

        collateral_currency="USD",

        recovery_instrument=40.0,

        recovery_deal=40.0,

        curve_id="0#BBBGBPBMK="

)

The SelfEntity class is used to define the primary entity involved in the CVA calculation, including its name, collateral currency, recovery rates, and curve ID. The object is also open for providing additional extended parameters for the Self Entity.

    	
            

class SelfEntity:

    def __init__(self, name: str, collateral_currency: str, recovery_instrument: float, 

                 recovery_deal: float, curve_id: str, extended_params=None):

        #Initialize the self entity parameters

        pass

    

    def to_dict(self):

        #Converts the object attributes to a dictionary.

        pass

To perform the CVA calculation, we run the calculate_cva method with the defined pricing parameters and self entity:

    	
            

cva_result = cva_calculator.calculate_cva(

        portfolio=portfolio,

        pricing_parameters=pricing_parameters,

        entity=self_entity

)

cva_result

To view the CVA universe and the counterparties involved in the calculation, we can run cva_calculator.cva_universe and cva_calculator.cva_counterparties respectively.

Visualizing CVA Results

Visualizing CVA results is the final step of this prototype which helps interpreting and understanding the credit risk associated with derivative transactions. We created and used the CVAVisualizer module to visualize the results. The module provides several plotting functions for visualizing the CVA results such as allocations, market values and exposures.

Below, we initialize an instance with CVA results which will be used to visualize the CVA results.

    	
            visualizer = CVAVisualizer(cva_result)
        
        
    

The CVAVisualizer class is initialized with the CVA output and defines several public and private functions to visualize different aspects of the CVA calculation results. Below is the part of the module to demonstrate the structure, for detailed implementation of the module, please refer to the GitHub repository.

    	
            

class CVAVisualizer:

    def __init__(self, cva_result):

        self.cva_result = cva_result

 

    def plot_allocations(self, csa_tag=None):

        # Code to plot CVA allocations

        pass

 

    def plot_market_values(self, csa_tag=None):

        # Code to plot market values

        pass

 

    def plot_potential_future_exposure(self, csa_tag=None):

        # Code to plot potential future exposure

        pass

 

    def plot_exposure(self, csa_tag=None):

        # Code to plot negative and positive exposure

        pass

To visualize the CVA allocations for all counterparties, we use the plot_allocations method. This method plots the CVA allocations, providing a view of how the CVA is distributed among different counterparties:

    	
            visualizer.plot_allocations()
        
        
    

To visualize the CVA allocations for a specific counterparty, we can pass the csa_tag of the counterparty to the plot_allocations method:

    	
            visualizer.plot_allocations(csa_tag="CitigroupInc")
        
        
    

The plot_market_values method plots the market values for all counterparties. This visualization helps in understanding the market value exposure to different counterparties:

    	
            visualizer.plot_market_values()
        
        
    

To visualize the market values for a specific counterparty, we pass the csa_tag of the counterparty to the plot_market_values method:

    	
            visualizer.plot_market_values(csa_tag="CitigroupInc")
        
        
    

The plot_potential_future_exposure method plots the potential future exposure to credit risk for a given counterparty:

    	
            visualizer.plot_potential_future_exposure(csa_tag='CitigroupInc')
        
        
    

The plot_exposure method plots the negative and positive exposure for a given counterparty.

    	
            visualizer.plot_exposure(csa_tag='CitigroupInc')
        
        
    

Conclusion

In this article, we demonstrated a pipeline of custom modules designed to simplify the CVA calculation using set of LSEG Data Platform endpoints. We began by creating a portfolio in MARVAL, which serves as the foundation for the CVA calculations. We then detailed the process of loading portfolios and instruments using the PortfolioLoader module, ensuring that all necessary data is available for CVA computation. Next, we managed counterparty information using the CounterpartyManager module, enhancing the counterparty data with counterparty risk profile details. We then performed the CVA calculation using the CVACalculator module, leveraging the PricingParameters and SelfEntity objects to define the necessary parameters and primary entity for the calculation. Finally, we visualized the CVA results using the CVAVisualizer module, providing clear and interpretable visualizations of the CVA allocations, market values, potential future exposure, and overall exposure to credit risk.

By integrating these modules, we have suggested a robust and efficient system for calculating and visualizing CVA, addressing the key challenges and meeting the needs of various stakeholders in the financial industry.

  • Register or Log in to applaud this article
  • Let the author know how much this article helped you
If you require assistance, please contact us here