from __future__ import annotations from .schemas import OptionLeg, OptionStrategy def leg_expiration_payoff(leg: OptionLeg, underlying_price: float) -> float: if leg.option_type == "call": intrinsic = max(underlying_price - leg.strike, 0.0) else: intrinsic = max(leg.strike - underlying_price, 0.0) return intrinsic * leg.signed_quantity() * 100 + leg.cash_flow() def expiration_payoff(legs: list[OptionLeg], underlying_price: float) -> float: return sum(leg_expiration_payoff(leg, underlying_price) for leg in legs) def estimate_breakevens(legs: list[OptionLeg]) -> list[float]: strikes = [leg.strike for leg in legs] low = max(min(strikes) * 0.5, 0.01) high = max(strikes) * 1.5 steps = 400 points = [low + (high - low) * index / steps for index in range(steps + 1)] payoffs = [expiration_payoff(legs, point) for point in points] breakevens = [] for index in range(1, len(points)): previous = payoffs[index - 1] current = payoffs[index] if previous == 0: breakevens.append(points[index - 1]) if previous * current < 0: ratio = abs(previous) / (abs(previous) + abs(current)) breakevens.append(points[index - 1] + (points[index] - points[index - 1]) * ratio) return [round(value, 2) for value in breakevens] def strategy_summary(strategy: OptionStrategy) -> dict: strikes = [leg.strike for leg in strategy.legs] low = max(min(strikes) * 0.6, 0.01) high = max(strikes) * 1.4 grid = [low + (high - low) * index / 80 for index in range(81)] payoffs = [expiration_payoff(strategy.legs, price) for price in grid] return { "min_grid_payoff": round(min(payoffs), 2), "max_grid_payoff": round(max(payoffs), 2), "payoff_at_middle_strike": round(expiration_payoff(strategy.legs, sum(strikes) / len(strikes)), 2), "sample_points": [ {"underlying_price": round(price, 2), "pnl": round(pnl, 2)} for price, pnl in zip(grid[::10], payoffs[::10]) ], }