FX volatility risk premia #

The category group contains simple estimates of FX volatility risk premia, calculated as the difference between implied and expected realized volatility divided by expected realized volatility. A positive volatility risk premium means that market participants demand a surcharge for exposure to volatility. Expected realized volatility is simply estimated using recent realized volatility, which is assumed to be an unbiased estimator over the short term. See Appendix 1 for further details.

FX volatility risk premia against dominant base currency #

Ticker : FXVRP7D_NSA / FXVRP2W_NSA / FXVRP1M_NSA / FXVRP2M_NSA / FXVRP3M_NSA

Label : FX volatility risk premia against main benchmark: 7 days / 2 weeks / 1 month / 2 months / 3 months

Definition : FX volatility risk premia based on exchange rate to the main benchmark currency: 7 days / 2 weeks / 1 month / 2 months / 3 months

Notes :

  • For most currencies the dominant benchmark is the dollar. For some European currencies (CHF, CZK, HUF, NOK, PLN, RON, SEK) the benchmark is the euro. And for GBP, TRY, and RUB an equally weighted basked of dollar and euro has been used.

  • Implied volatility is calculated as Black-Scholes volatility for at-the-money options with the expiries of 7 days to 3 months. The source of the underlying quotes is J.P. Morgan/ Dataquery.

  • Realised volatility is the exponentially weighted moving average (EWMA) of squared returns. The half-life has been set at 11 days, a convention frequently used for risk management.

FX volatility risk premia against USD #

Ticker : FXVRP7DUSD_NSA / FXVRP2WUSD_NSA / FXVRP1MUSD_NSA / FXVRP2MUSD_NSA / FXVRP3MUSD_NSA

Label : FX volatility risk premia against USD: 7 days / 2 weeks / 1 month / 2 months / 3 months

Definition : FX volatility risk premia based on exchange rate to the U.S. dollar: 7 days / 2 weeks / 1 month / 2 months / 3 months

Notes :

  • Implied volatility is calculated as Black-Scholes volatility for at-the-money options with the expiries of 7 days to 3 months. The source of the underlying quotes is J.P. Morgan/ Dataquery.

  • Realised volatility is the exponentially weighted moving average (EWMA) of squared returns. The half-life has been set at 11 days, a convention frequently used for risk management.

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 nb_black extension is already loaded. To reload it, use:
  %reload_ext nb_black

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_dm = ["GBP", "EUR", "JPY", "AUD", "CAD", "CHF", "NZD", "NOK", "SEK"]
cids_em = [
    "CNY",
    "KRW",
    "SGD",
    "MXN",
    "INR",
    "RUB",
    "ZAR",
    "TRY",
    "BRL",
    "TWD",
    "PLN",
    "THB",
    "IDR",
    "HUF",
    "CZK",
    "ILS",
    "CLP",
    "PHP",
    "COP",
    "MYR",
    "RON",
    "PEN",
]
cids = cids_dm + cids_em
vdic = {
    "EUR": ["NOK", "SEK", "CHF", "CZK", "HUF", "PLN", "RON", "GBP", "TRY", "RUB"],
    "USD": [
        "CAD",
        "CHF",
        "GBP",
        "EUR",
        "NZD",
        "AUD",
        "SEK",
        "NOK",
        "JPY",
        "ISK",
        "DKK",
        "CZK",
        "RUB",
        "RON",
        "TRY",
        "ZAR",
        "ILS",
        "HUF",
        "PLN",
        "PHP",
        "SGD",
        "THB",
        "HKD",
        "ARS",
        "BRL",
        "CLP",
        "COP",
        "PEN",
        "MXN",
        "MYR",
        "TWD",
        "INR",
        "IDR",
        "KRW",
        "CNY",
    ],
}
main = [
    "FXVRP7D_NSA",
    "FXVRP2W_NSA",
    "FXVRP1M_NSA",
    "FXVRP2M_NSA",
    "FXVRP3M_NSA",
    "FXVRP7DUSD_NSA",
    "FXVRP2WUSD_NSA",
    "FXVRP1MUSD_NSA",
    "FXVRP2MUSD_NSA",
    "FXVRP3MUSD_NSA",
]
econ = ["FXXRxEASD_NSA", "BXBGDPRATIO_NSA_12MMA"]
mark = ["EQXR_NSA", "EQXR_VT10", "FXXR_NSA", "FXXR_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()
    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 496
Downloading data from JPMaQS.
Timestamp UTC:  2023-06-02 23:52:22
Connection successful!
Number of expressions requested: 1984
Requesting data: 100%|████████████████████████| 100/100 [00:30<00:00,  3.23it/s]
Downloading data: 100%|███████████████████████| 100/100 [01:41<00:00,  1.02s/it]
Download time from DQ: 0:02:32.275674

Availability #

cids_exp = cids  # cids expected in category panels
msm.missing_in_df(dfd, xcats=main, cids=cids_exp)
Missing xcats across df:  set()
Missing cids for FXVRP1MUSD_NSA:  set()
Missing cids for FXVRP1M_NSA:  set()
Missing cids for FXVRP2MUSD_NSA:  set()
Missing cids for FXVRP2M_NSA:  set()
Missing cids for FXVRP2WUSD_NSA:  set()
Missing cids for FXVRP2W_NSA:  set()
Missing cids for FXVRP3MUSD_NSA:  set()
Missing cids for FXVRP3M_NSA:  set()
Missing cids for FXVRP7DUSD_NSA:  set()
Missing cids for FXVRP7D_NSA:  set()

Most indicators are available by 2000. In some emerging markets, most notably Taiwan, data is only available from the mid-2000s onwards.

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

print("Last updated:", date.today())
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/95f268dac57dacb97ed7d5bb86dce6ddcd50df2e5d52332b8c4aaaf6a9cf0d9b.png
Last updated: 2023-06-03
plot = msm.check_availability(
    dfd, xcats=main, cids=cids_exp, start_size=(20, 2), start_years=False
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/e8039d257a65749011485219ed7037ffffe5d51d972dcfe6cef5ee96a80b18ef.png
msp.heatmap_grades(dfd, xcats=main, cids=cids_exp, start="2000-01-10", size=(18, 6))
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/6e0506695ce434fa3ca5c2c8c3e32c7af1316a402f9070765045caf719663c23.png
xcatx = main[0:2]
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/shock-and-risk-measures/_images/01e7b7a969db26d4b558dd04c2b68af650eb7d168ce98a53cae608e19dab183b.png https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/886580cb22892dc51117f990a6da5df6a4b4e7c2b1aa5699fc39abf946566e1f.png

History #

Shorter-dated premia (7 days - 2 weeks) #

Average premia have been positive for all currencies, but much more so for currencies of emerging markets than those of developed markets. Partially or temporarily managed currencies showed particularly high premia, plausibly because of repressed historic volatility.

Estimated premia display much volatility, which may largely reflect that historic return volatility is a very crude metric of future short-term volatility. The latter can depend a lot on calendar effects, forthcoming central bank meetings or political events, for example.

xcatx = ["FXVRP7D_NSA", "FXVRP2W_NSA"]
msp.view_ranges(
    dfd,
    xcats=xcatx,
    cids=cidx,
    sort_cids_by="mean",
    start="2000-01-01",
    title="Means and standard deviations of short-dated volatility risk premia, since 2000",
    xcat_labels=["7 days", "2 weeks"],
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/629c5a0f93d570616b8c99753f3cdc476f9531aab21751fd44493f5bc618aea7.png
xcats_sel = ["FXVRP7D_NSA", "FXVRP2W_NSA"]
msp.view_timelines(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    start="2000-01-01",
    title="Shorter-dated FX volatility risk premia",
    title_adj=0.95,
    xcat_labels=None,
    ncol=4,
    same_y=False,
    label_adj=0.05,
    aspect=1.7,
    all_xticks=False,
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/582ae4dbb1df80cb190be51b46184be76cc0f7c70b62040f7cbf2f220146a66c.png

Using weekly averages, volatility risk premia have been predominantly positively correlated, with low and negative correlations focused on those currencies that trade against the euro.

dfd_vrp7 = dfd[dfd["xcat"] == "FXVRP7D_NSA"][
    ["real_date", "cid", "xcat", "value"]
].set_index("real_date")
dfw_vrp7 = dfd_vrp7.groupby(["cid", "xcat"]).resample("W").mean().reset_index()
msp.correl_matrix(dfw_vrp7, xcats="FXVRP7D_NSA", cids=cids_exp, size=(20, 14))
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/6843d216b835433b81f6a8ab136eba89e8292f009641e02ba352e949dd3053df.png

Volatility risk premium (1 - 3 months) #

Premia with longer forward horizons have also all been positive on average over the past two decades for all countries. Also on average EM premia have been notably higher than developed market premia.

xcats_sel = ["FXVRP1M_NSA", "FXVRP2M_NSA", "FXVRP3M_NSA"]
msp.view_ranges(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    sort_cids_by="mean",
    start="2000-01-01",
    kind="bar",
    size=(16, 8),
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/cec20a6dcd815ab29290e11a55d297f0ee75df657ed1cf740376f1a83be68ad7.png
xcats_sel = ["FXVRP1M_NSA", "FXVRP3M_NSA"]
msp.view_timelines(
    dfd,
    xcats=xcats_sel,
    cids=cids_exp,
    start="2000-01-01",
    title="Volatility risk premia: 1 month and 3 months ahead",
    title_adj=0.95,
    xcat_labels=["1-month horizon", "3-month horizon"],
    ncol=4,
    same_y=False,
    label_adj=0.05,
    aspect=1.7,
    all_xticks=True,
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/9360174574ac507f3a265b81f524d135c011acebbcb3ccba5a5bd872bffc9a7b.png

As for shorter-horizon premia, the 3-month premia have predominantly been positively correlated across currency areas (using weekly averages).

dfd_vrp7 = dfd[dfd["xcat"] == "FXVRP3M_NSA"][
    ["real_date", "cid", "xcat", "value"]
].set_index("real_date")
dfw_vrp7 = dfd_vrp7.groupby(["cid", "xcat"]).resample("W").mean().reset_index()
msp.correl_matrix(dfw_vrp7, xcats="FXVRP3M_NSA", cids=cids_exp, size=(20, 14))
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/b8ae686b9e897b6d88157e126fdd2003e8cb0b411b5420a30e3db2d9afe610e1.png

Importance #

Relevant research #

“Volatility risk premia – differences between implied and realized volatility – are plausible and empirically validated predictors of directional foreign exchange returns, particularly for EM currencies. The intuition is that excess implied volatility typically results from elevated risk aversion, which should be indicative of undershooting.” sr-sv

“Variance risk premiums mark the difference between implied (future) and past volatility. They indicate changes in risk aversion or uncertainty. As these changes may differ or have different implications across countries, they may cause FX overshooting and payback. The effect complements the simpler argument that rising currency volatility predicts lower FX carry returns. Academic papers support both effects empirically.” sr-sv

“The variance risk premium manifests as a long-term difference between option-implied and expected realized asset price volatility. It compensates investors for taking short volatility risk, which typically comes with a positive correlation with the equity market and occasional outsized drawdowns…Evidence since the mid-1990s suggests that variance is an attractive factor for the long run, particularly when positions take steady equal convexity exposure. Unlike other factor strategies, variance exposure has earned premia fairly consistently and typically recovered well from its intermittent large drawdowns.” sr-sv

Empirical clues #

As suggested by theory, volatility risk premia have bee significantly positively correlated with subsequent quarterly, monthly or weekly FX forward returns.

cr = msp.CategoryRelations(
    dfd,
    xcats=["FXVRP1M_NSA", "FXXR_VT10"],
    cids=cids_exp,
    freq="Q",
    lag=1,
    xcat_aggs=["last", "sum"],
    fwin=1,
    xcat_trims=[6, 40],
    start="2002-01-01",
)
cr.reg_scatter(
    title="Volatility risk premia and subsequent FX forward returns (all currencies since 2002)",
    labels=False,
    coef_box="upper right",
    xlab="Volatility risk premium (1 month horizon), end of quarter",
    ylab="Vol-targeted FX forward return, next quarter",
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/e97cfe5942e4a0c06427989886be1403306d7f027fbf96d91d7864cc6cdb2a2c.png
cr = msp.CategoryRelations(
    dfd,
    xcats=["FXVRP1M_NSA", "FXXR_VT10"],
    cids=cids_exp,
    freq="W",
    lag=1,
    xcat_aggs=["last", "sum"],
    fwin=1,
    xcat_trims=[6, 40],
    start="2002-01-01",
)
cr.reg_scatter(
    title="Volatility risk premia and subsequent weekly FX forward returns (all currencies since 2002)",
    labels=False,
    coef_box="upper right",
    xlab="Volatility risk premium (1 month horizon), end of week",
    ylab="Vol-targeted FX forward return, next week",
)
https://macrosynergy.com/notebooks.build/themes/shock-and-risk-measures/_images/2f554c8d79584e58cbb3fea9d0be7b1649ea2300c6662a59ff39a2438e6a27d3.png

Appendices #

Appendix 1: Calculation details #

The category group contains estimates of the volatility risk premium , defined for our purposes as the ratio

(2) # \[\begin{equation} \text{VRP}(\tau,H) \equiv \frac{\sigma_i(\tau)-\sigma_r(H)}{\sigma_r(H)}, \end{equation}\]

where \(\sigma_i(\tau)\) is the volatility implied by the market for at-the-money options with time to maturity \(\tau\) , and \(\sigma_r(H)\) is the realised (historical) volatility, extracted using an exponentially weighted moving average filter of half-life \(H\) . The VRP is unitless and refers to the fractional deviation of implied volatility from realised volatility.

Notes:

  • Implied volatility is the paramter \(\sigma_i\) which when substitued into the Black-Scholes formula yields the market price for FX options. It codifies the standard deviation of returns of the underlying currency-pair, and will generally be a function of both the strike of the option ( \(K\) ) and the time to maturity of the option ( \(\tau\) ).

  • Realised volatility is here defined as the running standard deviation of (daily) FX spot returns, exponentially weighted through time: \begin{equation} \sigma_{r,t} = \sqrt{261 \cdot \sum_{k=0}^\infty \lambda^k r_{t-k}^2}, \end{equation} where \(\lambda\) is a decay parameter. Following the RiskMetrics convention we set \(\lambda = 0.94\) , or identically, the half-life \(H\) equal to ~11 days (the value for \(k\) which renders \(\lambda^k \approx 0.5\) ). The \(\sqrt{261}\) is an annualisation factor reflecting the fact that currencies typically have 261 observation periods in a year. Assuming that returns are i.i.d.: \(\text{Var}[r_{\text{yearly}}]=\text{Var}[r_1+r_2+\cdots+r_{261}]=\text{Var}[r_1]+\text{Var}[r_2]+\cdots+\text{Var}[r_{261}]=261 \cdot\text{Var}[r_{\text{daily}}]\) .

  • Implied volatility codifies the market’s expectations of future uncertainty. Meanwhile realised volatility looks backwards in time.

  • Ample empirical evidence suggests implied volatility tends to exceed realised volatility (the so-called premium).

The image above depicts the historical 1M implied volatility of USDCAD alongside the exponentially weighted realised volatility. Note that implied generally exceeds realised volatility, except for particularly turbulent periods.