Sovereign CDS spreads and carry #
This group contains carry arising from selling protection though sovereign credit default swaps, as well as related spreads, for various maturities and a range of developed and emerging markets.
Sovereign CDS spreads #
Ticker : CDS02YSPRD_NSA / CDS05YSPRD_NSA / CDS10YSPRD_NSA
Label : Sovereign CDS spreads: 2-year maturity / 5-year maturity / 10-year maturity.
Definition : Daily CDS spreads, %: 2-year maturity / 5-year maturity / 10-year maturity.
Notes :
-
CDS spreads are taken from J.P. Morgan DataQuery. The contracts for calculation have changed over time. In 2014, regulation for CDS transactions changed and the new market standard became that of quoting standard coupons. Parties entering CDS contracts since then choose among the available standard coupons and pay or receive the corresponding upfront payment which would make the market value of the CDS equal to zero.
-
CDS spreads for emerging markets are ‘par spreads’, as per market conversions pre 2009. The pricing of a par-spread involves using as a coupon whatever the traded spread is at the time of entering the trade.
Sovereign CDS carry #
Ticker : CDS02YCRY_NSA / CDS05YCRY_NSA / CDS10YCRY_NSA
Label : Sovereign CDS carry: 2-year maturity / 5-year maturity / 10-year maturity.
Definition : Daily CDS carry calculated from the continuous series of CDS spreads: 2-year maturity / 5-year maturity / 10-year maturity.
Notes :
-
For most countries, a 1-month Libor rate has historically been used as a proxy for local funding costs. In the wake of the benchmark rate reform, this has changed to other rates. For more information on the specific short term nominal interest rates used, please see here .
-
Carry is calculated from the point of view of the seller of protection, i.e. the CDS seller.
-
Carry is calculated by adding the coupon amount to the roll, in order to take into account all components of the income generated by the contract.
-
Carry is calculated as the sum of spread and return from rolldown of the credit spread curve. For example, if a 5-year CDS spread is 100 bps and the 4-year CDS spread is 90 bps, the rolldown is 10 bps (all annualized). The carry is then 100 bps + 10 bps = 110 bps.
CDS vol-targeted nominal carry #
Ticker : CDS02YCRY_VT10 / CDS05YCRY_VT10 / CDS10YCRY_VT10
Label : Sovereign CDS carry for 10% vol target, %: 2-year maturity / 5-year maturity / 10-year maturity.
Definition : Sovereign CDS carry for 10% annualized volatility target, %: 2-year maturity / 5-year maturity / 10-year maturity.
Notes :
-
Positions are scaled to a 10% vol-target based on historic standard deviations for an exponential moving average with a half-life of 11 days. Leverage is subject to a maximum of 5.
CDS hedged carry #
Ticker : CDS02YCRYHvGDRB_NSA / CDS05YCRYHvGDRB_NSA / CDS10YCRYHvGDRB_NSA
Label : Nominal carry on short CDS position, hedged against market directional risk, %: 2-year maturity / 5-year maturity / 10-year maturity.
Definition : Sovereign CDS carry for a short protection position that has been hedged against directional risk through a position in a global directional risk basket: 2-year maturity / 5-year maturity / 10-year maturity.
Notes :
-
The global directional risk basket contains equal (volatility-weighted) sub-portfolios of equity index futures, CDS indices and FX forwards. See further the important notes here on directional risk basket returns (
DRBXR_NSA
). -
Hedge ratios are calculated based on historical “beta”, i.e. weighted least-squares regression coefficients of past returns with respect to global directional risk basket returns. The estimate uses two regressions. One is based on monthly returns with an exponentially-weighted lookback of 24 months half-life. The other is based on daily returns with exponentially-weighted lookback of 63 trading days. The usage of the two lookbacks seeks to strike a balance between timeliness of information and structural relations.
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
from pandas import Timestamp
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
from datetime import timedelta, date
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
import requests
from timeit import default_timer as timer
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 obtain the data. Here
tickers
is an array of ticker strings,
start_date
is the first release date to be considered and
metrics
denotes the types of information requested.
# Cross-sections of interest
cids_dmca = ["AUD", "CHF", "GBP", "NOK", "NZD", "SEK", "USD"]
cids_dmeu = [
"FRF",
"DEM",
"ITL",
"ESP",
] # DM euro area sovereigns
cids_latm = [
"BRL",
"CLP",
"COP",
"MXN",
"PEN",
] # Latam sovereigns
cids_emea = [
"CZK",
"HUF",
"ILS",
"PLN",
"RON",
"ZAR",
"TRY",
] # EMEA sovereigns
cids_emas = [
"CNY",
"IDR",
"KRW",
"MYR",
"PHP",
"THB",
] # EM Asia sovereigns
cids_dm = cids_dmca + cids_dmeu
cids_em = cids_emea + cids_latm + cids_emas
cids = cids_dm + cids_em
spreads = ["CDS02YSPRD_NSA", "CDS05YSPRD_NSA", "CDS10YSPRD_NSA"]
cry = ["CDS02YCRY_NSA", "CDS05YCRY_NSA", "CDS10YCRY_NSA"]
crvs = ["CDS02YCRY_VT10", "CDS05YCRY_VT10", "CDS10YCRY_VT10"]
hedged_cry = ["CDS02YCRYHvGDRB_NSA", "CDS05YCRYHvGDRB_NSA", "CDS10YCRYHvGDRB_NSA"]
rets = ["CDS02YXR_NSA", "CDS05YXR_NSA", "CDS10YXR_NSA"]
bond_ylds = ["GB05YYLD_NSA"]
main = spreads + cry + crvs + hedged_cry
xtra = rets + bond_ylds
xcats = main + xtra
# 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 464
Downloading data from JPMaQS.
Timestamp UTC: 2023-06-26 19:41:08
Connection successful!
Number of expressions requested: 1856
Requesting data: 100%|█████████████████████████████████████████████████████████████████| 93/93 [00:30<00:00, 3.09it/s]
Downloading data: 100%|████████████████████████████████████████████████████████████████| 93/93 [00:41<00:00, 2.23it/s]
Download time from DQ: 0:01:44.559787
Availability #
cids_exp = sorted(list(cids)) # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df: set()
Missing cids for CDS02YCRYHvGDRB_NSA: {'PHP'}
Missing cids for CDS02YCRY_NSA: set()
Missing cids for CDS02YCRY_VT10: set()
Missing cids for CDS02YSPRD_NSA: set()
Missing cids for CDS05YCRYHvGDRB_NSA: {'PHP'}
Missing cids for CDS05YCRY_NSA: set()
Missing cids for CDS05YCRY_VT10: set()
Missing cids for CDS05YSPRD_NSA: set()
Missing cids for CDS10YCRYHvGDRB_NSA: {'PHP'}
Missing cids for CDS10YCRY_NSA: set()
Missing cids for CDS10YCRY_VT10: set()
Missing cids for CDS10YSPRD_NSA: set()
Starting dates for sovereign CDS spreads and carry are mixed. For most cross-sections, data is available only from the mid-2000s onwards. The only countries for which data are consistently available from the 1990s are Brazil, Colombia, Mexico and Poland. Some indicators for Peru and Turkey are also available from the late 1990s.
For the explanation of currency symbols, which are related to currency areas or countries for which categories are available, please view Appendix 1 .
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())
Last updated: 2023-06-26
xcatx = main
cidx = cids_exp
plot = msm.check_availability(
dfd, xcats=xcatx, cids=cidx, start_size=(18, 3), start_years=False
)
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",
)
xcats_sel = ["CDS05YSPRD_NSA", "CDS05YCRY_NSA"]
msp.view_ranges(
dfd,
xcats=xcats_sel,
cids=cids,
val="eop_lag",
title="End of observation period lags (ranges of time elapsed since end of observation period in days)",
start="2000-01-01",
kind="box",
size=(16, 4),
)
History #
CDS spreads #
The history of CDS spreads reflects disproportionate spikes during crisis times. Spreads are naturally strongly skewed towards the high side, due to the option-like non-linear return distribution. Furthermore, the tendency towards outliers reflects the connection between credit events and CDS market liquidity. In systemic crises, credit risk rises and liquidity dries up at the same time.
xcatx = spreads
cidx = cids
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
title="Boxplots of CDS spreads, since 2000",
xcat_labels=["2-year maturity", "5-year maturity", "10-year maturity"],
sort_cids_by="std",
start="2000-01-01",
kind="box",
size=(16, 8),
)
xcatx = spreads
cidx = cids
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start="2000-01-01",
title="CDS spreads (%)",
title_adj=1.01,
title_fontsize=24,
legend_fontsize=17,
title_xadj=0.39,
xcat_labels=["2-year maturity", "5-year maturity", "10-year maturity"],
label_adj=0.05,
cumsum=False,
ncol=4,
same_y=False,
height=3,
aspect=2,
all_xticks=True,
)
CDS carry #
For higher-risk countries CDS carry and spread are very closely aligned. For low-risk countries there have occasionally been notable differences.
xcatx = ["CDS05YCRY_NSA", "CDS05YSPRD_NSA"]
cidx = cids
msp.view_ranges(
dfd,
xcats=xcatx,
cids=cidx,
title="Boxplots of CDS carry and spread, 5-year maturity, since 2000",
xcat_labels=["Carry", "Spread"],
sort_cids_by="std",
start="2000-01-01",
kind="box",
size=(16, 8),
)
xcatx = ["CDS05YCRY_NSA", "CDS05YSPRD_NSA"]
cidx = cids
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start="2000-01-01",
title="5-year CDS carry (blue) versus spreads (orange) (%)",
title_adj=1.01,
title_fontsize=24,
legend_fontsize=17,
title_xadj=0.45,
xcat_labels=["Carry", "Spread"],
cumsum=False,
ncol=4,
same_y=False,
height=3,
aspect=2,
all_xticks=True,
)
Vol-targeted CDS carry #
Vol-targeted CDS carry (for 10% annualized return volatility target) is very volatile and has regularly exceeded 40% or more. These features reflect the limited liquidity and the non-linear return distribution of CDS spreads.
xcatx = ["CDS05YCRY_VT10", "CDS05YCRY_NSA"]
cidx = cids
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start="2000-01-01",
title="5-year CDS carry: vol-targeted versus outright (%)",
title_xadj=0.44,
title_adj=1.01,
title_fontsize=24,
legend_fontsize=17,
xcat_labels=["Vol-targeted", "Outright"],
label_adj=0.05,
cumsum=False,
ncol=4,
same_y=True,
height=3,
aspect=2,
all_xticks=True,
)
CDS hedged carry #
Hedging reduces the carry on CDS short protection positions, but mainly in times of turmoil.
xcatx = ["CDS05YCRYHvGDRB_NSA", "CDS05YCRY_NSA"]
cidx = cids
msp.view_timelines(
dfd,
xcats=xcatx,
cids=cidx,
start="2000-01-01",
title="CDS carry metrics: hedged versus unhedged (%)",
title_adj=1.01,
title_fontsize=24,
title_xadj=0.45,
xcat_labels=["Hedged", "Unhedged"],
legend_fontsize=17,
label_adj=0.05,
cumsum=False,
ncol=4,
same_y=False,
height=3,
aspect=2,
all_xticks=True,
)
Importance #
Relevant research #
“Sovereign CDS spreads are highly driven by liquidity (55.6% of default risk and 44.32% of liquidity) and that sovereign bond spreads are less subject to liquidity frictions and therefore could represent a better proxy for sovereign default risk (73% of default risk and 26.86% of liquidity).” Badaoui, Cathcart and El-Jahel
“We find that sovereign CDS and bond markets are co-integrated. In five out of seven sovereigns (71%), the bond market leads in price discovery by adjusting to new information regarding credit risk before CDS.” Hassan, Ngene and Yu
“Credit Default Swap (CDS) spreads change significantly in accordance with (i) the direction of order flows, (ii) the size of transactions, and (iii) the type of counterparty. Apparent causes are asymmetric information, inventory risk and market power. The implication is powerful. Since transactions do not require commensurate changes in fundamentals and since CDS spreads are themselves used for risk management, institutional order flows can easily establish escalatory dynamics.” Macrosynergy
Empirical clues #
There has been a significant positive relation between 5-year CDS spreads and subsequent returns at a monthly or quarterly frequency.
xcatx = ["CDS05YCRY_NSA", "CDS05YXR_NSA"]
cidx = set(cids) - set(["PHP"])
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
xcat_aggs=["last", "sum"],
cids=cidx,
freq="Q",
lag=1,
slip=1,
start="2000-01-01",
)
cr.reg_scatter(
title="CDS carry and subsequent returns",
labels=False,
coef_box="lower right",
ylab="5-year CDS return, next quarter",
xlab="5-year CDS carry",
)
xcatx = ["CDS05YCRY_NSA", "CDS05YXR_NSA"]
cidx = set(cids) - set(["PHP"])
cr = msp.CategoryRelations(
dfd,
xcats=xcatx,
xcat_aggs=["last", "sum"],
cids=cidx,
freq="Q",
lag=1,
slip=1,
start="2000-01-01",
xcat_trims=[14, 14],
)
cr.reg_scatter(
title="CDS carry and subsequent returns (excluding outliers)",
labels=False,
coef_box="lower right",
ylab="5-year CDS return, next quarter",
xlab="5-year CDS carry",
)
Appendices #
Appendix 1: 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).