From 12f1199998701088a44f49356128c0ba7abeff26 Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:02:40 -0700 Subject: [PATCH] [BugFix] Handle Options Skew Error From Zero/Empty IV Value (#6701) * handle options skew error from zero/empty IV value * docstring touchup * too many positional arguments --------- Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com> --- .../standard_models/options_chains.py | 5 +-- .../utils/options_chains_properties.py | 34 +++++++++++++------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py b/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py index b09a900d4e56..bc0c4fa68164 100644 --- a/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py +++ b/openbb_platform/core/openbb_core/provider/standard_models/options_chains.py @@ -68,7 +68,8 @@ class OptionsChainsData(OptionsChainsProperties): Methods ------- filter_data( - date: Optional[Union[str, int]] = None, column: Optional[str] = None, + date: Optional[Union[str, int]] = None, + column: Optional[str] = None, option_type: Optional[Literal["call", "put"]] = None, moneyness: Optional[Literal["otm", "itm"]] = None, value_min: Optional[float] = None, @@ -78,7 +79,7 @@ class OptionsChainsData(OptionsChainsProperties): ) -> DataFrame: Return statistics by strike or expiration; or, the filtered chains data. skew( - days: Optional[int] = None, underlying_price: Optional[float] = None) + date: Optional[Union[int, str]] = None, underlying_price: Optional[float] = None) -> DataFrame: Return skewness of the options, either vertical or horizontal, by nearest DTE. straddle( diff --git a/openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py b/openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py index 1aa342397115..4e12435377f3 100644 --- a/openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py +++ b/openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py @@ -1,6 +1,6 @@ """Options Chains Properties.""" -# pylint: disable=too-many-lines, too-many-arguments, too-many-locals, too-many-statements +# pylint: disable=too-many-lines, too-many-arguments, too-many-locals, too-many-statements, too-many-positional-arguments from datetime import datetime from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union @@ -1673,6 +1673,9 @@ def skew( else data.underlying_price.iloc[0] ) + if moneyness is not None and date is None: + date = -1 + if moneyness is None and date is None: date = 30 moneyness = 20 @@ -1749,6 +1752,12 @@ def skew( puts = concat([puts, put_iv]) # type: ignore atm_put_iv = concat([atm_put_iv, atm_put]) # type: ignore + if calls.empty or puts.empty: + raise OpenBBError( + "Error: Not enough information to complete the operation." + " Likely due to zero values in the IV field of the expiration." + ) + calls = calls.drop_duplicates(subset=["expiration"]).set_index("expiration") # type: ignore atm_call_iv = atm_call_iv.drop_duplicates(subset=["expiration"]).set_index( # type: ignore "expiration" @@ -1789,10 +1798,11 @@ def skew( call = _calls.set_index("expiration").copy() # type: ignore call_atm_iv = call.query("`strike` == @atm_call_strike")[ "implied_volatility" - ].iloc[0] - call["ATM IV"] = call_atm_iv - call["Skew"] = call["implied_volatility"] - call["ATM IV"] - call_skew = concat([call_skew, call]) + ] + if len(call_atm_iv) > 0: + call["ATM IV"] = call_atm_iv.iloc[0] + call["Skew"] = call["implied_volatility"] - call["ATM IV"] + call_skew = concat([call_skew, call]) atm_put_strike = self._get_nearest_strike( "put", day, force_otm=False @@ -1805,11 +1815,15 @@ def skew( put = _puts.set_index("expiration").copy() # type: ignore put_atm_iv = put.query("`strike` == @atm_put_strike")[ "implied_volatility" - ].iloc[0] - put["ATM IV"] = put_atm_iv - put["Skew"] = put["implied_volatility"] - put["ATM IV"] - put_skew = concat([put_skew, put]) - + ] + if len(put_atm_iv) > 0: + put["ATM IV"] = put_atm_iv.iloc[0] + put["Skew"] = put["implied_volatility"] - put["ATM IV"] + put_skew = concat([put_skew, put]) + if call_skew.empty or put_skew.empty: + raise OpenBBError( + "Error: Not enough information to complete the operation. Likely due to zero values in the IV field." + ) call_skew = call_skew.set_index(["strike", "option_type"], append=True) put_skew = put_skew.set_index(["strike", "option_type"], append=True) skew_df = concat([call_skew, put_skew]).sort_index().reset_index()