Equity index future carry#

The category group contains daily indicators of equity index carry. These are based on equity yields and local nominal or real funding costs. A currency area’s equity yield is estimated based on analysts’ dividends and earnings expectations for the most liquid available local index.

Nominal equity index carry#

Ticker: EQCRY_NSA / EQCRY_VT10

Label: Nominal equity index future carry: % annualized / % annualized for 10% volatility target.

Definition: Estimated carry on main country equity index, based on the difference between (i) average of expected forward dividend and earnings yield and (ii) the main local-currency nominal short-term interest rate: % annualized of notional of the contract / % annualized of risk capital on position scaled to 10% (annualized) volatility target.

Notes:

  • The index-related predicted dividend and earnings yields are based on forecasts of the IBES sample of analysts. Since both yields are used for carry calculation and there is no universally accepted convention, JPMaQS uses an average of the 12-month forward expectations of earnings and dividend yields. We call this average the “equity yield”.

  • The nominal carry is the equity yield minus the local 1-month interbank or OIS rate or its closest proxy.

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

  • See Appendix 1 for the list of equity indicies used for each currency area.

Real equity index carry#

Ticker: EQCRR_NSA / EQCRR_VT10

Label: Real equity index future carry: % annualized / % annualized for 10% volatility target.

Definition: Estimated carry on main country equity index, based on the difference between (i) average of expected forward dividend and earnings yield and (ii) the main local-currency real short-term interest rate: % annualized of notional of the contract / % annualized of risk capital on position scaled to 10% (annualized) volatility target.

Notes:

  • Real carry means that the conventional (nominal) carry is adjusted for the effect of expected inflation. Since high inflation does not conceptually reduce the equity yield, which is calculated on the basis of a real asset, but does reduce funding costs, which is calculated on the basis of a nominal liability, it is the only funding cost proxy that must be adjusted. Hence, rather than using a nominal interest rate, this carry indicator uses a real interest rate.

  • The real interest rate is the main local 1-month interbank or OIS rate, or closest proxy, minus inflation expectation. The latter are the 1-year ahead estimated inflation expectations according to Macrosynergy methodology.

  • See the notes on INFE1Y_JA in “Inflation expectations (Macrosynergy methodology)”.

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_dmeq = ["AUD", "CAD", "CHF", "EUR", "GBP", "JPY", "SEK", "USD"]
cids_eueq = ["DEM", "ESP", "FRF", "ITL", "NLG"]
cids_aseq = ["CNY", "HKD", "INR", "KRW", "MYR", "SGD", "THB", "TWD"]
cids_exeq = ["BRL", "TRY", "ZAR"]
cids_nueq = ["MXN", "PLN"]

cids = sorted(cids_dmeq + cids_eueq + cids_aseq + cids_exeq + cids_nueq)
main = ["EQCRR_NSA", "EQCRR_VT10", "EQCRY_NSA", "EQCRY_VT10"]

econ = ["INFE5Y_JA", "RIR_NSA"]  # economic context

mark = [
    "EQXR_NSA",
    "EQXR_VT10",
]  # market links

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

start_date = "2000-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,
        show_progress=True,
    )
    end = timer()

dfd = df

print("Download time from DQ: " + str(timedelta(seconds=end - start)))
Maximum number of tickers is 208
Downloading data from JPMaQS.
Timestamp UTC:  2024-02-23 12:38:04
Connection successful!
Requesting data: 100%|██████████| 42/42 [00:09<00:00,  4.37it/s]
Downloading data: 100%|██████████| 42/42 [00:11<00:00,  3.67it/s]
Some expressions are missing from the downloaded data. Check logger output for complete list.
48 out of 832 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. 
2 out of 6302 dates are missing.
Download time from DQ: 0:00:25.011261

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 EQCRR_NSA:  []
Missing cids for EQCRR_VT10:  []
Missing cids for EQCRY_NSA:  []
Missing cids for EQCRY_VT10:  []

Quantamental indicators for equity index future carry are typically available, for developed markets, by the early 2000s. Many emerging markets only have data available from 2004 onwards.

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

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

print("Last updated:", date.today())
../_images/Equity index future carry_16_0.png
Last updated: 2024-02-23
xcatx = main
cidx = cids_exp

plot = msm.check_availability(
    dfd, xcats=xcatx, cids=cidx, start_size=(18, 4), start_years=False
)
../_images/Equity index future carry_17_0.png
xcatx = main
cidx = cids_exp

plot = msp.heatmap_grades(
    dfd,
    xcats=xcatx,
    cids=cidx,
    size=(18, 4),
    title=f"Average vintage grades from {start_date} onwards",
)
../_images/Equity index future carry_18_0.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),
)
../_images/Equity index future carry_19_0.png ../_images/Equity index future carry_19_1.png

History#

Equity carry in % of notional#

There have been large differences in real and nominal carry. Naturally, the nominal measure in high-inflation markets would have massively underestimated the real carry on a funded equity derivative. This illustrates that standard nominal equity carry can be misleading.

The importance of inflation suggests that real carry measures are economically more meaningful, even if there can be signficant estimation errors in real-time inflation expectations. Real carry measures are more even and comparable across countries.

xcatx = ["EQCRY_NSA", "EQCRR_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    kind="bar",
    title="Means and standard deviations of real and nominal equity index carry, % of notional",
    xcat_labels=["Nominal", "Real"],
    size=(16, 8),
)
../_images/Equity index future carry_23_0.png
xcatx = ["EQCRY_NSA", "EQCRR_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="Real and norminal equity index carry, % of notional",
    title_fontsize=27,
    legend_fontsize=17,
    xcat_labels=["Nominal", "Real"],
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.5,
    all_xticks=True,
)
../_images/Equity index future carry_24_0.png

Cross-country correlations of real equity have been mostly postive since since 2000.

xcatx = "EQCRR_NSA"
cidx = cids_exp

msp.correl_matrix(
    dfd,
    xcats=xcatx,
    cids=cidx,
    title="Cross-sectional correlations of real equity index future carry, since 2000",
    size=(20, 14),
)
../_images/Equity index future carry_26_0.png

Vol-targeted equity index carry#

Vol-targeted real equity carry has typically averaged 1%-5% across countries. Due to the combination of fast volatility-adjustment and sluggish equity yield revisions, the indicator has a lot more short-term variability than non-targeted carry. That volatility is all driven by recent market price fluctuations.

xcatx = ["EQCRY_VT10", "EQCRR_VT10"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    kind="bar",
    title="Means and standard deviations of real and nominal equity index carry, % of notional, 10% vol-target",
    xcat_labels=["Nominal", "Real"],
    size=(16, 8),
)
../_images/Equity index future carry_29_0.png
xcatx = ["EQCRY_VT10", "EQCRR_VT10"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="Real and nominal equity index carry, % of notional, 10% vol-targeting",
    title_fontsize=27,
    legend_fontsize=17,
    xcat_labels=["Nominal", "Real"],
    ncol=4,
    same_y=True,
    size=(12, 7),
    aspect=1.5,
    all_xticks=True,
)
../_images/Equity index future carry_30_0.png

Importance#

Empirical Clues#

There is some evidence of a positive correlation between real equity carry and subsequent returns at a monthly frequency, for developed markets.

xcatx = ["EQCRR_NSA", "EQXR_NSA"]
cidx = cids_dmeq

cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="Q",
    lag=1,
    xcat_aggs=["last", "sum"],
    start="2000-01-01",
)
cr.reg_scatter(
    title="Real equity carry and subsequent quarterly equity returns",
    labels=False,
    coef_box="lower right",
    xlab="Historic real equity carry",
    ylab="Equity index future return (next quarter)",
)
../_images/Equity index future carry_37_0.png

There is some evidence of predictive power in real equity carry for the direction of subsequent monthly equity returns, for developed markets. Moreover, real carry appears to be a better predictor of directional equity returns than nominal carry, with an increase in balanced accuracy of around 0.1.

sr = mss.SignalReturnRelations(
    dfd,
    rets=xcatx[1],
    sigs=[xcatx[0], "EQCRY_NSA"],
    cids=cids_dmeq,
)
sr.accuracy_bars()
../_images/Equity index future carry_39_0.png

Appendices#

Appendix 1: Equity index specification#

The following equity indices have been used for futures return calculations in each currency area:

  • AUD: Standard and Poor’s / Australian Stock Exchange 200

  • BRL: Brazil Bovespa

  • CAD: Standard and Poor’s / Toronto Stock Exchange 60 Index

  • CHF: Swiss Market (SMI)

  • CNY: Shanghai Shenzhen CSI 300

  • DEM: DAX 30 Performance (Xetra)

  • ESP: IBEX 35

  • EUR: EURO STOXX 50

  • FRF: CAC 40

  • GBP: FTSE 100

  • HKD: Hang Seng China Enterprises

  • INR: CNX Nifty (50)

  • ITL: FTSE MIB Index

  • JPY: Nikkei 225 Stock Average

  • KRW: Korea Stock Exchange KOSPI 200

  • MXN: Mexico IPC (Bolsa)

  • MYR: FTSE Bursa Malaysia KLCI

  • NLG: AEX Index (AEX)

  • PLN: Warsaw General Index 20

  • SEK: OMX Stockholm 30 (OMXS30)

  • SGD: MSCI Singapore (Free)

  • THB: Bangkok S.E.T. 50

  • TRY: Bist National 30

  • TWD: Taiwan Stock Exchange Weighed TAIEX

  • USD: Standard and Poor’s 500 Composite

  • ZAR: FTSE / JSE Top 40

For the older history of the Standard and Poor 500 (1990s), earnings yields have been used rather than the average of dividend and earnings yields.

See Appendix 2 for the list of currency symbols used to represent each cross-section.

Appendix 2: 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).