PPP exchange rates #

This category group contains information states of purchasing power parity (PPP) exchange rates and related concepts. A PPP exchange rate here is the spot price of a local currency per USD in the spot foreign exchange market that would equalize local and foreign prices. Information states are derived from vintages of (annual) official estimates and subsequent estimations based on relative consumer price index changes.

PPP USD exchange rates #

Ticker : PPPFXRATE_NSA

Label : PPP exchange rate, local currency per USD

Definition : PPP exchange rate, local currency per USD, estimate.

Notes :

  • PPP exchange rates are local currency prices in USD that equate the value of price baskets across currency areas. They are sometimes used as reference for long-term equilibrium exchange rates, following the “law of one price”. Technically, they are ratios of prices in the local currency area to prices in the United States.

  • JPMaQS information states of PPP exchange rates combine annual “official” PPP releases and CPI-based estimates of monthly changes up to the latest month for which CPIs have been released for both the local economy and the United States.

  • There are two official sources of estimation of annual PPP exchange rates: the OECD, for its member states, and the World Bank, for non-OECD countries. The OECD follows the the “Eurostat-OECD” method based on rolling annual special price surveys. The World Bank’s method for non-OECD countries uses irregular price surveys intermittently every few years and estimates other years based on annual GDP deflators. As of October 2023, the last price survey year used for the non-OECD calculations was 2017 and the preceding survey year was 2011. For the exact calculation of annualised PPP conversion factors see Appendix 1 .

  • The methodological differences between OECD and non-OECD explain the more drastic revisions of data vintages for the non-OECD countries. For instance, from 2005, until the next base year, the PPP figures for non-OECD countries were calculated using the extrapolation methodology. Then, in April 2014, the annual observation periods from 2005 were recalculated using the rolling PPP Fisher-type index. In some cases this triggered huge revisions of the originally extrapolated series, because estimation errors that had accumulated over many years were revealed. For further information please see ICP 2011 results were released in April 2014 [worldbank.org] .

  • To estimate at a monthly frequency, we use CPI vintages for the involved currency areas. Then we use a “double price ratio” to estimate and interpolate PPP exchange rates from the latest base year, i.e. the latest year for which official PPP exchange rates have been published. This is the ratio of (1) the local CPI in month m divided by the average CPI of the base year and (2) the local CPI in month m divided by the average CPI of the base year . The calculations are rebased after each new release of annual PPP exchange rates. For details, see the bottom of Appendix 2 .

  • The PPPs under the International Comparison Programme, ICP, have a broad coverage of goods and services, as PPPs are designed to convert the nominal value aggregates in national accounts into real value aggregates. Therefore, the consumption basket used in headline CPI is reasonably well aligned to that of ICP.

  • The Euro series, EUR_PPPFXRATE, will comprise a weighted average of the PPP conversion factors for Italy, France, Germany, Netherlands and Spain. The weights involved in the arithmetic mean will be the nominal GDP. Further, the series will commence from 1999 onwards to reflect the establishment of the Euro. However, for the first three annual observation periods, up until 2003, the PPP calculations for the Euro constituents still used the national currencies in the price relatives. Thus, the PPP figures for the aforementioned years will be multiplied by the fixed nominal exchange to ensure the indicator starts when the currency union was formed.

  • PPP exchange rate series for Romania, Russia and Turkey are adjusted for currency re-denominations in the late 1990s and 2000s.

PPP overvaluation ratio #

Ticker : PPPFXOVERVALUE_NSA

Label : Ratio of market price of currency and PPP-based price (overvaluation ratio)

Definition : Ratio of current market price of currency, based on spot exchange rate, and the estimated PPP-based price.

Notes :

  • A ratio above one indicates local-currency overvaluation versus the USD, based on the PPP exchange rate estimate. A ratio below one indicates undervaluation. A ratio of 1.2, for example, suggests that the local currency is 20% overvalued versus the USD based on the law of one price for comparable baskets.

  • The calculation tracks daily movements in the spot exchange rate relative to the concurrently estimated PPP exchange rate.

PPP excess overvaluation ratios #

Ticker : PPPFXOVERVALUE_NSA_P1MvLTXL1 / _P1DvLTXL1

Label : Excess ratio of market price of currency and PPP: % latest month / % latest day versus expanding long-term median.

Definition : Excess ratio of market price of currency and PPP-based price: % latest month over expanding long-term median since vintage inception lagged by one month / % latest day over expanding long-term median since vintage inception lagged by one day.

Notes :

  • See notes on “PPP overvaluation ratio”.

  • Long-term trends are also calculated based on the latest available full vintage of estimated overvaluation ratios. Latest month means latest 21 days.

  • To differentiate long-term trends from conventional trends, the percentage change will be calculated over an expanding median, as opposed to a moving average of varying time horizons. The expanding median will be lagged by one period (day or month).

Imports #

Only the standard Python data science packages and the specialized macrosynergy package are needed.

import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import math

import json
import yaml

import macrosynergy.management as msm
import macrosynergy.panel as msp
import macrosynergy.signal as mss
import macrosynergy.pnl as msn


from macrosynergy.download import JPMaQSDownload

from timeit import default_timer as timer
from datetime import timedelta, date, datetime

import warnings

warnings.simplefilter("ignore")

The JPMaQS indicators we consider are downloaded using the J.P. Morgan Dataquery API interface within the macrosynergy package. This is done by specifying ticker strings , formed by appending an indicator category code <category> to a currency area code <cross_section> . These constitute the main part of a full quantamental indicator ticker, taking the form DB(JPMAQS,<cross_section>_<category>,<info>) , where <info> denotes the time series of information for the given cross-section and category. The following types of information are available:

  • value giving the latest available values for the indicator

  • eop_lag referring to days elapsed since the end of the observation period

  • mop_lag referring to the number of days elapsed since the mean observation period

  • grade denoting a grade of the observation, giving a metric of real time information quality.

After instantiating the JPMaQSDownload class within the macrosynergy.download module, one can use the download(tickers,start_date,metrics) method to easily download the necessary data, where tickers is an array of ticker strings, start_date is the first collection date to be considered and metrics is an array comprising the times series information to be downloaded.

# Cross-sections of interest

cids_dm = [
    "AUD",
    "CAD",
    "CHF",
    "EUR",
    "GBP",
    "JPY",
    "NOK",
    "NZD",
    "SEK",
    "USD",
]  # DM currency areas

cids_latm = ["BRL", "COP", "CLP", "MXN", "PEN"]  # Latam countries
cids_emea = ["CZK", "HUF", "ILS", "PLN", "RON", "RUB", "TRY", "ZAR"]  # EMEA countries
cids_emas = [
    "CNY",
    "IDR",
    "INR",
    "KRW",
    "MYR",
    "PHP",
    "SGD",
    "THB",
]  # EM Asia countries

cids_em = cids_latm + cids_emea + cids_emas

cids = sorted(cids_dm + cids_em)
cids_ppp = sorted(list(set(cids) - set(["USD"])))
main = [
    "PPPFXRATE_NSA",
    "PPPFXOVERVALUE_NSA",
    "PPPFXOVERVALUE_NSA_P1M12ML1",
    "PPPFXOVERVALUE_NSA_P1M36ML1",
    "PPPFXOVERVALUE_NSA_P1M60ML1",
    "PPPFXOVERVALUE_NSA_D1M12ML1",
    "PPPFXOVERVALUE_NSA_D1M36ML1",
    "PPPFXOVERVALUE_NSA_D1M60ML1",
    "PPPFXOVERVALUE_NSA_P1MvLTXL1",
    "PPPFXOVERVALUE_NSA_P1D260DL1",
    "PPPFXOVERVALUE_NSA_P1D780DL1",
    "PPPFXOVERVALUE_NSA_P1D1300DL1",
    "PPPFXOVERVALUE_NSA_D1D260DL1",
    "PPPFXOVERVALUE_NSA_D1D780DL1",
    "PPPFXOVERVALUE_NSA_D1D1300DL1",
    "PPPFXOVERVALUE_NSA_P1DvLTXL1",
]
econ = [
    "REER_NSA_P1W4WL1",
    "REER_NSA_P1M12ML1",
    "REER_NSA_P1M60ML1",
    "RIR_NSA",
]  # economic context
mark = [
    "FXXR_NSA",
    "FXXRUSD_NSA",
    "FXXR_VT10",
    "FXXRHvGDRB_NSA",
    "FXCRR_NSA",
    "FXCRR_VT10",
    "DU02YXR_NSA",
    "DU05YXR_NSA",
]  # market links

xcats = main + econ + mark
# Download series from J.P. Morgan DataQuery by tickers

start_date = "1990-01-01"
tickers = [cid + "_" + xcat for cid in cids for xcat in xcats]
print(f"Maximum number of tickers is {len(tickers)}")

# Retrieve credentials

client_id: str = os.getenv("DQ_CLIENT_ID")
client_secret: str = os.getenv("DQ_CLIENT_SECRET")

# Download from DataQuery

with JPMaQSDownload(client_id=client_id, client_secret=client_secret) as downloader:
    start = timer()
    df = downloader.download(
        tickers=tickers,
        start_date=start_date,
        show_progress=True,
        metrics=["value", "eop_lag", "mop_lag", "grading"],
        suppress_warning=True,
    )
    end = timer()

dfd = df

print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 868
Downloading data from JPMaQS.
Timestamp UTC:  2024-03-07 14:34:27
Connection successful!
Number of expressions requested: 3472
Requesting data: 100%|███████████████████████████████████████████████████████████████| 174/174 [01:05<00:00,  2.66it/s]
Downloading data: 100%|██████████████████████████████████████████████████████████████| 174/174 [00:59<00:00,  2.94it/s]
Download time from DQ: 0:04:33.180427

Availability #

cids_exp = cids_ppp  # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df:  []
Missing cids for PPPFXOVERVALUE_NSA:  []
Missing cids for PPPFXOVERVALUE_NSA_D1D1300DL1:  []
Missing cids for PPPFXOVERVALUE_NSA_D1D260DL1:  []
Missing cids for PPPFXOVERVALUE_NSA_D1D780DL1:  []
Missing cids for PPPFXOVERVALUE_NSA_D1M12ML1:  []
Missing cids for PPPFXOVERVALUE_NSA_D1M36ML1:  []
Missing cids for PPPFXOVERVALUE_NSA_D1M60ML1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1D1300DL1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1D260DL1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1D780DL1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1DvLTXL1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1M12ML1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1M36ML1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1M60ML1:  []
Missing cids for PPPFXOVERVALUE_NSA_P1MvLTXL1:  []
Missing cids for PPPFXRATE_NSA:  []

Real-time monthly quantamental indicators of PPP exchange rate are available from 1991 for most of the countries. The main exceptions are Brazil, Columbia, China, Czech Republic and the Eurozone which are all available from 1999.

For the explanation of currency symbols, which are related to currency areas or countries for which categories are available, please view Appendix 3 .

xcatx = main

cidx = cids_exp

dfx = msm.reduce_df(dfd, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(
    dfx,
)
msm.visual_paneldates(dfs, size=(18, 6))

print("Last updated:", date.today())
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/10920466ba3283b74c1f3740b61a136cdc76f6a00a34ed45e9333d0a6f12feb5.png
Last updated: 2024-03-07
xcatx = main
cidx = cids_exp

plot = msm.check_availability(
    dfd, xcats=xcatx, cids=cids_exp, start_size=(18, 6), start_years=False
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/0bd6aa57a16fa028c6f1dd0da310f7547384f594e512d9564b089907e6642e8c.png

The PPP exchange rate indicators have high grading since all data are based on source vintages.

xcatx = main
cidx = cids_exp

plot = msp.heatmap_grades(
    dfd,
    xcats=xcatx,
    cids=cidx,
    size=(18, 6),
    title=f"Average vintage grades from {start_date} onwards",
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/0d10832c9eeae69ab65cb7069606f24184095ed74d1b33adb2b86ce9eb8bd916.png
xcatx = ["PPPFXRATE_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    val="eop_lag",
    title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
    start="1995-01-01",
    kind="box",
    size=(16, 4),
)

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    val="mop_lag",
    title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
    start="1995-01-01",
    kind="box",
    size=(16, 4),
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/03e9b94b3326f540b0a9f6d562abeb4cf0b6d6f46964423038cfb3071479367a.png https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/9477d66b841d7b42031b01370776d837ee8534af29641700ae0ff23294ce65c8.png

History #

PPP exchange rate series #

PPP exchange rates of developed or emerging countries countries with similar inflation rates to the U.S. have been fairly stationary. Emerging countries with higher inflation rates have naturally seen an upward drift in the USD price in local currency. Low inflation or deflation countries, such as Switzerland and Japan have experienced a downward drift.

The data also show the effect of the World Bank’s revision cycle, with a number of sharp upward and downward shifts in EM PPP exchange rates on the days of publication.

xcatx = ["PPPFXRATE_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="1991-01-01",
    title="PPP exchange rate, local currency per USD, since 1991",
    title_adj=1.03,
    title_xadj=0.45,
    label_adj=0.075,
    title_fontsize=27,
    legend_fontsize=17,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/8b8fed35b009eee7d9579dabf52ed807462642c2e1f42bba2e573765e28f57d2.png

PPP overvaluation ratio #

Overvaluation ratios have been stationary in the long-term. This suggests that PPP exchange rates have exerted some long-term “gravity” on spot exchange rates: price differences may have remained but they did not drift apart on a sustained basis. Exceptions were Japan and Singapore, but even in these countries the residual drift was gentle.

xcatx = ["PPPFXOVERVALUE_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="1991-01-01",
    title="PPP overvaluation ratio, since 1991",
    title_adj=1.03,
    title_xadj=0.45,
    label_adj=0.075,
    title_fontsize=27,
    legend_fontsize=17,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/4d9240c1f006da42b0115d1a8f044a24d3462b693e1fa8c9da846cc40c6d1f8d.png

PPP excess overvaluation ratios #

Excess overvaluation ratios track the part of ratios of currency prices to PPP-based prices that is above the long-term average since vintage inception. Excessed ratios are naturally closer to zero than simple overvaluation ratios and typically stationary.

xcatx = ["PPPFXOVERVALUE_NSA_P1MvLTXL1", "PPPFXOVERVALUE_NSA_P1DvLTXL1"]

cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="1991-01-01",
    title="PPP overvaluation long-term trends, latest month percentage over expanding window since 1991",
    title_adj=1.03,
    title_xadj=0.45,
    label_adj=0.075,
    title_fontsize=27,
    legend_fontsize=17,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/2966725849ff5b0ccdabfbef62ed28574e2a2e4ccea184b864177ec70ba61b86.png

Importance #

Empirical clues #

Exclude periods of exchange rates pegs or untradable forward/NDF market.

dfb = dfd[dfd["xcat"].isin(["FXTARGETED_NSA", "FXUNTRADABLE_NSA"])].loc[
    :, ["cid", "xcat", "real_date", "value"]
]
dfba = (
    dfb.groupby(["cid", "real_date"])
    .aggregate(value=pd.NamedAgg(column="value", aggfunc="max"))
    .reset_index()
)
dfba["xcat"] = "FXBLACK"
fxblack = msp.make_blacklist(dfba, "FXBLACK")

Overvaluation ratios #

In line with general intuition, there has been signficant negative correlation between overaluation ratios and subsequent FX forward returns at a monthly, quarterly or annual horizon. This holds true for both the developed and emerging markets.

xcatx = ["PPPFXOVERVALUE_NSA", "FXXR_NSA"]
cidx = cids_ppp

cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="Q",
    lag=1,
    xcat_aggs=["last", "sum"],
    start="2000-01-01",
    blacklist=fxblack,
)

cr.reg_scatter(
    title="PPP overvaluation and subsequent FX returns since 2000 (all developed and emerging markets)",
    labels=False,
    coef_box="upper left",
    ylab="FX returns, following quarter",
    xlab="PPP overvalue ratio, quarter-end",
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/90295841c04599ac39bc23efcdc18bfb3d5dfdfe1e54cac6624bd5b663ef2261.png

In accordance with its reputation as a long-term indicator, the overvaluation ratio’s predictive power shows particularly well at longer horizons, such as 1 year ahead.

xcatx = ["PPPFXOVERVALUE_NSA", "FXXR_NSA"]
cidx = cids_em

cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="A",
    lag=1,
    xcat_aggs=["last", "sum"],
    start="2000-01-01",
    blacklist=fxblack,
)

cr.reg_scatter(
    title="PPP overvaluation and subsequent annual FX returns since 2000 (all developed and emerging markets)",
    labels=False,
    coef_box="upper right",
    ylab="FX returns, following year",
    xlab="PPP overvalue ratio, year-end",
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/83950ebbbc195b6dc6e1a3497a4863c003780ae9d977e8fab712577b5c137593.png

Excess overvaluation ratios, as opposed to simple overvaluation ratios, are particularly important as predictors of emerging markets FX returns. Unlike simple overvaluation ratios they do not imply structural long or short positions across currencies. There has been a significant negative predictive relation between excess overvaluation and subsequent EM FX returns in the 2000s as well as in the 2010s and 2020s.

xcatx = ["PPPFXOVERVALUE_NSA_P1MvLTXL1", "FXXR_NSA"]
cidx = cids_em

cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="Q",
    lag=1,
    xcat_aggs=["last", "sum"],
    start="2000-01-01",
    blacklist=fxblack,
)

cr.reg_scatter(
    title="Excess PPP overvaluation and subsequent FX returns since 2000 (emerging markets)",
    labels=False,
    coef_box="upper left",
    ylab="FX returns, following quarter",
    xlab="Excess PPP overvalue ratio, quarter-end",
    separator=2010,
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/financial-conditions/_images/01b223ce3833edbc490036eed72027e1ac2ad15f867479faf21202bdfe39c38c.png

Appendix 1: How official annual purchasing power parity exchange rates are calculated #

Conceptually, PPP exchange rates are ratios of prices for comparable goods and services in local and foreign currency areas. They are exchange rates for which comparable goods and services would cost the same in both places.

PPP exchange rates are based on a Fisher-type index whereby the aggregate PPP exchange rate level is a weighted average of elementary PPP exchange rate levels. Each elementary PPP exchange rate represents a basic heading, covering similar goods and services across areas. An example of a basic heading would be wine which is disaggregated into the three groups: red wine, white wine and sparkling wine. At the elementary level, basket indices do not use weights and a geometric mean is calculated. The prices used for the elementary PPP exchange rates are annual averages. Index number formulae beyond the elementary level are derived using systems of weights. Thus, to produce the aggreate PPP exchange rate, the procedure is broken into three steps: i) a matrix of Laspeyres-type PPP exchange rates; ii) a matrix of Paasche-type PPP exchange rates; iii) a geometric mean of the two, a matrix of Fisher-type PPP exchange rates.

For detailed methodological notes, please see here .

Appendix 2: How JPMaQS monthly purchasing power parity indices are calculated #

Appendix 3: Currency symbols #

The word ‘cross-section’ refers to currencies, currency areas or economic areas. In alphabetical order, these are AUD (Australian dollar), BRL (Brazilian real), CAD (Canadian dollar), CHF (Swiss franc), CLP (Chilean peso), CNY (Chinese yuan renminbi), COP (Colombian peso), CZK (Czech Republic koruna), DEM (German mark), ESP (Spanish peseta), EUR (Euro), FRF (French franc), GBP (British pound), HKD (Hong Kong dollar), HUF (Hungarian forint), IDR (Indonesian rupiah), ITL (Italian lira), JPY (Japanese yen), KRW (Korean won), MXN (Mexican peso), MYR (Malaysian ringgit), NLG (Dutch guilder), NOK (Norwegian krone), NZD (New Zealand dollar), PEN (Peruvian sol), PHP (Phillipine peso), PLN (Polish zloty), RON (Romanian leu), RUB (Russian ruble), SEK (Swedish krona), SGD (Singaporean dollar), THB (Thai baht), TRY (Turkish lira), TWD (Taiwanese dollar), USD (U.S. dollar), ZAR (South African rand).