Junaid Hasan
Initialize option-implied strategy lab v1 with clean repo artifacts
b6d53e2
Raw
History Blame Contribute Delete
1.93 kB
from __future__ import annotations
from datetime import datetime, timezone
import pandas as pd
import yfinance as yf
from .types import OptionSnapshot
def _normalize_chain(
df: pd.DataFrame, option_type: str, expiry: pd.Timestamp, spot: float
) -> pd.DataFrame:
out = df.copy()
out["option_type"] = option_type
out["expiry"] = pd.to_datetime(expiry).tz_localize(None)
out["mid"] = (out["bid"].fillna(0.0) + out["ask"].fillna(0.0)) / 2.0
out["volume"] = out.get("volume", 0.0)
out["openInterest"] = out.get("openInterest", 0.0)
out = out[
[
"expiry",
"option_type",
"strike",
"bid",
"ask",
"mid",
"volume",
"openInterest",
]
].copy()
out = out.dropna(subset=["strike", "mid"])
out = out[out["strike"] > 0].copy()
out["moneyness"] = out["strike"] / float(spot)
return out
def fetch_option_snapshot(ticker: str, max_expiries: int = 2) -> OptionSnapshot:
tk = yf.Ticker(ticker)
hist = tk.history(period="1d")
if hist.empty:
raise ValueError(f"No price history for ticker {ticker}")
spot = float(hist["Close"].iloc[-1])
expiries = tk.options[:max_expiries]
if not expiries:
raise ValueError(f"No option expiries for ticker {ticker}")
rows = []
for expiry_str in expiries:
chain = tk.option_chain(expiry_str)
expiry = pd.to_datetime(expiry_str)
rows.append(_normalize_chain(chain.calls, "call", expiry, spot))
rows.append(_normalize_chain(chain.puts, "put", expiry, spot))
options = pd.concat(rows, ignore_index=True)
options = options.sort_values(["expiry", "option_type", "strike"]).reset_index(
drop=True
)
return OptionSnapshot(
ticker=ticker,
snapshot_time=datetime.now(timezone.utc),
spot=spot,
options=options,
)