Porfolio Financial Impact Scoring

Calculate financial impact scores for portfolios

Overview

Climate on Demand financial impact modeling enables organizations to evaluate the potential financial impact of catastrophe events on their facilities. Facilities may be modeled individually (one or more) or as part of a portfolio (a collection of facilities). his endpoint returns financial impact metrics aggregated at the portfolio level for all the facilities specified in a request.

Financial modeling is supported across seven risk categories including floods, heat stress, hurricanes & typhoons, sea level rise, water stress, wildfires, and earthquakes. Climate on Demand models project changes in the future severity or frequency of catastrophe events for each risk category due to climate change.

For each risk category, this Climate on Demand enables you to calculate financial impact metrics that measure the projected financial damages to those facilities. These projections are based on the stated value of the facilities analyzed, the period analyzed, the assumptions made about future change, and other configurable factors. Metrics include annualized damage rates (ADR), standard deviations in ADR (damage volatility), and impact scores that contextualize estimated damages globally.

🍐

Climate on Demand Pro Licensing

This tutorial describes a financial impact modeling process that leverages operations that are are only available to tenants who have licensed Climate on Demand Pro.

In this tutorial, we will review the process for impact scoring a single portfolio. We will review key request parameters to understand how these parameters determine the calculated impact scores. Finally, we will review the impact scores returned. This process is described using sample code in cURL and Python.

Step 1: Calculate financial impact

The Calculate financial impact by portfolio operation calculates the financial impact of climate change on an entire portfolio of facilities (assets).

The request body defines an object with three required parameters: facilities, an array of objects that defines the assets analyzed, th (the time horizon analyzed), and m, which specifies the version of the Financial Impact Methodology used to calculate the impact scores.

curl --request POST \
     --url https://{host}/AppsServices/api/v1/score-portfolio-impact/jobs \
     --header 'Authorization: XXXXXXXXXX' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
    "m": "P.2023.1",
    "th": "2050",
    "rcp": "RCP8.5",
    "facilities": [
        {
        	"id": "1",
        	"name": "newark office",
        	"activity": "office",
        	"street1": "7575 gateway blvd., suite 300",
        	"city": "newark",
        	"state": "ca",
        	"postal_code": "94560",
        	"country": "united states",
        	"latitude": 37.5412000000,
        	"longitude": -122.060610000
        }
    ]
}
def scoreFacilitiesImpact(token, request_json, host):
    endpoint = host + "/cod/AppsServices/api/v1/score-facilities-impact/jobs"
    r = requests.post(url=endpoint, json=request_json, headers={'Authorization':'Bearer ' + token})
    print(f'response code: {r.status_code}') if r.status_code == requests.codes.ok:
        result = r.json()
        return result
else:        
    return None
def getJobResult(token, host, job_id):
    endpoint = host + "/cod/AppsServices/api/jobs/" + job_id
    r = requests.get(url=endpoint, headers={'Authorization': 'Bearer ' + token})
    print(f'response code: {r.status_code}')
    if r.status_code == requests.codes.ok:
        result = r.json()
        return result
else:
    return None
data = {
    "m": "P.2023.1",
    "th": "2050",
    "rcp": "RCP8.5",
    "facilities": [{
        "id": "1",
        "name": "Berkeley Office",
        "activity": "Office",
        "exposure_value": 1000000,
        "street1": "2000 Hearst Avenue",
        "city": "Berkeley",
        "state": "CA",
        "postal_code": "94709",
        "country": "USA",
        "latitude": 37.8734494,
        "longitude": -122.2706614
    }]
}
response_job = scoreFacilitiesImpact(access_token, data, my_host)
my_job = response_job.get('job_id')
time.sleep(30)
result = getJobResult(access_token, my_host, str(my_job))

For each facility, you must specify the ID number of the facility (id) and how that facility is used (activity), e.g. Agriculture, Data Center, Residential.

If the latitude and longitude of the facility are not provided, Climate on Demand Pro attempts to automatically geocode the address.

If successful, initiates a workflow job and returns a 200 response includes the job ID and job status (IN PROGRESS, FINISHED, FAILED):

{ "job_id": 1676
}

Once the job is FINISHED, you can retrieve financial impact scores for the specified facilities.

Step 2: Get results

The Get results by job returns the risk data calculated by a specific job.

curl --request GET \
     --url https://[host]/AppsServices/api/jobs/jobId \
     --header 'Authorization: XXXXXXXXXX' \
     --header 'accept: application/json'
def scoreFacilitiesImpact(token, request_json, host):
    endpoint = host + "/cod/AppsServices/api/v1/score-facilities-impact/jobs"
    r = requests.post(url=endpoint, json=request_json, headers={'Authorization':'Bearer ' + token})
    print(f'response code: {r.status_code}') if r.status_code == requests.codes.ok:
        result = r.json()
        return result
else:        
    return None
def getJobResult(token, host, job_id):
    endpoint = host + "/cod/AppsServices/api/jobs/" + job_id
    r = requests.get(url=endpoint, headers={'Authorization': 'Bearer ' + token})
    print(f'response code: {r.status_code}')
    if r.status_code == requests.codes.ok:
        result = r.json()
        return result
else:
    return None
data = {
    "m": "P.2023.1",
    "th": "2050",
    "rcp": "RCP8.5",
    "facilities": [{
        "id": "1",
        "name": "Berkeley Office",
        "activity": "Office",
        "exposure_value": 1000000,
        "street1": "2000 Hearst Avenue",
        "city": "Berkeley",
        "state": "CA",
        "postal_code": "94709",
        "country": "USA",
        "latitude": 37.8734494,
        "longitude": -122.2706614
    }]
}
response_job = scoreFacilitiesImpact(access_token, data, my_host)
my_job = response_job.get('job_id')
time.sleep(30)
result = getJobResult(access_token, my_host, str(my_job))

If the specified job is finished, the response returns an output that includes the risk data. Depending on the resource used to initiate the job, the output attribute may include an array of risk category scores, risk subcategory scores, or financial impact scores:

{
    "job_id": 6189,
    "user_id": 639,
    "status": "FINISHED",
    "percent_complete": 100,
    "output": [
        {
            "id": "1",
            "m": "P.2023.1",
            "latitude": 37.5412000000,
        	  "longitude": -122.060610000,
            "geo_status": "User",
            "exposure_value": 1000000,
            "line_of_business": "COM",
            "rcp": "RCP8.5",
            "TH": "2050",
            "risk_categories": {
                "all categories": {
                    "adr_std_dev": 0.038314,
                    "annualized_damage_rate": 0.0032593,
                    "average_annual_damage": 3259.306,
                    "impact_score": 97
                },
                "Floods": {
                    "adr_std_dev": 0.0
                    "annualized_damage_rate": 0.0,
                    "average_annual_damage": 0.0,
                    "impact_score": 0
                },
                "Heat Stress": {
                    "adr_std_dev": 0.0000735,
                    "annualized_damage_rate": 0.0000696,
                    "average_annual_damage": 69.586,
                    "impact_score": 51
                },
                "Hurricanes & Typhoons": {
                        "adr_std_dev": 0.0,
                        "annualized_damage_rate": 0.0,
                        "average_annual_damage": 0.0,
                        "impact_score": 0
                },
                "Sea Level Rise": {
                        "adr_std_dev": 0.0,
                        "annualized_damage_rate": 0.0,
                        "average_annual_damage": 0.0,
                        "impact_score": 0
                },
                "Water Stress": {
                        "adr_std_dev": 0.0,
                        "annualized_damage_rate": 0.0,
                        "average_annual_damage": 0.0,
                        "impact_score": 0
                },
                "Wildfires": {
                        "adr_std_dev": 0.0021415,
                        "annualized_damage_rate": 0.000056,
                        "average_annual_damage": 55.98,
                        "impact_score": 77
                },
                "Earthquakes": {
                "adr_std_dev": 0.038254,
                "annualized_damage_rate": 0.0031337,
                "average_annual_damage": 3133.74,
                "impact_score": 100
                }
            }
        }
    ]
}

For each risk category, clients may access multiple financial impact metrics including annualized damage rates (ADR), standard deviations in ADR (damage volatility), as well as an impact score ranging from 0-100 to contextualize estimated damages globally.

"The output includes the following: annualized damage rate (ADR), the expected average annual damage (AAD), the standard deviation of ADR (Stdev), and impact scores (derived from both ADR and Stdev) from 0–100; these outputs will be available at the facility (asset) level as well as at the portfolio aggregate level."

AttributeDescription
adr_std_devThe ADR standard deviation is a measure of volatility in the annualized damage rate (ADR) each year and varies by peril and location. A facility or a portfolio with a higher standard deviation is riskier than one with a low standard deviation, even if on average the expected damage (the ADR) is the same, with the potential for bigger extreme losses and large volatility year-to-year.
annualized_damage_rateThe annualized damage rate (ADR) projects the expected damage per year, as a ratio of the total value. ADR enables comparisons between facilities and portfolios on a normalized basis, as it does not depend on the value of the facilities, but still distinguishes between locations based on hazard and vulnerability.
average_annual_damageThe AAD is the expected damage in dollars, or other currency, per year, on average, over time and depends on the value of the facilities as well as the hazard and the vulnerability. It is the product of the ADR * value. This metric is provided only when Climate on Demand Pro users provide an exposure value.
impact_scoreThe risk at any location or for a portfolio is defined as the combination of the ADR and standard deviation at each location or for a whole portfolio, accounting for both the expected damage and the volatility. Climate on Demand Pro provides an impact score on a scale from 0–100 to contextualize the risk metrics, enabling quick comparisons between facilities and portfolios, and banding of locations into different risk zones.

For details on Climate On Demand impact scoring, see Real Assets Physical Climate Risk Methodology: Climate on Demand Methodology Version 2023.3 in the Moody's RMS Support Center.

Python example

def scorePortfolioImpact(token, request_json, host):
    endpoint = host + "/cod/AppsServices/api/v1/score-portfolio-impact/jobs"
    r = requests.post(url=endpoint, json=request_json, headers={'Authorization':'Bearer ' + token})
    print(f'response code: {r.status_code}') if r.status_code == requests.codes.ok:
        result = r.json()
        return result
else:        
    return None
def getJobResult(token, host, job_id):
    endpoint = host + "/cod/AppsServices/api/jobs/" + job_id
    r = requests.get(url=endpoint, headers={'Authorization': 'Bearer ' + token})
    print(f'response code: {r.status_code}')
    if r.status_code == requests.codes.ok:
        result = r.json()
        return result
else:
    return None
data = {
    "m": "P.2023.1",
    "th": "2050",
    "rcp": "RCP8.5",
    "facilities": [{
        "id": "1",
        "name": "Newark Office",
        "activity": "Office",
        "exposure_value": 1000000,
        "street1": "2000 Hearst Avenue",
        "city": "Newark",
        "state": "CA",
        "postal_code": "94560",
        "country": "USA",
        "latitude": 37.5412000000,
        "longitude": -122.060610000
    }]
}
response_job = scorePortfolioImpact(access_token, data, my_host)
my_job = response_job.get('job_id')
time.sleep(30)
result = getJobResult(access_token, my_host, str(my_job))

Climate on Demand also supports the financial impact modeling of individual facilities. For step-by-step instructions, see Facility Financial Impact Scoring.