Add nasdaq as an options source (#2397)

* Get all nasdaq chains

* Get all expirations

* Add chain by expiry and clean up columns

* add nasdaq to load and exp

* Add OI plot for nasdaq

* add volume plot

* Update vol + oi to have nasdaq + raw flag

* Add VOI functionality with nasdaq

* Add nasdaq chains function

* Run isort

* doesnt

* Change menu for sources and edit yfinance to expiration instead of expiry

* lint

* oops

* unused imports

Co-authored-by: DidierRLopes <dro.lopes@campus.fct.unl.pt>
This commit is contained in:
James Maslek
2022-08-23 04:35:18 -04:00
committed by GitHub
parent 815787df8f
commit cf9b5ad832
19 changed files with 785 additions and 156 deletions

View File

@@ -39,7 +39,8 @@
"options": {
"load": [
"yf",
"tradier"
"tradier",
"nasdaq"
],
"hist": [
"chartexchange",
@@ -47,19 +48,26 @@
],
"vol": [
"yf",
"tradier"
"tradier",
"nasdaq"
],
"voi": [
"yf",
"tradier"
"tradier",
"nasdaq"
],
"oi": [
"yf",
"tradier"
"tradier",
"nasdaq"
],
"chains": [
"yf",
"tradier"
"tradier",
"nasdaq"
],
"greeks": [
"yf"
]
},
"disc": {

View File

@@ -1,20 +1,20 @@
"""AlphaQuery View"""
__docforma__ = "numpy"
from typing import Optional, List
import logging
import os
from datetime import datetime, timedelta
from typing import List, Optional
import matplotlib.pyplot as plt
from openbb_terminal.config_terminal import theme
from openbb_terminal.config_plot import PLOT_DPI
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
plot_autoscale,
is_valid_axes_count,
plot_autoscale,
)
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options import alphaquery_model

View File

@@ -1,12 +1,12 @@
import logging
from typing import Dict, Optional, List
from typing import Dict, List, Optional
from matplotlib import pyplot as plt
from openbb_terminal.config_terminal import theme
from openbb_terminal.config_plot import PLOT_DPI
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import plot_autoscale, is_valid_axes_count
from openbb_terminal.helper_funcs import is_valid_axes_count, plot_autoscale
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options import calculator_model

View File

@@ -3,25 +3,23 @@ __docformat__ = "numpy"
import logging
import os
from typing import Optional, List
from typing import List, Optional
import matplotlib.pyplot as plt
import mplfinance as mpf
import pandas as pd
import mplfinance as mpf
import matplotlib.pyplot as plt
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
print_rich_table,
is_valid_axes_count,
)
from openbb_terminal.stocks.options import chartexchange_model
from openbb_terminal.config_terminal import theme
from openbb_terminal.rich_config import console
from openbb_terminal.helper_funcs import (
plot_autoscale,
lambda_long_number_format_y_axis,
plot_autoscale,
print_rich_table,
)
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options import chartexchange_model
logger = logging.getLogger(__name__)

View File

@@ -11,19 +11,13 @@ from prompt_toolkit.completion import NestedCompleter
from openbb_terminal import feature_flags as obbff
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
check_non_negative,
print_rich_table,
)
from openbb_terminal.helper_funcs import check_non_negative, print_rich_table
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import console, MenuText
from openbb_terminal.rich_config import MenuText, console
from openbb_terminal.stocks.options.hedge import hedge_view
from openbb_terminal.stocks.options.hedge.hedge_model import add_hedge_option
from openbb_terminal.stocks.options.yfinance_model import (
get_option_chain,
get_price,
)
from openbb_terminal.stocks.options.yfinance_model import get_option_chain, get_price
from openbb_terminal.stocks.options.yfinance_view import plot_payoff
# pylint: disable=R0902

View File

@@ -5,9 +5,7 @@ import logging
import pandas as pd
from openbb_terminal.helper_funcs import (
print_rich_table,
)
from openbb_terminal.helper_funcs import print_rich_table
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options.hedge import hedge_model

View File

@@ -0,0 +1,230 @@
"""Nasdaq Model"""
__docformat__ = "numpy"
import logging
from datetime import datetime
from typing import List
import numpy as np
import pandas as pd
import requests
from openbb_terminal.decorators import log_start_end
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def get_full_chain(symbol: str) -> pd.DataFrame:
"""Get the full option chain for symbol over all expirations
Parameters
----------
symbol: str
Symbol to get options for. Can be a stock, etf or index.
Returns
-------
pd.DataFrame
Dataframe of option chain
"""
# Nasdaq requires an asset code, so instead of making user supply one, just loop through all
for asset in ["stocks", "index", "etf"]:
url = (
f"https://api.nasdaq.com/api/quote/{symbol}/option-chain?assetclass={asset}&"
"fromdate=2010-09-09&todate=2030-09-09&excode=oprac&callput=callput&money=all&type=all"
)
# I have had issues with nasdaq requests, and this user agent seems to work in US and EU
response_json = requests.get(
url,
headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
" AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"
},
).json()
if response_json["status"]["rCode"] == 200:
df = pd.DataFrame(response_json["data"]["table"]["rows"]).drop(
columns=["c_colour", "p_colour", "drillDownURL"]
)
df["expirygroup"] = (
df["expirygroup"].replace("", np.nan).fillna(method="ffill")
)
# Make numeric
df = (
df.fillna(np.nan)
.dropna(axis=0)
.replace("--", 0)
.astype(
{
"c_Last": float,
"c_Change": float,
"c_Bid": float,
"c_Ask": float,
"c_Volume": int,
"c_Openinterest": int,
"strike": float,
"p_Last": float,
"p_Change": float,
"p_Bid": float,
"p_Ask": float,
"p_Volume": int,
"p_Openinterest": int,
}
)
)
return df
console.print(f"[red]{symbol} Option Chain not found.[/red]\n")
return pd.DataFrame()
@log_start_end(log=logger)
def get_expirations(symbol: str) -> List[str]:
"""Get available expirations
Parameters
----------
symbol
Returns
-------
List[str]
List of expiration dates
"""
df = get_full_chain(symbol)
if df.empty:
return []
# get everything that is not an empty string
exps = [exp for exp in list(df.expirygroup.unique()) if exp]
# Convert 'January 11, 1993' into '1993-01-11'
return [datetime.strptime(exp, "%B %d, %Y").strftime("%Y-%m-%d") for exp in exps]
@log_start_end(log=logger)
def get_chain_given_expiration(symbol: str, expiration: str) -> pd.DataFrame:
"""Get option chain for symbol at a given expiration
Parameters
----------
symbol: str
Symbol to get chain for
expiration
Expiration to get chain for
Returns
-------
pd.DataFrame
Dataframe of option chain
"""
for asset in ["stocks", "index", "etf"]:
url = (
f"https://api.nasdaq.com/api/quote/{symbol}/option-chain?assetclass={asset}&"
f"fromdate={expiration}&todate={expiration}&excode=oprac&callput=callput&money=all&type=all"
)
response_json = requests.get(
url,
headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
" AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"
},
).json()
if response_json["status"]["rCode"] == 200:
df = (
pd.DataFrame(response_json["data"]["table"]["rows"])
.drop(columns=["c_colour", "p_colour", "drillDownURL", "expirygroup"])
.fillna(np.nan)
.dropna(axis=0)
)
# Make numeric
df = df.replace("--", 0).astype(
{
"c_Last": float,
"c_Change": float,
"c_Bid": float,
"c_Ask": float,
"c_Volume": int,
"c_Openinterest": int,
"strike": float,
"p_Last": float,
"p_Change": float,
"p_Bid": float,
"p_Ask": float,
"p_Volume": int,
"p_Openinterest": int,
}
)
return df
console.print(f"[red]{symbol} Option Chain not found.[/red]\n")
return pd.DataFrame()
@log_start_end(log=logger)
def get_last_price(symbol: str) -> float:
"""Get the last price from nasdaq
Parameters
----------
symbol: str
Symbol to get quote for
Returns
-------
float
Last price
"""
for asset in ["stocks", "index", "etf"]:
url = f"https://api.nasdaq.com/api/quote/{symbol}/info?assetclass={asset}"
response_json = requests.get(
url,
headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
" AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"
},
).json()
if response_json["status"]["rCode"] == 200:
return float(
response_json["data"]["primaryData"]["lastSalePrice"]
.strip("$")
.replace(",", "")
)
console.print(f"[red]Last price for {symbol} not found[/red]\n")
return np.nan
# Ugh this doesn't get the full chain
# TODO: apply CRR binomial tree to backtrack IV for greeks
@log_start_end(log=logger)
def get_option_greeks(symbol: str, expiration: str) -> pd.DataFrame:
"""Get option greeks from nasdaq
Parameters
----------
symbol: str
Symbol to get
expiration: str
Option expiration
Returns
-------
pd.DataFrame
Dataframe with option greeks
"""
for asset in ["stocks", "index", "etf"]:
url_greeks = f"https://api.nasdaq.com/api/quote/{symbol}/option-chain/greeks?assetclass={asset}&date={expiration}"
response_json = requests.get(
url_greeks,
headers={
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
" AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6 Safari/605.1.15"
},
).json()
if response_json["status"]["rCode"] == 200:
greeks = pd.DataFrame(response_json["data"]["table"]["rows"])
greeks = greeks.drop(columns="url")
return greeks
console.print(f"[red]Greeks not found for {symbol} on {expiration}[/red].")
return pd.DataFrame()

View File

@@ -0,0 +1,333 @@
"""Nasdaq View"""
__docformat__ = "numpy"
import logging
import os
from typing import List, Optional
import matplotlib.pyplot as plt
import openbb_terminal.config_plot as cfp
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
is_valid_axes_count,
plot_autoscale,
print_rich_table,
)
from openbb_terminal.stocks.options import nasdaq_model
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def display_oi(
symbol: str,
expiration: str,
min_sp: float = -1,
max_sp: float = -1,
raw: bool = False,
export: str = "",
external_axes: Optional[List[plt.Axes]] = None,
):
"""Plot open interest
Parameters
----------
symbol: str
Ticker symbol
expiration: str
Expiry date for options
min_sp: float
Min strike to consider
max_sp: float
Max strike to consider
raw: bool
Flag to display raw data
export: str
Format to export file
external_axes : Optional[List[plt.Axes]], optional
External axes (1 axis is expected in the list), by default None
"""
option_chain = nasdaq_model.get_chain_given_expiration(symbol, expiration)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"oi_nasdaq",
option_chain,
)
current_price = nasdaq_model.get_last_price(symbol)
if min_sp == -1:
min_strike = 0.75 * current_price
else:
min_strike = min_sp
if max_sp == -1:
max_strike = 1.25 * current_price
else:
max_strike = max_sp
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
elif is_valid_axes_count(external_axes, 1):
(ax,) = external_axes
else:
return
ax.plot(
option_chain.strike,
option_chain["c_Openinterest"] / 1000,
ls="-",
marker="o",
label="Calls",
)
ax.plot(
option_chain.strike,
option_chain["p_Openinterest"] / 1000,
ls="-",
marker="o",
label="Puts",
)
ax.axvline(current_price, lw=2, ls="--", label="Current Price", alpha=0.7)
ax.set_xlabel("Strike Price")
ax.set_ylabel("Open Interest (1k) ")
ax.set_xlim(min_strike, max_strike)
ax.legend(loc="best")
ax.set_title(f"Open Interest for {symbol.upper()} expiring {expiration}")
theme.style_primary_axis(ax)
if external_axes is None:
theme.visualize_output()
if raw:
to_print = option_chain[["c_Openinterest", "strike", "p_Openinterest"]]
print_rich_table(
to_print[(to_print.strike < max_strike) & (to_print.strike > min_strike)],
headers=to_print.columns,
title=f"Open Interest for {symbol} expiring on {expiration}.",
)
@log_start_end(log=logger)
def display_volume(
symbol: str,
expiration: str,
min_sp: float = -1,
max_sp: float = -1,
raw: bool = False,
export: str = "",
external_axes: Optional[List[plt.Axes]] = None,
):
"""Plot volume
Parameters
----------
symbol: str
Ticker symbol
expiration: str
Expiry date for options
min_sp: float
Min strike to consider
max_sp: float
Max strike to consider
raw:bool
Flag to display raw data
export: str
Format to export file
external_axes : Optional[List[plt.Axes]], optional
External axes (1 axis is expected in the list), by default None
"""
option_chain = nasdaq_model.get_chain_given_expiration(symbol, expiration)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"oi_nasdaq",
option_chain,
)
current_price = nasdaq_model.get_last_price(symbol)
if min_sp == -1:
min_strike = 0.75 * current_price
else:
min_strike = min_sp
if max_sp == -1:
max_strike = 1.25 * current_price
else:
max_strike = max_sp
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
elif is_valid_axes_count(external_axes, 1):
(ax,) = external_axes
else:
return
ax.plot(
option_chain.strike,
option_chain["c_Volume"] / 1000,
ls="-",
marker="o",
label="Calls",
)
ax.plot(
option_chain.strike,
option_chain["p_Volume"] / 1000,
ls="-",
marker="o",
label="Puts",
)
ax.axvline(current_price, lw=2, ls="--", label="Current Price", alpha=0.7)
ax.set_xlabel("Strike Price")
ax.set_ylabel("Volume (1k) ")
ax.set_xlim(min_strike, max_strike)
ax.legend(loc="best")
ax.set_title(f"Volume for {symbol.upper()} expiring {expiration}")
theme.style_primary_axis(ax)
if external_axes is None:
theme.visualize_output()
if raw:
to_print = option_chain[["c_Volume", "strike", "p_Volume"]]
print_rich_table(
to_print[(to_print.strike < max_strike) & (to_print.strike > min_strike)],
headers=to_print.columns,
title=f"Volume for {symbol} expiring on {expiration}.",
)
@log_start_end(log=logger)
def display_volume_and_oi(
symbol: str,
expiration: str,
min_sp: float = -1,
max_sp: float = -1,
raw: bool = False,
export: str = "",
external_axes: Optional[List[plt.Axes]] = None,
):
"""Plot volume and open interest
Parameters
----------
symbol: str
Ticker symbol
expiration: str
Expiry date for options
min_sp: float
Min strike to consider
max_sp: float
Max strike to consider
raw:bool
Flag to display raw data
export: str
Format to export file
external_axes : Optional[List[plt.Axes]], optional
External axes (1 axis is expected in the list), by default None
"""
option_chain = nasdaq_model.get_chain_given_expiration(symbol, expiration)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"voi_nasdaq",
option_chain,
)
current_price = nasdaq_model.get_last_price(symbol)
if min_sp == -1:
min_strike = 0.75 * current_price
else:
min_strike = min_sp
if max_sp == -1:
max_strike = 1.25 * current_price
else:
max_strike = max_sp
if raw:
to_print = option_chain[
["c_Openinterest", "c_Volume", "strike", "p_Volume", "p_Openinterest"]
]
print_rich_table(
to_print[(to_print.strike < max_strike) & (to_print.strike > min_strike)],
headers=to_print.columns,
title=f"Volume and Open Interest for {symbol} expiring on {expiration}.",
)
option_chain = option_chain.copy()[
["c_Volume", "c_Openinterest", "strike", "p_Openinterest", "p_Volume"]
]
option_chain[["p_Openinterest", "p_Volume"]] = (
option_chain[["p_Openinterest", "p_Volume"]] * -1 / 1000
)
option_chain[["c_Openinterest", "c_Volume"]] = (
option_chain[["c_Openinterest", "c_Volume"]] / 1000
)
if external_axes is None:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
elif is_valid_axes_count(external_axes, 1):
(ax,) = external_axes
else:
return
ax.bar(
option_chain.strike,
option_chain.c_Openinterest,
color="green",
label="Calls: OI",
)
ax.bar(
option_chain.strike,
option_chain.c_Volume,
color="lightgreen",
label="Calls: Vol",
)
ax.bar(
option_chain.strike, option_chain.p_Openinterest, color="red", label="Puts: OI"
)
ax.bar(option_chain.strike, option_chain.p_Volume, color="pink", label="Puts:Vol")
ax.axvline(
current_price, lw=2, ls="--", label=f"Current Price: {current_price}", alpha=0.7
)
ax.set_xlabel("Strike Price")
ax.set_ylabel("Volume or OI (1k)")
ax.set_xlim(min_strike, max_strike)
ax.legend(loc="best")
ax.set_title(f"Volume and Open Interest for {symbol.upper()} expiring {expiration}")
theme.style_primary_axis(ax)
if external_axes is None:
theme.visualize_output()
@log_start_end(log=logger)
def display_chains(symbol: str, expiration: str, export: str = ""):
"""Display option chain for given expiration
Parameters
----------
symbol: str
Ticker symbol
expiration: str
Expiry date for options
export: str
Format to export data
"""
option_chain = nasdaq_model.get_chain_given_expiration(symbol, expiration)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"chain_nasdaq",
option_chain,
)
print_rich_table(option_chain, headers=option_chain.columns)

View File

@@ -1,8 +1,9 @@
"""Option helper functions"""
__docformat__ = "numpy"
from math import log, e
from math import e, log
from typing import Union
import numpy as np
import pandas as pd
from scipy.stats import norm
@@ -131,9 +132,23 @@ opt_chain_cols = {
# pylint: disable=R0903
class Chain:
def __init__(self, df: pd.DataFrame):
self.calls = df[df["option_type"] == "call"]
self.puts = df[df["option_type"] == "put"]
def __init__(self, df: pd.DataFrame, source: str = "tradier"):
if source == "tradier":
self.calls = df[df["option_type"] == "call"]
self.puts = df[df["option_type"] == "put"]
elif source == "nasdaq":
# These guys have different column names
call_columns = ["expiryDate", "strike"] + [
col for col in df.columns if col.startswith("c_")
]
put_columns = ["expiryDate", "strike"] + [
col for col in df.columns if col.startswith("p_")
]
self.calls = df[call_columns]
self.puts = df[put_columns]
else:
self.calls = None
self.puts = None
class Option:

View File

@@ -1,31 +1,38 @@
"""Options API."""
import os
from openbb_terminal.helper_classes import ModelsNamespace as _models
# flake8: noqa
# pylint: disable=unused-import
# Menu commands
from .calculator_view import view_calculator as calc
from .fdscanner_view import display_options as unu
import os
from openbb_terminal.helper_classes import ModelsNamespace as _models
from .alphaquery_view import display_put_call_ratio as pcr
from .barchart_view import print_options_data as info
from .screen.syncretism_view import view_historical_greeks as grhist
# Menu commands
from .calculator_view import view_calculator as calc
from .chartexchange_view import display_raw as hist_ce
from .tradier_view import display_historical as hist_tr
from .fdscanner_view import display_options as unu
from .screen.syncretism_view import view_historical_greeks as grhist
from .tradier_view import display_chains as chains
from .tradier_view import plot_vol as vol_tr
from .yfinance_view import plot_vol as vol_yf
from .tradier_view import plot_volume_open_interest as voi_tr
from .yfinance_view import plot_volume_open_interest as voi_yf
from .tradier_view import plot_oi as oi_tr
from .tradier_view import display_expirations as exp
from .tradier_view import display_historical as hist_tr
from .tradier_view import plot_oi as oi_tr
from .tradier_view import plot_vol as vol_tr
from .tradier_view import plot_volume_open_interest as voi_tr
from .yfinance_view import display_vol_surface as vsurf
from .yfinance_view import plot_oi as oi_yf
from .yfinance_view import plot_plot as plot
from .yfinance_view import show_parity as parity
from .yfinance_view import plot_vol as vol_yf
from .yfinance_view import plot_volume_open_interest as voi_yf
from .yfinance_view import show_binom as binom
from .yfinance_view import show_greeks as greeks
from .yfinance_view import display_vol_surface as vsurf
from .yfinance_view import show_parity as parity
# flake8: noqa
# pylint: disable=unused-import
# Models

View File

@@ -5,7 +5,7 @@ import argparse
import logging
import os
from datetime import datetime, timedelta
from typing import List, Any
from typing import Any, List
import pandas as pd
from prompt_toolkit.completion import NestedCompleter
@@ -17,8 +17,8 @@ from openbb_terminal.helper_funcs import (
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
EXPORT_ONLY_FIGURES_ALLOWED,
EXPORT_ONLY_RAW_DATA_ALLOWED,
valid_date,
get_ordered_list_sources,
valid_date,
)
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
@@ -29,18 +29,18 @@ from openbb_terminal.stocks.options import (
calculator_view,
chartexchange_view,
fdscanner_view,
nasdaq_model,
nasdaq_view,
op_helpers,
tradier_model,
tradier_view,
yfinance_model,
yfinance_view,
op_helpers,
)
from openbb_terminal.stocks.options.pricing import pricing_controller
from openbb_terminal.stocks.options.hedge import hedge_controller
from openbb_terminal.stocks.options.pricing import pricing_controller
from openbb_terminal.stocks.options.screen import screener_controller, syncretism_view
# pylint: disable=R1710,C0302,R0916
# TODO: HELP WANTED! This controller requires some MVC style refactoring
@@ -183,10 +183,12 @@ class OptionsController(BaseController):
mt.add_raw("\n")
mt.add_cmd("pcr", "AlphaQuery", self.ticker and self.selected_date)
mt.add_cmd("info", "Barchart", self.ticker and self.selected_date)
mt.add_cmd("chains", "Tradier", self.ticker and self.selected_date)
mt.add_cmd("oi", "Tradier/YFinance", self.ticker and self.selected_date)
mt.add_cmd("vol", "Tradier/YFinance", self.ticker and self.selected_date)
mt.add_cmd("voi", "Tradier/YFinance", self.ticker and self.selected_date)
mt.add_cmd(
"chains", "Tradier/NASDAQ/Yfinance", self.ticker and self.selected_date
)
mt.add_cmd("oi", "Tradier/YFinance/NASDAQ", self.ticker and self.selected_date)
mt.add_cmd("vol", "Tradier/YFinance/NASDAQ", self.ticker and self.selected_date)
mt.add_cmd("voi", "Tradier/YFinance/NASDAQ", self.ticker and self.selected_date)
mt.add_cmd("hist", "Tradier", self.ticker and self.selected_date)
mt.add_cmd("vsurf", "Yfinance", self.ticker and self.selected_date)
mt.add_cmd("grhist", "Syncretism", self.ticker and self.selected_date)
@@ -539,6 +541,9 @@ class OptionsController(BaseController):
if ns_parser.source == "yf":
self.source = "yf"
self.expiry_dates = yfinance_model.option_expirations(self.ticker)
elif ns_parser.source == "nasdaq":
self.source = "nasdaq"
self.expiry_dates = nasdaq_model.get_expirations(self.ticker)
else:
self.source = "tradier"
self.expiry_dates = tradier_model.option_expirations(self.ticker)
@@ -611,6 +616,11 @@ class OptionsController(BaseController):
self.chain = yfinance_model.get_option_chain(
self.ticker, self.selected_date
)
elif self.source == "nasdaq":
df = nasdaq_model.get_chain_given_expiration(
self.ticker, self.selected_date
)
self.chain = op_helpers.Chain(df, self.source)
else:
df = tradier_model.get_option_chains(
self.ticker, self.selected_date
@@ -776,13 +786,19 @@ class OptionsController(BaseController):
elif ns_parser.source == "yf":
yfinance_view.display_chains(
symbol=self.ticker,
expiry=self.selected_date,
expiration=self.selected_date,
min_sp=ns_parser.min_sp,
max_sp=ns_parser.max_sp,
calls_only=ns_parser.calls,
puts_only=ns_parser.puts,
export=ns_parser.export,
)
elif ns_parser.source == "nasdaq":
nasdaq_view.display_chains(
symbol=self.ticker,
expiration=self.selected_date,
export=ns_parser.export,
)
else:
console.print("No expiry loaded. First use `exp {expiry date}`\n")
else:
@@ -832,7 +848,8 @@ class OptionsController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES,
raw=True,
)
if ns_parser:
if self.ticker:
@@ -850,16 +867,27 @@ class OptionsController(BaseController):
puts_only=ns_parser.puts,
export=ns_parser.export,
)
else:
elif ns_parser.source == "yf":
yfinance_view.plot_vol(
symbol=self.ticker,
expiry=self.selected_date,
expiration=self.selected_date,
min_sp=ns_parser.min,
max_sp=ns_parser.max,
calls_only=ns_parser.calls,
puts_only=ns_parser.puts,
export=ns_parser.export,
)
elif ns_parser.source == "nasdaq":
nasdaq_view.display_volume(
symbol=self.ticker,
expiration=self.selected_date,
min_sp=ns_parser.min,
max_sp=ns_parser.max,
export=ns_parser.export,
raw=ns_parser.raw,
)
else:
return
else:
console.print("No expiry loaded. First use `exp {expiry date}`\n")
else:
@@ -899,9 +927,7 @@ class OptionsController(BaseController):
help="maximum strike price to consider in the plot.",
)
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True
)
if ns_parser:
if self.ticker:
@@ -918,15 +944,26 @@ class OptionsController(BaseController):
min_vol=ns_parser.min_vol,
export=ns_parser.export,
)
else:
elif ns_parser.source == "yf":
yfinance_view.plot_volume_open_interest(
symbol=self.ticker,
expiry=self.selected_date,
expiration=self.selected_date,
min_sp=ns_parser.min_sp,
max_sp=ns_parser.max_sp,
min_vol=ns_parser.min_vol,
export=ns_parser.export,
)
elif ns_parser.source == "nasdaq":
nasdaq_view.display_volume_and_oi(
symbol=self.ticker,
expiration=self.selected_date,
min_sp=ns_parser.min_sp,
max_sp=ns_parser.max_sp,
raw=ns_parser.raw,
export=ns_parser.export,
)
else:
pass
else:
console.print("No expiry loaded. First use `exp {expiry date}`\n")
else:
@@ -977,7 +1014,8 @@ class OptionsController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser,
other_args,
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
raw=True,
export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES,
)
if ns_parser:
if self.ticker:
@@ -995,16 +1033,25 @@ class OptionsController(BaseController):
puts_only=ns_parser.puts,
export=ns_parser.export,
)
else:
elif ns_parser.source == "yf":
yfinance_view.plot_oi(
symbol=self.ticker,
expiry=self.selected_date,
expiration=self.selected_date,
min_sp=ns_parser.min,
max_sp=ns_parser.max,
calls_only=ns_parser.calls,
puts_only=ns_parser.puts,
export=ns_parser.export,
)
elif ns_parser.source == "nasdaq":
nasdaq_view.display_oi(
self.ticker,
expiration=self.selected_date,
min_sp=ns_parser.min,
max_sp=ns_parser.max,
export=ns_parser.export,
raw=ns_parser.raw,
)
else:
console.print("No expiry loaded. First use `exp {expiry date}`\n")
else:
@@ -1165,7 +1212,8 @@ class OptionsController(BaseController):
help="Maximum strike price to show.",
)
parser.add_argument(
"-a" "--all",
"-a",
"--all",
dest="all",
action="store_true",
default=False,
@@ -1181,17 +1229,18 @@ class OptionsController(BaseController):
elif not self.selected_date:
console.print("No expiry loaded. First use `exp {expiry date}`\n")
else:
opt_type = -1 if ns_parser.put else 1
yfinance_view.show_greeks(
symbol=self.ticker,
div_cont=ns_parser.dividend,
expiry=self.selected_date,
rf=ns_parser.risk_free,
opt_type=opt_type,
mini=ns_parser.min,
maxi=ns_parser.max,
show_all=ns_parser.all,
)
if ns_parser.source == "yf":
opt_type = -1 if ns_parser.put else 1
yfinance_view.show_greeks(
symbol=self.ticker,
div_cont=ns_parser.dividend,
expiration=self.selected_date,
rf=ns_parser.risk_free,
opt_type=opt_type,
mini=ns_parser.min,
maxi=ns_parser.max,
show_all=ns_parser.all,
)
@log_start_end(log=logger)
def call_parity(self, other_args: List[str]):

View File

@@ -13,7 +13,7 @@ from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import print_rich_table
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import console, MenuText
from openbb_terminal.rich_config import MenuText, console
from openbb_terminal.stocks.options import yfinance_view
logger = logging.getLogger(__name__)

View File

@@ -10,13 +10,10 @@ from prompt_toolkit.completion import NestedCompleter
from openbb_terminal import feature_flags as obbff
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
EXPORT_ONLY_RAW_DATA_ALLOWED,
check_positive,
)
from openbb_terminal.helper_funcs import EXPORT_ONLY_RAW_DATA_ALLOWED, check_positive
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import console, MenuText
from openbb_terminal.rich_config import MenuText, console
from openbb_terminal.stocks.comparison_analysis import ca_controller
from openbb_terminal.stocks.options.screen import syncretism_view

View File

@@ -8,14 +8,14 @@ from typing import List, Optional
import matplotlib.pyplot as plt
from openbb_terminal.config_terminal import theme
from openbb_terminal import config_plot as cfp
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
is_valid_axes_count,
plot_autoscale,
print_rich_table,
is_valid_axes_count,
)
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options.screen import syncretism_model

View File

@@ -1,10 +1,10 @@
"""Tradier options view"""
__docformat__ = "numpy"
import warnings
import argparse
import logging
import os
import warnings
from bisect import bisect_left
from typing import List, Optional, Tuple
@@ -14,20 +14,20 @@ import numpy as np
import pandas as pd
import seaborn as sns
from openbb_terminal.config_terminal import theme
from openbb_terminal import config_plot as cfp
from openbb_terminal import rich_config
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
is_valid_axes_count,
lambda_long_number_format_y_axis,
patch_pandas_text_adjustment,
plot_autoscale,
print_rich_table,
lambda_long_number_format_y_axis,
is_valid_axes_count,
)
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options import op_helpers, tradier_model, yfinance_model
from openbb_terminal import rich_config
logger = logging.getLogger(__name__)

View File

@@ -1,19 +1,19 @@
"""Yfinance options model"""
__docformat__ = "numpy"
import warnings
import logging
import math
from datetime import datetime, date, timedelta
import warnings
from datetime import date, datetime, timedelta
from typing import Any, Dict, List, Tuple
import pandas as pd
import yfinance as yf
from openbb_terminal.stocks.options.op_helpers import Option
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import get_rf
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options.op_helpers import Option
logger = logging.getLogger(__name__)

View File

@@ -3,9 +3,9 @@ __docformat__ = "numpy"
import logging
import os
import re
from bisect import bisect_left
from datetime import date, datetime, timedelta
import re
from typing import Any, Dict, List, Optional
import matplotlib.dates as mdates
@@ -18,17 +18,17 @@ import yfinance as yf
from openpyxl import Workbook
from scipy.stats import binom
from openbb_terminal.config_terminal import theme
from openbb_terminal.config_plot import PLOT_DPI
import openbb_terminal.config_plot as cfp
from openbb_terminal.config_plot import PLOT_DPI
from openbb_terminal.config_terminal import theme
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
excel_columns,
export_data,
get_rf,
is_valid_axes_count,
plot_autoscale,
print_rich_table,
is_valid_axes_count,
)
from openbb_terminal.rich_config import console
from openbb_terminal.stocks.options import op_helpers, yfinance_model
@@ -70,7 +70,7 @@ def header_fmt(header: str) -> str:
@log_start_end(log=logger)
def display_chains(
symbol: str,
expiry: str,
expiration: str,
min_sp: float = 0.0,
max_sp: float = np.inf,
calls_only: bool = False,
@@ -83,7 +83,7 @@ def display_chains(
----------
symbol: str
Stock ticker symbol
expiry: str
expiration: str
Expiration for option chain
min_sp: float
Min strike
@@ -108,7 +108,7 @@ def display_chains(
put_bool = True
option_chains = yfinance_model.get_full_option_chain(
symbol=symbol, expiration=expiry, calls=call_bool, puts=put_bool
symbol=symbol, expiration=expiration, calls=call_bool, puts=put_bool
).fillna("-")
if option_chains.empty:
console.print("[red]Option chains not found.[/red]")
@@ -231,7 +231,7 @@ def display_chains(
print_rich_table(
option_chains,
title=f"Yahoo Option Chain (15 min delayed) for {expiry} (Greeks calculated by OpenBB)",
title=f"Yahoo Option Chain (15 min delayed) for {expiration} (Greeks calculated by OpenBB)",
headers=[header_fmt(x) for x in option_chains.columns],
)
export_data(
@@ -245,7 +245,7 @@ def display_chains(
@log_start_end(log=logger)
def plot_oi(
symbol: str,
expiry: str,
expiration: str,
min_sp: float = -1,
max_sp: float = -1,
calls_only: bool = False,
@@ -259,8 +259,8 @@ def plot_oi(
----------
symbol: str
Ticker symbol
expiry: str
Expiry date for options
expiration: str
expiration date for options
min_sp: float
Min strike to consider
max_sp: float
@@ -274,7 +274,7 @@ def plot_oi(
external_axes : Optional[List[plt.Axes]], optional
External axes (1 axis is expected in the list), by default None
"""
options = yfinance_model.get_option_chain(symbol, expiry)
options = yfinance_model.get_option_chain(symbol, expiration)
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
@@ -339,7 +339,7 @@ def plot_oi(
ax.set_ylabel("Open Interest [1k] ")
ax.set_xlim(min_strike, max_strike)
ax.legend()
ax.set_title(f"Open Interest for {symbol.upper()} expiring {expiry}")
ax.set_title(f"Open Interest for {symbol.upper()} expiring {expiration}")
theme.style_primary_axis(ax)
@@ -350,7 +350,7 @@ def plot_oi(
@log_start_end(log=logger)
def plot_vol(
symbol: str,
expiry: str,
expiration: str,
min_sp: float = -1,
max_sp: float = -1,
calls_only: bool = False,
@@ -364,8 +364,8 @@ def plot_vol(
----------
symbol: str
Ticker symbol
expiry: str
Expiry date for options
expiration: str
expiration date for options
min_sp: float
Min strike to consider
max_sp: float
@@ -379,7 +379,7 @@ def plot_vol(
external_axes : Optional[List[plt.Axes]], optional
External axes (1 axis is expected in the list), by default None
"""
options = yfinance_model.get_option_chain(symbol, expiry)
options = yfinance_model.get_option_chain(symbol, expiration)
calls = options.calls
puts = options.puts
@@ -431,7 +431,7 @@ def plot_vol(
ax.set_ylabel("Volume [1k] ")
ax.set_xlim(min_strike, max_strike)
ax.legend()
ax.set_title(f"Volume for {symbol.upper()} expiring {expiry}")
ax.set_title(f"Volume for {symbol.upper()} expiring {expiration}")
theme.style_primary_axis(ax)
if external_axes is None:
@@ -447,7 +447,7 @@ def plot_vol(
@log_start_end(log=logger)
def plot_volume_open_interest(
symbol: str,
expiry: str,
expiration: str,
min_sp: float = -1,
max_sp: float = -1,
min_vol: float = -1,
@@ -460,7 +460,7 @@ def plot_volume_open_interest(
----------
symbol: str
Stock ticker symbol
expiry: str
expiration: str
Option expiration
min_sp: float
Min strike price
@@ -474,7 +474,7 @@ def plot_volume_open_interest(
External axes (1 axis is expected in the list), by default None
"""
options = yfinance_model.get_option_chain(symbol, expiry)
options = yfinance_model.get_option_chain(symbol, expiration)
calls = options.calls
puts = options.puts
@@ -604,7 +604,7 @@ def plot_volume_open_interest(
g.set_xticklabels(xlabels)
ax.set_title(
f"{symbol} volumes for {expiry} \n(open interest displayed only during market hours)",
f"{symbol} volumes for {expiration} \n(open interest displayed only during market hours)",
)
ax.invert_yaxis()
@@ -640,7 +640,7 @@ def plot_volume_open_interest(
@log_start_end(log=logger)
def plot_plot(
symbol: str,
expiry: str,
expiration: str,
put: bool = False,
x: str = "s",
y: str = "iv",
@@ -654,7 +654,7 @@ def plot_plot(
----------
symbol: str
Stock ticker symbol
expiry: str
expiration: str
Option expiration
x: str
variable to display in x axis, choose from:
@@ -690,7 +690,7 @@ def plot_plot(
x = convert[x]
y = convert[y]
varis = op_helpers.opt_chain_cols
chain = yfinance_model.get_option_chain(symbol, expiry)
chain = yfinance_model.get_option_chain(symbol, expiration)
values = chain.puts if put else chain.calls
if external_axes is None:
@@ -705,7 +705,7 @@ def plot_plot(
ax.plot(x_data, y_data, "--bo")
option = "puts" if put else "calls"
ax.set_title(
f"{varis[y]['label']} vs. {varis[x]['label']} for {symbol} {option} on {expiry}"
f"{varis[y]['label']} vs. {varis[x]['label']} for {symbol} {option} on {expiration}"
)
ax.set_ylabel(varis[y]["label"])
ax.set_xlabel(varis[x]["label"])
@@ -732,7 +732,7 @@ def plot_payoff(
options: List[Dict[Any, Any]],
underlying: float,
symbol: str,
expiry: str,
expiration: str,
external_axes: Optional[List[plt.Axes]] = None,
) -> None:
"""Generate a graph showing the option payoff diagram"""
@@ -750,7 +750,7 @@ def plot_payoff(
ax.plot(x, ya, label="Payoff After Premium")
else:
ax.plot(x, yb, label="Payoff")
ax.set_title(f"Option Payoff Diagram for {symbol} on {expiry}")
ax.set_title(f"Option Payoff Diagram for {symbol} on {expiration}")
ax.set_ylabel("Profit")
ax.set_xlabel("Underlying Asset Price at Expiration")
ax.legend()
@@ -765,7 +765,7 @@ def plot_payoff(
@log_start_end(log=logger)
def show_parity(
symbol: str,
expiry: str,
expiration: str,
put: bool = False,
ask: bool = False,
mini: float = None,
@@ -778,7 +778,7 @@ def show_parity(
----------
symbol : str
Ticker symbol to get expirations for
expiry : str
expiration : str
Expiration to use for options
put : bool
Whether to use puts or calls
@@ -791,7 +791,7 @@ def show_parity(
export : str
Export data
"""
r_date = datetime.strptime(expiry, "%Y-%m-%d").date()
r_date = datetime.strptime(expiration, "%Y-%m-%d").date()
delta = (r_date - date.today()).days
rate = ((1 + get_rf()) ** (delta / 365)) - 1
stock = get_price(symbol)
@@ -809,7 +809,7 @@ def show_parity(
next_div = last_div + timedelta(days=91)
dividends = []
while next_div < datetime.strptime(expiry, "%Y-%m-%d"):
while next_div < datetime.strptime(expiration, "%Y-%m-%d"):
day_dif = (next_div - datetime.now()).days
dividends.append((avg_div, day_dif))
next_div += timedelta(days=91)
@@ -818,7 +818,7 @@ def show_parity(
else:
pv_dividend = 0
chain = get_option_chain(symbol, expiry)
chain = get_option_chain(symbol, expiration)
name = "ask" if ask else "lastPrice"
o_type = "put" if put else "call"
@@ -1050,7 +1050,7 @@ def export_binomial_calcs(
@log_start_end(log=logger)
def show_binom(
symbol: str,
expiry: str,
expiration: str,
strike: float = 0,
put: bool = False,
europe: bool = False,
@@ -1064,7 +1064,7 @@ def show_binom(
----------
symbol : str
The ticker symbol of the option's underlying asset
expiry : str
expiration : str
The expiration for the option
strike : float
The strike price for the option
@@ -1080,18 +1080,18 @@ def show_binom(
The annualized volatility for the underlying asset
"""
up, prob_up, discount, und_vals, opt_vals, days = yfinance_model.get_binom(
symbol, expiry, strike, put, europe, vol
symbol, expiration, strike, put, europe, vol
)
if export:
export_binomial_calcs(up, prob_up, discount, und_vals, opt_vals, days, symbol)
if plot:
plot_expected_prices(und_vals, prob_up, symbol, expiry)
plot_expected_prices(und_vals, prob_up, symbol, expiration)
option = "put" if put else "call"
console.print(
f"{symbol} {option} at ${strike:.2f} expiring on {expiry} is worth ${opt_vals[0][0]:.2f}\n"
f"{symbol} {option} at ${strike:.2f} expiring on {expiration} is worth ${opt_vals[0][0]:.2f}\n"
)
@@ -1155,7 +1155,7 @@ def display_vol_surface(
@log_start_end(log=logger)
def show_greeks(
symbol: str,
expiry: str,
expiration: str,
div_cont: float = 0,
rf: float = None,
opt_type: int = 1,
@@ -1172,7 +1172,7 @@ def show_greeks(
The ticker symbol value of the option
div_cont: float
The dividend continuous rate
expiry: str
expiration: str
The date of expiration, format "YYYY-MM-DD", i.e. 2010-12-31.
rf: float
The risk-free rate
@@ -1187,7 +1187,7 @@ def show_greeks(
"""
df = yfinance_model.get_greeks(
symbol, expiry, div_cont, rf, opt_type, mini, maxi, show_all
symbol, expiration, div_cont, rf, opt_type, mini, maxi, show_all
)
column_formatting = [

View File

@@ -38,7 +38,7 @@ def test_plot_oi(calls_only, max_sp, min_sp, mocker, puts_only):
yfinance_view.plot_oi(
symbol="PM",
expiry="2022-01-07",
expiration="2022-01-07",
min_sp=min_sp,
max_sp=max_sp,
calls_only=calls_only,
@@ -67,7 +67,7 @@ def test_plot_vol(calls_only, max_sp, min_sp, mocker, puts_only):
yfinance_view.plot_vol(
symbol="PM",
expiry="2022-01-07",
expiration="2022-01-07",
min_sp=min_sp,
max_sp=max_sp,
calls_only=calls_only,
@@ -96,7 +96,7 @@ def test_plot_volume_open_interest(max_sp, min_sp, min_vol, mocker):
yfinance_view.plot_volume_open_interest(
symbol="PM",
expiry="2022-01-07",
expiration="2022-01-07",
min_sp=min_sp,
max_sp=max_sp,
min_vol=min_vol,
@@ -117,7 +117,7 @@ def test_plot_plot(mocker):
yfinance_view.plot_plot(
symbol="PM",
expiry="2022-01-07",
expiration="2022-01-07",
put=True,
x="c",
y="v",
@@ -142,7 +142,7 @@ def test_plot_payoff(mocker):
options=[],
underlying=0,
symbol="PM",
expiry="2022-06-05",
expiration="2022-06-05",
)
@@ -159,7 +159,7 @@ def test_show_parity(mocker):
yfinance_view.show_parity(
symbol="PM",
expiry="2022-01-07",
expiration="2022-01-07",
put=True,
ask=True,
mini=0.0,
@@ -206,7 +206,7 @@ def test_show_binom(mocker):
yfinance_view.show_binom(
symbol="PM",
expiry="2022-01-07",
expiration="2022-01-07",
strike=90.0,
put=True,
europe=False,
@@ -219,12 +219,12 @@ def test_show_binom(mocker):
@pytest.mark.default_cassette("test_show_greeks")
@pytest.mark.vcr
def test_show_greeks():
yfinance_view.show_greeks(symbol="AAPL", expiry="2022-01-28", div_cont=0)
yfinance_view.show_greeks(symbol="AAPL", expiration="2022-01-28", div_cont=0)
@pytest.mark.default_cassette("test_display_chains")
@pytest.mark.vcr
def test_display_chains():
yfinance_view.display_chains(
symbol="AAPL", expiry="2022-07-29", min_sp=-1, max_sp=-1
symbol="AAPL", expiration="2022-07-29", min_sp=-1, max_sp=-1
)

View File

@@ -11,10 +11,10 @@ Expiry:
[unvl] pcr display put call ratio for ticker[/unvl] [AlphaQuery]
[unvl] info display option information (volatility, IV rank etc)[/unvl] [Barchart]
[unvl] chains display option chains with greeks[/unvl] [Tradier]
[unvl] oi plot open interest[/unvl] [Tradier/YFinance]
[unvl] vol plot volume[/unvl] [Tradier/YFinance]
[unvl] voi plot volume and open interest[/unvl] [Tradier/YFinance]
[unvl] chains display option chains with greeks[/unvl] [Tradier/NASDAQ/Yfinance]
[unvl] oi plot open interest[/unvl] [Tradier/YFinance/NASDAQ]
[unvl] vol plot volume[/unvl] [Tradier/YFinance/NASDAQ]
[unvl] voi plot volume and open interest[/unvl] [Tradier/YFinance/NASDAQ]
[unvl] hist plot option history[/unvl] [Tradier]
[unvl] vsurf show 3D volatility surface[/unvl] [Yfinance]
[unvl] grhist plot option greek history[/unvl] [Syncretism]