FX forward carry #

The category group contains nominal and real carry measures for 1-month FX forward contracts of global currencies versus their respective dominant cross (typically USD or EUR or a basket of both). All measures are annualized and always for one month maturity.

FX forward nominal carry of going long the base currency , is calculated from the covered interest parity no-arbitrage condition as follows:

(3) # \[\begin{equation} CRY_{t} = \left(\frac{S_{t}}{F_{t}}\right)^{1/h} - 1 \end{equation}\]

Where \(S_{t}\) is the FX spot price, \(F_{t}\) is the forward contract of tenor \(h\) (i.e. 1m forward: \(h=1/12\) ).

The FX spot and forward prices are from the JPMorgan trading desks, with Asian, European, and USDCAD taken at London close. Latin American FX crosses (USDBRL, USDCLP, USDCOP, USDMXN, and USDPEN) are end-of-day mark of each country’s trading desk.

Nominal carry versus dominant cross (no hedges) #

Ticker : FXCRY_NSA / FXCRY_VT10

Label : Nominal forward-implied carry vs. dominant cross: % ar / % ar for 10% vol target.

Definition : 1-month FX forward carry against dominant cross(es), % annualized: based on notional of the contract / based on risk capital on position scaled to 10% (annualized) volatility target.

Notes :

  • The carry metric is calculated for a contract that is long the local currency of the cross section against its dominant traded benchmark(s). For most currencies the benchmark is the dollar. For some European currencies (Switzerland, the Czech Republic, Hungary, Norway, Poland, Romania, Sweden), the benchmark is the Euro. And for Great Britain, Turkey and Russia, an equally weighted basket of dollar and euro has been used.

  • For the following currencies, returns are based on non-deliverable contracts: Indonesia, India, South Korea, China, Malaysia and Taiwan.

  • Volatility-targeted carry positions are scaled to a 10% annualized standard deviation target, estimated based on an exponential moving average of daily returns with a half-life of 11 days. Positions are rebalanced at the end of each month. Also, a maximum leverage ratio of 5 (of implied notional to cash position) has been imposed.

  • For some currencies, carry data include periods of low liquidity and FX targeting. If one wishes to ‘blacklist’ such periods one should use the non-tradability and FX-target dummmies, which have category ticker codes FXUNTRADABLE_NSA and FXTARGETED_NSA .

Nominal carry versus USD (no hedges) #

Ticker : FXCRYUSD_NSA / FXCRYUSD_VT10

Label : Nominal forward-implied carry vs. USD: % ar / % ar for 10% vol target.

Definition : 1-month FX forward carry against USD, % annualized: based on notional of the contract / based on risk capital on position scaled to 10% (annualized) volatility target.

Notes :

  • The carry metric is calculated for a contract that is long the local currency of the cross section against USD.

  • For the following currencies returns are based on non-deliverable contracts: Indonesia, India, South Korea, China, Malaysia and Taiwan.

  • Volatility-targeted carry positions are scaled to a 10% annualized standard deviation target, estimated based on an exponential moving average of daily returns with a half-life of 11 days. Positions are rebalanced at the end of each month. Also, a maximum leverage ratio of 5 (of implied notional to cash position) has been imposed.

  • For some currencies, carry data include periods of low liquidity and FX targeting. If one wishes to ‘blacklist’ such periods one should use the non-tradability and FX-target dummmies, which have category ticker codes FXUNTRADABLE_NSA and FXTARGETED_NSA .

Nominal carry versus dominant cross (with hedges) #

Ticker : FXCRYHvGDRB_NSA

Label : Nominal carry on 1-month FX forward position, hedged against market directional risk.

Definition : 1-month FX forward carry against dominant cross(es), % annualized, for a forward position that has been hedged against directional risk through a position in a global directional risk basket.

Notes :

  • The global directional risk basket contains equal volatility-weighted positions in equity index futures, CDS indices and FX forwards. See also the notes for directional risk basket carry ( DRBCRY_NSA ) here .

  • Hedge ratios are calculated based on historical “beta”, i.e. OLS regression coefficients of past forward returns with respect of 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.

  • See also related important notes on “Nominal carry metrics (no hedges)” ( FXCRY_NSA and FXCRY_VT10 ).

Real carry versus dominant cross (no hedges) #

Ticker : FXCRR_NSA / FXCRR_VT10

Label : Real forward-implied carry vs. dominant cross: % ar / % ar for 10% vol target.

Definition : 1-month FX forward carry against dominant cross(es), % annualized and adjusted for expected inflation differential: based on notional of the contract / based on risk capital on position scaled to 10% (annualized) volatility target.

Notes :

  • In contrast to nominal carry, real carry subtracts the differential between the expected local and benchmark inflation rate from annualized carry. The basis for the adjustment is the quantamental indicator “Estimated 1-year ahead inflation expectation” ( INFE1Y_JA ). The basis of these expectations are the dominant headline and core price indices (50-50) according to popular local conventions. Please check out the related notes in the section “Inflation expectations (Macrosynergy method)” here .

  • See also related important notes on “Nominal carry metrics (no hedges)” ( FXCRY_NSA and FXCRY_VT10 ).

Real carry versus USD (no hedges) #

Ticker : FXCRRUSD_NSA / FXCRRUSD_VT10

Label : Real forward-implied carry vs. USD: % ar / % ar for 10% vol target.

Definition : 1-month FX forward carry against USD, % annualized and adjusted for expected inflation differential: based on notional of the contract / based on risk capital on position scaled to 10% (annualized) volatility target.

Notes :

  • In contrast to nominal carry, real carry subtracts the differential between the expected local and benchmark inflation rate from annualized carry. The basis for the adjustment is the quantamental indicator “Estimated 1-year ahead inflation expectation” ( INFE1Y_JA ). Please check out related notes in the section “Inflation expectations (Macrosynergy method)” here .

  • See also related important notes on “Nominal carry metrics (no hedges)” ( FXCRY_NSA and FXCRY_VT10 ).

Real carry versus dominant cross (with hedges) #

Ticker : FXCRRHvGDRB_NSA

Label : Real carry on 1-month FX forward position, hedged against market directional risk.

Definition : 1-month FX forward carry against dominant cross(es), % annualized and adjusted for expected inflation differential, for a forward position that has been hedged against directional risk through a position in a global directional risk basket.

Notes :

  • The global directional risk basket contains equal volatility-weighted positions in equity index futures, CDS indices and FX forwards. See also notes for directional risk basket carry ( DRBCRR_NSA ) here .

  • Hedge ratios are calculated based on historical “beta”, i.e. OLS regression coefficients of past forward returns with respect of 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 strikes a balance between timeliness of information and structural relations.

  • See also related important notes on “Nominal carry metrics (no hedges)” ( FXCRY_NSA and FXCRY_VT10 ).

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.

cids_dmca = [
    "AUD",
    "CAD",
    "CHF",
    "EUR",
    "GBP",
    "JPY",
    "NOK",
    "NZD",
    "SEK",
    "USD",
]  # DM currency areas
cids_dmec = ["DEM", "ESP", "FRF", "ITL", "NLG"]  # DM euro area countries
cids_latm = ["BRL", "COP", "CLP", "MXN", "PEN"]  # Latam countries
cids_emea = ["CZK", "HUF", "ILS", "PLN", "RON", "RUB", "TRY", "ZAR"]  # EMEA countries
cids_emas = [
    "CNY",
    "HKD",
    "IDR",
    "INR",
    "KRW",
    "MYR",
    "PHP",
    "SGD",
    "THB",
    "TWD",
]  # EM Asia countries
cids_dm = cids_dmca + cids_dmec
cids_em = cids_latm + cids_emea + cids_emas
cids = sorted(cids_dm + cids_em)
main = [
    "FXCRR_NSA",
    "FXCRR_VT10",
    "FXCRRUSD_NSA",
    "FXCRRUSD_VT10",
    "FXCRY_NSA",
    "FXCRY_VT10",
    "FXCRYUSD_NSA",
    "FXCRYUSD_VT10",
    "FXCRRHvGDRB_NSA",
    "FXCRYHvGDRB_NSA",
]
econ = [
    "INTRGDPv5Y_NSA_P1M1ML12_3MMA",
    "INTRGDP_NSA_P1M1ML12_3MMA",
    "FXTARGETED_NSA",
    "FXUNTRADABLE_NSA",
]  # economic context
mark = ["FXXR_NSA", "FXXR_VT10", "FXXRHvGDRB_NSA"]  # 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()
    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 646
Downloading data from JPMaQS.
Timestamp UTC:  2023-06-14 09:55:24
Connection successful!
Number of expressions requested: 2584
Requesting data: 100%|███████████████████████████████████████████████████████████████| 130/130 [00:40<00:00,  3.21it/s]
Downloading data: 100%|██████████████████████████████████████████████████████████████| 130/130 [01:22<00:00,  1.58it/s]
Download time from DQ: 0:02:31.872078

Availability #

cids_exp = sorted(
    list(set(cids) - set(cids_dmec + ["ARS", "HKD", "USD"]))
)  # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df:  set()
Missing cids for FXCRRHvGDRB_NSA:  set()
Missing cids for FXCRRUSD_NSA:  set()
Missing cids for FXCRRUSD_VT10:  set()
Missing cids for FXCRR_NSA:  set()
Missing cids for FXCRR_VT10:  set()
Missing cids for FXCRYHvGDRB_NSA:  set()
Missing cids for FXCRYUSD_NSA:  set()
Missing cids for FXCRYUSD_VT10:  set()
Missing cids for FXCRY_NSA:  set()
Missing cids for FXCRY_VT10:  set()

History of nominal carry metrics begins mostly in the 1990s, with some EM countries only available from the early 2000s. Real carry metrics are mostly available from the first half of the 2000s.

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=(20, 5))

print("Last updated:", date.today())
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/fbdfe91d30adeabaf69b9701eea3bd08197ef9be7928cbfa8536778e80424700.png
Last updated: 2023-06-14
plot = msm.check_availability(
    dfd, xcats=main, cids=cids_exp, start_size=(20, 5), start_years=False
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/8eed789630f02b69dba50ab3aed5acff658afe4e618abbba4dfa5fdd356c71ee.png
plot = msp.heatmap_grades(
    dfd,
    xcats=main,
    cids=cids_exp,
    size=(19, 5),
    title=f"Average vintage grades from {start_date} onwards",
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/be97f18097058624a7df424d393360c6c05ecdf11b2d9451293ea112e6b6e040.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="2000-01-01",
    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="2000-01-01",
    kind="box",
    size=(16, 4),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/90b6294757eef4f922776486ff6ba49ddf335e0a3064a4b030b9b838d9e80fe9.png https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/57a4b17c2b413f9febbb67214281ed1ce67d9fc3221554a8fd226b64b6ff3a6c.png

History #

Nominal carry metrics (no hedges) #

Nominal carry metrics and their variations have been very diverse across currencies. Equalizing targeted volatility makes a significant differences for carry both intertemporally, as it introduces much greater variation, and in cross-sectional comparison, as it penalizes currencies with large stop exchange rate fluctuations.

Carry metrics can be misleading for periods were currencies are not tradable or pegged, highlighting the importance of non-tradability and FX-target dummmies, which have category ticker codes FXUNTRADABLE_NSA and FXTARGETED_NSA in JPMaQS.

xcatx = ["FXCRY_NSA", "FXCRY_VT10"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    title="Means and standard deviations of Nominal FX forward-implied carry vs. dominant cross, since 2000",
    xcat_labels=["Non-seasonally adjusted", "10% vol-target"],
    sort_cids_by="mean",
    start="2000-01-01",
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/5fe801976423a58e4398fbc6e5e969a6cde18d9febede335cc2eb5eb076069bb.png
xcatx = ["FXCRY_NSA", "FXCRY_VT10"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="1-month forward-implied FX carry: simple (blue) and 10% vol-targeted positions (orange)",
    title_fontsize=27,
    legend_fontsize=17,
    title_adj=1.02,
    title_xadj=0.43,
    cumsum=False,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/43611fa87a9fe19e00a129b24ec879744681adf85eadeef8113194f3e8c603fe.png

Nominal carry is predominantly positively correlated across currency pairs. That is because most currencies are traded against the same benchmark (USD).

msp.correl_matrix(
    dfd,
    xcats="FXCRY_NSA",
    cids=cids_exp,
    size=(20, 14),
    title="Cross-sectional correlations of nominal forward-implied FX carry, dominant cross, since 2000",
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/8bc853b58f7067974579ac65663956ad72dc205c2de3c5f38756a37cae42fce8.png

If one looks at USD-based carry alone, global correlation is naturally stronger abut still not uniformly positive for all currencies.

msp.correl_matrix(
    dfd,
    xcats="FXCRYUSD_NSA",
    cids=cids_exp,
    title="Cross-sectional correlations of nominal forward-implied FX carry, against USD, since 2000",
    size=(20, 14),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/4d341eba005b2d9a7b708ccc7c7ad78305a9c4f3a49114f4ab90bb4c2c188ce5.png

Nominal carry with hedge #

Hedging makes a significant difference for long-term averages and intertemporal dynamics of FX carry positions. Carry for hedged positions has not only been substantially lower for “high-beta” countries, but also a lot more unstable. This is due to the instability of the simple regression estimate of the beta of currency positions with respect to the global directional risk basket.

xcatx = ["FXCRY_NSA", "FXCRYHvGDRB_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    title="Means and standard deviations of nominal carry on 1-month FX forward positions, since 2000",
    xcat_labels=["Unhedged", "Hedged"],
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/743fed413d30b51c235297c904d6f9be347ee4c817f5afa01e8cd063a28157fc.png
xcatx = ["FXCRY_NSA", "FXCRYHvGDRB_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="Unhedged (blue) and directionally hedged (orange) nominal FX forward-implied carry",
    title_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.43,
    cumsum=False,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/aa52a2fd93abf757a9351097bbf9a12b8eec19e14bbd9d287d500c2d00e8e7ff.png

Real carry metrics (no hedges) #

Adjusting FX carry for inflation expectations differentials makes a huge difference for carry-based signals in the long run. This applies to both vol-adjusted and non-vol-adjusted carry. The case for inflation expectation adjustment for measuring actual incentives to hold currency deposits is compelling from a theroetical and empirical perspective.

xcatx = ["FXCRR_NSA", "FXCRY_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    title="Means and standard deviations of FX carry metrics, without hedging, since 2000",
    xcat_labels=["Real", "Nominal"],
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/cecd99f3e9491a949f95b9f187ccdccb0d4cfd6e45e8a1f05bba6e815d94373f.png
xcatx = ["FXCRR_VT10", "FXCRY_VT10"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    title="Means and standard deviations of FX carry metrics, 10% vol-target, without hedging, since 2000",
    xcat_labels=["Real", "Nominal"],
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/b683b30cc0b454163267731a7a19ee4c8bd19563827372c2c0799fa74e558826.png
xcatx = ["FXCRR_NSA", "FXCRY_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="1-month forward-implied carry, real (blue) and nominal (orange)",
    xcat_labels=["Real", "Nominal"],
    title_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.43,
    cumsum=False,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/c9fe7af59fc690a2742ce9124d4e58eac7fe7ffe37296ebd275330ee567faeda.png
xcatx = ["FXCRR_VT10", "FXCRY_VT10"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="1-month forward-implied carry on 10% vol-targeted position, real and nominal",
    xcat_labels=["Real", "Nominal"],
    title_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.4,
    cumsum=False,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/a8c596c3145f976d22ab1dce68bd3b63c8e199effd3e599221f7085f219a5c60.png

Real carry with hedge #

As for nominal carry, hedging makes a significant difference for long-term averages and intertemporal dynamics of real carry. Several currencies have displayed positive real carry without a directional hedge but negative real carry with a directional hedge. This illustrates that FX-specific carry does not always sufficiently compensate for the borader market “beta” of a currency.

xcatx = ["FXCRR_NSA", "FXCRRHvGDRB_NSA"]
cidx = cids_exp

msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    title="Means and standard deviations of real FX carry, since 2000",
    xcat_labels=["Real, no hedge", "Real, hedged"],
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/9d6e225d9ad3a2a91378d4593264703c9e938ceeff7bbdb2c7a4a4f9ac9115c0.png
xcatx = ["FXCRR_NSA", "FXCRRHvGDRB_NSA"]
cidx = cids_exp

msp.view_timelines(
    dfd,
    xcats=xcatx,
    cids=cidx,
    start="2000-01-01",
    title="1-month forward-implied carry, unhedged (blue) and hedged (orange)",
    xcat_labels=["No hedge", "Hedge"],
    title_adj=1.02,
    title_fontsize=27,
    legend_fontsize=17,
    title_xadj=0.43,
    cumsum=False,
    ncol=4,
    same_y=False,
    size=(12, 7),
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/9e9ce96fca34a815e140e2d199d4e909687a9d14c733e6c8c2276f1e5eeaf787.png

Importance #

Empirical clues #

# Blacklist dictionary for invalid periods across currency areas since 2000.

dfb = dfd[dfd["xcat"].isin(["FXTARGETED_NSA", "FXUNTRADABLE_NSA"])].loc[
    :, ["cid", "xcat", "real_date", "value"]
]
dfba = (
    dfb.groupby(["cid", "real_date"])
    .aggregate(value=pd.NamedAgg(column="value", aggfunc="max"))
    .reset_index()
)
dfba["xcat"] = "FXBLACK"
fxblack = msp.make_blacklist(dfba, "FXBLACK")
fxblack
{'BRL': (Timestamp('2012-12-03 00:00:00'), Timestamp('2013-09-30 00:00:00')),
 'CHF': (Timestamp('2011-05-02 00:00:00'), Timestamp('2016-06-30 00:00:00')),
 'CNY': (Timestamp('2000-01-03 00:00:00'), Timestamp('2023-06-13 00:00:00')),
 'CZK': (Timestamp('2014-01-01 00:00:00'), Timestamp('2017-07-31 00:00:00')),
 'HKD': (Timestamp('2000-01-03 00:00:00'), Timestamp('2023-06-13 00:00:00')),
 'ILS': (Timestamp('2000-01-03 00:00:00'), Timestamp('2005-12-30 00:00:00')),
 'INR': (Timestamp('2000-01-03 00:00:00'), Timestamp('2004-12-31 00:00:00')),
 'MYR_1': (Timestamp('2000-01-03 00:00:00'), Timestamp('2007-11-30 00:00:00')),
 'MYR_2': (Timestamp('2018-07-02 00:00:00'), Timestamp('2023-06-13 00:00:00')),
 'PEN': (Timestamp('2021-07-01 00:00:00'), Timestamp('2021-07-30 00:00:00')),
 'RON': (Timestamp('2000-01-03 00:00:00'), Timestamp('2005-11-30 00:00:00')),
 'RUB_1': (Timestamp('2000-01-03 00:00:00'), Timestamp('2005-11-30 00:00:00')),
 'RUB_2': (Timestamp('2022-02-01 00:00:00'), Timestamp('2023-06-13 00:00:00')),
 'SGD': (Timestamp('2000-01-03 00:00:00'), Timestamp('2023-06-13 00:00:00')),
 'THB': (Timestamp('2007-01-01 00:00:00'), Timestamp('2008-11-28 00:00:00')),
 'TRY_1': (Timestamp('2000-01-03 00:00:00'), Timestamp('2003-09-30 00:00:00')),
 'TRY_2': (Timestamp('2020-01-01 00:00:00'), Timestamp('2023-06-13 00:00:00'))}

All versions of real carry have been positively correlated with FX forwrad returns over longer horizons.

xcatx = ["FXCRRHvGDRB_NSA", "FXXRHvGDRB_NSA"]
cidx = cids_exp
cr = msp.CategoryRelations(
    dfd,
    xcats=xcatx,
    cids=cidx,
    freq="M",
    lag=0,
    xcat_aggs=["mean", "sum"],
    blacklist=fxblack,
    start="2000-01-01",
    years=3,
)
cr.reg_scatter(
    title="Hedged FX real implied carry and returns (3-year periods)",
    labels=True,
    coef_box="upper left",
    xlab="Hedged FX real implied carry, % ar",
    ylab="FX forward returns hedged against broad directional market influences",
)
https://macrosynergy.com/notebooks.build/themes/stylized-trading-factors/_images/3ee6c8f704c0f8ba0eca6ca5eaffa285e4f14d8614aeecd6d1e1dbd02d2a15bb.png

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