Term premia estimates #
The category group contains term premia estimates for fixed income contracts, based on macro-quantamental indicators.
Interest rate swap term premia (Macrosynergy method) #
Ticker : DU02YETP_NSA / DU05YETP_NSA / DU10YETP_NSA
Label : Estimated IRS term premia, % ar, Macrosynergy method: 2-year maturity / 5-year maturity / 10-year maturity.
Definition : Estimated IRS term premia, % ar, Macrosynergy method: 2-year maturity / 5-year maturity / 10-year maturity.
Notes :
-
Conceptually the fixed income term premium refers to the gap between the yield of a longer-maturity bond and the average expected risk-free short-term rate for the same maturity. Essentially, it represents the cost of commitment. Although the term premium itself cannot be directly observed, it can be estimated by employing a term structure model that differentiates between expected short-term rates and risk premia.
-
The simple Macrosynergy term premium model mainly makes an assumption about the expected path of the short-term interest rate. It posits a long-term “convergence point” of the short rate at the estimated long-term rate of nominal GDP growth, which, in turn, is estimated as the sum of the 5-year moving median of past GDP growth ( documentation here ) and the effective inflation targeted ( documentation here ). This reflects the idea that the long-term real interest rate should be near the real return on the existing capital stock. The assumption is that the short-term nominal interest rate drifts towards a long-term convergence rate linearly over 5 years. The term premium is then the difference between the long-term yield on a swap and the geometric average of the forward short-term rates implied by that drift.
-
The main calculations are summarized below in Appendix 1 and further details are given in Appendix 2 .
-
This version of the estimated term premium is an off-the-shelf simple benchmark based on macroeconomics without complicated modelling. It is open to modification using other quantamental indicators. For example, the current state of inflation and “slack” in the economy will plausibly influence the path of a short-term rate.
-
Term premia for Turkey have been rather volatile win the last two years due to a non-updating short-term interest rate.
-
Term premia for Taiwan have been negative throughout the observation period. This is due to our approximation of the trend in real rates (r*) and growth rates (g*). The growth rate of Taiwan has been very high and this has resulted in a very high steady-state rate which in turn has pushed the resulting term premia in negative territory.
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 = ["COP", "CLP", "MXN"] # Latam countries
cids_emea = ["CZK", "HUF", "ILS", "PLN", "RUB", "TRY", "ZAR"] # EMEA countries
cids_emas = [
"CNY",
"IDR",
"INR",
"KRW",
"SGD",
"THB",
"TWD",
] # EM Asia countries
cids_em = cids_latm + cids_emea + cids_emas
cids = sorted(cids_dm + cids_em)
tenors = ["02", "05", "10"]
main = ["DU02YETP_NSA", "DU05YETP_NSA", "DU10YETP_NSA"]
xtra = [
"DU02YYLD_NSA",
"DU05YYLD_NSA",
"DU10YYLD_NSA",
"DU02YCRY_NSA",
"DU05YCRY_NSA",
"DU10YCRY_NSA",
"INFTEFF_NSA",
"RGDP_SA_P1Q1QL4_20QMM",
]
rets = [
"DU02YXR_NSA",
"DU02YXR_VT10",
"DU05YXR_NSA",
"DU05YXR_VT10",
"DU10YXR_NSA",
"DU10YXR_VT10",
] # market links
xcats = main + xtra + rets
# Download series from J.P. Morgan DataQuery by tickers
start_date = "1996-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,
metrics=["value", "eop_lag", "mop_lag", "grading"],
suppress_warning=True,
show_progress=True,
)
end = timer()
dfd = df
print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 459
Downloading data from JPMaQS.
Timestamp UTC: 2023-08-23 09:56:56
Connection successful!
Number of expressions requested: 1836
Requesting data: 100%|█████████████████████████████████████████████████████████████████| 92/92 [00:27<00:00, 3.30it/s]
Downloading data: 100%|████████████████████████████████████████████████████████████████| 92/92 [03:59<00:00, 2.60s/it]
Download time from DQ: 0:04:59.668652
Availability #
cids_exp = cids # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df: []
Missing cids for DU02YETP_NSA: ['SGD', 'NOK', 'AUD', 'EUR', 'IDR']
Missing cids for DU05YETP_NSA: []
Missing cids for DU10YETP_NSA: ['MXN', 'JPY', 'CZK']
xcatx = main
cidx = cids_exp
dfx = msm.reduce_df(dfd, xcats=xcatx, cids=cidx)
dfs = msm.check_startyears(
dfx,
)
msm.visual_paneldates(dfs, size=(14, 2))
print("Last updated:", date.today())
Last updated: 2023-08-23
xcatx = main
cidx = cids_exp
plot = msm.check_availability(
dfd, xcats=xcatx, cids=cids_exp, start_size=(14, 3), start_years=False
)
xcatx = main
cidx = cids_exp
plot = msp.heatmap_grades(
dfd,
xcats=xcatx,
cids=cidx,
size=(14, 3),
title=f"Average vintage grades from {start_date} onwards",
)
History #
IRS term premia according to the Macrosynergy method #
Estimated IRS term premia have shown the following stylized features since 2000:
-
10-year term premia have been mostly positive, but the those for shorter maturities have been mixed and predominantly negative for many developed and emerging markets.
-
In most countries term premia decined after the great financial crisis.
-
The volatility of term premia in emerging markets has been higher than that in developed markets.
xcatx = main
cidx = cids
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
sort_cids_by="std",
start="2000-01-01",
kind="box",
size=(16, 8),
)
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start="2000-01-01",
title="Estimated term premia according to the Macrosynergy methodology",
xcat_labels=["2-year", "5-year", "10-year"],
title_adj=1.01,
title_fontsize=22,
label_adj=0.05,
ncol=4,
cumsum=False,
same_y=False,
size=(12, 7),
aspect=2,
all_xticks=True,
)
Estimated term-premia correlations have been high, above 50%, across the panel. Correlation between 2-year and 5-year estimated term premia has been larger than between the other maturities.
xcats_sel = main
msp.correl_matrix(
df,
xcats=xcats_sel,
cids=cids,
freq="M",
start="2003-01-01",
size=(6, 3),
)
Importance #
Research links #
“This paper proposes a semi-structural dynamic term structure model augmented with macroeconomic factors to include cyclical dynamics with a focus on medium- to long-run forecasts. Our results clearly show that a macroeconomic approach is warranted: While term premium estimates are in line with those from other studies, we provide (i) plausible, stable estimates of expected long-term interest rates and (ii) forecasts of short- and long-term interest rates as well as cyclical macroeconomic variables that are stunningly close to those generated from large-scale macroeconomic models.” IMF .
“Equilibrium models suggest that as long as the policy rate is firmly near zero, the term premium on longer-dated yields is compressed by a reduced sensitivity of rates to economic change. However, when policy rates are on the move again this sensitivity recovers, while proximity of the zero lower bound implies high economic risks and a surcharge on the term premia. Hence, term premium uncertainty would be highest at the time of “lift-off”, when policy rates are expected to move upward from near zero.” Macrosynergy .
“Furthermore, our estimates indicate that term premia have been close to zero, as well as negative in periods, during the last decade of global extraordinary monetary policy measures”. See Risstad et al. .
“We find that the recent rise in yields due to increasing inflation and related uncertainty with respect to future levels of inflation and economic growth has triggered corresponding increases in term premia.” See Risstad et al. .
Empirical clues #
There has been strong and significant correlation of estimated term premia and subsequent monthly or quarterly returns. This is consistent with the notion of term premia indicating risk premia being paid for duration exposure.
Remember that the JPMaQS definition of duration returns is for going long the duration exposure by receiving the fixed rate and paying the floating rate. Hence when duration increases, by failing short-term interest rates, we see positive returns (and wise versa).
cr = msp.CategoryRelations(
dfd,
xcats=["DU05YETP_NSA", "DU05YXR_NSA"],
cids=cids,
freq="Q",
lag=1,
xcat_aggs=["last", "sum"],
start="2000-01-01",
)
cr.reg_scatter(
title="Estimated term premia and subsequent IRS returns, all markets, since 2000",
labels=False,
prob_est="map",
coef_box="lower right",
ylab="5-year IRS return, next quarter",
xlab="Estimated 5-year IRS term premium",
)
DU05YXR_NSA misses: ['ZAR'].
Appendices #
Appendix 1: Calculation of estimated term premia according to the macrosynergy method #
Here a term premium is the difference between a longer maturity yield and an average of estimated expected short-term yields over the maturity. In order to proxy the average short-term rate, we use the following method.
As local-currency short-term interest rate we use the main 1-month money market rate or closest proxy. For many countries the short-term interest rate has been the 1-month local-currency LIBOR rate until 2021. As of the end of 2021 LIBOR are being replaced in many countries by overnight index swap (OIS) rates. These rates have different economic meaning (as they imply almost no credit risk) but have become the new standard. For more details see the documentation on JPMaQS short-term interest rates here .
We estimate a natural short-term interest rate as the sum of expected long-term real GDP growth and expected inflation. The long-term growth rate is estimated as the last 5-year moving median of the real GDP growth rate, as documented here . Long-term expected inflation is assumed to be equal to the effective inflation target of the central bank here .
The estimated natural rate is given by the sum of the long-term real GDP growth and the effective inflation target like so:
Where \(g^{*}\) is the 5-year GDP growth rate and \(\pi^{*}\) represents the effective inflation target.
We assume that the short-term interest rate converges to the natural rate linearly over a 5-year period. Accordingly, we calculate a naive curve of forward rates by computing the simple yield increment starting from the short-term yield and leading up to our proxy of the long-term GDP growth.
The expected drift in the short-term interest rate per month is given by two stages. Firstly, the increment is calculated by taking the difference between the estimated natural rate and the current short-term rate and dividing by the number of months to the 5-year convergence date. This drift applies to the first 60 months of the contract.
where \(di\) is the monthly drift or increment in the short rate, and \(i_{0}\) is the initial (current) short term rate and \(N=60\) months (5 years).
The second stage is a zero drift after the 60 months have passed, whereby we expect the steady state natural rate to prevail.
The average short-term interest rate is the mean of a vector of the short-term interest rates that results from applying the two drifts to the months to maturity of the contract. The following formula applies:
Where \(n\) represents the tenor of the contract.
We can then calculate the mean interest rate towards convergence of a 2-year, 5-year and 10-year yield as:
Where \(\hat{y}_{n}\) is the convergence towards the long-term steady state of the IRS yield with tenor \(n\) .
The term premium is the difference between the interest rate swap yield and the average short-term interest rate.
Appendix 2: Convergence of short-term interest rates and the term-premia #
To simplify notation in the following, we use the arithmetic term-structure decomposition as in Bauer and Rudebusch (2020) . With this assumption we can write the decomposition of the fixed yields as:
where \(y_{t}^{(n)}\) is the fixed yield at tenor \(n\) , \(i_{t}\) is the short-term (nominal) interest rate ( \(\mathbb{E}_{t}(i_{t+j})\) is the expectation for period \(j\) , and \({TP}_{t}^{(n)}\) is the unobserved term-premium.
We can split the above equation into it’s long-term convergence path of \(i_{t}^{*}\) and the short-term deviations:
where \(i_{t+j}^{c} = i_{t+j} - i_{t}^{*}\) .
Bauer and Rudebusch who defines \(i_{t}^{*}\) as the Beveridge-Nelson trend in the short-term nominal interest rate,
In a similar spirit to Bauer and Rudebusch (2020) we use the Fisher equation of:
Assumign we are in the long-run (steady state) we have
Our assumption is that the trend in real-rates (
\(r_{t}^{*}\)
) can be approximated by stochastic trends in real-growth rates (
\(g_{t}^{*}\)
), which in JPMaQS is captured by the 20-month (5-year) moving median (
RGDP_SA_P1Q1QL4_20QMM
).
We approximate the stochastic end-points for short-term rates (
\(i_{t}^{*}\)
), with long-term inflation trend (
\(\pi_{t}^{*}\)
) set by our effective inflation targets of each country (
INFTEFF_NSA
) and the equilibrium trend of real short-term rates (
\(r_{t}^{*}\)
) by the trend in real GDP (
\(r_{t}^{*}=g_{t}^{*}\)
i.e. 5-year moving median:
RGDP_SA_P1Q1QL4_20QMM
).
We assume convergence in 5-years ( \(K=60\) months) with a linear path for short-term interests rates to the trend value of \(i_{t}^{*}\) . We model a linear path to the convergence of the short-term interest rates over the next 5-years:
where \(w(j,K) = \min\left(\frac{j}{K}, 1\right) \;\forall j\ge 0\) .
We define \(\hat{y}_{t}^{(n)}\) as the expectation component of the fixed yield ( \(y_{t}^{(n)}\) ):
which once we substitute in our path of convergence is equal to:
Subtracting our estimate of the expectations components from observed fixed IRS yields, we can estimate the term premiums as:
# Constants
K_CONVERGENCE: int = 60 # 60months = 5years
# Initial parameters
istar = 5.5 # i*(t)
i0_low = 3.5 # i(t) from a low starting point below i*(t)
i0_high = 7.5 # i(t) from a high starting point above i*(t)
months = np.array([mm for mm in range(120 + 1)]) # Set up of months
def expected_short_term_rate(i0, istar, n, k_converge: int = K_CONVERGENCE) -> float:
weight = min(n / k_converge, 1)
return weight * istar + (1 - weight) * i0
def yhat_fn(i0: float, istar: float, n: int, k_converge: int = 60) -> float:
if n == 0:
# Return short-term rate for no-tenor
return i0
# Calculate expected yield for convergence
yhat: float = (
sum(expected_short_term_rate(i0=i0, istar=istar, n=jj) for jj in range(n)) / n
)
return yhat
# Plot model
fig = plt.figure(1)
# Plot short-term rate convergence
ax = fig.add_subplot(211)
plt.scatter([0], [i0_low])
plt.scatter([0], [i0_high])
plt.plot(
months / 12,
[expected_short_term_rate(istar=istar, i0=i0_low, n=mm) for mm in months],
ls="--",
label=f"Initial low ($i_{{t}}$): {i0_low:0.1f}%",
)
plt.plot(
months / 12,
[expected_short_term_rate(istar=istar, i0=i0_high, n=mm) for mm in months],
ls="--",
label=f"Initial high ($i_{{t}}$): {i0_high:0.1f}%",
)
plt.plot(
[min(months), max(months)],
[istar, istar],
ls="-",
c="g",
label=f"Trend short-term rate ($i_{{t}}^{{*}}$): {istar:0.1f}%",
)
plt.title("Convergence paths of short-term rates: $\mathbb{E}_{t}(i_{t+n})$")
plt.ylabel("Percent")
plt.xlabel("Tenor ($n$) in years")
plt.xlim(xmin=0, xmax=120 / 12)
# Plot convergence longer fixed yields paths
ax = fig.add_subplot(212)
plt.scatter([0], [i0_low])
plt.scatter([0], [i0_high])
plt.plot(
months / 12,
[yhat_fn(istar=istar, i0=i0_low, n=mm) for mm in months],
ls="--",
label=f"Initial low ($i_{{t}}$): {i0_low:0.1f}%",
)
plt.plot(
months / 12,
[yhat_fn(istar=istar, i0=i0_high, n=mm) for mm in months],
ls="--",
label=f"Initial high ($i_{{t}}$): {i0_high:0.1f}%",
)
plt.plot(
[min(months), max(months)],
[istar, istar],
ls="-",
c="g",
label=f"Trend short-term rate ($i_{{t}}^{{*}}$): {istar:0.1f}%",
)
plt.title("Expectations component of fixed-yields: $\hat{y}_{t}^{(n)}$")
plt.ylabel("Percent")
plt.xlabel("Tenor ($n$) in years")
plt.xlim(xmin=0, xmax=120 / 12)
plt.grid(True)
for ax in fig.get_axes():
ax.label_outer()
legend = plt.legend(
loc="lower center",
ncol=4,
frameon=True,
fontsize=9,
bbox_to_anchor=(0.5, -0.03),
bbox_transform=plt.gcf().transFigure,
)
legend.set_zorder(60)
fig.tight_layout()
plt.show()
Natural convergence point: #
From JPMaQS we have the convergence point of short-term rates ( \(i_{t}^{*}\) ) given by the two components of trend growth and effective inflation targets. Both are available as seperate indicators in JPMaQS, and hence for convenience we show them below.
Remember our assumption that we approximate the trend in real-rates ( \(r_{t}^{*}\) ) by growth rates ( \(g_{t}^{*}\) ). As can be seen both inflation trends and growth trends show persistenty changes to their trends throughout the periods. This is in line with findings such as Bauer and Rudebusch (2020) .
cidx = cids
msp.view_timelines(
dfd,
xcats=["INFTEFF_NSA", "RGDP_SA_P1Q1QL4_20QMM"],
cids=cidx,
start="2000-01-01",
title="Natural short-term rate estimate components",
xcat_labels=[
"Effective inflation target ($\pi^{*}_{t}$)",
"Real GDP growth trend ($g_{t}^{*}$)",
],
title_adj=1.01,
title_fontsize=22,
label_adj=0.05,
ncol=4,
cumsum=False,
same_y=False,
size=(12, 7),
aspect=2,
all_xticks=True,
)