Skip to content
All library documents

Estimating an Illiquid Gilt Futures Price from Sparse Rates and FX Data

Article Quant Q&A · Author: Lmnop

Summary

The document outlines a way to estimate a five-year UK gilt yield, and a potential futures price, when the contract is illiquid and only a few related market quotes are available. It first fits a US Treasury curve to short and longer maturity rate observations, then infers a sterling curve using a UK short rate, a ten-year gilt yield, and GBP/USD forward quotes. A bond-pricing model applied to that inferred curve provides the five-year gilt yield estimate.

The procedure illustrates how curve construction and covered interest parity can combine otherwise sparse data. Its example uses spline interpolation and a solver to fit the curves to observed instruments. The result is only an indicative model estimate: the author warns the inputs are insufficient for trading accuracy, assumes no default risk, treats Treasury yields as FX parity rates, and sets cross-currency basis to zero. These assumptions can materially distort the sterling curve and therefore the estimated gilt yield.

Key ideas

  • A sparse set of US rate observations can be used to fit a Treasury discount curve through interpolation.
  • A sterling curve can be inferred by combining UK rates with GBP/USD forward prices and the US curve.
  • The example estimates a five-year gilt yield by repricing a bond against the inferred sterling curve.
  • Using Treasury yields for FX parity and assuming zero cross-currency basis are significant limitations.
  • The resulting estimate is illustrative and lacks the accuracy needed for confident trading.

Tags

Full text
# Estimating the price of an illiquid 5y bond futures contract


# Estimating the price of an illiquid 5y bond futures contract












Say I know the price of 10y Gilt futures, 10y Treasury futures, 5y Treasury futures, and GBPUSD futures.

I am asked to produce a quote on 5y Gilt futures using only this data. What is a sensible approach to this problem?

WLOG we may assume the risk of default is zero.

## Answer by Attack68 (score 5)

https://quant.stackexchange.com/a/79588

That is not a lot of information and is certainly not enough to do this with sufficient accuracy for trading, but none-the-less we can do the exercise.

#### Information

I will assume that you also know additional information so your full suite is something like:



- The current SONIA rate: 5.25%



- Some forward GBPUSD rates (from your futures), e.g. 1y fwd: 1.276 2y fwd: 1.279 3y fwd: 1.281

- 10y and 5y US treasury rates: 4.52% and 4.51%

- 10y UK gilt rate: 4.32%

#### US Curve

Proceed by first building a US treasury curve, from the three US rates you have. Since this is particularly sparse I will use log-cubic spline interpolation and derive something like the following:

```
# PYTHON 
from rateslib import *  # requires >= 1.3.0

usd = Curve(
    # Discount factor nodes at 6M, 5Y and 10Y
    nodes={dt(2000, 1, 1): 1.0, dt(2000, 7, 1): 1.0, dt(2005, 1, 1): 1.0, dt(2010, 1, 5): 1.0},
    # Specify knots for a cubic spline
    t=[dt(2000, 1, 1), dt(2000, 1, 1), dt(2000, 1, 1), dt(2000, 1, 1), 
       dt(2000, 7, 1), dt(2005, 1, 1), 
       dt(2010, 1, 5), dt(2010, 1, 5), dt(2010, 1, 5), dt(2010, 1, 5)],
)
solver = Solver(
    curves=[usd],
    instruments=[
        IRS(dt(2000, 1, 4), "1b", spec="usd_irs", curves=usd),
        (FixedRateBond(dt(2000, 1, 1), "5y", spec="ust", curves=usd, fixed_rate=2.0), (), {"metric": "ytm"}),
        (FixedRateBond(dt(2000, 1, 1), "10y", spec="ust", curves=usd, fixed_rate=2.0), (), {"metric": "ytm"}),
    ],
    s=[5.25, 4.52, 4.51]  # <- SOFR rate + US treasury yields
)
SUCCESS: `func_tol` reached after 5 iterations (levenberg_marquardt), `f_val`: 4.9611913959284605e-12, `time`: 0.0374s
```

This will produce an overnight rates curve that looks as follows:

```
usd.plot("1b")
```

#### GBP Curve

Now is the tricky part. You are making 2 key assumptions that are very poor in practice:

- Treasury rates are affecting FX parity calculations. (RFR rates are usually used for FX parity calculations not Treasury rates, since theTreasury and RFR rates diverge over tenors)

- The cross currency basis is zero. FX parity equations are usually impacted by currency basis but you have none of that information so we assume it is zero. It can be quite significant, however.

So we will build a GBP curve that tries to assert FX parity under these assumptions.

```
gbp = Curve(
    # Same discount factor nodes at 6M, 5Y and 10Y
    nodes={dt(2000, 1, 1): 1.0, dt(2000, 7, 1): 1.0, dt(2005, 1, 1): 1.0, dt(2010, 1, 5): 1.0},
    # Same spline knot points
    t=[dt(2000, 1, 1), dt(2000, 1, 1), dt(2000, 1, 1), dt(2000, 1, 1), 
       dt(2000, 7, 1), dt(2005, 1, 1), 
       dt(2010, 1, 5), dt(2010, 1, 5), dt(2010, 1, 5), dt(2010, 1, 5)],
)
# Create an FX forward market from spot and the rates curves.
fxf = FXForwards(
    fx_rates=FXRates({"gbpusd": 1.275}, settlement=dt(2000, 1, 1)),
    fx_curves={"gbpgbp": gbp, "usdusd": usd, "gbpusd": gbp}
)
# Solve everything matching all the known instruments.
solver2 = Solver(
    pre_solvers=[solver],
    curves=[gbp],
    instruments=[
        IRS(dt(2000, 1, 4), "5b", spec="gbp_irs", curves=gbp),
        (FixedRateBond(dt(2000, 1, 1), "10y", spec="ukt", curves=gbp, fixed_rate=2.0), (), {"metric": "ytm"}),
        FXExchange(pair="gbpusd", settlement=dt(2001, 1, 1), curves=[None, gbp, None, usd]),
        FXExchange(pair="gbpusd", settlement=dt(2002, 1, 1), curves=[None, gbp, None, usd]),
        FXExchange(pair="gbpusd", settlement=dt(2003, 1, 1), curves=[None, gbp, None, usd]),
    ],
    s=[5.25, 4.32, 1.276, 1.279, 1.281],
    fx=fxf,
)
SUCCESS: `conv_tol` reached after 10 iterations (levenberg_marquardt), `f_val`: 1.3783402897407275e-06, `time`: 0.0493s
```

The process of solving tries to find a suitable GBP curve that matches all of the known information and will reprice the known GBP rates and also solve the FX parity equations (albeit not exactly in this case since the curve is overspecified)

Your curves now look like this:

```
usd.plot("1b", comparators=[gbp], labels=["usd", "gbp"])
```

With this curve you can estimate the yield of the 5y Gilt (and convert that to a potential futures price)

```
FixedRateBond(dt(2000, 1, 1), "5y", spec="ukt", fixed_rate=2.0).rate(gbp, metric="ytm")
# 4.296175
```

So the best guess for all this information is 4.296%.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.