CDS index returns #

The category group features daily returns of credit derivative swap indices, particularly CDX and iTraxx investment-grade and high-yield indices for the U.S. and euro area.

CDS index return in % of notional #

Ticker : CRXR_NSA

Label : CDS index return, in % of notional

Definition : Unfunded return of swap on the CDS index, % of notional.

Notes :

  • The source of the underlying market quotes is J.P. Morgan/DataQuery.

  • Returns are unfunded swap returns on the underlying CDS index based on J.P. Morgan calculations.

  • A new index series is determined on the basis of liquidity every six months. Positions are rolled accordingly.

Vol-targeted CDS index return #

Ticker : CRXR_VT10

Label : CDS index return for 10% vol target.

Definition : Return on the CDS index, % of risk capital on position scaled to 10% (annualized) volatility target.

Notes :

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

  • See further the notes for “CDS index return in % of notional” above ( CRXR_NSA ).

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.

# Define cross sections (currency tickers)

cids_g2 = ["EUR", "USD"]
cids_cr = ["EIG", "EHY", "UIG", "UHY"]
cids = sorted(cids_g2 + cids_cr)
# Define quantamental indicators (category tickers)

main = ["CRXR_NSA", "CRXR_VT10"]
econ = ["PCREDITBN_SJA_P1M1ML12", "PCREDITGDP_SJA_D1M1ML12"]  # economic context
mark = ["CRCRY_NSA", "CRCRY_VT10"]  # market links

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

start_date = "2002-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()
    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()

dfd_1 = df[df["cid"].isin(cids_cr)]
dfd_x = df[df["cid"].isin(["EUR", "USD"])]
dfd_2 = df.replace({"^EUR": "EIG", "^USD": "UIG"}, regex=True)
dfd_3 = df.replace({"^EUR": "EHY", "^USD": "UHY"}, regex=True)

dfd = pd.concat([dfd_1, dfd_2, dfd_3])

print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 36
Downloading data from JPMaQS.
Timestamp UTC:  2024-02-23 12:31:26
Connection successful!
Some expressions are missing from the downloaded data. Check logger output for complete list.
64 out of 144 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. 
3 out of 5781 dates are missing.
Download time from DQ: 0:00:07.822595

Availability #

cids_exp = cids_cr
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df:  []
Missing cids for CRXR_NSA:  []
Missing cids for CRXR_VT10:  []

JPMaQS contains four cross sections for CDS index performance data:

  • EHY: European high yield

  • EIG: European investment grade

  • UHY: U.S. high yield

  • UIG: U.S. investment grade

CDS index returns are all available from the early 2000s.

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, 1))

print("Last updated:", date.today())
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/c723d89ca70e92dbf2e5d728ac6286d2a0bd82b3adfa5778ac4e946c2d5a6a84.png
Last updated: 2024-02-23
xcatx = main
cidx = cids_exp

plot = msm.check_availability(
    dfd, xcats=xcatx, cids=cidx, start_size=(18, 1), start_years=False
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/1effe9961188855a1de8743d286478f5f5891a91ca8d6fe3944a6fcbdbf4b40b.png
xcatx = main
cidx = cids_exp

plot = msp.heatmap_grades(
    dfd,
    xcats=xcatx,
    cids=cidx,
    size=(18, 1),
    title=f"Average vintage grades from {start_date} onwards",
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/687832c16f741eac04aaeb39c26e73a9a2f4be0b00804ff2594c5dd0d76d7de0.png
xcatx = main
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=start_date,
    kind="box",
    size=(16, 4),
)
msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    val="mop_lag",
    title="Median of observation period lags (ranges of time elapsed since middle of observation period in days)",
    start=start_date,
    kind="box",
    size=(16, 4),
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/a40680f3fe93fe72cb9281de584fd8b7738fe5268b5c42b0be7af7e8374de401.png https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/55a13566322620f090eb1dcc4e6808de6c1de0578fa866153e2e51774c6b227e.png

History #

CDS index returns in % of notional #

The high-yield returns have been subject to substantially greater variability than the investment-grade returns.

xcatx = ["CRXR_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="std",
    start=start_date,
    kind="box",
    title="Boxplots of CDS index returns, % of notional, since 2002",
    xcat_labels=["CDS index returns"],
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/d7084053e70be2ff4eacd695add70614681e32549ae64026fac41bd80cbff0a9.png
xcatx = ["CRXR_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    title="EUR, USD: Cumulative CDS index returns, high yield and investment grade",
    cumsum=True,
    ncol=2,
    same_y=True,
    size=(12, 7),
    aspect=2,
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/eecc3a2494709295972bf464722f5403cd759b344b2fbde29e3b34020843b865.png

Vol-targeted CDS index returns #

Since the mid-2000s, high-yield indices have outperformed investment grade indices on a volatility parity basis.

xcatx = ["CRXR_VT10"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start=start_date,
    title="EUR, USD: Cumulative CDS index returns, 10% annualized vol target",
    cumsum=True,
    ncol=2,
    same_y=True,
    size=(12, 7),
    aspect=2,
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/05c194d267cc318c5c747b27c9511fa704a46647704b8b66aec025c6d608d99e.png

Importance #

Empirical Clues #

Historically, there has been a negative relation between monthly, quarterly and annual mean expansion of private credit in the U.S. and Euro areas, and concurrent cumulative CDS index returns. This means that periods of higher credit growth have coincided with lower credit returns for investors.

xcatx = ["PCREDITGDP_SJA_D1M1ML12", "CRXR_NSA"]
cidx = cids_exp

cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="A",
    lag=0,
    xcat_aggs=["mean", "sum"],
    start=start_date,
    # xcat_trims=[8, 5],
    years=None,
)

cr.reg_scatter(
    title="Private credit expansion and concurrent cumulative CDS index returns, annual, since 2002",
    labels=True,
    coef_box="lower left",
    xlab="Private credit growth, % of GDP",
    ylab="Cumulative CDS index return",
    prob_est="map",
)
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/5b2704986f1f8199a82efa892dc0b0f2e7019e3cc1f68f11870ec6c7a9e2736e.png

There is also some evidence to suggest that CDS index carry is a good predictor of the direction of subsequent CDX index returns.

xcatx = ["CRCRY_NSA", "CRXR_NSA"]
cidx = cids_exp

sr = mss.SignalReturnRelations(
    df=dfd, rets=xcatx[1], sigs=xcatx[0], cids=cidx, freqs="M", start=start_date
)
sr.accuracy_bars()
https://macrosynergy.com/notebooks.build/themes/generic-returns/_images/04ce66a052fa070632931b873e3515f98d442e57ca515fe3e6d6f645edfc78f2.png