A. Treasury Yield Curve Construction

  • Data from the product page of each of the following, as of 9/25/2025

\[\]

Ticker

Fund

SGOV

iShares 0-3 Month Treasury Bond ETF

SHY

iShares 1-3 Year Treasury Bond ETF

IEI

iShares 3-7 Year Treasury Bond ETF

IEF

iShares 7-10 Year Treasury Bond ETF

TLH

iShares 10-20 Year Treasury Bond ETF

TLT

iShares 20+ Year Treasury Bond ETF

\[\]
[67]:
import pandas as pd

etfs = ['SGOV', 'SHY', 'IEI', 'IEF', 'TLH', 'TLT']

dfs = []

for etf in etfs:
    df = pd.read_csv(f'data/{etf}_holdings.csv', header=9)

    # Coerce date-like columns after parsing
    for c in ['Maturity', 'Accrual Date', 'Effective Date']:
        if c in df.columns:
            df[c] = pd.to_datetime(df[c].astype(str).str.strip(), format='%b %d, %Y', errors='coerce')

    mask_treasury = df['Name'].astype(str).str.contains('TREASURY', case=False, na=False)
    mask_bond = df['Name'].astype(str).str.contains('BOND', case=False, na=False)
    mask_note = df['Name'].astype(str).str.contains('NOTE', case=False, na=False)
    mask_bill = df['Name'].astype(str).str.contains('BILL', case=False, na=False)
    df = df.loc[mask_treasury & (mask_bill | mask_bond | mask_note)][['Name', 'Price', 'Maturity', 'Coupon (%)', 'Accrual Date', 'Effective Date']]
    dfs.append(df)

all = pd.concat(dfs).sort_values(by='Maturity').reset_index(drop=True)
all.to_csv('data/all.csv')

From ZCB Only

[110]:
from numpy import log
from pandas import DataFrame

base_date = pd.Timestamp(2025, 9, 25)

df = all[all['Coupon (%)']==0]
df['Base Date'] = base_date
T = (df['Maturity'] - df['Base Date']).apply(lambda time_diff: time_diff.days).values/365
P = df['Price'].values

DataFrame({'Term (Year)': T, 'Zero Rate': -log(P/100)/T}).set_index('Term (Year)').plot(style='.-')
C:\Users\zhang\AppData\Local\Temp\ipykernel_33780\1447081203.py:7: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['Base Date'] = base_date
[110]:
<Axes: xlabel='Term (Year)'>
_images/A_yc_construction_4_2.png