4

I cannot re-calculate the "asked yield" from today's Wall Street Journal for the following two Treasury notes/bonds:

MATURITY    COUPON  BID     ASK     ASK-YIELD
5/15/2023   0.125   98.2500 98.2540 4.695
5/15/2023   1.750   99.0640 99.0700 4.709

Here is what I did. I calculated 0.26 years left to maturity by counting days divided by 365, and similarly 0.24 years since last coupon date of 11/13/2022. I multiplied the coupon rate by 0.24 to get the accrued interest of 0.0298 for the first bond. Adding it to the quoted ask price, I get 98.2798 for the first bond. Next I calculate the payment on maturity as 100 + half the coupon rate = 100.0625. I then calculate the ratio of payment on maturity and cash price: 1.0181 and then raise it to the power (1 / 0.26) to get 1.0707 implying a yield to maturity of 7.07%

But 7.07% is very far from the yield quoted in the WSJ, which is 4.695%. So what am I doing wrong in my calculation?

98.2500 0.125   5/15/2023   0.26    11/13/2022  0.24    0.0298  98.2798 100.0625    1.0181  1.0707
99.0700 1.750   5/15/2023   0.26    11/13/2022  0.24    0.4171  99.4871 100.8750    1.0140  1.0541
user2297550
  • 155
  • 4

1 Answers1

7

There are a lot of things wrong in here:

  • WSJ does not seem to display the quotes properly. According to the website, Tullett Prebon is the source. Tullett is a very reputable broker with high quality data. The quotes are most likely received following market standard, which is to quote in 1/32nd. When you asked the question, the quote was 98-25+ when I looked at it, which is equal to 98 + 25/32 + 1/64 ~98.7968.
  • Your daycount is wrong because you need to use ACT/ACT and the period for the last coupon payment is only 181 days.
  • The way you solve for YTM is also not how it usually works. Generally, you have NPV = cashflow / (1+ytm*dcf) where dcf stands for daycount fraction and NPV is net present value (the current bond price).

Below is a screenshot from Bloomberg YAS, on the day you asked the question. I use bid price because my YAS screen is set to default to bid. There is no difference computation wise. enter image description here

Below, I will replicate the computation in Julia. There is no need to know the language. I used names similar to the Bloomberg screen and the code is mainly "mathematical" expressions. The relevant code is in 3 sections:

  • Firstly, I compute all relevant dates and daycount fractions.
  • Secondly, clean price, accrued interest and dirty price is computed
  • Lastly, ytm is used to show the final value is indeed the result from dirty price * ytm (adjusted for proper daycount). I manually compute YTM and show that the logic the OP used to compute YTM is called Equiv 1/Yr in Bloomberg. This however is not the conventionally computed yield for treasuries.

In general, yield computations are more often than not depending on a lot of details and treasury bills are computed differently for example, as can be seen here.

Everything starting at #combine results in a table can be ignored as this simply prepares the data in a readable format.

# import relevant tables 
using Dates, DataFrames, PrettyTables

compute dates

today = Date(2023,02,08) accrued_days = Day(86) start = today - accrued_days settle_date = Date(2023,05,15) days_to_settle = Day(1) coupon_date = settle_date-days_to_settle days_to_next_coupon = coupon_date - today accrued_days_between_coupon = coupon_date - start dcf_next_coupon = (days_to_next_coupon/accrued_days_between_coupon)/2 dcf_accrued = accrued_days/accrued_days_between_coupon/2

compute clean price, accrued interest and dirty price

price_clean = 98+25/32+1/64 accrued = dcf_accrued*(1/8) price_dirty = price_clean + accrued

ytm as quoted

ytm = 4.765459

final cashflow (notional plus interest)

final_cashflow = 100 + (1/8)/2

compute final cashflow based on ytm

final_cf_computed = round(price_dirty(1+ytm/100(dcf_next_coupon)), digits = 6)

compute YTM

ytm_computed = (final_cash_flow/price_dirty - 1)/(yf)200 op_logic = ((final_cash_flow/price_dirty)^(1/(yf/2))-1)100

combine results in a table

yield_comp = ["Price and Yield", "Price Clean", "Accrued Interest", "Price Dirty", "Final Cashflow", "Yield to Maturity (YTM)", "Final Cashflow according to YTM", "YTM computed", "Equiv 1/Yr (OP Logic)"] yields = ["",price_clean, accrued, price_dirty, final_cashflow, ytm, final_cf_computed, ytm_computed, op_logic] text = ["Dates", "Today", "Coupon Date", "Days To Next Coupon", "Coupon Start Date", "Accrued Days between coupons", "Daycount Fraction Accrued","Daycount Fraction next coupon" ] date_vals = ["", today, coupon_date, days_to_next_coupon, start, accrued_days_between_coupon, dcf_accrued , dcf_next_coupon]

df_res = DataFrame(Fields = append!(text, yield_comp), Values = append!(date_vals, yields) )

pretty print

hl_1 = Highlighter((data,i,j) -> data[i,1] == "Dates", crayon"bg:dark_gray white bold") hl_2 = Highlighter((data,i,j) -> data[i,1] == "Price and Yield", crayon"bg:dark_gray white bold") PrettyTables.pretty_table(df_res, border_crayon = Crayons.crayon"blue", header_crayon = Crayons.crayon"bold green", formatters = ft_printf("%.6f", [2]), highlighters = (hl_1, hl_2))

enter image description here

AKdemy
  • 3,059
  • 1
  • 13
  • 34