From 604e7ed5b4ce2daab037db8e5e0e2461865b98e6 Mon Sep 17 00:00:00 2001 From: Laurent Andriamifidy Date: Wed, 24 Mar 2021 12:16:26 +0100 Subject: [PATCH] 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()