There is a liquidity premium between on-the-run treasury issues and off-the-run issues with similar characteristics. This is why when building a yield curve, typically on-the-run issues are used to compute this curve as a representation of the risk-free rate.
It seems to be common academic and practitioner practice to remove these "special" securities (for obvious issues) when building the curve. However, when doing relative value analysis between these bonds, these off-the-runs are used exclusively.
Typically there are still discrepancies between very seasoned issues.
- How are these issues (eg an original 30 year bond with less than 2 years to maturity and very high coupon) incorporated into yield curve calculations?
- How can I meaningfully get a YTM for this example maturity?
For example, today's closest maturity original 30-year bond is:
912810EB0 - Nov15'18 9.0 - BID 101.14000 ASK 101.30600 Mark Yield 1.994%
(edit: this yield is provided by the brokerage, but does seem reasonable so their "bond math" did not blow up like mine did).
I have tried my own bond math and some calculators available here: http://www.quantwolf.com/calculators/bondyieldcalc.html
def bond_ytm(bond): price = float(bond['END OF DAY']) par = 100. T = bond['T'] t = pd.to_datetime(bond['date']) if bond['SECURITY TYPE'] == 'MARKET BASED FRN': freq = 4 else: freq = 2 coupon = float(bond['RATE'].strip('%'))/freq coupon_dates = get_coupon_dates(bond, afterDate=t) # zero-coupon if len(coupon_dates) == 0: return (par/price)**(1/T) - 1.0 dt = dates_to_relative(coupon_dates, anchor=t) def Px(Rate): return price - (((par + coupon) / (1 + Rate/freq)**(T)) + ((coupon/Rate) * sum([(1 / (1+Rate/freq)**(time*freq)) for time in dt]) )) ytm_func = lambda y: coupon*sum([1/(1+y/freq)**(time) for time in dt]) + 1/(1+y/freq)**(freq*T) guess = coupon/par return optimize.newton(Px, 0.03, maxiter=500)
Both of these are giving me results that are nonsense (like a 25% YTM).
How can I get some meaningful results for these very seasoned issues?