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