Article

Enhancing Portfolio Visualization: Exploring APIs with Python and Jupyter

Nick Zincone
Lead Developer Advocate Lead Developer Advocate

In the world of portfolio management, effective visualization and analysis are paramount for making informed investment decisions.  While the existing software landscape does offer powerful and extensive analysis options, it's impossible to expect all scenarios and use-cases will be covered.  For example, powerful features included are robust reporting frameworks, offering users the ability to make informed investment decisions.  As users become more savvy and sophisticated, these features often introduce limitations, restricting users to predefined metrics and time granularity. However, with the emergence of portfolio APIs, users now have the power to transcend these limitations and tailor their analysis to suit their specific requirements.

In this article, we'll explore the potential and power of portfolio APIs using Python and Jupyter notebooks.  Specifically, we'll discuss LSEGs Portfolio and Management (PAM) APIs, and introduce basic concepts related to expanding the capabilities available within LSEGs Workspace Portfolio Analytics application. Whether you're a seasoned portfolio manager looking to enhance your analytics capabilities or a newcomer seeking to gain deeper insights and possibilities, we'll provide some basic capabilities to elevate your portfolio visualization through the power of APIs.

Prerequisites

For this analysis, the following components will be required to execute the source code available within this article.

  • Delivery Platform Credentials to access Portfolio Analytics
  • Python 3.9 and above

PAM - Portfolio Analytics & Management APIs

The suite of PAM APIs have been designed to expose the underlying details provided by the portfolio service allowing users to perform various types of analysis on their portfolios. PAM APIs delivers multi-currency performance attribution, sophisticated portfolio profiling, and portfolio returns statistics, allowing users to keep portfolios aligned with their investment objectives. PAM API users can access industry-leading methodology through attribution analysis, analyze the structure and characteristics of portfolios and access hundreds of global benchmarks.

Included within this source code package is a simple convenient Python package called '<b>pam</b>'.  This package defines an easy-to-use set of API calls to extract the underlying portfolio details, presenting the results within Python Pandas DataFrames.  Users are not obligated to use this package but you will find it does eliminate the overhead of call management and data processing.  In addition, will serve as a better tool to demonstrate the concepts of the portfolio service.

    	
            

# The library responsible for accessing endpoints and services within LSEG

import refinitiv.data as rd

 

# The convenient package included within this source code example

import pam

from pam.portfolios import *

from pam.analytics import *

 

# Data management and presentation

import pandas as pd

import plotly.express as px

 

# Progress presentation for long-running tasks

from tqdm import tqdm

 

rd.__version__

Open a Session to access data

The refinitiv.data Python library is the gateway into LSEG's data environment. In order to begin the retrieval of content, you must first establish connectivity and authentication within the data environment. LSEG provides access to content within either the desktop (Workspace) or directly to the cloud (Delivery Platform). In either case, it is assumed you have credentials to access content from LSEG and have utilized the Python data library within your own Python environment.

For management and flexibility, it is easiest to define your credentials within the 'refinitiv-data.config.json' file

    	
            

# Open a session into the data platform

# Ensure you have defined your access credentials within the 'refinitiv-data.config.json' file.

session = rd.open_session()

Accessing portfolio content

The suite of services provided by PAM are broken down into logical sections:

  • Metadata
    A series of capabilities to retrieve metadata such as classifications, characteristics, asset classes, etc.
  • Analytics
    The ability to run analysis on both user-defined portfolios and market indices.
  • Portfolio
    Retrieve portfolio details including searching for portfolios and listing header values.

When performing basic analysis, users are required to provide a key element called the Portfolio ID which identifies the specific user-defined portfolio or market index they wish to analyze. Users who are experienced working with LSEG portfolios are quite familiar with the Workspace Apps: PAL (Portfolio and Lists Manager) and PORTF Portfolio Analytics. These Apps are tightly integrated allowing users to easily select the portfolios they wish to analyze. However, when working within PAM, users will be required to pull down the Portfolio ID. The current challenge today is that the Workspace Apps do not expose the Portfolio ID and thus will require some interrogation and searching using a combination of Workspace and PAM APIs.

To support the selection and identification of portfolios for use within the PAM APIs, there are a couple of choices presented below to help.

Portfolio Finder

The PortfolioFinder is a simple, convenient tool allowing users of PAM APIs the ability to identify a specific ID required for purpose of analyzing portfolios. This GUI-based tool provides the ability to sort and filter content for ease of selection.  For example:

The primary purpose of the utility is the display of the portfolioid column.  This ID is the driving key for the usage of the PAM APIs, allowing users to pull up metrics and portfolio details required for analysis.

While this utility does allow for a simple and effective way to interrogate the portfolio service to access the required portfolio IDs, users may prefer a programmatic mechanism to interrogate and select the portfolios for their specific work-flow.  As part of the PAM APIs, a specific portfolio search capability is defined.  The following examples outline what users can do with the APIs:

    	
            

# The portfolio search function defines the mechanism to pull down the required IDs.

help(pam.portfolios.search)

The Portfolio Type drives the main selection of the portfolios of interest.  Here is a brief explanation of the types:

  • FundedPortfolio
    A set of Holdings Statements that represent the holdings of a portfolio over time.  A Holdings Statement is a list of securities, their position amounts (e.g. shares, Par value, etc.) and costs on a particular date.
  • CompositeFundedPortfolio
    A weighted portfolio containing other portfolios or benchmarks. For example, you can use this portfolio type to create a custom benchmark with 50% FT 350 and 50% Barclays Aggregates
  • WatchList
    Simple list of securities with no weights or holdings.
  • MarketIndex
    Representing Indices and benchmarks (S&P, MSCI, etc).
  • CarveOutPortfolio
    A portfolio based on a filter or ‘slice’ of an existing portfolio or benchmark and focused on a specific sector or classification. For example, you could pick FTSE AllShare as a base portfolio and exclude/include all the securities in Consumer Discretionary.
  • ModelPortfolio
    Similar to a FundedPortfolio, but position amounts are specified as weights (e.g. percentages) rather that shares, etc..
  • PeerList
    A List of peers of an instrument. For example, a new peer list gets created when you go to a company page -> peer list and add another company to the default list of peers.
  • MonitorList
    Similar to a WatchList, a list of instruments created in Monitor app

User-defined portfolios

Users will undoubtely need to analyze the portfolios they create.  Whether its just a few or many dozens, the search facilitly does provide a way to pull down all or filter out specific ones of interest. 

    	
            

# Pull down all user-defined portfolios. 

# The 'portfolioTypes' specifier is conveniently defaulted to all user-defined types

pam.portfolios.search()

In the above display, I have created a few test portfolios that are easily visualized and does allow me to refer to the specific outlined portfolioId of interest. However, depending on the number of user-defined portfolios you may be responsible for, further interrogation may be required to narrow down the ones of interest. With search, you can narrow down your collection based on either the name or code fields. As a simple test, if I choose to narrow down any "test" portfolios, I can do the following:

    	
            

# Let's filter down based on a query string contains within "Any" (name or code) fields...

pam.portfolios.search(query="test", queryField="Any", queryCondition="Contains")

Index Portfolios

Depending on their entitlements, users potentially have access to thousands of system-defined indices, acting as benchmarks and used to derive composite portfolios for use. In the above output, there are 2 key columns of data, name and code. When searching for portfolios, the API will allow users to filter out portfolios of interest by utilizing 1 or both of these key columns.

®The number of indices available is extremely large and if there is any desire to get the entire data set, the query will ultimately timeout. That being said, the option to retrieve thousands of results will be difficult to navigate and will likely be unmanageable for use. The service was specifically designed to filter down the result set, allowing users to narrow down the set of portfolios of interest. By specifying very specific queries, users will not only experience a reduction of time but more manageable results to work with.

For this analysis, I plan to choose a benchmark of interest for comparison. Specifically, I'm going to look for an Intercontinental Exchange (ICE) index. At this point, you will likely need to be quite specific in your request to narrow down the one of interest.

    	
            

# The 'portfolioTypes' specifier is required to ensure you are search for index-based portfolios

pam.portfolios.search(portfolioTypes="MarketIndex", 

                      query="ICE BofA Global High Yield European", 

                      queryField="Name", 

                      queryCondition="Contains")

The query I specified above was derived from persistent experimentation. I started off with a basic search and as expected, over a thousand portfolios were returned. I ultimately derived a specific, but relevant query that returned a reasonable result set. From there, I was able to choose the specific benchmark portfolio for my use.

For users that are more experienced, they can utilize the code field. The code field is more specific and if known, can help narrow down results. For example, I'm aware of a specific benchmark that defines the code value: 'HWPE'. Knowing this, I can request using the following:

    	
            

# Specify the code in our query instead

pam.portfolios.search(portfolioTypes="MarketIndex", 

                      query="HWPE", 

                      queryField="Code", 

                      queryCondition="Contains")

From here, I can continue the same trend to narrow down portfolios of interest.  For example, below are 2 benchmark portfolios I wish to compare against.  For this task, I've chosen to use the PortfolioFinder utility as I simply need to identify the ID to perform my analysis.

In the first case, I'm performing a query based on an assumption of the name of the index:

Next, I happen to know the code for the portfolio and will give that a shot:

Portfolio Charts

Once we have successfully narrowed down the portfolios of interest, we can begin to demonstrate some of the value-add capabilities that users can perform using PAM.  For example, within Workspace PORTF, when charting your portfolios, users are limited to the canned frequency of monthly measures.  While this representation does provide a general overview, users may have the need to provide more granular reporting.

The following examples demonstrate how to retrieve the portfolio attribution measures and chart these metrics within both daily and weekly frequencies.

To support these requirements, I have setup a couple of convenient functions that will aid in the extract and presentation.

Step 1 - Data generation definition

Within the following function, contains a code segment that defines the request to retrieve portfolio attribution measures and captures these results that will be available for report generation.

    	
            

# Define the core request to retrieve performance attributes

query_payload = {

    "analysisDateRange": {},

    "baseCurrency": "USD",    

    "portfolioOptions": {

        # Programmatically assign our portoflio ID: "portfolioId": id,

    },

    "cashHandlingOptions": {},

}

 

# The portfolio attribution measures generation function supports the specification

# of multiple portfolios.

def generate_data(ids, start_date, end_date):

    # Ensure the portfolios data is a list

    if isinstance(ids, str):

        ids = [ids]

 

    query_payload["analysisDateRange"]["startDate"] = start_date

    query_payload["analysisDateRange"]["endDate"] = end_date

 

    # Step 1 - retrieve the portfolio labels/names

    portfolios = pam.portfolios.get_portfolios(ids)

 

    # Step 2 - retrieve daily statistics

    df_result = pd.DataFrame()

    for id in tqdm(ids):

        # ID we're analyzing

        query_payload["portfolioOptions"]["portfolioId"] = id

 

        # Retrieve data... 

        performance = pam.analytics.get_performance_attribution(query_payload)

 

        # Organize the data

        df = performance.dailyCumulative[['date','portfolioCumulativeReturn']]

 

        # Provide a friendly label for this data set

        df = df.rename(columns={'portfolioCumulativeReturn':portfolios.headers.loc[id]["name"]})

 

        # Collect the data

        if df_result.empty:

            df_result = df

        else:

            df_result = pd.merge(df_result, df, on ="date")

 

    # Ensure our time scale is the a date-only index

    df_result['date'] = pd.to_datetime(df_result['date'])

    df_result.set_index('date', inplace=True)

 

    print("Done")

    return df_result

Step 2 - Data visualization definition

Once the data has been successfully generated, we can perform a simple visualization of the results.  The chart_data function utilizes the Plotly Express package to chart our data, providing the ability to brush over the data points displaying statistics.

    	
            

def chart_data(data):

    fig = px.line(data, height=600)

 

    # Update layout to move the legend inside the plot area

    fig.update_layout(

        title='Portfolio Analysis',

        plot_bgcolor='black',  # Change plot background color to black

        paper_bgcolor='black',  # Change paper (entire figure) background color to black

        legend_title_text='',

        legend=dict(

            x=0,  # x position of top-left corner of legend

            y=1,  # y position of top-left corner of legend

            bgcolor="rgba(0, 0, 0, 0)",  # semi-transparent white background

            bordercolor="white",

            borderwidth=0

        ),

        font=dict(color='white')  # Change font color to white                

    )

 

    fig.update_yaxes(tickfont=dict(size=10), gridwidth=0.1, gridcolor='grey', title_text="returns")

    fig.update_xaxes(gridwidth=0.1, gridcolor='grey')  # Adjust the x grid lines

 

    fig.show()

Step 3 - Generate and plot

Putting it altogether, we can now simply generate the portfolio attribution measures for our portolio(s) and chart the results.

    	
            

# Example 1 - generate measures for our user-defined portfolio

#             The date is based on the portfolio we created

df = generate_data("840976194", "2020-07-31", "2023-10-31")

    	
            

# Then chart the results...

chart_data(df)

While the above chart can easily be generated within Workspaces' Portf application, the purpose was to demonstrate the basics of charting. With the basics in place, we can move on to something a little more interesting - comparing our portfolio to the 3 benchmarks we chose above.

    	
            

# Example 2 - Include the <index 1> and <index 2> benchmarks for our analysis...

df = generate_data(["840976194", "189063994", "4954351", "4954027"], "2020-07-31", "2023-10-31" )

 

# Then chart the results...

chart_data(df)

By default, the data returned from our portfolio backend contains daily measures across the specified timeframe. From our results, the above chart was produced based on these daily measures. However, we can manipulate the data to present the granularity in any timeframe of our choosing.

    	
            

# Weekly

df2 = df.resample('W').mean()

chart_data(df2)

Summary

Working with GUI-based desktop applications will always be a powerful mechanism for easy and rapid analysis. Their intuitive interfaces and comprehensive capabilities provide users with immediate insights into their portfolios.  However, these applications, despite their ease-of-use and quick analytical abilities, are not without limitations.  Exposing the inner capabilities of portfolio analysis through their APIs, empowers the user to customize, extend, and integrate portfolio data into their workflows seamlessly. Providing users with the underlying data allows the flexibility of accessing and presenting the results within their own applications.  While this article does touch on a very simple subset of the capabilities available, users are encouraged to explore the wealth of PAM APIs to discover the full potential of their data for comprehensive analysis and decision-making. 

  • 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