import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
import pandas as pd
= pd.read_csv("data/spx_option_data.csv")
df # Filter the data for options with 352 days to maturity
= df[df['maturity'] == 352].copy()
df_352
# Compute the put-call differential
'put_call_diff'] = df_352['put_price'] - df_352['call_price']
df_352[
# Perform a linear regression of put-call differential against strike price
= df_352['strike']
X = df_352['put_call_diff']
y = sm.add_constant(X) # Add constant for intercept
X
= sm.OLS(y, X).fit()
model = model.params[1]
slope
# Compute implied box rate R_T
= 352
T_days = 1 / slope
R_T
# Plot put-call differential against strike price
=(8, 5))
plt.figure(figsize'strike'], df_352['put_call_diff'], label="Put-Call Differential", alpha=0.7)
plt.scatter(df_352['strike'], model.predict(X), color='red', label="Regression Line")
plt.plot(df_352["Strike Price")
plt.xlabel("Put-Call Differential")
plt.ylabel("Put-Call Differential vs. Strike Price")
plt.title(
plt.legend()True)
plt.grid( plt.show()
Box Spread Solution
Task 1
# Calculate cost of synthetic long at K=4000 and synthetic short at K=6000
= 4000, 6000
K1, K2 = df_352.loc[df_352['strike'] == K2, 'put_call_diff'].values[0] - \
box_spread_cost 'strike'] == K1, 'put_call_diff'].values[0]
df_352.loc[df_352[
# Risk-free payoff at T
= K2 - K1
box_spread_payoff
# Display results
print(f"Implied Box Rate (R_T): {R_T}")
print(f"Upfront Cost for Box Spread: {box_spread_cost}")
print(f"Risk-Free Payoff in 352 Days: {box_spread_payoff}")
Implied Box Rate (R_T): 1.0102565338916818
Upfront Cost for Box Spread: 1979.65
Risk-Free Payoff in 352 Days: 2000
Task 2
# Compute the left-hand side (LHS) of put-call parity: P - C
= df_352['put_call_diff']
lhs
# Compute the right-hand side (RHS) of put-call parity: K * e^(-rT) - S0
= 1 / R_T # e^(-rT)
discount_factor = df_352['strike'] * discount_factor - df_352['spx']
rhs
# Plot LHS against RHS with a 45-degree line
=(7, 7))
plt.figure(figsize="Put-Call Parity", alpha=0.7)
plt.scatter(rhs, lhs, labelmin(lhs), max(lhs)], [min(lhs), max(lhs)], color='red', linestyle="--", label="45-degree Line")
plt.plot(["Right-Hand Side (K * e^(-rT) - S0)")
plt.xlabel("Left-Hand Side (P - C)")
plt.ylabel("Checking Put-Call Parity")
plt.title(
plt.legend()True)
plt.grid( plt.show()
Task 3
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
# Load datasets
= "data/spx_option_data.csv" # Update with your file path
option_file_path = "data/treasury_yield.csv"
treasury_file_path
= pd.read_csv(option_file_path)
df_options = pd.read_csv(treasury_file_path)
df_treasury
# Compute box rates for each available maturity in the option dataset
= df_options.groupby('maturity')
maturity_groups
= []
box_rates
for maturity, group in maturity_groups:
= group['strike']
X = group['put_price'] - group['call_price']
y = sm.add_constant(X) # Add intercept
X
= sm.OLS(y, X).fit()
model = model.params[1]
slope
# Compute box rate and correctly annualize it: (R_T)^(360/T) - 1
= 1 / slope
R_T = (R_T ** (360 / maturity)) - 1
annualized_R_T
'maturity_days': maturity, 'box_rate': annualized_R_T * 100}) # Convert to percentage
box_rates.append({
# Convert to DataFrame
= pd.DataFrame(box_rates)
df_box_rates 'maturity_months'] = df_box_rates['maturity_days'] / 30
df_box_rates[
# Plot the annualized box rate against maturity in months
=(8, 5))
plt.figure(figsize'maturity_months'], df_box_rates['box_rate'], marker='o', linestyle='-', label="Annualized Box Rate")
plt.plot(df_box_rates[
# Overlay the Treasury yield curve
'months'], df_treasury['tbill_rate'], marker='s', linestyle='--', label="Treasury Yield")
plt.plot(df_treasury[
"Maturity (Months)")
plt.xlabel("Annualized Rate (%)")
plt.ylabel("Annualized Box Rate vs. Treasury Yield Curve")
plt.title(
plt.legend()True)
plt.grid( plt.show()