I have a slight confusion with regards to what price the repo rate impacts.

Assume the repo for a particular bond richens. My current thought process is, spot should also richen (as now that bond carries more attractively). All else the same, it should be bid versus its peers.

However, cash & carry arguments also tell me that the forward should cheapen. As for a given level of spot i should be willing to sell forward at a lower level (as I carry more positively or less negatively with a lower repo if I am long spot).

I am 99% sure these are part of the same 'effect' - just wanted to confirm this is the case as:

If I am long CTD basis spot richening should cause the basis to widen as spot increases (carry increases) but also conceptually, looking at the future as a forward (i.e. ignoring optionality for now), the forward should cheapen. Which means the IRR comes down along with the actual repo rate (all else the same) => BNOC stays fairly unchanged.

Many thanks - please feel free to correct any part of this thought process which is flawed.


1 Answer 1


This is my general approach for analysing repo specific bonds, or bonds that gone "special" on repo.

The Spot Price

Suppose firstly that we have 3 bonds, a 1y, 2y and 5y with different coupons and different YTMs currently. We can use this information to construct a bond curve that will value these bonds.

from rateslib import *

bond_curve = Curve(  # create a curve defined by discount factors that we will solve
        dt(2023, 1, 1): 1.0,
        dt(2024, 1, 1): 1.0,
        dt(2025, 1, 1): 1.0,
        dt(2028, 1, 1): 1.0,
b1y = FixedRateBond(dt(2023, 1, 1), "1Y", spec="ust", curves=bond_curve, fixed_rate=2.0)
b2y = FixedRateBond(dt(2023, 1, 1), "2Y", spec="ust", curves=bond_curve, fixed_rate=3.5)
b5y = FixedRateBond(dt(2023, 1, 1), "5Y", spec="ust", curves=bond_curve, fixed_rate=1.5)

Now we will solve the curve for YTM: 4.2%, 3.95% and 3.15%

solver = Solver(
        (b1y, (), {"metric": "ytm"}),
        (b2y, (), {"metric": "ytm"}),
        (b5y, (), {"metric": "ytm"}),
    s=[4.2, 3.95, 3.15]

The bond_curve now looks like this for overnight forwards:

enter image description here

We can use a curve or a price from ytm function to get the price of each bond:

b1y.rate(bond_curve)  # 97.4295
b2y.rate(bond_curve)  # 99.1448 
b5y.rate(bond_curve)  # 92.4295

Now suppose one of these bonds goes "special". We need to adjust the bond_curve to reflect the new discounting regime for certain bonds cashflows.

We can create a curve just with the specialness. You can see the construction below (I use a cubic spline) and have plotted the forwards. In this case the specialness fades over the next 6 months initially worth about 1.5%.

specialness = Curve(
        dt(2023, 1, 1): 1.0,
        dt(2023, 7, 1): 1.005,
        dt(2023, 7, 2): 1.005,
        dt(2028, 1, 1): 1.005,
    t=[dt(2023, 1, 1), dt(2023, 1, 1), dt(2023, 1, 1), dt(2023, 1, 1),
       dt(2023, 7, 1), dt(2023, 7, 2),
       dt(2028, 1, 1), dt(2028, 1, 1), dt(2028, 1, 1), dt(2028, 1, 1)]

enter image description here

OK now for the magic. We add the specialness to the bond_curve and reprice every bond:

composite = CompositeCurve([bond_curve, specialness])

enter image description here

b1y.rate(composite)  # 98.3589
b2y.rate(composite)  # 99.6301
b5y.rate(composite)  # 92.8839

Notice how all bond prices have gone up to reflect the specialness.

You can quite easily make back of the envelope calculations to arrive at very similar prices by valuing the specialness and adding into the unadjusted bond price, but the analysis is here none-the-less to experiment.

The Forward Price

The interesting question is then what happens to the forward of the bond after the specialness?

In this example the "specialness" ceasaed as of 1st July 2023, i.e. after 6-months. The curves bond_curve and composite can be observed to converge to as of 1st July 2023.

If we calculate the forward prices as measured by each curve they are the same:

b5y.rate(composite, metric="fwd_clean_price", forward_settlement=dt(2023, 7, 1))  # 93.5867
b5y.rate(bond_curve, metric="fwd_clean_price", forward_settlement=dt(2023, 7, 1))  # 93.5868

It is also possible to observe that the forward prices derived from the adjusted spot according to the different repo rates are the same:

b5y.fwd_from_repo(92.8839, dt(2023, 1, 3), dt(2023, 7, 1), 3.131)  # 93.5884
b5y.fwd_from_repo(92.429, dt(2023, 1, 3), dt(2023, 7, 1), 4.131)  # 93.5861

So all is economically consistent.

  • $\begingroup$ Thank you. I think I get it..in my simple world (pen and paper) I would probably do something like: if the bond is special by 1.5% then over 3months all else the same I'd carry 1.5%x0.25=37.5cents. Say for a 10y bond this would that would mean it should richen by 3.75bps in yield. $\endgroup$
    – user68819
    Commented Sep 26, 2023 at 17:15
  • $\begingroup$ Thats it! The advantage of using curves is that you can rely on the objects to determine the right prices and you can have different bonds at different repo rates etc. $\endgroup$
    – Attack68
    Commented Sep 26, 2023 at 18:06
  • $\begingroup$ Cheers, thank you for your help $\endgroup$
    – user68819
    Commented Sep 26, 2023 at 19:46
  • $\begingroup$ the only issue I still have is, in the example above, bond A repo goes special i.e. 1.5% under GC. so it richens. Intuition tells me: its carry increases. But - what changes, the spot or the fwd? I am probably missing some basic linkage here in my mind. In our example above, I assumed spot richens 3.75bps in yield..but that surely means the fwd is unchanged in this scenario (all else constant). $\endgroup$
    – user67825
    Commented Sep 26, 2023 at 21:01
  • $\begingroup$ my naive thinking would be, spot is more liquid so any changes in repo are reflected there first. and if we extrapolate this to bond/future CTD basis - the basis (all else constant) should be wider if the CTD goes special? $\endgroup$
    – user67825
    Commented Sep 26, 2023 at 21:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.