ACADEMIC ARTICLE SERIES: ECONOMICS AND FINANCE INVESTIGATION:

Create an ESG Pitchbook with React and Next.js using the Refinitiv Data Library for TypeScript

This article was written by the winner of 2023 Q.4 Refinitiv Academic Competition. It is aimed at Academics from Undergraduate level up.

Refinitiv Academic Article Competition

Refinitiv is pleased to host its Academic Article Competition.
This competition aims at helping students - from undergraduate to postdoctorate level - to publish their work on internationally recognized platforms and to aid them in (i) academic style writing, (ii) implementing code in their study, (iii) coding, (iv) financial knowledge, (v) developing a contacts network in the finance, and (vi) generating ideas for economics/finance papers.

It will have to be an economic or financial study, and in a style akin to these articles:

As per these examples, the length of the study in question is not a limit; there is no min. or max. length your article has to be to qualify. However, it needs to show and explain the code used in its study. It can be in any (reasonable) format (such as LaTeX, PDF, Word, Markdown, …) but it would preferably be a Jupyter Notebook (you may choose to use Refinitiv’s CodeBook to write it). You will also need to use Refintiv APIs/data-sets before being published on our Developer Portal – were it to win the competition.
There is no min. or max. number of winners; any work exceeding our standards will be selected for publication. Thus, do not hesitate to share this with your colleagues, informing them of this opportunity, they are not competitors, but collaborators; you may even choose to work together on this project.

We are ready and happy to provide any help in writing such articles. You can simply email Jonathan Legrand - the Competition Leader - at jonathan.legrand@lseg.com.

 

ESG Pitchbook

 

The Refinitiv Data Platform offers a vast selection of financial, as well as non-financial data. With ESG growing in importance for investors, the demand for educational and insightful reporting has grown tremendously. Over the past decade, consumer expectations have changed toward preferring interactive and accessible solutions, which favor the web as a platform. In the following, we will use the Refinitiv Data Library for TypeScript to fetch company and ESG data to automate the creation of interactive pitch books, ready to be consumed by investors and academia.

While there are several ways to deliver content on the web, we will use React to render dynamic components through Next.js, a popular framework for static as well as dynamic applications. We will make use of incremental static regeneration to pre-load popular data and speed up load times for our end users.

 

Setting up our project

 

There are many ways to get started with Next.js, but we will use the automatic setup for TypeScript.

    	
            npx create-next-app@latest --typescript
        
        
    

After running this command, you should end up with a basic starter project. Run

    	
            npm run dev
        
        
    

to start a local development server.

Fetching data from Refinitiv

First, we’ll have to add the Refinitiv Data Library for TypeScript to our dependencies.

    	
            npm install @refinitiv-data/data
        
        
    

Before we can fetch data from Refinitiv, we have to acquire credentials for accessing the Refinitiv Data Platform. For development purposes, you can store your credentials as environment variables in a file called .env.local:

    	
            

REFINITIV_APP_KEY=<your app key>

REFINITIV_USER_NAME=<your Refinitiv username / email address>

REFINITIV_PASSWORD=<your Refinitiv password>

Once our environment variables are ready to be used, we can create a file containing our data-fetching code, for the purpose of this guide, we’ll save it as refinitiv.ts.

Our first step is to create a session that is used for sending requests later on. Here, we need to decide whether we should use a desktop session, which only works if an instance of Refinitiv Workspace is running, or a platform session which directly connects to the Refinitiv Data Platform. If you can rely on the fact that your end users are technical or want to build an application for testing purposes, a desktop session will be easier to use. If, however, you would like to deploy your application, you will have to use a platform session.

    	
            

import { Session } from "@refinitiv-data/data";

 

/**

Create and open a platform session

*/

export async function createSession() {

  const sess = Session.Platform.Definition({

    appKey: process.env.REFINITIV_APP_KEY!,

    userName: process.env.REFINITIV_USER_NAME!,

    password: process.env.REFINITIV_PASSWORD!,

    takeSignOnControl: true,

  }).getSession();

 

  return sess.open();

}

Up next, we can use our session to fetch some data! We will use the fundamentals and reference endpoint to gather company and ESG information for an arbitrary company using its RIC.

Before we can fetch the data, we’ll have to decide which fields we want to fetch and convert the result to a type-safe data structure. We can get a preview of the available fields in the data item browser in Refinitiv Workspace.

    	
            

export interface DataResult {

  RIC: string;

  CommonName: string;

  BusinessSummary: string | null;

  ESGCombinedScore: number | null;

  ESGEnvironmentalScore: number | null;

  ESGGovernanceScore: number | null;

  ESGSocialScore: number | null;

}

 

const relevantFields = [

  "TR.RIC",

  "TR.TRESGScore",

  "TR.EnvironmentPillarScore",

  "TR.SocialPillarScore",

  "TR.GovernancePillarScore",

  "TR.BusinessSummary",

  "TR.CommonName",

];

 

function rowToData(ric: string, row: Record<string, unknown>): DataResult {

  return {

    RIC: ric,

    ESGCombinedScore: (row["TR.TRESGScore"] as number) || null,

    ESGEnvironmentalScore: (row["TR.EnvironmentPillarScore"] as number) || null,

    ESGGovernanceScore: (row["TR.GovernancePillarScore"] as number) || null,

    ESGSocialScore: (row["TR.SocialPillarScore"] as number) || null,

    BusinessSummary: (row["TR.BusinessSummary"] as string) || null,

    CommonName: row["TR.CommonName"] as string,

  };

}

Now that we can parse our result into an interface, we can go ahead and send a request using the session we opened previously.

    	
            

import { FundamentalAndReference } from "@refinitiv-data/data";

import { format, parseISO } from "date-fns";

 

export async function getData(

  session: Session.Session,

  ric: string,

  date?: string

): Promise<DataResult | null> {

  if (!date) {

    date = format(new Date(), "yyyy-MM-dd");

  }

 

  // <https://github.com/Refinitiv-API-Samples/Example.DataLibrary.TypeScript/blob/main/src/2.Content/2.8-Fundamental/fundamental-reference.ts>

  const result = await FundamentalAndReference.Definition({

    universe: [ric],

    fields: relevantFields,

    parameters: {

      SDate: date,

    },

  }).getData(session);

 

  const row = (result.data.table as Record<string, unknown>)[ric] as Record<

    string,

    unknown

  >;

  if (!row) {

    return null;

  }

 

  return rowToData(ric, row);

}

In addition to fundamental and reference data, the Refinitiv Library for TypeScript offers further endpoints such as search, which we can use for finding the company RIC in advance.

Incrementally generating static pages

To render data on a page, we need to ensure the page exists and the data is fetched on the server side. For this, we use static pages and incremental static regeneration to refresh pages in the background after a certain duration. This ensures users experience fast page loads while data stays fresh enough for our use case.

In a new file [ric].tsx in the pages directory, we start by adding the following function

    	
            

import { GetStaticProps } from "next/types";

import {

 DataResult,

 getData,

} from ".. .. refinitiv";

 

export interface OrganizationProps {

  data: {

    company: DataResult;

  };

}

 

export const getStaticProps: GetStaticProps<OrganizationProps> = async (

  context

) => {

  const RIC = context.params?.ric;

  if (typeof RIC !== "string") {

    return {

      notFound: true,

    };

  }

 

         const session = await createSession();

 

  let companyData;

         try {

                 companyData = await getData(session, RIC);

         } finally {

                 await session.close();

         }

        

  if (!companyData) {

           return {

      notFound: true,

    };

  }

 

  return {

           company: companyData,

  };

 

  if (!data) {

    return {

      notFound: true,

    };

  }

 

  return {

    // Passed to the page component as props

    props: {

      data,

    },

    revalidate: 60 * 10, // Attempt to regenerate at most once every 10 minutes

  };

};

getStaticProps is called on page load and fetches the data which are then returned to the page as props. Using the revalidate attribute, we ensure that we page is loaded at most once every 10 minutes.

We might have a couple of companies in mind that are loaded relatively often, perhaps related to our business use case, or just high-performing companies. While getStaticProps ensures fast page loads after the initial request, we can pre-load certain pages at build time, to get fast results immediately using getStaticPaths.

    	
            

export async function getStaticPaths() {

  return {

    paths: ["AAPL.O", "MSFT.O", "GOOG.O"].map((p) => ({ params: { ric: p } })),

    fallback: true,

  };

}

Fallback is required to allow requesting companies not known at build time, and the array added in paths provides default companies that should always be loaded upfront.

Providing an interactive experience

As all prerequisites are met, we can finally render our data!

    	
            

export function LoadingPage() {

  return (

                 <span>Loading page...</p>

         );

}

 

export default function OrganizationPage({ data }: OrganizationProps) {

         // If the page is not yet generated, this will be displayed

  // initially until getStaticProps() finishes running

  if (router.isFallback) {

    return <LoadingPage />;

  }

 

         return (

                 <div>

                          <p>{data.company.CommonName}</p>

                          <p>{data.company.BusinessSummary}</p>

                          <p>{data.company.ESGCombinedScore}</p>

                 </div>

         );

}

If you haven’t done so, we can go ahead and start our server with npm run dev and visit 

[<http://localhos:3000/AAPL.O>](<http://localhos:3000/AAPL.O>) 

in our browser. You should see a basic page with the values we just added. From here on, we can add a proper layout, charts, and more.

References

 

This Hackathon team is from the Technische Universität München

Tags