diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py index 6496f714735..f2d103ccab4 100644 --- a/gamestonk_terminal/main_helper.py +++ b/gamestonk_terminal/main_helper.py @@ -53,7 +53,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): print( " fred economic data, \t\t\t from: Federal Reserve Bank of St. Louis " ) - print(" port portfolio, \t\t\t Currently: Robinhood ") + print(" pa portfolio analysis, \t\t Currently Supported: Robinhood ") if s_ticker: print( diff --git a/gamestonk_terminal/portfolio/README.md b/gamestonk_terminal/portfolio/README.md index de0511d4e42..52eb70cf214 100644 --- a/gamestonk_terminal/portfolio/README.md +++ b/gamestonk_terminal/portfolio/README.md @@ -2,12 +2,26 @@ This page shows the available brokers for loading in data. If yours is not listed, please submit a new issue and we will look at adding it. +Current brokers: +* Robinhood + Once this screen is accessed, the first command to be run is ```` -login +login [brokers] ```` -Which will use your login information, which should be stored as environment variables in [config file](/gamestonk_terminal/config_terminal.py) + +The brokers should just be your brokers from the list shown on the first menu. To login to Robinhood, the command is +```` +login rh +```` +When td is added, the command will be +```` +login rh td +```` +After logging in, the help menu will display which brokers you logged into. + +Your login information should be stored as environment variables in [config file](/gamestonk_terminal/config_terminal.py) Robinhood has Two Factor Authentication, so you will likely be prompted to enter a code that is texted/emailed to you. ###NOTE THAT LOGGING IN WILL SAVE A TOKEN TO `os.path.expanduser("~")/.tokens` WHICH CAN BE USED TO LOGIN EVEN IF USER CREDENTIALS ARE INCORRECT. diff --git a/gamestonk_terminal/portfolio/port_controller.py b/gamestonk_terminal/portfolio/port_controller.py new file mode 100644 index 00000000000..597ffeeb094 --- /dev/null +++ b/gamestonk_terminal/portfolio/port_controller.py @@ -0,0 +1,157 @@ +__docformat__ = "numpy" + +import argparse +from typing import List +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.menu import session +from gamestonk_terminal.portfolio import rh_api + + +class PortfolioController: + + CHOICES = [ + "help", + "q", + "quit", + "login", + "hold", + "rhhist", + ] + + BROKERS = [ + "rh", + ] + + def __init__(self): + self.port_parser = argparse.ArgumentParser(add_help=False, prog="port") + self.port_parser.add_argument("cmd", choices=self.CHOICES) + self.brokers_list = set() + + @staticmethod + def print_help(broker_list): + if not broker_list: + broker_list = [" None"] + + """ Print help """ + print("\nPortfolio:") + print(" help show this menu again") + print( + " q quit this menu, and shows back to main menu, logs out of brokers" + ) + print(" quit quit to abandon program, logs out of brokers") + print(" login login to your brokers") + print(f"\nCurrent Brokers : {('', ', '.join(broker_list))[bool(broker_list)]}") + print("\nCurrently Supported :") + print(" rh Robinhood - fuck these guys") + print("\nCommands (login required):") + print("") + print(" hold view rh holdings") + print(" rhhist plot rh portfolio history") + print("") + + def print_portfolio_menu(self): + + print_broke = " " + for broker in self.brokers_list: + print_broke += broker + " " + print("\nCurrent Brokers :" + print_broke) + print("") + print(" hold check holdings") + print(" rhhist plot historical RH portfolio") + print("") + + 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.port_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.brokers_list) + + 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_login(self, other_args): + + logged_in = False + if not other_args: + print("Please enter brokers you wish to login to") + print("") + return + for broker in other_args: + if broker in self.BROKERS: + api = broker + "_api" + try: + eval(api + ".login()") + self.brokers_list.add(broker) + logged_in = True + except Exception as e: + print("") + print(f"Error at broker : {broker}") + print(e) + print("Make sure credentials are defined in config_terminal.py ") + print("") + else: + print(f"{broker} not supported") + + if logged_in: + self.print_portfolio_menu() + + def call_rhhist(self, other_args: List[str]): + rh_api.plot_historical(other_args) + + def call_hold(self, _): + try: + rh_api.show_holdings() + except Exception as e: + print(e) + print("") + + +def menu(): + """Portfolio Analysis Menu""" + + port_controller = PortfolioController() + port_controller.print_help(port_controller.brokers_list) + + while True: + # Get input command from user + if session and gtff.USE_PROMPT_TOOLKIT: + completer = NestedCompleter.from_nested_dict( + {c: None for c in port_controller.CHOICES} + ) + an_input = session.prompt( + f"{get_flair()} (pa)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (pa)> ") + + try: + process_input = port_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/portfolio/port_menu.py b/gamestonk_terminal/portfolio/port_menu.py deleted file mode 100644 index df46626a2c6..00000000000 --- a/gamestonk_terminal/portfolio/port_menu.py +++ /dev/null @@ -1,100 +0,0 @@ -import argparse -from matplotlib import pyplot as plt -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.menu import session -from gamestonk_terminal.portfolio import rh_api - - -def print_port(show_login): - """ Print help """ - print("\nPortfolio:") - 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("Robinhood:") - print("") - if show_login: - print(" login login") - print(" hold look at current holdings") - print(" rhhist look at historical portfolio") - print(" ") - - -def port_menu(): - plt.close("all") - port_parser = argparse.ArgumentParser(prog="port", add_help=False) - choices = ["help", "q", "quit", "hold", "rhhist", "login"] - port_parser.add_argument("cmd", choices=choices) - completer = NestedCompleter.from_nested_dict({c: None for c in choices}) - should_print_help = True - print_login = True - while True: - if should_print_help: - print_port(print_login) - should_print_help = False - - # Get input command from user - if session and gtff.USE_PROMPT_TOOLKIT: - as_input = session.prompt( - f"{get_flair()} (port)> ", - completer=completer, - ) - else: - as_input = input(f"{get_flair()} (port)> ") - try: - (ns_known_args, l_args) = port_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": - # Leave the port menu + logout of robinhood - # logoff raises error if not logged in - try: - rh_api.logoff() - except Exception: - pass - return False - - elif ns_known_args.cmd == "quit": - # Will raise exception if not logged in - try: - rh_api.logoff() - except Exception: - pass - # Abandon the program - return True - - elif ns_known_args.cmd == "hold": - try: - rh_api.show_holdings() - except Exception as e: - print(e) - print("") - - elif ns_known_args.cmd == "rhhist": - rh_api.plot_historical(l_args) - - elif ns_known_args.cmd == "login": - try: - rh_api.login() - should_print_help = True - print_login = False - except Exception as e: - print("") - print(e) - print("Make sure credentials are defined in config_terminal.py ") - print("") - should_print_help = False - print_login = True - - else: - print("Command not recognized") - print("") diff --git a/requirements.txt b/requirements.txt index 06971cc844c..2a0bc3abb1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ aiohttp==3.7.4.post0; python_version >= "3.6" +alpaca-trade-api==1.0.1 alpha-vantage==2.3.1 ansiwrap==0.8.4; python_version >= "3.6" appdirs==1.4.4; python_version >= "3.6" @@ -103,6 +104,7 @@ rapidfuzz==1.2.1; python_version >= "2.7" regex==2020.11.13; python_version >= "3.6" requests==2.25.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") retrying==1.3.3 +robin-stocks==2.0.3 scipy==1.5.4; python_version >= "3.6" seaborn==0.11.1; python_version >= "3.6" send2trash==1.5.0; python_version >= "3.5" diff --git a/terminal.py b/terminal.py index a3f8f0b6fd6..071e6c0f3c7 100644 --- a/terminal.py +++ b/terminal.py @@ -25,7 +25,7 @@ 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.fred import fred_menu as fm -from gamestonk_terminal.portfolio import port_menu +from gamestonk_terminal.portfolio import port_controller # import warnings # warnings.simplefilter("always") @@ -77,7 +77,7 @@ def main(): "ca", "op", "fred", - "port", + "pa", ] menu_parser.add_argument("opt", choices=choices) completer = NestedCompleter.from_nested_dict({c: None for c in choices}) @@ -205,8 +205,8 @@ def main(): elif ns_known_args.opt == "fred": b_quit = fm.fred_menu() - elif ns_known_args.opt == "port": - b_quit = port_menu.port_menu() + elif ns_known_args.opt == "pa": + b_quit = port_controller.menu() elif ns_known_args.opt == "pred":