mathidot's picture
build option trading agent modules
8f1601b
from __future__ import annotations
import json
from smolagents import tool
from market_data.providers import get_price_history
from strategy.payoff import expiration_payoff, strategy_summary
from strategy.schemas import OptionLeg, OptionStrategy
from .option_backtest import backtest_long_straddle_from_quotes, load_option_quotes_csv
from .vol_backtest import backtest_realized_vol_signal
def parse_legs(legs_json: str) -> list[OptionLeg]:
payload = json.loads(legs_json)
if isinstance(payload, dict) and "legs" in payload:
payload = payload["legs"]
return [OptionLeg(**leg) for leg in payload]
@tool
def analyze_strategy_payoff(legs_json: str, min_price: float, max_price: float, steps: int = 25) -> str:
"""Analyze expiration payoff for an option strategy.
Args:
legs_json: JSON list of option legs from build_volatility_strategy.
min_price: Minimum underlying price scenario.
max_price: Maximum underlying price scenario.
steps: Number of scenario steps.
"""
try:
legs = parse_legs(legs_json)
points = [
min_price + (max_price - min_price) * index / max(steps, 1)
for index in range(max(steps, 1) + 1)
]
rows = [
{"underlying_price": round(price, 2), "pnl": round(expiration_payoff(legs, price), 2)}
for price in points
]
temp_strategy = OptionStrategy(
name="custom_strategy",
volatility_view="unknown",
directional_view="unknown",
legs=legs,
rationale="custom payoff analysis",
risks=[],
max_profit=None,
max_loss=None,
breakevens=[],
net_debit_or_credit=round(sum(leg.premium * leg.signed_quantity() * 100 for leg in legs), 2),
score=0.0,
)
return json.dumps(
{
"status": "success",
"payoff_rows": rows,
"payoff_summary": strategy_summary(temp_strategy),
},
ensure_ascii=False,
indent=2,
)
except Exception as exc:
return json.dumps({"status": "error", "message": str(exc)}, ensure_ascii=False, indent=2)
@tool
def backtest_volatility_signal(
symbol: str,
signal: str = "long_vol",
period: str = "2y",
short_window: int = 10,
long_window: int = 30,
holding_days: int = 5,
) -> str:
"""Backtest a simple realized-volatility expansion/compression signal on the underlying.
Args:
symbol: Yahoo Finance ticker.
signal: long_vol or short_vol.
period: Yahoo Finance history period.
short_window: Short realized volatility lookback.
long_window: Long realized volatility lookback.
holding_days: Holding period after entry.
"""
try:
history = get_price_history(symbol, period=period, interval="1d")
result = backtest_realized_vol_signal(
history["Close"],
short_window=short_window,
long_window=long_window,
holding_days=holding_days,
signal=signal,
)
return json.dumps({"status": "success", "symbol": symbol.upper(), **result}, ensure_ascii=False, indent=2)
except Exception as exc:
return json.dumps({"status": "error", "symbol": symbol, "message": str(exc)}, ensure_ascii=False, indent=2)
@tool
def backtest_long_straddle_csv(
csv_path: str,
symbol: str,
target_dte: int = 30,
holding_days: int = 5,
entry_every_days: int = 5,
price_field: str = "trade",
) -> str:
"""Run a real option-quote backtest for repeated ATM long straddles.
This is a true option PnL backtest when supplied with historical option quotes.
Required CSV columns: date, underlying_symbol, underlying_price, contract_symbol,
option_type, expiration, strike, bid, ask. Optional columns include mid, delta,
gamma, theta, vega, implied_volatility, volume, open_interest.
Args:
csv_path: Path to historical option quotes CSV.
symbol: Underlying ticker.
target_dte: Target days to expiration at entry.
holding_days: Number of calendar days to hold each straddle.
entry_every_days: Minimum days between new entries.
price_field: trade for buy-at-ask/sell-at-bid, or mid for mid-price marks.
"""
try:
quotes = load_option_quotes_csv(csv_path)
result = backtest_long_straddle_from_quotes(
quotes=quotes,
symbol=symbol,
target_dte=target_dte,
holding_days=holding_days,
entry_every_days=entry_every_days,
price_field=price_field,
)
return json.dumps({"status": "success", **result}, ensure_ascii=False, indent=2)
except Exception as exc:
return json.dumps(
{
"status": "error",
"symbol": symbol,
"message": str(exc),
"note": "A real option backtest requires historical option quote data. yfinance does not provide reliable historical option chains.",
},
ensure_ascii=False,
indent=2,
)