1. Home
2. Article Catalog
3. Multi-factor approach to understanding a portfolio's country risk exposure

Article

Multi-factor approach to understanding a portfolio's country risk exposure

Nick Zincone

How do you assess your portfolio’s risk exposure when the entities you invest in are international, complex and face a multitude of regulatory challenges? How do you assess the risk exposure to a company that is headquartered in a relatively “safe” country, but has key subsidiaries spread across more volatile, less-known locations?

By utilizing country risk models, we can provide a new and bigger picture of your portfolio’s country risk exposure. The following article will draw on these risk factors and weigh your portfolio’s investment strategy based on key factors such as the revenue distribution and the location of the company’s headquarters to highlight your investment exposure.

## Getting Started

For this analysis, we plan to utilize data and the associated models available available within LSEG's comprehensive data set.  To get started we will need to import the Refinitiv Data (RD) Library responsible for risk predictions. In addition, we'll use some popular charting packages to highlight the risk exposure based on our portfolio of holdings.

    	
import refinitiv.data as rd
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
plt.style.use('dark_background')




    	
# Open a session into the desktop.  The session represents the interface to extract our risk data.
rd.open_session()





## Process Workflow

The following workflow outlines concrete steps involved in evaluating the risk exposure for a given portfolio of holdings. The steps have been intentionally broken out into well-defined segments for the purposes of reusability as well as a basis for understanding the processing details.

1. Portfolio Definition
To simplify the algorithm, the following notebook will assume an existing portfolio has been defined within the Eikon or Refinitiv Workspace desktop application. The portfolio definition provides the user multiple options to pull in specific details related to the holdings within the portfolio. Users can create their own portfolios, define the list of companies and the holdings for each, and can generate a risk factor breakdown based on country.
2. Clean the Portfolio
When constructing a portfolio, there may be instances where some of the positions may not define a risk factor, for example, cash. To analyze our exposure, we need to remove these holdings to properly weigh and calculate risk within each country.
3. Data Preparation
With a clean portfolio, we'll need to prepare the data set by calculating the risk, based on a weighted factor. Because we may have removed holdings from our data set, this will require an adjustment to the weight of each position. Coupled with the StarMines' risk factor calculation, we can prepare the a weighted risk across the portfolio.
4. Visualization and Analysis
Finally, we can put it all together by bucketing our results into a suitable generation of charts showing a breakdown of risk across each country and where the greatest exposure exists.

## 1. Portfolio Definition

To analyze the exposure of your holdings, we can utilize the risk factors defined within the StarMine Countries of Risk model. For our analysis, we assume the user has utilized the power of the PAL (Portfolios & Lists) manager app defined within the Eikon or Refinitiv Workspace desktop application. Users simply create the list of instruments and what they are holding. The PAL app assigns a weighted factor based on the market value of each holding. Coupled with these calculated values, we can utilize the capabilities of the StarMine database to pull down the risk factors and country details. These measures will represent the basis of our analysis.

### StarMine Countries of Risk Model

Available to the user are a number relevant properties required to analyze risk based on where the company does business. For our analysis, we are pulling down the following:

• Countries of Risk Fraction
The fractional exposures allocated to the top 10 countries of risk. While many companies do business in more than 10 countries, it is entirely possible the list of countries may be limited to 1 or only a few participating countries.
• The name of the country
The list of the top 10 countries for each portfolio holding. These countries are used to highlight the distribution of holdings to evaluate our exposure.

The StarMine Countries of Risk Model uses four sources of data, which are, in order of importances:
• Revenue distribution by geography
• The location of a company's headquarters
• The country where its primary equity security listing trades
• Financial reporting currency

The model provides estimates on the countries to which a company is exposed, and estimates a fractional contribution to each. The higher the value, the higher exposure to the country.

    	
# Retrieve our portfolio and specify the risk factors for each
fields = ['TR.PortfolioConstituentName','TR.PortfolioWeight', 'TR.CoRFractionByCountry',
'TR.CoRFraction','TR.CoRFraction.countryname']

# Choose a portfolio (Users simply change the value below to reflect their defined portfilio)
data = rd.get_data('Portfolio(SAMPLE_GL_DEV)', fields)





By utilizing the 'Portolio()' syntax, the library will take care of extracting and pulling in multiple attributes specific to the referenced portfolio, in particular, the risk factors.

    	
# Provide a quick snapshot to show what we captured





## 2. Clean the Portfolio

In the above portfolio, we can see a partial list of instruments, a breakdown of the risk factors for the top 10 countries and the weight of each instrument within the portfolio. However, it may be possible the portfolio contains instruments that are not susceptible to exposure, for example cash, and thus will not carry any risk. As such, we need to clean out this data. As a result, the weights may be skewed and thus will need to be normalized after we clean the data.

    	
# Remove all rows/entries where we are missing relevant data.
# The missing data within our risk column needs to be removed.
data.dropna(inplace=True, subset=['Countries of Risk Fraction'])





Depending on the selected portfolio, there may be many positions removed or none at all.

## 3. Data Preparation

With our data clean, we can begin the process of preparing the portfolio for our risk analysis. We do this by utilizing the risk factors we captured per country to perform the following calculations:
Because we potentially removed positions within our portfolio, we'll need to adjust each positions weight relative to the new portfolio.
• Weighted Risk
Based on the adjusted weight of each holdings and StarMines' country of risk fraction, calculate a weighted risk value for each country per Instrument.
• Group Country factors
The goal of our analysis is to highlight the total risk exposure within a country, based on our holdings. We do this by grouping our country weighted risk factors across all holdings.

    	
# Calculate the adjusted weight factor...
adjusted_weight_factor = data['Portfolio Weight'].sum()

# Then use this value to created a new 'Adjusted Weight' value for our collection
data['Adjusted Weight'] = data['Portfolio Weight'].transform(lambda x: x / adjusted_weight_factor)




    	
# Show the new 'adjusted weight' calculation...





With our portfolio clean, the above table shows the positions that have been removed and a newly adjusted weight for our updated portfolio.

### Weighted Risk

The Weighted Risk represents the risk based on our holdings for each position and the risk factor defined. This value will act as the basis to determine the exposure for each country. The following represents the details around this calculated value:

    	
# Prior to calculating the 'weighted risk', we need to backfill the Adjusted weights...




    	
# Calculate our weighted risk
data['Weighted Risk'] = data[adj_weight] * data['Countries of Risk Fraction']





### Group Country Factors

Now that we have derived our Weighted Risk values for each country, we can now sum the total risk for each country within our portfolio.

    	
# Bucket the total weighted risk across our portfolio, grouped by country
group = data.groupby('Country Name')['Weighted Risk'].sum().to_frame()
group





## 4. Visualization and Analysis

With our data categorized and totaled, we can begin our analysis. The following function prepares our calculated metrics for presentation. Specifically, we choose to sort our collection to highlight the countries with the greatest exposure.

    	
# First, normalize the factors to represent %100 coverage...
adjusted_risk_factor = group['Weighted Risk'].sum()
group['Adjusted Weighted Risk'] = group['Weighted Risk'].transform(lambda x: x / adjusted_risk_factor)

# Sort the table to highlight the countries with the greatest exposure
result = group.sort_values(by='Adjusted Weighted Risk', ascending=False)

# Prepare
result.reset_index(level=0, inplace=True)




    	
# Output a tabular display of our exposure sorted based on the highest risk
result





### Presentation

For simplicity, we've defined a couple of presentation displays, bar and pie chart, representing a way to easily highlight countries with the greatest exposure:

    	
# Bar graph function showing exposure
def bar(data):
plt.rcParams['figure.figsize'] = (13,7)
fig, ax = plt.subplots()
ax.tick_params(labelrotation=90)

ax.bar(
x = np.arange(data.shape[0]),
height = data['Adjusted Weighted Risk'],
tick_label=data['Country Name'],
alpha=0.5
)

# Remove the borders from the graph - leave the bottom
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)

ax.tick_params(bottom=False, left=False)

# Add a faint grid
ax.yaxis.grid(True, alpha=0.1)
ax.xaxis.grid(True, alpha=0.1)

# Add labels and a title. Note the use of labelpad and pad to add some
# extra space between the text and the tick labels.
ax.set_ylabel('Risk Factor', labelpad=15, fontsize=16, color='cyan')
ax.set_title("Portfolio Country Risk Exposure", pad=15, fontsize=16, color='cyan')

fig.tight_layout()




    	
# Show to top 5...




    	
# Top 10...




    	
# Pie Chart function showing exposure
def pie(data):
labels = data['Country Name'].tolist()
values = data['Adjusted Weighted Risk'].tolist()

# Use hole to create a donut-like pie chart
fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=.3)])

fig.update_layout(
font={
'size': 9,
'color': 'cyan'
},
margin={
't': 50,
'b': 0,
'r': 0,
'l': 0,
},
title={
'text': f'Portfolio Country Risk Exposure',
'y': 0.95,
'x': 0.425
},
titlefont={
'size': 20,
'color': 'cyan'
},
paper_bgcolor='rgba(0,0,0,0)'
)

fig.show()




    	
# Show to top 5...




    	
# Top 10...