import pandas as pd
import numpy as np
import plotly.express as px
from fredapi import Fred
import streamlit as st
def fetch_yield_data(api_key, start_date, end_date):
"""
Fetches bond yield data from the FRED API.
Parameters:
api_key (str): Your FRED API key.
start_date (str): Start date for the data (YYYY-MM-DD).
end_date (str): End date for the data (YYYY-MM-DD).
Returns:
pd.DataFrame: DataFrame containing various bond yields.
"""
fred = Fred(api_key=api_key)
data = {
'3M': fred.get_series('DGS3MO', observation_start=start_date, observation_end=end_date),
'1Y': fred.get_series('DGS1', observation_start=start_date, observation_end=end_date),
'2Y': fred.get_series('DGS2', observation_start=start_date, observation_end=end_date),
'5Y': fred.get_series('DGS5', observation_start=start_date, observation_end=end_date),
'7Y': fred.get_series('DGS7', observation_start=start_date, observation_end=end_date),
'10Y': fred.get_series('DGS10', observation_start=start_date, observation_end=end_date),
'20Y': fred.get_series('DGS20', observation_start=start_date, observation_end=end_date),
'30Y': fred.get_series('DGS30', observation_start=start_date, observation_end=end_date)
}
df = pd.DataFrame(data)
df.index = pd.to_datetime(df.index)
return df
def classify_spreads(df, front_leg, back_leg, lookback):
"""
Calculates spreads and classifies yield regimes based on front/back legs and lookback.
Parameters:
df (pd.DataFrame): DataFrame containing bond yields.
front_leg (str): Front leg maturity (e.g., '2Y').
back_leg (str): Back leg maturity (e.g., '10Y').
lookback (int): Lookback period for comparison.
Returns:
pd.DataFrame: DataFrame with spreads and regime classification.
"""
front = df[front_leg]
back = df[back_leg]
curve = (back * 100) - (front * 100)
df['Curve'] = curve
df['Curve_Lookback'] = df['Curve'].shift(lookback)
df['Front_Lookback'] = front.shift(lookback)
df['Back_Lookback'] = back.shift(lookback)
conditions = [
(df['Curve'] > df['Curve_Lookback']) & (front < df['Front_Lookback']) & (back < df['Back_Lookback']), # Bull Steepener
(df['Curve'] > df['Curve_Lookback']) & (front > df['Front_Lookback']) & (back > df['Back_Lookback']), # Bear Steepener
(df['Curve'] > df['Curve_Lookback']) & (front < df['Front_Lookback']) & (back > df['Back_Lookback']), # Steepener Twist
(df['Curve'] < df['Curve_Lookback']) & (front < df['Front_Lookback']) & (back < df['Back_Lookback']), # Bull Flattener
(df['Curve'] < df['Curve_Lookback']) & (front > df['Front_Lookback']) & (back > df['Back_Lookback']), # Bear Flattener
(df['Curve'] < df['Curve_Lookback']) & (front > df['Front_Lookback']) & (back < df['Back_Lookback']), # Flattener Twist
]
labels = [
'Bull Steepener', 'Bear Steepener', 'Steepener Twist',
'Bull Flattener', 'Bear Flattener', 'Flattener Twist'
]
df['Regime'] = np.select(conditions, labels, default='No Change')
return df
def plot_spread(df):
"""
Plots the curve spread with regime classification using Plotly.
Parameters:
df (pd.DataFrame): DataFrame containing spreads and regimes.
Returns:
None: Displays an interactive Plotly plot.
"""
fig = px.scatter(
df.reset_index(),
x='index',
y='Curve',
color='Regime',
color_discrete_map={
'Bull Steepener': 'blue',
'Bear Steepener': 'red',
'Steepener Twist': 'purple',
'Bull Flattener': 'green',
'Bear Flattener': 'orange',
'Flattener Twist': 'cyan',
'No Change': 'black'
},
title=f'Yield Curve Spread with Regime Classification - {front_leg}-{back_leg}',
labels={
'index': 'Date',
'Curve': 'Curve Spread (bps)',
'Regime': 'Yield Curve State'
}
)
fig.update_layout(
legend_title="Regime",
xaxis_title="Date",
yaxis_title="Spread (bps)",
)
st.plotly_chart(fig)
# Bar chart for regime counts
regime_counts = df['Regime'].value_counts().reset_index()
regime_counts.columns = ['Regime', 'Days']
regime_counts = regime_counts.sort_values(by='Days', ascending=False)
most_common_regime = regime_counts.iloc[0]['Regime']
title = (f"Regime Counts from {start_date} to {end_date} (Lookback: {lookback}, Spread: {front_leg}-{back_leg})\n"
f"Most Common Regime: {most_common_regime}")
fig_bar = px.bar(
regime_counts,
x='Regime',
y='Days',
title=title,
labels={
'Regime': 'Yield Curve State',
'Days': 'Number of Days'
}
)
fig_bar.update_layout(
xaxis_title="Regime",
yaxis_title="Number of Days",
)
st.plotly_chart(fig_bar)
def plot_curve(df, days_ago):
"""
Plots the full curve based on selected days-ago index.
Parameters:
df (pd.DataFrame): DataFrame containing bond yields.
days_ago (list of int): Days ago indices to select curves.
Returns:
None: Displays an interactive Plotly plot.
"""
selected_indices = [-int(day) for day in days_ago]
selected_data = df.iloc[selected_indices]
curve_data = selected_data.transpose()
curve_data.columns = [f"{abs(idx)} days ago" for idx in selected_indices]
curve_data.reset_index(inplace=True)
curve_data.rename(columns={"index": "Term"}, inplace=True)
fig = px.line(
curve_data.melt(id_vars="Term", var_name="Observation", value_name="Yield"),
x="Term", y="Yield", color="Observation",
title="Yield Curve",
labels={"Term": "Maturity", "Yield": "Yield (%)", "Observation": "Observation"}
)
fig.update_layout(
legend_title="Observation",
xaxis_title="Maturity",
yaxis_title="Yield (%)",
)
st.plotly_chart(fig)
# Streamlit App
st.title("Yield Curve Spread Analysis")
tabs = st.tabs(["Regime Classification Tool", "Curve Builder"])
with tabs[0]:
lst =['3M', '1Y', '2Y', '5Y', '7Y', '10Y', '20Y', '30Y']
col1,col2,col3 = st.columns(3)
api_key = col3.text_input("Enter your FRED API Key:")
start_date = col1.date_input("Start Date:", value=pd.to_datetime("2024-01-01"))
end_date = col2.date_input("End Date:", value=pd.to_datetime("2025-01-01"))
front_leg = col1.selectbox("Select Front Leg:",lst,index=2)
back_leg = col2.selectbox("Select Back Leg:", lst, index=3)
lookback = st.slider("Select Lookback Period:", min_value=1, max_value=50, value=1)
if api_key:
st.write("Fetching yield data...")
df_yields = fetch_yield_data(api_key, start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d"))
df_yields = df_yields.ffill()
#st.write("Yield Data:", df_yields.head())
st.write("Calculating spreads and classifying regimes...")
df_classified = classify_spreads(df_yields, front_leg, back_leg, lookback)
#st.write("Classified Data:", df_classified.head())
plot_spread(df_classified)
with tabs[1]:
st.header("Curve Builder")
api_key = st.text_input("Enter your FRED API Key for Curve Builder:", key="curve_key")
if api_key:
st.write("Fetching yield data for curve...")
df_yields = fetch_yield_data(api_key, start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d"))
st.write("Yield Data:", df_yields.head())
days_ago_input = st.text_input("Enter days ago indices for the curve (e.g., '1,2,5'):")
if days_ago_input:
days_ago = days_ago_input.split(',')
st.write("Building the yield curve...")
plot_curve(df_yields, days_ago)