Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] OptionsChains Properties #6564

Merged
merged 30 commits into from
Aug 1, 2024

Conversation

deeleeramone
Copy link
Contributor

@deeleeramone deeleeramone commented Jul 8, 2024

WIP

More explanation to follow.

  1. Why?:

    • This PR aims to provide a set of methods for working directly with OptionsChains results, compatible with any instance of the class (validated data).
    • These enhancements facilitate views and analysis of the complete options chain with parameterized methods and direct properties. A standardized way to get total open interest and volume statistics, etc.
  2. What?:

    • Adds class properties and methods to the OptionsChains standard model for reliably extracting key statistics and attributes specific to this class.
    • Standard docstring is accessible, via data.results?, from all providers. This contains a summary of all properties and methods (pasted below).
  3. Impact (1-2 sentences or a bullet point list):

    • Breaking Change: derivatives.options.chains returned as a Dictionary of arrays.
      • No change to, to_df().
    • Extra code is all lazy-load and should not impact the Pydantic model validation.
  4. Testing Done:

    • Added additional assert checks to the provider's fetcher test.
    • Recaptured cassettes.
  5. Reviewer Notes:

    • In certain instances, "underlying_price" is not returned by a provider/symbol combination. In this event, the last_price property can be set as a replacement. This can also be used as a manual override to the returned data of the provider.
Options Chains Data.

Properties
----------
dataframe: DataFrame
    Return all data as a Pandas DataFrame, with additional computed columns (Breakeven, GEX, DEX) if available.
expirations: List[str]
    Return a list of unique expiration dates, as strings.
strikes: List[float]
    Return a list of unique strike prices.
has_iv: bool
    Return True if the data contains implied volatility.
has_greeks: bool
    Return True if the data contains greeks.
total_oi: Dict
    Return open interest stats as a nested dictionary with keys: total, expiration, strike.
    Both, "expiration" and "strike", contain a list of records with fields: Calls, Puts, Total, Net Percent, PCR.
total_volume: Dict
    Return volume stats as a nested dictionary with keys: total, expiration, strike.
    Both, "expiration" and "strike", contain a list of records with fields: Calls, Puts, Total, Net Percent, PCR.
total_dex: Dict
    Return Delta Dollars (DEX), if available, as a nested dictionary with keys: total, expiration, strike.
    Both, "expiration" and "strike", contain a list of records with fields: Calls, Puts, Total, Net Percent, PCR.
total_gex: Dict
    Return Gamma Exposure (GEX), if available, as a nested dictionary with keys: total, expiration, strike.
    Both, "expiration" and "strike", contain a list of records with fields: Calls, Puts, Total, Net Percent, PCR.
last_price: float
    Manually set the underlying price by assigning a float value to this property.
    Certain provider/symbol combinations may not return the underlying price,
    and it may be necessary, or desirable, to set it post-initialization.
    This property can be used to override the underlying price returned by the provider.

Methods
-------
filter_data(
    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,
    value_max: Optional[float] = None,
    stat: Optional[Literal["open_interest", "volume", "dex", "gex"]] = None,
    by: Literal["expiration", "strike"] = "expiration",
) -> DataFrame:
    Return statistics by strike or expiration; or, the filtered chains data.
skew(
    days: Optional[int] = None, underlying_price: Optional[float] = None)
-> DataFrame:
    Return skewness of the options, either vertical or horizontal, by nearest DTE.
straddle(
    days: Optional[int] = None, strike: Optional[float] = None, underlying_price: Optional[float] = None
) -> DataFrame:
    Calculates the cost of a straddle, by nearest DTE. Use a negative strike price for short options.
strangle(
    days: Optional[int] = None, moneyness: Optional[float] = None, underlying_price: Optional[float] = None
) -> DataFrame:
    Calculates the cost of a strangle, by nearest DTE and % moneyness.
    Use a negative value for moneyness for short options.
synthetic_long(
    days: Optional[int] = None, strike: Optional[float] = None, underlying_price: Optional[float] = None
) -> DataFrame:
    Calculates the cost of a synthetic long position, by nearest DTE and strike price.
synthetic_short(
    days: Optional[int] = None, strike: Optional[float] = None, underlying_price: Optional[float] = None
) -> DataFrame:
    Calculates the cost of a synthetic short position, by nearest DTE and strike price.
vertical_call(
    days: Optional[int] = None, sold: Optional[float] = None, bought: Optional[float] = None,
    underlying_price: Optional[float] = None
) -> DataFrame:
    Calculates the cost of a vertical call spread, by nearest DTE and strike price to sold and bought levels.
vertical_put(
    days: Optional[int] = None, sold: Optional[float] = None, bought: Optional[float] = None,
    underlying_price: Optional[float] = None
) -> DataFrame:
    Calculates the cost of a vertical put spread, by nearest DTE and strike price to sold and bought levels.
strategies(
    days: Optional[int] = None,
    straddle_strike: Optional[float] = None,
    strangle_moneyness: Optional[List[float]] = None,
    synthetic_longs: Optional[List[float]] = None,
    synthetic_shorts: Optional[List[float]] = None,
    vertical_calls: Optional[List[tuple]] = None,
    vertical_puts: Optional[List[tuple]] = None,
    underlying_price: Optional[float] = None,
) -> DataFrame:
    Method for combining multiple strategies and parameters in a single DataFrame.
    To get all expirations, set days to -1.

Raises
------
OpenBBError
    OpenBBError will raise when accessing properties and methods if required, specific, data was not found.

@deeleeramone deeleeramone added enhancement Enhancement platform OpenBB Platform v4 PRs for v4 labels Jul 8, 2024
@jmaslek
Copy link
Collaborator

jmaslek commented Jul 9, 2024

For readability purposes and general best practices, I think it makes sense to define all the functions outside of the model file. i.e for straddle, move the logic goes into a seperate util/helper somewhere and then call straddle(self.dataframe, ...) (Also - I would have thought this goes into an options specific extension, but no new deps are added, so is good)

@deeleeramone
Copy link
Contributor Author

For readability purposes and general best practices, I think it makes sense to define all the functions outside of the model file. i.e for straddle, move the logic goes into a seperate util/helper somewhere and then call straddle(self.dataframe, ...) (Also - I would have thought this goes into an options specific extension, but no new deps are added, so is good)

The purpose is to bind them to the class specifically, because this is so specific to this one particular piece of data, so that any validated instance of the class can use them. If this was doing something like incorporating a pricing or volatility model, I would agree that it should be a separate extension, but that is not within scope here.

@jmaslek
Copy link
Collaborator

jmaslek commented Jul 9, 2024

The purpose is to bind them to the class specifically, because this is so specific to this one particular piece of data, so that any validated instance of the class can use them. If this was doing something like incorporating a pricing or volatility model, I would agree that it should be a separate extension, but that is not within scope here.

Yeah I have no argument for it not being in core. But for readability and organization, you should definitely break out the calculation logic from the model file.

@jmaslek
Copy link
Collaborator

jmaslek commented Jul 24, 2024

Thanks for moving to a separate file.

I can confirm the following work (using chains("AAPL").results and chains("IONQ", provider="intrinio"):

  • c.skew(date = -1)
  • c.filter_data( bunch of permutations )
  • c.straddle
  • c.strangle
  • c.vertical_put_spread
  • c,vertical_call_spread

Attributes are all there.

From a code styling, looks like a lot has been repeated and could be cleaned up a little bit to make it easier to read.

Nice work.

@IgorWounds IgorWounds added this pull request to the merge queue Aug 1, 2024
Merged via the queue into develop with commit 29066b1 Aug 1, 2024
10 checks passed
@IgorWounds IgorWounds deleted the feature/options-chains-properties branch August 1, 2024 08:51
deeleeramone added a commit that referenced this pull request Aug 1, 2024
* stashing

* update providers

* edge cases

* linter

* grammar police

* yfinance tests?

* linter

* more linting

* one test update

* move properties code to separate file

* to_df

* tmx return type

* tradier tests

* cboe cassettes

* elif instead of if

* sort expirations and strikes

* test cassettes

* change tests

---------

Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
Co-authored-by: James Maslek <jmaslek11@gmail.com>
deeleeramone added a commit that referenced this pull request Aug 1, 2024
* stashing

* update providers

* edge cases

* linter

* grammar police

* yfinance tests?

* linter

* more linting

* one test update

* move properties code to separate file

* to_df

* tmx return type

* tradier tests

* cboe cassettes

* elif instead of if

* sort expirations and strikes

* test cassettes

* change tests

---------

Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
Co-authored-by: James Maslek <jmaslek11@gmail.com>
deeleeramone added a commit that referenced this pull request Aug 1, 2024
* stashing

* update providers

* edge cases

* linter

* grammar police

* yfinance tests?

* linter

* more linting

* one test update

* move properties code to separate file

* to_df

* tmx return type

* tradier tests

* cboe cassettes

* elif instead of if

* sort expirations and strikes

* test cassettes

* change tests

---------

Co-authored-by: Igor Radovanovic <74266147+IgorWounds@users.noreply.github.com>
Co-authored-by: James Maslek <jmaslek11@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking_change enhancement Enhancement platform OpenBB Platform v4 PRs for v4
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants