From 07117a9beaa9b19f20d416a3ec7ced98cbb2d70a Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Thu, 18 Mar 2021 09:57:03 +0100 Subject: [PATCH 01/11] added more help info prompt help on op menu launch made linter hap --- gamestonk_terminal/main_helper.py | 2 +- gamestonk_terminal/options/op_menu.py | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py index 4010e85a204..4a800212d56 100644 --- a/gamestonk_terminal/main_helper.py +++ b/gamestonk_terminal/main_helper.py @@ -76,7 +76,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): print( " pred prediction techniques, \t e.g.: regression, arima, rnn, lstm, prophet" ) - print(" op options info, \t e.g.: volume") + print(" op options info, \t e.g.: volume, oi") print("") diff --git a/gamestonk_terminal/options/op_menu.py b/gamestonk_terminal/options/op_menu.py index a386a584ec2..0eaf0fc813a 100644 --- a/gamestonk_terminal/options/op_menu.py +++ b/gamestonk_terminal/options/op_menu.py @@ -15,10 +15,14 @@ def opt_menu(s_ticker): opt_parser.add_argument("cmd", choices=choices) completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - # print_options(s_ticker, s_start, s_interval) + # print_options(s_ticker) + should_print_help = True # Loop forever and ever while True: + if should_print_help: + print_options(s_ticker) + should_print_help = False # Get input command from user if session and gtff.USE_PROMPT_TOOLKIT: as_input = session.prompt( @@ -37,8 +41,7 @@ def opt_menu(s_ticker): continue if ns_known_args.cmd == "help": - pass - # print_options(s_ticker) + should_print_help = True elif ns_known_args.cmd == "q": # Just leave the options menu @@ -57,3 +60,21 @@ def opt_menu(s_ticker): vol.open_interest_graph(l_args, s_ticker) else: print("Command not recognized!") + + +def print_options(s_ticker): + """ Print help """ + + print(f"\nOptions analytics for {s_ticker}:") + print(" help show this options menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + print( + " volume -e show traded volume for expiry date. Usage : volume -e yyyy-mm-dd [Yahoo finance]" + ) + print( + " oi -e show open interest for expiry date. Usage : oi -e yyyy-mm-dd [Yahoo finance]" + ) + print("") + return From cad5100dee113fb4f3e51ffa3249d6ab60444453 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Tue, 23 Mar 2021 15:04:44 +0100 Subject: [PATCH 02/11] fully reworked options merged all info in one graph --- gamestonk_terminal/options/op_menu.py | 41 +++-- gamestonk_terminal/options/volume.py | 233 +++++++++++++++++--------- 2 files changed, 175 insertions(+), 99 deletions(-) diff --git a/gamestonk_terminal/options/op_menu.py b/gamestonk_terminal/options/op_menu.py index 0eaf0fc813a..1305dce19d0 100644 --- a/gamestonk_terminal/options/op_menu.py +++ b/gamestonk_terminal/options/op_menu.py @@ -1,5 +1,6 @@ import argparse + from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair @@ -10,27 +11,40 @@ from gamestonk_terminal.menu import session def opt_menu(s_ticker): # Add list of arguments that the options parser accepts - opt_parser = argparse.ArgumentParser(prog="opt", add_help=False) + opt_parser = argparse.ArgumentParser(prog="op", add_help=False) choices = ["help", "q", "quit", "volume", "oi"] opt_parser.add_argument("cmd", choices=choices) completer = NestedCompleter.from_nested_dict({c: None for c in choices}) # print_options(s_ticker) should_print_help = True - + options_data_loaded = False + exp_date_chosen = False # Loop forever and ever + + # print expiry dates 1 time + while True: + if not options_data_loaded: + # raw_options_data = options_helper.load_op_data(s_ticker) + # options_data_loaded = True + pass + if not exp_date_chosen: + # exp_date = options_helper.choose_exp_date(raw_options_data) + # exp_date_chosen = True + pass + if should_print_help: print_options(s_ticker) should_print_help = False # Get input command from user if session and gtff.USE_PROMPT_TOOLKIT: as_input = session.prompt( - f"{get_flair()} (opt)> ", + f"{get_flair()} (op)> ", completer=completer, ) else: - as_input = input(f"{get_flair()} (opt)> ") + as_input = input(f"{get_flair()} (op)> ") # Parse fundamental analysis command of the list of possible commands try: @@ -53,28 +67,23 @@ def opt_menu(s_ticker): elif ns_known_args.cmd == "volume": # call the volume graph - vol.volume_graph(l_args, s_ticker) + # vol.volume_graph(l_args, s_ticker) + vol.volume_graph(s_ticker, l_args) - elif ns_known_args.cmd == "oi": - # call the volume graph - vol.open_interest_graph(l_args, s_ticker) else: print("Command not recognized!") def print_options(s_ticker): - """ Print help """ - + """Print help.""" print(f"\nOptions analytics for {s_ticker}:") print(" help show this options menu again") print(" q quit this menu, and shows back to main menu") print(" quit quit to abandon program") print("") - print( - " volume -e show traded volume for expiry date. Usage : volume -e yyyy-mm-dd [Yahoo finance]" - ) - print( - " oi -e show open interest for expiry date. Usage : oi -e yyyy-mm-dd [Yahoo finance]" - ) + # print( + # " volume -e show traded volume for expiry date. Usage : volume -e yyyy-mm-dd [Yahoo finance]" + # ) + print("") return diff --git a/gamestonk_terminal/options/volume.py b/gamestonk_terminal/options/volume.py index 14440ef101e..d4478bb0f40 100644 --- a/gamestonk_terminal/options/volume.py +++ b/gamestonk_terminal/options/volume.py @@ -1,47 +1,18 @@ import argparse +from bisect import bisect_left import yfinance as yf import seaborn as sns import pandas as pd import matplotlib.pyplot as plt -import plotly.express as px +import numpy as np from gamestonk_terminal.helper_funcs import ( parse_known_args_and_warn, valid_date, ) -def open_interest_graph(l_args, s_ticker): - """ Show traded options volume. [Source: Yahoo Finance] """ - parser = argparse.ArgumentParser( - add_help=False, - prog="volume", - description="""Display volume graph for a date. [Source: Yahoo Finance].""", - ) - parser.add_argument( - "-e", - "--expiry", - type=valid_date, - dest="s_expiry_date", - help="The expiry date (format YYYY-MM-DD) for the option chain", - required=True, - ) - try: - ns_parser = parse_known_args_and_warn(parser, l_args) - if not ns_parser: - return - - __get_open_interest_graph( - s_ticker, ns_parser.s_expiry_date.strftime("%Y-%m-%d") - ) - - except SystemExit: - print("") - except Exception as e: - print(e) - - -def volume_graph(l_args, s_ticker): - """ Show traded options volume. [Source: Yahoo Finance] """ +def volume_graph(s_ticker, l_args): + """Show traded options volume. [Source: Yahoo Finance].""" parser = argparse.ArgumentParser( add_help=False, prog="volume", @@ -69,76 +40,172 @@ def volume_graph(l_args, s_ticker): print(e) -def __get_open_interest_graph(ticker_name, exp_date): - df = __get_volume_data(ticker_name, exp_date) - __generate_graph_sns(df, ticker_name, exp_date, True) - # __generate_graph_plotly(df, ticker_name, exp_date) +def __get_volume_graph(ticker_name, volume_percentile_threshold=50): + # SET VOLUME TO BE FILTERED, default = 50 + PERCENTILE_THRESHOLD = volume_percentile_threshold + TICKER_NAME = ticker_name + raw_data_options = yf.Ticker(TICKER_NAME) + EXP_DATE = __get_exp_data(raw_data_options) -def __get_volume_graph(ticker_name, exp_date): - df = __get_volume_data(ticker_name, exp_date) - __generate_graph_sns(df, ticker_name, exp_date) - # __generate_graph_plotly(df, ticker_name, exp_date) + # current stock price + spot = __get_current_spot(raw_data_options) + calls = __parse_opt_data(raw_data_options, EXP_DATE) + puts = __parse_opt_data(raw_data_options, EXP_DATE, is_calls=False) -def __pull_call_put_data(call_put, flag): - df = call_put.pivot_table( - index="strike", values=["volume", "openInterest"], aggfunc="sum" + calls = __add_max_pain_data(calls, spot) + + puts = __add_max_pain_data(puts, spot, is_calls=False) + + max_pain = __calc_max_pain(calls, puts) + + # Initialize the matplotlib figure + sns.set_style("darkgrid") + plt.rcParams["figure.dpi"] = 360 + _, ax = plt.subplots(figsize=(15, 12)) + sns.set_style(style="darkgrid") + + # make x axis symetric + axis_origin = max(abs(max(puts["oi+v"])), abs(max(calls["oi+v"]))) + ax.set_xlim(-axis_origin, +axis_origin) + + VOLUME_THRESHOLD = np.percentile(calls["oi+v"], PERCENTILE_THRESHOLD) + + g = sns.barplot( + x="oi+v", + y="strike", + data=calls[calls["oi+v"] > VOLUME_THRESHOLD], + label="Calls: Open Interest", + color="lightgreen", + orient="h", ) - df.reindex() + g = sns.barplot( + x="volume", + y="strike", + data=calls[calls["oi+v"] > VOLUME_THRESHOLD], + label="Calls: Volume", + color="green", + orient="h", + ) - df["strike"] = df.index - df["type"] = flag + g = sns.barplot( + x="oi+v", + y="strike", + data=puts[puts["oi+v"] < -VOLUME_THRESHOLD], + label="Puts: Open Interest", + color="pink", + orient="h", + ) + g = sns.barplot( + x="volume", + y="strike", + data=puts[puts["oi+v"] < -VOLUME_THRESHOLD], + label="Puts: Volume", + color="red", + orient="h", + ) + + # draw spot line + s = [float(strike.get_text()) for strike in ax.get_yticklabels()] + spot_index = bisect_left(s, spot) # find where the spot is on the graph + spot_line = ax.axhline(spot_index, ls="--", color="dodgerblue", alpha=0.3) + + # draw max pain line + max_pain_index = bisect_left(s, max_pain) + max_pain_line = ax.axhline(max_pain_index, ls="-", color="black", alpha=0.3) + max_pain_line.set_linewidth(5) + + # ax.axhline(max_pain_index, ls='--') + # format ticklabels without - for puts + g.set_xticks(g.get_xticks()) + xlabels = ["{:,.0f}".format(x).replace("-", "") for x in g.get_xticks()] + g.set_xticklabels(xlabels) + + plt.title( + f"{TICKER_NAME.upper()} volumes for {EXP_DATE} (open interest displayed only during market hours)" + ) + ax.invert_yaxis() + + # ax.spines['left'].set_position('center') + + _ = ax.legend() + handles, _ = ax.get_legend_handles_labels() + handles.append(spot_line) + handles.append(max_pain_line) + + # create legend labels + add to graph + labels = [ + "Calls open interest", + "Calls volume ", + "Puts open interest", + "Puts volume", + "Current stock price", + f"Max pain = {max_pain}", + ] + + plt.legend(handles=handles[:], labels=labels) + + sns.despine(left=True, bottom=True) + plt.show() + + +def __add_max_pain_data(df, spot, is_calls=True): + # max pain parsing + calculation + df["spot"] = round(spot, 2) + if is_calls: + df["dv"] = spot - df.index + else: + df["dv"] = df.index - spot + df["dv"] = df["dv"].apply(lambda x: max(0, x)) + df["dv"] = abs(df["dv"] * df["volume"]) return df -def __get_volume_data(ticker_name, exp_date): +def __calc_max_pain(calls, puts): - option_chain = yf.Ticker(ticker_name).option_chain(exp_date) + df = pd.merge(calls, puts, left_index=True, right_index=True) + df["dv"] = round(df["dv_x"] + df["dv_y"], 2) - calls = __pull_call_put_data(option_chain.calls, "calls") - - puts = __pull_call_put_data(option_chain.puts, "puts") - - volume_data = pd.concat([calls, puts], axis=0) - # dataframe - return volume_data + max_pain = df["dv"].idxmax() + return max_pain -def __generate_graph_plotly(df, ticker_name, exp_date, for_open_interest=False): - # version with plotly express - if for_open_interest: - op_type = "openInterest" +def __parse_opt_data(raw_data_options, exp_date, is_calls=True): + # get option chain for specific expiration + opt = raw_data_options.option_chain(exp_date) + + # PARSE DATA + if is_calls: + option_data = opt.calls + flag = "calls" else: - op_type = "volume" + option_data = opt.puts + flag = "puts" - fig = px.line( - df, - x="strike", - y=op_type, - title=f"{ticker_name} options {op_type} for {exp_date}", - color="type", - ) - fig.show() + data = option_data.pivot_table( + index="strike", values=["volume", "openInterest"], aggfunc="sum" + ).reindex() - return + data["strike"] = data.index + data["type"] = flag - -def __generate_graph_sns(df, ticker_name, exp_date, for_open_interest=False): - # version with seaborn express - if for_open_interest: - op_type = "openInterest" + if is_calls: + data["openInterest"] = data["openInterest"] + data["volume"] = data["volume"] else: - op_type = "volume" + data["openInterest"] = -data["openInterest"] + data["volume"] = -data["volume"] - plt.figure(figsize=(12, 6)) - fig = sns.lineplot( - data=df, x="strike", y=op_type, hue="type", palette=["limegreen", "tomato"] - ) + data["oi+v"] = data["openInterest"] + data["volume"] + return data - plt.title(f"{ticker_name} options {op_type} for {exp_date}") - plt.show() - return fig +def __get_current_spot(raw_data_options): + return raw_data_options.history().tail(1)["Close"].iloc[0] + + +def __get_exp_data(raw_data_options, date_index=0): + return raw_data_options.options[date_index] From 91e355d6a013cb8e8c2e739edb01676193128202 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Tue, 23 Mar 2021 15:34:32 +0100 Subject: [PATCH 03/11] linting --- gamestonk_terminal/options/volume.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamestonk_terminal/options/volume.py b/gamestonk_terminal/options/volume.py index d4478bb0f40..59e9d1a9a4e 100644 --- a/gamestonk_terminal/options/volume.py +++ b/gamestonk_terminal/options/volume.py @@ -66,7 +66,7 @@ def __get_volume_graph(ticker_name, volume_percentile_threshold=50): _, ax = plt.subplots(figsize=(15, 12)) sns.set_style(style="darkgrid") - # make x axis symetric + # make x axis symmetric axis_origin = max(abs(max(puts["oi+v"])), abs(max(calls["oi+v"]))) ax.set_xlim(-axis_origin, +axis_origin) From ed0b378e7da39bdf8e0148d43578b16c8b46b149 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Tue, 23 Mar 2021 16:17:00 +0100 Subject: [PATCH 04/11] linting --- gamestonk_terminal/options/op_menu.py | 3 ++- gamestonk_terminal/options/volume.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gamestonk_terminal/options/op_menu.py b/gamestonk_terminal/options/op_menu.py index 1305dce19d0..f4d9ef3e4b2 100644 --- a/gamestonk_terminal/options/op_menu.py +++ b/gamestonk_terminal/options/op_menu.py @@ -1,3 +1,4 @@ +"""Control options main menu.""" import argparse @@ -9,7 +10,7 @@ from gamestonk_terminal.menu import session def opt_menu(s_ticker): - + """Control options main menu.""" # Add list of arguments that the options parser accepts opt_parser = argparse.ArgumentParser(prog="op", add_help=False) choices = ["help", "q", "quit", "volume", "oi"] diff --git a/gamestonk_terminal/options/volume.py b/gamestonk_terminal/options/volume.py index 59e9d1a9a4e..7b870fd1c0c 100644 --- a/gamestonk_terminal/options/volume.py +++ b/gamestonk_terminal/options/volume.py @@ -1,3 +1,4 @@ +"""options info. [Source: Yahoo Finance].""" import argparse from bisect import bisect_left import yfinance as yf From 604e7ed5b4ce2daab037db8e5e0e2461865b98e6 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 12:16:26 +0100 Subject: [PATCH 05/11] deleted op_menu module implemented class paradigm for options menu in op_controller module added terminal menu reworked op logics --- gamestonk_terminal/main_helper.py | 2 +- gamestonk_terminal/options/op_controller.py | 170 ++++++++++++++++++++ gamestonk_terminal/options/op_menu.py | 90 ----------- gamestonk_terminal/options/volume.py | 51 +----- terminal.py | 8 +- 5 files changed, 182 insertions(+), 139 deletions(-) create mode 100644 gamestonk_terminal/options/op_controller.py delete mode 100644 gamestonk_terminal/options/op_menu.py diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py index c9fa0c99bda..f2d103ccab4 100644 --- a/gamestonk_terminal/main_helper.py +++ b/gamestonk_terminal/main_helper.py @@ -77,7 +77,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): print( " pred prediction techniques, \t e.g.: regression, arima, rnn, lstm, prophet" ) - print(" op options info, \t e.g.: volume, oi") + print(" op options info, \t e.g.: volume") print("") diff --git a/gamestonk_terminal/options/op_controller.py b/gamestonk_terminal/options/op_controller.py new file mode 100644 index 00000000000..a093d015bfb --- /dev/null +++ b/gamestonk_terminal/options/op_controller.py @@ -0,0 +1,170 @@ +"""Options Controller Module.""" +__docformat__ = "numpy" + +import argparse +import matplotlib.pyplot as plt + +import yfinance as yf +from prompt_toolkit.completion import NestedCompleter +from gamestonk_terminal.helper_funcs import get_flair +from gamestonk_terminal import feature_flags as gtff +from gamestonk_terminal.options import volume +from gamestonk_terminal.menu import session + + +class OptionsController: + """Options Controller class.""" + + # Command choices + CHOICES = ["help", "q", "quit", "exp", "volume"] + + def __init__( + self, + ticker: str, + ): + """Construct data.""" + self.ticker = ticker + self.raw_data_options = yf.Ticker(self.ticker) + self.expiry_date = "" + + self.__set_exp_date() + + self.op_parser = argparse.ArgumentParser(add_help=False, prog="ca") + self.op_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + + def __set_exp_date(self, date_index=0): + self.expiry_date = self.raw_data_options.options[date_index] + return + + @staticmethod + def print_exp_dates(expiry_date, all_dates): + """Print all available expiry dates.""" + print(f"current selected expiry date :[{expiry_date}]") + print("\nAvailable dates:") + for i, d in enumerate(all_dates): + print(f"[{i}] {d}") + print(f"\nPlease select date: input a number between 0 and {len(all_dates)-1}") + return + + @staticmethod + def print_help(expiry_date): + """Print help.""" + print("\nOptions insight Mode:") + print(" help show this menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + + print( + f" exp set expiry date, current selected date : [{expiry_date}]" + ) + print(" volume plot options trading volume / open interest") + print("") + return + + def switch(self, an_input: str): + """Process and dispatch input. + + Returns + ------- + True, False or None + False - quit the menu + True - quit the program + None - continue in the menu + """ + (known_args, other_args) = self.op_parser.parse_known_args(an_input.split()) + + return getattr( + self, "call_" + known_args.cmd, lambda: "Command not recognized!" + )(other_args) + + def call_help(self, _): + """Process Help command.""" + self.print_help(self.expiry_date) + + def call_q(self, _): + """Process Q command - quit the menu.""" + return False + + def call_quit(self, _): + """Process Quit command - quit the program.""" + return True + + def call_volume(self, _): + """Process volume command.""" + volume.volume_graph( + self.raw_data_options, + self.ticker, + self.expiry_date, + volume_percentile_threshold=60, + ) + + def call_exp(self, _): + """Process exp command.""" + self.print_exp_dates(self.expiry_date, self.raw_data_options.options) + + date_index_choices = list(range(len(self.raw_data_options.options) - 1)) + while True: + # Get input command from user + if session and gtff.USE_PROMPT_TOOLKIT: + completer = NestedCompleter.from_nested_dict( + {c: None for c in date_index_choices} + ) + an_input = session.prompt( + f"{get_flair()} (exp)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (exp)> ") + + try: + plt.close("all") + + process_input = an_input + + if process_input is not None: + self.__set_exp_date(int(an_input)) + print(f"\nNew selected expiry date : [{self.expiry_date}]\n") + return + # menu(self.ticker, self) + + except SystemExit: + print("The command selected doesn't exist\n") + continue + + +def menu(ticker: str, op_con=None): + """Options info Menu.""" + if op_con is None: + op_controller = OptionsController(ticker) + else: + op_controller = op_con + op_controller.call_help("") + + while True: + # Get input command from user + if session and gtff.USE_PROMPT_TOOLKIT: + completer = NestedCompleter.from_nested_dict( + {c: None for c in op_controller.CHOICES} + ) + an_input = session.prompt( + f"{get_flair()} (op)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (op)> ") + + try: + plt.close("all") + + process_input = op_controller.switch(an_input) + + if process_input is not None: + return process_input + + except SystemExit: + print("The command selected doesn't exist\n") + continue diff --git a/gamestonk_terminal/options/op_menu.py b/gamestonk_terminal/options/op_menu.py deleted file mode 100644 index f4d9ef3e4b2..00000000000 --- a/gamestonk_terminal/options/op_menu.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Control options main menu.""" -import argparse - - -from prompt_toolkit.completion import NestedCompleter -from gamestonk_terminal import feature_flags as gtff -from gamestonk_terminal.helper_funcs import get_flair -from gamestonk_terminal.options import volume as vol -from gamestonk_terminal.menu import session - - -def opt_menu(s_ticker): - """Control options main menu.""" - # Add list of arguments that the options parser accepts - opt_parser = argparse.ArgumentParser(prog="op", add_help=False) - choices = ["help", "q", "quit", "volume", "oi"] - opt_parser.add_argument("cmd", choices=choices) - completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - - # print_options(s_ticker) - should_print_help = True - options_data_loaded = False - exp_date_chosen = False - # Loop forever and ever - - # print expiry dates 1 time - - while True: - if not options_data_loaded: - # raw_options_data = options_helper.load_op_data(s_ticker) - # options_data_loaded = True - pass - if not exp_date_chosen: - # exp_date = options_helper.choose_exp_date(raw_options_data) - # exp_date_chosen = True - pass - - if should_print_help: - print_options(s_ticker) - should_print_help = False - # Get input command from user - if session and gtff.USE_PROMPT_TOOLKIT: - as_input = session.prompt( - f"{get_flair()} (op)> ", - completer=completer, - ) - else: - as_input = input(f"{get_flair()} (op)> ") - - # Parse fundamental analysis command of the list of possible commands - try: - (ns_known_args, l_args) = opt_parser.parse_known_args(as_input.split()) - - except SystemExit: - print("The command selected doesn't exist\n") - continue - - if ns_known_args.cmd == "help": - should_print_help = True - - elif ns_known_args.cmd == "q": - # Just leave the options menu - return False - - elif ns_known_args.cmd == "quit": - # Abandon the program - return True - - elif ns_known_args.cmd == "volume": - # call the volume graph - # vol.volume_graph(l_args, s_ticker) - vol.volume_graph(s_ticker, l_args) - - else: - print("Command not recognized!") - - -def print_options(s_ticker): - """Print help.""" - print(f"\nOptions analytics for {s_ticker}:") - print(" help show this options menu again") - print(" q quit this menu, and shows back to main menu") - print(" quit quit to abandon program") - print("") - # print( - # " volume -e show traded volume for expiry date. Usage : volume -e yyyy-mm-dd [Yahoo finance]" - # ) - - print("") - return diff --git a/gamestonk_terminal/options/volume.py b/gamestonk_terminal/options/volume.py index 7b870fd1c0c..4d6653e9364 100644 --- a/gamestonk_terminal/options/volume.py +++ b/gamestonk_terminal/options/volume.py @@ -1,53 +1,19 @@ """options info. [Source: Yahoo Finance].""" -import argparse from bisect import bisect_left -import yfinance as yf import seaborn as sns import pandas as pd import matplotlib.pyplot as plt import numpy as np -from gamestonk_terminal.helper_funcs import ( - parse_known_args_and_warn, - valid_date, -) -def volume_graph(s_ticker, l_args): - """Show traded options volume. [Source: Yahoo Finance].""" - parser = argparse.ArgumentParser( - add_help=False, - prog="volume", - description="""Display volume graph for a date. [Source: Yahoo Finance].""", - ) - parser.add_argument( - "-e", - "--expiry", - type=valid_date, - dest="s_expiry_date", - help="The expiry date (format YYYY-MM-DD) for the option chain", - required=True, - ) - - try: - ns_parser = parse_known_args_and_warn(parser, l_args) - if not ns_parser: - return - - __get_volume_graph(s_ticker, ns_parser.s_expiry_date.strftime("%Y-%m-%d")) - - except SystemExit: - print("") - except Exception as e: - print(e) - - -def __get_volume_graph(ticker_name, volume_percentile_threshold=50): +def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50): + """Docstring make linter hap.""" # SET VOLUME TO BE FILTERED, default = 50 PERCENTILE_THRESHOLD = volume_percentile_threshold TICKER_NAME = ticker_name - raw_data_options = yf.Ticker(TICKER_NAME) - EXP_DATE = __get_exp_data(raw_data_options) + raw_data_options = raw_data + EXP_DATE = exp_date # current stock price spot = __get_current_spot(raw_data_options) @@ -62,9 +28,8 @@ def __get_volume_graph(ticker_name, volume_percentile_threshold=50): max_pain = __calc_max_pain(calls, puts) # Initialize the matplotlib figure - sns.set_style("darkgrid") - plt.rcParams["figure.dpi"] = 360 - _, ax = plt.subplots(figsize=(15, 12)) + + _, ax = plt.subplots(figsize=(12, 10)) sns.set_style(style="darkgrid") # make x axis symmetric @@ -206,7 +171,3 @@ def __parse_opt_data(raw_data_options, exp_date, is_calls=True): def __get_current_spot(raw_data_options): return raw_data_options.history().tail(1)["Close"].iloc[0] - - -def __get_exp_data(raw_data_options, date_index=0): - return raw_data_options.options[date_index] diff --git a/terminal.py b/terminal.py index 071e6c0f3c7..96d44883bd2 100644 --- a/terminal.py +++ b/terminal.py @@ -1,3 +1,4 @@ +"""Docstring makes linters happy.""" #!/usr/bin/env python import argparse @@ -23,7 +24,7 @@ from gamestonk_terminal.papermill import papermill_controller as mill from gamestonk_terminal.behavioural_analysis import ba_controller from gamestonk_terminal.technical_analysis import ta_menu as tam from gamestonk_terminal.comparison_analysis import ca_controller -from gamestonk_terminal.options import op_menu as opm +from gamestonk_terminal.options import op_controller from gamestonk_terminal.fred import fred_menu as fm from gamestonk_terminal.portfolio import port_controller @@ -32,12 +33,13 @@ from gamestonk_terminal.portfolio import port_controller # pylint: disable=too-many-branches + + def main(): """ Gamestonk Terminal is an awesome stock market terminal that has been developed for fun, while I saw my GME shares tanking. But hey, I like the stock. """ - # Enable VT100 Escape Sequence for WINDOWS 10 Ver. 1607 if sys.platform == "win32": os.system("") @@ -200,7 +202,7 @@ def main(): b_quit = ddm.dd_menu(df_stock, s_ticker, s_start, s_interval) elif ns_known_args.opt == "op": - b_quit = opm.opt_menu(s_ticker) + b_quit = op_controller.menu(s_ticker) elif ns_known_args.opt == "fred": b_quit = fm.fred_menu() From d9e99f1ec097dbe051395323df7ad4d1a0b3b376 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 12:18:46 +0100 Subject: [PATCH 06/11] linting --- terminal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/terminal.py b/terminal.py index 96d44883bd2..4199658323c 100644 --- a/terminal.py +++ b/terminal.py @@ -1,4 +1,3 @@ -"""Docstring makes linters happy.""" #!/usr/bin/env python import argparse From 6d6df45f41c9fa83dabe8f90a4bc98956b7e100d Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 13:50:42 +0100 Subject: [PATCH 07/11] ran 'pyupgrade --py36-plus gamestonk_terminal/options/volume.py' to pass tests --- gamestonk_terminal/options/volume.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gamestonk_terminal/options/volume.py b/gamestonk_terminal/options/volume.py index 4d6653e9364..ca2e7421adc 100644 --- a/gamestonk_terminal/options/volume.py +++ b/gamestonk_terminal/options/volume.py @@ -30,7 +30,6 @@ def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50 # Initialize the matplotlib figure _, ax = plt.subplots(figsize=(12, 10)) - sns.set_style(style="darkgrid") # make x axis symmetric axis_origin = max(abs(max(puts["oi+v"])), abs(max(calls["oi+v"]))) @@ -38,6 +37,8 @@ def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50 VOLUME_THRESHOLD = np.percentile(calls["oi+v"], PERCENTILE_THRESHOLD) + sns.set_style(style="darkgrid") + g = sns.barplot( x="oi+v", y="strike", @@ -87,7 +88,7 @@ def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50 # ax.axhline(max_pain_index, ls='--') # format ticklabels without - for puts g.set_xticks(g.get_xticks()) - xlabels = ["{:,.0f}".format(x).replace("-", "") for x in g.get_xticks()] + xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()] g.set_xticklabels(xlabels) plt.title( @@ -113,7 +114,6 @@ def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50 ] plt.legend(handles=handles[:], labels=labels) - sns.despine(left=True, bottom=True) plt.show() From 85f8acb2c79dab768d932a767742499087dd2b53 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 14:20:17 +0100 Subject: [PATCH 08/11] added helper func plot autoscaling added helper func dpi cfgplot --- gamestonk_terminal/options/volume.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gamestonk_terminal/options/volume.py b/gamestonk_terminal/options/volume.py index ca2e7421adc..3b87ac8efe7 100644 --- a/gamestonk_terminal/options/volume.py +++ b/gamestonk_terminal/options/volume.py @@ -5,6 +5,9 @@ import pandas as pd import matplotlib.pyplot as plt import numpy as np +from gamestonk_terminal.helper_funcs import plot_autoscale +from gamestonk_terminal import config_plot as cfgPlot + def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50): """Docstring make linter hap.""" @@ -28,8 +31,8 @@ def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50 max_pain = __calc_max_pain(calls, puts) # Initialize the matplotlib figure - - _, ax = plt.subplots(figsize=(12, 10)) + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI) + # _, ax = plt.subplots(figsize=(12, 10)) # make x axis symmetric axis_origin = max(abs(max(puts["oi+v"])), abs(max(calls["oi+v"]))) From c92148bd27c0186f34dd4e6df415c4a538da7a16 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 14:24:20 +0100 Subject: [PATCH 09/11] solved crash when expiry date index out of range --- gamestonk_terminal/options/op_controller.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/gamestonk_terminal/options/op_controller.py b/gamestonk_terminal/options/op_controller.py index a093d015bfb..97f78740ae3 100644 --- a/gamestonk_terminal/options/op_controller.py +++ b/gamestonk_terminal/options/op_controller.py @@ -126,10 +126,15 @@ class OptionsController: process_input = an_input if process_input is not None: - self.__set_exp_date(int(an_input)) - print(f"\nNew selected expiry date : [{self.expiry_date}]\n") - return - # menu(self.ticker, self) + try: + self.__set_exp_date(int(an_input)) + print(f"\nNew selected expiry date : [{self.expiry_date}]\n") + return + # menu(self.ticker, self) + except IndexError: + print( + f"\nSelection must be between [0 - {len(self.raw_data_options.options) - 1}]\n" + ) except SystemExit: print("The command selected doesn't exist\n") From c66a9243a7bbc429ce03a6ac5d3edd69264df637 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 14:51:06 +0100 Subject: [PATCH 10/11] prettified expiry dates printing --- gamestonk_terminal/options/op_controller.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gamestonk_terminal/options/op_controller.py b/gamestonk_terminal/options/op_controller.py index 97f78740ae3..2cee60108b0 100644 --- a/gamestonk_terminal/options/op_controller.py +++ b/gamestonk_terminal/options/op_controller.py @@ -42,11 +42,17 @@ class OptionsController: @staticmethod def print_exp_dates(expiry_date, all_dates): """Print all available expiry dates.""" - print(f"current selected expiry date :[{expiry_date}]") - print("\nAvailable dates:") + print(f"\nCurrent selected expiry date: [{expiry_date}]") + print("\nAvailable dates:\n") for i, d in enumerate(all_dates): - print(f"[{i}] {d}") - print(f"\nPlease select date: input a number between 0 and {len(all_dates)-1}") + if len(str(i)) == 1: + spacer = " " + else: + spacer = "" + print(f" [{i}] {spacer}{d}") + print( + f"\nPlease select date: input a number between 0 and {len(all_dates)-1}\n" + ) return @staticmethod @@ -101,6 +107,7 @@ class OptionsController: self.expiry_date, volume_percentile_threshold=60, ) + print("") def call_exp(self, _): """Process exp command.""" @@ -135,6 +142,7 @@ class OptionsController: print( f"\nSelection must be between [0 - {len(self.raw_data_options.options) - 1}]\n" ) + continue except SystemExit: print("The command selected doesn't exist\n") From ebaee866caf17895c084f9de2c1cb974f556fbc9 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 15:15:00 +0100 Subject: [PATCH 11/11] captured non int expiry date index choice error --- gamestonk_terminal/options/op_controller.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gamestonk_terminal/options/op_controller.py b/gamestonk_terminal/options/op_controller.py index 2cee60108b0..6c4ce0ff1fa 100644 --- a/gamestonk_terminal/options/op_controller.py +++ b/gamestonk_terminal/options/op_controller.py @@ -42,7 +42,7 @@ class OptionsController: @staticmethod def print_exp_dates(expiry_date, all_dates): """Print all available expiry dates.""" - print(f"\nCurrent selected expiry date: [{expiry_date}]") + print(f"Current selected expiry date: [{expiry_date}]") print("\nAvailable dates:\n") for i, d in enumerate(all_dates): if len(str(i)) == 1: @@ -143,6 +143,11 @@ class OptionsController: f"\nSelection must be between [0 - {len(self.raw_data_options.options) - 1}]\n" ) continue + except ValueError: + print("The command selected doesn't exist\n") + self.print_exp_dates( + self.expiry_date, self.raw_data_options.options + ) except SystemExit: print("The command selected doesn't exist\n")