Article
Understand and Retrieve your portfolio's credit risk exposures
Overview
A UK based food manufacturer has excess cash and has set up its own fund to invest into equities and shares. For reporting purposes, this company wants to know for each position in their 15 portfolios, what is its credit exposure? By knowing the price of the CDS 5Y, the food manufacturer can determine how much they have to pay to offset the credit risk associated to an instrument within a portfolio. When utilizing Refinitiv's desktop Portfolio management tools, users have access to a powerful suite of details. Unfortunately, the credit exposure information is neither available directly within the portfolio nor in any templates of the portfolio reporting tool.
Does that mean it’s not possible to provide this information when the CDS 5Y price is quoted by Refinitiv? Absolutely not! In this article, we will describe how to leverage Python to enrich the portfolio details with the credit risk exposures.
Getting Started
The following notebook utilizes our Portfolio Analytics desktop capabilities to review and assess your issuer level and portfolios' credit risk exposure. Refinitiv's extensive content set, coupled with our Refinitiv Data Libraries offers a simple mechanism to retrieve your portfolio details and retrieve current pricing data used to generate desired results.
For convenience, the workflow generates output in an Excel sheet containing the results of our analysis.
Learn more
To learn more about the Refinitiv Data Library for Python please join the Refinitiv Developer Community. By registering and logging into the Refinitiv Developer Community portal you will have free access to a number of learning materials like Quick Start guides, Tutorials, Documentation and much more.
Getting Help and Support
If you have any questions regarding using the API, please post them on the Refinitiv Data Q&A Forum. The Refinitiv Developer Community will be happy to help.
Some Imports to start with
# The Refinitiv Data Library for Python
import refinitiv.data as rd
# Popular container/dataframe to capture and minipulate data we extract
import pandas as pd
# Some basic UI
from refinitiv_widgets import Checkbox
from refinitiv_widgets import ProgressBar
rd.__version__
Open the data session
The open_session() function establishes a data session to retrieve our content within the desktop.
Note: A desktop session is intentionally defined here to take advantage of some convenient capabilities within the desktop application, such as the ability to use the Portfolio & Lists Manager (PAL) feature.
rd.open_session()
Code Setup
It's usually best practice to define constants that are potentially used through the coding segments within your notebook. A constant is a special type of variable whose value cannot be changed. Using a constant in a script improves its readability, its reusability and its maintainability.
# Maximum number of items a request to get data can retrieve
MAX_ITEMS_PER_REQUEST = 10000
# Fields to request
PORTFOLIO_NAME_FLD = "TR.PortfolioName"
PORTFOLIO_CONSTITUENTS_FLD = "TR.PortfolioConstituentName"
PORTFOLIO_SHARES_FLD = "TR.PortfolioShares"
ASSET_CATEGORY_FLD = "TR.AssetCategory"
CDS_PRIMARY_RIC_FLD = "TR.CDSPrimaryCDSRic"
# Some columns used below by the dataframe
ASSET_CATEGORY_COL = "Asset Category Description"
PRIMARY_CDS_RIC_COL = "Primary CDS RIC"
CLOSE_PRICE_COL = "Close Price"
# Price data
HISTORICAL_CLOSE = "HST_CLOSE"
Portfolio Definition
At the heart of the following workflow is the ability for users to evaluate their portfolios to determine the credit exposure. For simplicity, we are going to use a pre-built portfolio defining the list of instruments that can be used within our analysis.
Note: Users can define their own portfolios by utilizing the power and capabilities of the Portfolio & List Manager service within the desktop.
The following fields are requested to provide a breakdown of the constituents within the portfolio:
The Name of the Portfolio [TR.PortfolioName]
The issuer Name of the Constituents in Portfolio [TR.PortfolioConstituentName]
The Number of units of portfolio constituent [TR.PortfolioShares]
The full description of the asset category [TR.AssetCategory]
The primary CDS RIC for this reference entity [TR.CDSPrimaryCDSRic]
The 'TR.CDSPrimaryCDSRic' field represents the fundamental data field defining the RIC (Refinitiv Identification Code) of the 5-year Credit Default Swap (CDS 5Y). This field is important for portfolio managers as it allows them to monitor how much credit exposure they have on the securities they are placing their bets on.
The id of the portfolio.
# The id of the portfolio as defined within the desktop (Eikon/Refinitiv Workspace)
portfolio_id = "SAMPLE_EU_DEV_FI"
# Open the portfolio. Define the list of fields required for our analysis
portfolio_columns = [PORTFOLIO_NAME_FLD,PORTFOLIO_CONSTITUENTS_FLD,PORTFOLIO_SHARES_FLD,
ASSET_CATEGORY_FLD,CDS_PRIMARY_RIC_FLD]
portfolio = f"Portfolio({portfolio_id})"
df = rd.get_data(portfolio, portfolio_columns)
if "Instrument" not in df.columns:
print(f"Impossible to open the portfolio {portfolio_id}.")
# Display some of the data...
df.head(10)
Data Cleansing
In some cases, the referenced portfolio of instruments may include entries not related to CDS-based positions. As such, we'll need to clean the data by removing all unrelated entries. Specifically, the rows where the Primary CDS RIC cell is unavailable, i.e. missing values or values filled with <NA>.
For example, the above display shows the first 10 positions within our portfolio where a number of them contain missing values for the Primary CDS RIC. We are only interested in the positions where a valid RIC is present.
# Remove all rows/entries where we are missing relevant data.
# To simplify this, we'll first ensure blank values contain <NA>.
df[ASSET_CATEGORY_COL] = df[ASSET_CATEGORY_COL].replace('', pd.NaT)
df[PRIMARY_CDS_RIC_COL] = df[PRIMARY_CDS_RIC_COL].replace('', pd.NaT)
# Now we can simply remove all rows where the following columns contain <NA>
df = df.dropna(subset=[ASSET_CATEGORY_COL, PRIMARY_CDS_RIC_COL], axis=0)
# The results will only contain entries where we have populated data
df.head(10)
If you pay attention to the left column value, which represents an index into the table, you can get a sense of specific rows we filtered out.
Return only price data for bonds?
As a final step to data cleansing, we can optionally choose to process only bonds within our portfolio. The following UI prompt is a simple way to filter out all positions that are not classified as a bond.
# The final, cleansed data will live within a container called 'assets'
assets = df
bonds = Checkbox(label='Check to retrieve bonds only')
bonds
if bonds.checked:
assets = assets[assets[ASSET_CATEGORY_COL].str.contains('Bond')].copy()
assets
Price Data
At this step, we want to retrieve the snapshot price for all the Primary CDS RICs. This is the key step within our workflow as this value represents the price of the CDS instrument that we'll assess as the credit risk value. Refinitiv allows us to retrieve snapshot data for a list of instruments up to MAX_ITEMS_PER_REQUEST size. In that case, we must split the list of instruments into manageable chunks. After retrieving the snapshot data of each chunk, we'll merge the prices within our the original filtered portfolio.
# split a list into 'nbItems' chunks
def split_list_into_chunks(rics, nbItems):
n = max(1, nbItems)
return (rics[i:i+n] for i in range(0, len(rics), n))
# In the case our universe of RICs is very large, we'll capture the universe within manageable chunks
rics = list(assets[PRIMARY_CDS_RIC_COL])
rics_cnt = len(rics)
cds_5Y_rics_batch = split_list_into_chunks(rics, MAX_ITEMS_PER_REQUEST)
# Request for the pricing data for each item within our batch
#
# Note: The batch (cds_5Y_rics_batch), is a special type that returns an iterator. Once iterated,
# the variable has completed. That is, you will not be able to run through the batch again.
# If yhou wish to iterate again, you will have generate the value again by executing the
# cell above.
cds_5y_prices_df = pd.DataFrame()
# Simple progress bar
pb = ProgressBar(value=0, color="green")
display(pb)
print(f"Processing a total of {rics_cnt} positions...")
for items in cds_5Y_rics_batch:
pb.value += int(min(MAX_ITEMS_PER_REQUEST, len(items))/rics_cnt * 100) # Progress bar increment
res = rd.get_data(universe = items, fields = [HISTORICAL_CLOSE])
cds_5y_prices_df = cds_5y_prices_df.append(res, ignore_index=True)
print("Done")
Processing a total of 72 positions...
Done
# Display the price data for each constituent within the portfolio
cds_5y_prices_df.head(10)
# Merge the initial dataframe with the realtime values
assets[CLOSE_PRICE_COL] = cds_5y_prices_df[HISTORICAL_CLOSE].to_list()
# Clean out any missing values
result = assets.dropna(subset=[CLOSE_PRICE_COL])
result.head(10)
Analysis
Now that we have successfully cleaned and priced the CDS 5Y positions, we can now use this value to derive our credit risk. That is, the 'Close Price' value is by definition a financial derivative that allows an investor to swap or offset their credit risk with that of another investor. With this data, it may be desirable to export the results within a simple spreadsheet for further analysis. We perform this task next.
Export to excel
Export the result to excel for future usage.
filename = f"Credit_Risk_{portfolio_id}.csv"
result.to_csv(filename)
# export to excel
filename = f"Credit_Risk_{portfolio_id}.xlsx"
result.to_excel(filename)
Conclusion
While the strengths of the PAL - Portfolio & List Manager within the desktop are extremely powerful, we may come across scenarios where we need to extend the capabilities of what is available. By coupling the features of PAL and the Refinitiv Data Libraries, we can easily extract the required CDS 5Y price values for a given portfolio. By retrieving the CDS 5Y price, we can assess the credit risk exposure of the portfolio.
- Register or Log in to applaud this article
- Let the author know how much this article helped you