diff --git a/openbb_terminal/helper_funcs.py b/openbb_terminal/helper_funcs.py index c29c09130d4..e93d183cfd5 100644 --- a/openbb_terminal/helper_funcs.py +++ b/openbb_terminal/helper_funcs.py @@ -278,6 +278,7 @@ def print_rich_table( print_to_console: bool = False, limit: Optional[int] = 1000, source: Optional[str] = None, + columns_keep_types: Optional[List[str]] = None, ): """Prepare a table from df in rich. @@ -313,6 +314,8 @@ def print_rich_table( console. source: Optional[str] Source of the table. If provided, it will be displayed in the header of the table. + columns_keep_types: Optional[List[str]] + Columns to keep their types, i.e. not convert to numeric """ if export: return @@ -329,6 +332,8 @@ def print_rich_table( # convert non-str that are not timestamp or int into str # eg) praw.models.reddit.subreddit.Subreddit for col in df.columns: + if columns_keep_types is not None and col in columns_keep_types: + continue try: if not any( isinstance(df[col].iloc[x], pd.Timestamp) diff --git a/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py b/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py index fb3b42c19ed..89d92287f4a 100644 --- a/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py +++ b/openbb_terminal/stocks/fundamental_analysis/business_insider_view.py @@ -172,9 +172,17 @@ def display_price_target_from_analysts( name="Price Target", mode="markers", customdata=df_analyst_plot.apply( - lambda row: f"{row['Company']} ({row['Rating']})", axis=1 - ).values, - hovertemplate="%{customdata}

Price Target: %{y:.2f}", + lambda row: "
".join( + [ + f"${x['Price Target']} - {x['Company']} ({x['Rating']})" + for _, x in df_analyst_plot[df_analyst_plot.index == row.name] + .sort_values(by="Price Target", ascending=False) + .iterrows() + ] + ), + axis=1, + ), + hovertemplate="
%{customdata}", marker=dict( color=colors, line=dict(width=1, color="DarkSlateGrey"), @@ -183,6 +191,8 @@ def display_price_target_from_analysts( line=dict(color=theme.get_colors()[1]), ) + fig.update_layout(hovermode="x unified") + export_data( export, os.path.dirname(os.path.abspath(__file__)), diff --git a/openbb_terminal/stocks/options/chartexchange_model.py b/openbb_terminal/stocks/options/chartexchange_model.py index b6cb847d763..f1e4234ec18 100644 --- a/openbb_terminal/stocks/options/chartexchange_model.py +++ b/openbb_terminal/stocks/options/chartexchange_model.py @@ -35,7 +35,8 @@ def get_option_history( price : Union[str, Union[int, float]] Strike price for a specific option chain_id: str - OCC option symbol. Overwrites other inputs + Option symbol. Overwrites other inputs. ChartExchange uses the format: + `{TICKER}{YYYYMMDD}{C/P}{PRICE}`, where the price has no leading and trailing zeros. Returns ------- diff --git a/openbb_terminal/stocks/options/chartexchange_view.py b/openbb_terminal/stocks/options/chartexchange_view.py index 2667091d837..fbf818ad2ba 100644 --- a/openbb_terminal/stocks/options/chartexchange_view.py +++ b/openbb_terminal/stocks/options/chartexchange_view.py @@ -72,6 +72,7 @@ def display_raw( price: float = 90, limit: int = 10, chain_id: Optional[str] = None, + raw: bool = False, export: str = "", sheet_name: Optional[str] = None, external_axes: bool = False, @@ -109,22 +110,27 @@ def display_raw( option_type = "call" if call else "put" fig = plot_chart(df, option_type, symbol, price) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "hist", - df, - sheet_name, - fig, - ) - print_rich_table( - df, - headers=list(df.columns), - show_index=True, - index_name="Date", - title=f"{symbol.upper()} raw data", - export=bool(export), - limit=limit, - ) + if export: + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "hist", + df, + sheet_name, + fig, + ) + return None + + if raw: + print_rich_table( + df, + headers=list(df.columns), + show_index=True, + index_name="Date", + title="Historical Option Prices", + export=bool(export), + limit=limit, + ) + return None return fig.show(external=external_axes) diff --git a/openbb_terminal/stocks/options/intrinio_view.py b/openbb_terminal/stocks/options/intrinio_view.py index 1198ac85291..d7becd0ae05 100644 --- a/openbb_terminal/stocks/options/intrinio_view.py +++ b/openbb_terminal/stocks/options/intrinio_view.py @@ -71,6 +71,8 @@ def display_historical( headers=[x.title() for x in df_hist.columns], title="Historical Option Prices", export=bool(export), + show_index=True, + index_name="Date", ) df_hist.columns = [x.title() for x in df_hist.columns] diff --git a/openbb_terminal/stocks/options/options_controller.py b/openbb_terminal/stocks/options/options_controller.py index 677e0ae6be6..325399b6202 100644 --- a/openbb_terminal/stocks/options/options_controller.py +++ b/openbb_terminal/stocks/options/options_controller.py @@ -800,6 +800,7 @@ class OptionsController(BaseController): call=not ns_parser.put, price=ns_parser.strike, limit=ns_parser.limit, + raw=ns_parser.raw, export=ns_parser.export, chain_id=ns_parser.chain_id, sheet_name=" ".join(ns_parser.sheet_name) diff --git a/openbb_terminal/stocks/options/tradier_view.py b/openbb_terminal/stocks/options/tradier_view.py index dc76bb1225c..d4e5faac49d 100644 --- a/openbb_terminal/stocks/options/tradier_view.py +++ b/openbb_terminal/stocks/options/tradier_view.py @@ -72,6 +72,8 @@ def display_historical( headers=[x.title() for x in df_hist.columns], title="Historical Option Prices", export=bool(export), + show_index=True, + index_name="Date", ) op_type = ["call", "put"][put] diff --git a/openbb_terminal/terminal_controller.py b/openbb_terminal/terminal_controller.py index 16d7f18437f..7e3df6be9f3 100644 --- a/openbb_terminal/terminal_controller.py +++ b/openbb_terminal/terminal_controller.py @@ -18,6 +18,7 @@ from typing import Any, Dict, List, Optional import certifi import pandas as pd +import requests from prompt_toolkit import PromptSession from prompt_toolkit.completion import NestedCompleter from prompt_toolkit.formatted_text import HTML @@ -331,15 +332,15 @@ class TerminalController(BaseController): ) def parse_input(self, an_input: str) -> List: - """Overwrite the BaseController parse_input for `askobb` + """Overwrite the BaseController parse_input for `askobb` and 'exe' This will allow us to search for something like "P/E" ratio """ # Filtering out sorting parameters with forward slashes like P/E sort_filter = r"((\ -q |\ --question|\ ).*?(/))" - - custom_filters = [sort_filter] - + # Filter out urls + url = r"(exe (--url )?(https?://)?my\.openbb\.(dev|co)/u/.*/routine/.*)" + custom_filters = [sort_filter, url] return parse_and_split_input(an_input=an_input, custom_filters=custom_filters) def call_guess(self, other_args: List[str]) -> None: @@ -591,16 +592,6 @@ class TerminalController(BaseController): def call_intro(self, _): """Process intro command.""" webbrowser.open("https://docs.openbb.co/terminal/usage/basics") - # import json - - # intro: dict = json.load((Path(__file__).parent / "intro.json").open()) # type: ignore - - # for prompt in intro.get("prompts", []): - # console.print(panel.Panel(f"[purple]{prompt['header']}[/purple]")) - # console.print("".join(prompt["content"])) - # if input("") == "q": - # break - # console.print("\n") def call_exe(self, other_args: List[str]): """Process exe command.""" @@ -629,7 +620,9 @@ class TerminalController(BaseController): required="-h" not in other_args and "--help" not in other_args and "-e" not in other_args - and "--example" not in other_args, + and "--example" not in other_args + and "--url" not in other_args + and "my.openbb" not in other_args[0], type=str, nargs="+", ) @@ -648,10 +641,15 @@ class TerminalController(BaseController): action="store_true", default=False, ) + parser.add_argument( + "--url", help="URL to run openbb script from.", dest="url", type=str + ) if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "--file") + if other_args[0].startswith("my.") or other_args[0].startswith("http"): + other_args.insert(0, "--url") + else: + other_args.insert(0, "--file") ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: if ns_parser.example: routine_path = ( @@ -662,11 +660,37 @@ class TerminalController(BaseController): "to learn how to create your own script.[/info]\n" ) time.sleep(3) + elif ns_parser.url: + if not ns_parser.url.startswith( + "https" + ) and not ns_parser.url.startswith("http:"): + url = "https://" + ns_parser.url + else: + if ns_parser.url.startswith("http://"): + url = ns_parser.url.replace("http://", "https://") + else: + url = ns_parser.url + username = url.split("/")[-3] + script_name = url.split("/")[-1] + file_name = f"{username}_{script_name}.openbb" + final_url = f"{url}?raw=true" + response = requests.get(final_url, timeout=10) + if response.status_code != 200: + console.print("[red]Could not find the requested script.[/red]") + return + routine_text = response.json()["script"] + file_path = Path(get_current_user().preferences.USER_ROUTINES_DIRECTORY) + routine_path = file_path / file_name + with open(routine_path, "w") as file: + file.write(routine_text) + self.update_runtime_choices() + elif ns_parser.file: + file_path = " ".join(ns_parser.file) # type: ignore # if string is not in this format "default/file.openbb" then check for files in ROUTINE_FILES - file_path = " ".join(ns_parser.file) full_path = file_path - hub_routine = file_path.split("/") + hub_routine = file_path.split("/") # type: ignore + # Change with: my.openbb.co if hub_routine[0] == "default": routine_path = Path( self.ROUTINE_DEFAULT_FILES.get(hub_routine[1], full_path) @@ -676,7 +700,7 @@ class TerminalController(BaseController): self.ROUTINE_PERSONAL_FILES.get(hub_routine[1], full_path) ) else: - routine_path = Path(self.ROUTINE_FILES.get(file_path, full_path)) + routine_path = Path(self.ROUTINE_FILES.get(file_path, full_path)) # type: ignore else: return diff --git a/tests/openbb_terminal/stocks/options/test_chartexchange_view.py b/tests/openbb_terminal/stocks/options/test_chartexchange_view.py index 4ae4fceaeeb..322a665f129 100644 --- a/tests/openbb_terminal/stocks/options/test_chartexchange_view.py +++ b/tests/openbb_terminal/stocks/options/test_chartexchange_view.py @@ -23,12 +23,12 @@ def vcr_config(): @pytest.mark.vcr @pytest.mark.record_stdout @pytest.mark.parametrize( - "tab", + "raw", [True, False], ) -def test_display_raw(mocker, tab): +def test_display_raw(mocker, raw): # MOCK CHARTS - preferences = PreferencesModel(USE_TABULATE_DF=tab) + preferences = PreferencesModel(USE_TABULATE_DF=raw) mock_current_user = copy_user(preferences=preferences) mocker.patch( target="openbb_terminal.core.session.current_user.__current_user", @@ -39,9 +39,5 @@ def test_display_raw(mocker, tab): mocker.patch(target="openbb_terminal.stocks.options.chartexchange_view.plot_chart") chartexchange_view.display_raw( - export="", - symbol="GME", - expiry="2021-02-05", - call=True, - price=90, + export="", symbol="GME", expiry="2021-02-05", call=True, price=90, raw=raw ) diff --git a/tests/openbb_terminal/stocks/options/txt/test_chartexchange_view/test_display_raw[False].txt b/tests/openbb_terminal/stocks/options/txt/test_chartexchange_view/test_display_raw[False].txt index e094a6c806e..e69de29bb2d 100644 --- a/tests/openbb_terminal/stocks/options/txt/test_chartexchange_view/test_display_raw[False].txt +++ b/tests/openbb_terminal/stocks/options/txt/test_chartexchange_view/test_display_raw[False].txt @@ -1,12 +0,0 @@ - Open High Low Close Change Volume Open Interest Change Since -Date -2021-01-25 20.70 85.70 13.90 20.00 0.00000 1834.0 552.0 -0.99850 -2021-01-26 27.50 75.79 21.00 70.17 2.50850 1763.0 572.0 -0.99957 -2021-01-27 210.20 285.50 70.80 255.90 2.64686 309.0 461.0 -0.99988 -2021-01-28 225.40 385.55 62.75 130.65 -0.48945 49.0 450.0 -0.99977 -2021-01-29 232.15 296.20 183.10 183.10 0.40145 37.0 445.0 -0.99984 -2021-02-01 232.60 232.60 133.09 133.09 -0.27313 107.0 441.0 -0.99977 -2021-02-02 64.61 76.20 18.50 23.70 -0.82193 1991.0 555.0 -0.99873 -2021-02-03 40.45 40.45 13.50 14.26 -0.39831 2124.0 1074.0 -0.99790 -2021-02-04 13.70 14.00 0.75 0.75 -0.94741 5945.0 2386.0 -0.96000 -2021-02-05 0.57 14.79 0.01 0.03 -0.96000 6580.0 2017.0 0.00000