File size: 2,073 Bytes
8f1601b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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])
        ],
    }