Emerging market bond index carry #

The category group features approximate daily carry of two major emerging market indices, the JP Morgan Government Bond Index in local currency, and the EMBI Global (EMBIG), in hard currency.

Local currency bond index carry #

Ticker : LCBICRY_NSA / LCBICRY_VT10

Label : Local currency bond index (GBI-EM): carry / carry for 10% vol target

Definition : Local currency bond index (J.P. Morgan GBI-EM): carry / carry for 10% vol target

Notes :

  • The J.P. Morgan Government Bond Index Emerging Markets (GBI-EM) provides investors with a benchmark that tracks local currency bonds issued by emerging market governments. The GBI-EM closely follows the methodology of JPM’s family of GBI Indices, which are widely used as benchmarks for investing in developed government bond markets. More information on index specifications is presented in Appendix 1 .

  • The level of the index is calculated via the Superbond method, where the level is the yield to maturity of a synthetic bond built by deconstructing and reconstructing the cashflows of the bonds in the index. More information on this methodology can be found here

  • For Chile and Egypt we use the aggregate traded index, due to no data after 2019.

  • We excluded the Dominican Republic and Uruguay for carry calculation due to absence of meaningful daily local funding rates.

  • The Philippines have stale data for the yield index levels in 2024.

  • For volatility targeting positions are scaled to 10% vol target based on historic standard deviation for an exponential moving average with a half-time of 11 days. Positions are rebalanced at the end of each month.

Foreign currency bond index carry #

Ticker : FCBICRY_NSA / FCBICRY_VT10

Label : Foreign-currency bond index (EMBIG): carry / carry for 10% vol target

Definition : Foreign-currency bond index (J.P. Morgan EMBIG): carry / carry for 10% vol target

Notes :

  • The EMBI Global (EMBIG) includes USD-denominated bonds from EM sovereign & quasi-sovereign issuers. The EMBI suite of indices considers US dollar-denominated bonds issued by sovereign and quasi-sovereign EM entities eligible for inclusion. More information on index specifications is presented in Appendix 1 .

  • The level of the index is calculated via the Superbond method, where the level is the yield to maturity of a synthetic bond built by deconstructing and reconstructing the cashflows of the bonds in the index. More information on this methodology can be found here

  • Since Thailand’s data are available until 2004 we excluded the country. Nigeria (NGN) dropped out of the index in 2007 but was included again in 2011.

  • For volatility targeting positions are scaled to 10% vol target based on historic standard deviation for an exponential moving average with a half- time of 11 days. Positions are rebalanced at the end of each 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")
C:\Users\GlennRegis\AppData\Roaming\Python\Python38\site-packages\pandas\core\computation\expressions.py:21: UserWarning: Pandas requires version '2.7.3' or newer of 'numexpr' (version '2.7.1' currently installed).
  from pandas.core.computation.check import NUMEXPR_INSTALLED

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.

cids_lc_latam = [  # Latam local currency debt countries
    "BRL",
    "CLP",
    "COP",
    "MXN",
    "PEN",
]
cids_lc_emeu = [  # EM Europe local currency debt countries
    "CZK",
    "HUF",
    "PLN",
    "RON",
    "RUB",
    "RSD",
    "TRY",
]
cids_lc_meaf = [  # Middle-East and Africa local currency debt countries
    "EGP",
    "NGN",
    "ZAR",
]
cids_lc_asia = [  # Asia local currency debt countries
    "CNY",
    "IDR",
    "MYR",
    "PHP",
    "THB",
]

cids_lc = sorted(list(set(cids_lc_latam + cids_lc_emeu + cids_lc_meaf + cids_lc_asia)))


cids_fc_latam = [  # Latam foreign currency debt countries
    "BRL",
    "CLP",
    "COP",
    "DOP",
    "MXN",
    "PEN",
    "UYU",
]
cids_fc_emeu = [  # EM Europe foreign currency debt countries
    "HUF",
    "PLN",
    "RON",
    "RUB",
    "RSD",
    "TRY",
]
cids_fc_meaf = [  # Middle-East and Africa foreign currency debt countries
    "AED",
    "BHD",
    "EGP",
    "NGN",
    "OMR",
    "QAR",
    "ZAR",
]
cids_fc_asia = [  # Asia foreign currency debt countries
    "CNY",
    "IDR",
    "INR",
    "PHP",
    "SAR",
]
cids_fc = sorted(list(set(cids_fc_latam + cids_fc_emeu + cids_fc_meaf + cids_fc_asia)))
cids = sorted(list(set(cids_lc + cids_fc)))
locs = ["LCBICRY_NSA", "LCBICRY_VT10"]
fors = ["FCBICRY_NSA", "FCBICRY_VT10"]

main = locs + fors

xtra = [
    "LCBIR_NSA",
    "FCBIR_NSA",
    "LCBIXR_VT10",
    "FCBIXR_VT10",
    "REER_NSA_P1W4WL1",
    "NIIPGDP_NSA",
]
xcats = main + xtra
tix_lc = [cid + "_" + xcat for cid in cids_lc for xcat in locs]
tix_fc = [cid + "_" + xcat for cid in cids_fc for xcat in fors]
tix_xtra = [cid + "_" + xcat for cid in cids for xcat in xcats]

tickers = tix_lc + tix_fc + tix_xtra
# Download series from J.P. Morgan DataQuery by tickers

start_date = "2000-01-01"
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()
    assert downloader.check_connection()
    df = downloader.download(
        tickers=tickers,
        start_date=start_date,
        metrics=["value", "eop_lag", "mop_lag", "grading"],
        suppress_warning=True,
    )
    end = timer()


print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 370
Downloading data from JPMaQS.
Timestamp UTC:  2024-05-09 12:13:20
Connection successful!
Some expressions are missing from the downloaded data. Check logger output for complete list.
232 out of 1120 expressions are missing. To download the catalogue of all available expressions and filter the unavailable expressions, set `get_catalogue=True` in the call to `JPMaQSDownload.download()`.
Some dates are missing from the downloaded data. 
6356 out of 6356 dates are missing.
Download time from DQ: 0:00:56.665510

Availability #

dfd = df.copy()
msm.missing_in_df(dfd, xcats=locs, cids=cids_lc)
No missing XCATs across DataFrame.
Missing cids for LCBICRY_NSA:   []
Missing cids for LCBICRY_VT10:  []
msm.missing_in_df(dfd, xcats=fors, cids=cids_fc)
No missing XCATs across DataFrame.
Missing cids for FCBICRY_NSA:   []
Missing cids for FCBICRY_VT10:  []
cids_exp = cids
msm.check_availability(dfd, xcats=main, cids=cids_exp)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/17234c9102d9e381f7b8f0d9d6091a6978630427ce2eb9f559b1c3f2fcc86098.png https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/d91dad1d4e03073c6fadb05f9afa6e3e05412c785a32d50a91e9c118c759951a.png

Blacklisted periods #

Following the Russian invasion of Ukraine and internation sanctions, all Russian and Belarus debt was excluded from the JPM indexes on March 31st. In the period prior to that, the debt continued to reflect valuations provided by the pricing provider. More information on the methodology used can be found here

We also exclude some bried periods from PHP and NGN due to the unavailability of data.

current_date = pd.Timestamp('now').strftime('%Y-%m-%d')
ngn_na = ["2007-04-30", "2007-05-07"]
php_na = ["2024-01-31", current_date]
rub_na = ["2022-02-24", current_date]
black = {
    "RUB": rub_na,
}
# Restricted Tradability of Russia and no-price days for Nigeria
dfd = msm.reduce_df(dfd, blacklist=black).reset_index(drop=True)

black = {"NGN": ngn_na}
dfd = msm.reduce_df(dfd, blacklist=black).reset_index(drop=True)
black = {"PHP": php_na}
dfd = msm.reduce_df(dfd, blacklist=black).reset_index(drop=True)

History #

Local currency bond carry #

Cross-country heterogeneity of local-currency carry has been striking in terms of means and variances.

xcats_sel = ["LCBICRY_NSA"]
cids_exp = cids_lc
msp.view_ranges(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    sort_cids_by="mean",
    start="2000-01-01",
    kind="box",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/7a6fef0dc7f3b8d7094fee9e24d56f4767bddadd092012767a68b2f4f1a9977f.png
xcats_sel = ["LCBICRY_NSA"]
msp.view_timelines(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    start="2000-01-01",
    title="Local currency bond index carry across key market segments",
    title_fontsize=20,
    cumsum=False,
    ncol=3,
    same_y=False,
    aspect=1.7,
)
plt.show()
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/01eb2571f2280ecede9fce47d344644ceda448b67305051e277ebf472d943342.png

Foreign currency bond carry #

Foreign-currency bond carry has been less heterogeneous across countries than local-currency bond carry due mainly to the uniform application of USD funding rates.

xcats_sel = ["FCBICRY_NSA"]
cids_exp = cids_lc
msp.view_ranges(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    sort_cids_by="mean",
    start="2000-01-01",
    kind="box",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/476328dcd78a1dc2b51927317e6b199f76fac12c92652378677d2acc0b33f417.png
cids_exp = cids_fc
xcats_sel = ["FCBICRY_NSA"]
msp.view_timelines(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    start="2004-01-01",
    title="Foreign currency bond index carry across key market segments",
    title_fontsize=20,    
    cumsum=False,
    ncol=3,
    same_y=False,
    aspect=1.7,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/3cf2b187bd73e5103fdc930665a96bfce1a31dff078d38d047f685f70a41d103.png

Vol-targeted bond index carry #

cids_exp = cids_lc
xcats_sel = ["LCBICRY_VT10", "FCBICRY_VT10"]
msp.view_timelines(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    start="2004-01-01",
    title="Bond index volatility adjusted carry across key market segments, local and foreign currency",
    cumsum=False,
    ncol=3,
    same_y=False,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/90126f991cf5f69bcc37459c396e72373bc0d5e883b8145051c65003edd9d0c4.png

Importance #

Empirical Clues #

There has been a strong positive predictive relation between local-currency carry and subsequent monthly index returns. This corresponds to the common view that carry is indicative of policy subsidies and risk premia.

cidx = cids_lc
xcatx = ["LCBICRY_NSA", "LCBIR_NSA"]
cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="M",
    lag=1,
    slip=1,  # one-day slippage to avoid time zone distortions
    start="2000-01-01",
    years=None,
    xcat_aggs=["last", "sum"],
)
cr.reg_scatter(
    title="Local currency bond index carry and subsequent bond index returns, 20 countries, since 2000",
    labels=False,
    coef_box="lower right",
    ylab="Local currency bond index returns, % next month",
    xlab="Local currency bond index carry, end of month, change over end of previous month",
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/94a965f5a2f23c48fa7bb90c0f13d2159ac622cdf5726a7551d736cc5c9fa00c.png

However, the change in carry a negative predictor of next month’s bond returns. This is consistent with the idea that rising carry reflects deteriorating fundamentals in the short run that ultimately can escalate.

cidx = cids_lc
xcatx = ["LCBICRY_NSA", "LCBIR_NSA"]
cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="M",
    lag=1,
    slip=1,  # one-day slippage to avoid time zone distortions
    start="2000-01-01",
    years=None,
    xcat_aggs=["last", "sum"],
    xcat1_chg="diff",
)
cr.reg_scatter(
    title="Local currency bond index carry changes and subsequent bond index returns, 20 countries, since 2000",
    labels=False,
    coef_box="lower right",
    ylab="Local currency bond index returns, % next month",
    xlab="Local currency bond index carry, end of month, change over end of previous month",
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/3e478cff5c9ee49af27cf7f8a98c98dd685826fae8b9c1efc3ec813aad8e7b8f.png

There has been an even stronger positive relation between foreign currency bond carry and subsequent index returns.

cidx = cids_fc
xcatx = ["FCBICRY_NSA", "FCBIR_NSA"]
cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="M",
    lag=1,
    slip=1,  # one-day slippage to avoid time zone distortions
    start="2000-01-01",
    years=None,
    xcat_aggs=["last", "sum"],
)
cr.reg_scatter(
    title="USD bond index carry and subsequent bond index returns, 25 countries, since 2000",
    labels=False,
    coef_box="lower right",
    ylab="Foreign currency bond index returns, % next month",
    xlab="Foreign currency bond index carry, end of month, change over end of previous month",
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/bed2710919e25f600ef2990fa3bbd75cd3677d64a154db2e611fc0218760fc54.png

Appendix 1 #

Government Bond Index - Emerging Markets (GBI-EM) #

The J.P. Morgan Government Bond Index - Emerging Markets (GBI-EM) Indices provide investors with a benchmark that tracks local currency bonds issued by emerging market governments. The index was launched in June 2005 and is the first comprehensive global local Emerging Markets index.

As Emerging Market governments look increasingly toward their domestic market for sources of finance, investors continue to look more closely at local markets in search for higher yield and greater diversification. The GBI-EM Indices are comprised of only those countries from the GBI universe that meet the JP Morgan criteria for an Emerging Market, resulting in 18 countries from four regions.

For a country to be eligible for inclusion in our GBI-EM indices, GNI per capita must be below the Index Income Ceiling (IIC) for three consecutive years. J.P. Morgan defines the IIC as the GNI per capita level that is adjusted every year by the growth rate of the World GNI per capita, Atlas method (current US$), provided by the World Bank annually.

The GBI-EM consists of regularly traded, fixed-rate, domestic currency government bonds which international investors can readily access. We determine eligibility for local currency issues using the following criteria: instrument type, current amount outstanding, liquidity, maturity. The GBI-EM indices only include fixed coupon instruments. Additionally, bonds with callable, puttable or convertible features are not part of the indices. Only instruments with a current face amount outstanding of US $1 billion equivalent for onshore local currency bonds and USD 500 million equivalent for global bonds (offshore currency linked bonds) or more will be considered for inclusion.

More information can be found in the documantation

EMBI Global (EMBIG) #

The J.P. Morgan Emerging Markets Bond Index Global (EMBIG) (and Emerging Markets Bond Index Global Diversified (EMBIGD) ) are J.P. Morgan’s most comprehensive US dollar emerging markets debt benchmarks. The EMBI suite of indices considers US dollar-denominated bonds issued by sovereign and quasi-sovereign EM entities eligible for inclusion.

The EMBI suite classifies countries as Emerging Markets or Developed Markets by applying a combination of income-based and purchasing power-based criteria. These multi-dimensional rules allow the EMBIG to include a number of higher-rated countries that international investors have nevertheless considered part of the emerging markets universe.

A country is eligible for inclusion in the EMBIG/EMBIGD if either the GNI per capita of the country’s economy is below the Index Income Ceiling (IIC) for three consecutive years or the nation’s cost of living (purchasing power) is below the Index PPP Ratio (IPR) for three consecutive years.

More information can be found in the documentation

Appendix 2 #

Carry calculation #

Carry calculations are built upon the yield to maturity index levels. These index levels are not simple weighted averages of yields - they are calculated using the Superbond methodology. According to this methodology, the cashflows of each bond in the index are deconstructed and recostructed into one ‘Super bond’. The IRR of this theoretical instrument is therefore the yield of the portfolio.

We calculate carry by taking the yield level of the index at the end of day and calculating the present value of this index by including the risk-free rate. This is done in a similar way to the carry calculated in our government bond carry notebook , so that we can appreciate carry as a measure of the return on an index position when taking into account its cost of funding.

(2) # \[\begin{equation} carry_{t} = \frac{I_{t}}{(1+r^{f}_{t})} \end{equation}\]

where I is the yield level of the bond index on day t and related funding rate. For local currency indices, the funding rate will be the local currency funding rate. For hard currency indices, we use the dollar funding rate. When we don’t have the funding rate available, we remove that country from the group.

Appendix 2: Currency symbols #

The word ‘cross-section’ refers to currencies, currency areas or economic areas. In alphabetical order, these are

  • AED (Emirates dirham)

  • AUD (Australian dollar)

  • BHD (Bahraini Dinar)

  • 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)

  • DOP (Dominican Peso)

  • EGP (Egyptian Pound)

  • 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)

  • NGN (Nigerian Naira)

  • NLG (Dutch guilder)

  • NOK (Norwegian krone)

  • NZD (New Zealand dollar)

  • OMR (Omani Rial)

  • QAR (Qatari Riyal)

  • PAB (Panamanian balboa)

  • PEN (Peruvian sol)

  • PHP (Phillipine peso)

  • PLN (Polish zloty)

  • RON (Romanian leu)

  • RSD (Serbian Dinar)

  • RUB (Russian ruble)

  • SEK (Swedish krona)

  • SAR (Saudi riyal)

  • SGD (Singaporean dollar)

  • THB (Thai baht)

  • TRY (Turkish lira)

  • TWD (Taiwanese dollar)

  • UYU (Peso Uruguayo)

  • USD (U.S. dollar)

  • VEF (Venezuelan bolívar)

  • ZAR (South African rand).