For e.g., when pricing a GBPUSD FX-Forward we build the USD SOFR curve through which we get USD risk-free rate. For the GBP risk-free rate, we use the sum of GBPUSD Basis and SONIA. However, if we are valuing a Forward for something like GBPJPY, how do we use a GBPUSD basis and the USDJPY basis to achieve our goal. Basically, how do we incorporate the cross-currency risk in valuation of FX-Forwards for these types of currencies?
2 Answers
If your accounting is in ccy$_0$, such as USD or EUR, then you discount the ccy$_1$ leg of the ccy$_1$-ccy$_2$ forward with ccy$_1$ rfr + ccy$_1$-ccy$_0$ cross-currency basis spread and likewise discount the ccy$_2$ leg with ccy$_2$ rfr + ccy$_2$-ccy$_0$ basis. You don't use any ccy$_1$-ccy$_2$ basis. For market risk, you calculate the sensitivities to ccy$_i$ rfr and to ccy$_i$-ccy$_0$ basis.
For some currencies, the ccy-EUR basis is more observable than the ccy-USD basis, so you need to estimate the latter from the former, so that you can correctly see your sensitivity to it.
Note that if you were pricing fx options, rather than simple linear forwards, then you'd need to oserve or guess the ccy$_1$-ccy$_2$ implied volatility surface, and wouldn't be able to estimate it reasonably well from other currency pairs, like you can the cross-currency basis spread.
For some emerging markets currencies, you can observe the sum of ccy$_e$ rfr + ccy$_e$-ccy$_0$ basis because people do trade offshore non-delivery forwards and cross-currency swaps, but can't observe them separately because few people trade onshore fix v float interest rate swaps. In this case, for market risk, you can either try harder to decompose by "guessing" what the unobservable rfr would be, or calculate the sensitivities only to the sum. Both approaches have their disadvantages.
Note that if the accounting were in some other currency ccy$_0'$, then the mark to market would not necessarilly exactly equal ccy$_0$ mark to market $\times$ spot rate, although they would be in line.
-
$\begingroup$ Does this work only for USD accounting? If we were to find the value in one of the currencies(GBP or JPY for GBPJPY currency pairs) , would this approach not work? $\endgroup$– humanoidCommented Nov 24, 2023 at 9:33
-
$\begingroup$ For any currency. I'll edit the answer to make it more general. $\endgroup$ Commented Nov 24, 2023 at 11:40
This is just an extension of @Dimitri 's answer but with numbers to eluciate the concept.
when pricing a GBPUSD FX-Forward we build the USD SOFR curve through which we get USD risk-free rate. For the GBP risk-free rate, we use the sum of GBPUSD Basis and SONIA.
Yes that is correct. Say USD 1Y rate is 5% and the GBP rate is 3% and the basis is -25bps, with GBPUSD at 1.25, then the 1Y forward you roughly expect is:
$$ 1.25 * \frac{1 + 5\%}{1 + 3\% -25bps} = 1.2773 $$
In pratice you will build a USD curve, a GBP curve and a basis curve. The USD and GBP curves can be built separately, in their local rates markets.
## Python: from rateslib import *
usdusd = Curve({dt(2023, 11, 24): 1.0, dt(2024, 11, 30): 1.0}, convention="act360")
gbpgbp = Curve({dt(2023, 11, 24): 1.0, dt(2024, 11, 30): 1.0}, convention="act365f")
solver = Solver(
curves=[usdusd, gbpgbp], # calibrate these curves
instruments=[
IRS(dt(2023, 11, 24), "1Y", spec="usd_irs", curves=usdusd),
IRS(dt(2023, 11, 24), "1Y", spec="gbp_irs", curves=gbpgbp)
],
s=[5.0, 3.0], # these are the rates
id="rates 1",
instrument_labels=["US1Y", "GB1Y"]
)
The cross currency market needs to know about FX rates
gbpusd = Curve({dt(2023, 11, 24): 1.0, dt(2024, 11, 30): 1.0}, convention="act365f")
fxf = FXForwards(
fx_rates=FXRates({"gbpusd": 1.25}, settlement=dt(2023, 11, 24)),
fx_curves={"usdusd": usdusd, "gbpgbp": gbpgbp, "gbpusd": gbpusd},
)
solver2 = Solver(
curves=[gbpusd], # calibrate this curve
pre_solvers=[solver], # with pre existing curve knowledge
instruments=[
XCS(dt(2023, 11, 24), "1Y", spec="gbpusd_xcs", curves=[gbpgbp, gbpusd, usdusd, usdusd]),
],
s=[-25.0],
id="gbpusd",
fx=fxf,
)
So setting up these Curves and calibrating them to the market has created an accurate, arbitrage free FX Forward framework. We can use it to price the 1Y forward GBPUSD rate, close to the above back of the envelope calculation.
fxf.rate("gbpusd", dt(2024, 11, 24))
# 1.278262
However, if we are valuing a Forward for something like GBPEUR, how do we use a GBPUSD basis and the USDEUR basis to achieve our goal. Basically, how do we incorporate the cross-currency risk in valuation of FX-Forwards for these types of currencies?
Well, now we can extend and repeat this for USDEUR. Suppose the EUR 1Y rate is 4% and the basis is +25bps, then we expect.
$$ 1.10 * \frac{1+5\%}{1+4\%+25bps} = 1.1079 $$
eureur = Curve({dt(2023, 11, 24): 1.0, dt(2024, 11, 30): 1.0}, convention="act360")
eurusd = Curve({dt(2023, 11, 24): 1.0, dt(2024, 11, 30): 1.0}, convention="act360")
fxf = FXForwards(
fx_rates=FXRates({"gbpusd": 1.25, "eurusd": 1.10}, settlement=dt(2023, 11, 24)),
fx_curves={"usdusd": usdusd,"gbpgbp": gbpgbp,"gbpusd": gbpusd,"eureur": eureur,"eurusd": eurusd},
)
solver3 = Solver(
curves=[eureur, eurusd], # calibrate these curves
pre_solvers=[solver2], # with preexisting curve knowledge
instruments=[
IRS(dt(2023, 11, 24), "1y", spec="eur_irs", curves=eureur),
XCS(dt(2023, 11, 24), "1Y", spec="eurusd_xcs", curves=[eureur, eurusd, usdusd, usdusd]),
],
s=[4.0, 25.0],
id="eur",
instrument_labels=["EU1Y", "eurusd"],
fx=fxf,
)
fxf.rate("eurusd", dt(2024, 11, 24))
# 1.107949
This has now created an FX Forwards framework containing market knowledge for any of the three currencies and can produce discount curves versus any collateral currency due to the principles of no arbitrage. In your case you would then discount the flows of your forward transactions using the appropriate discount curve and you could convert the NPV of each leg to your preferred accounting currency.
In particular you are interested in the GBPEUR forward.
$$ GBPEUR = GBPUSD * USDEUR = \frac{GBPUSD}{EURUSD} \\ GBPEUR = \frac{1.25}{1.10} \frac{1 + 4\% +25bps}{1+3\% -25bps} = 1.1529 $$
fxf.rate("gbpeur", dt(2024, 11, 24))
# 1.153790 # the difference is due to ACT360 v ACT365F in case you are wondering