feat(DATA-003): calculate live option greeks
This commit is contained in:
97
tests/test_option_greeks.py
Normal file
97
tests/test_option_greeks.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from app.core.calculations import option_row_greeks
|
||||
from app.core.pricing.black_scholes import (
|
||||
DEFAULT_RISK_FREE_RATE,
|
||||
DEFAULT_VOLATILITY,
|
||||
BlackScholesInputs,
|
||||
black_scholes_price_and_greeks,
|
||||
)
|
||||
from app.services.cache import CacheService
|
||||
from app.services.data_service import DataService
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"row, expected_volatility",
|
||||
[
|
||||
(
|
||||
{
|
||||
"strike": 460.0,
|
||||
"expiry": "2026-06-19",
|
||||
"type": "put",
|
||||
"impliedVolatility": 0.24,
|
||||
},
|
||||
0.24,
|
||||
),
|
||||
(
|
||||
{
|
||||
"strike": 460.0,
|
||||
"expiry": "2026-06-19",
|
||||
"type": "put",
|
||||
"impliedVolatility": 0.0,
|
||||
},
|
||||
DEFAULT_VOLATILITY,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_option_row_greeks_uses_live_iv_or_falls_back(row: dict[str, object], expected_volatility: float) -> None:
|
||||
valuation_date = datetime(2026, 3, 23).date()
|
||||
|
||||
greeks = option_row_greeks(row, underlying_price=460.0, valuation_date=valuation_date)
|
||||
expected = black_scholes_price_and_greeks(
|
||||
BlackScholesInputs(
|
||||
spot=460.0,
|
||||
strike=460.0,
|
||||
time_to_expiry=(datetime(2026, 6, 19).date() - valuation_date).days / 365.0,
|
||||
risk_free_rate=DEFAULT_RISK_FREE_RATE,
|
||||
volatility=expected_volatility,
|
||||
option_type="put",
|
||||
valuation_date=valuation_date,
|
||||
)
|
||||
)
|
||||
|
||||
assert greeks["delta"] == pytest.approx(expected.delta, rel=1e-9)
|
||||
assert greeks["gamma"] == pytest.approx(expected.gamma, rel=1e-9)
|
||||
assert greeks["theta"] == pytest.approx(expected.theta, rel=1e-9)
|
||||
assert greeks["vega"] == pytest.approx(expected.vega, rel=1e-9)
|
||||
|
||||
|
||||
def test_option_row_greeks_handles_invalid_input_gracefully() -> None:
|
||||
greeks = option_row_greeks(
|
||||
{"strike": 0.0, "expiry": "not-a-date", "type": "call", "impliedVolatility": -1.0},
|
||||
underlying_price=0.0,
|
||||
)
|
||||
|
||||
assert greeks == {"delta": 0.0, "gamma": 0.0, "theta": 0.0, "vega": 0.0, "rho": 0.0}
|
||||
|
||||
|
||||
def test_normalize_option_rows_populates_greeks() -> None:
|
||||
frame = pd.DataFrame(
|
||||
[
|
||||
{
|
||||
"contractSymbol": "GLD260619P00460000",
|
||||
"strike": 460.0,
|
||||
"bid": 18.5,
|
||||
"ask": 19.5,
|
||||
"lastPrice": 19.0,
|
||||
"impliedVolatility": 0.22,
|
||||
"openInterest": 100,
|
||||
"volume": 50,
|
||||
}
|
||||
]
|
||||
)
|
||||
service = DataService(CacheService(None))
|
||||
|
||||
rows = service._normalize_option_rows(frame, "GLD", "2026-06-19", "put", 460.0)
|
||||
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["symbol"] == "GLD260619P00460000"
|
||||
assert rows[0]["delta"] < 0.0
|
||||
assert rows[0]["gamma"] > 0.0
|
||||
assert rows[0]["theta"] < 0.0
|
||||
assert rows[0]["vega"] > 0.0
|
||||
Reference in New Issue
Block a user