diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 6e98c849c87..b23a0070afc 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -49,7 +49,7 @@ jobs: if: github.event_name == 'pull_request' run: | # "Checking PR diff" - echo "terminal_files=$(git diff --name-only origin/${{ github.base_ref }}...${{ github.head_ref }} | grep 'openbb_terminal/.*\.py$' | xargs)" >> $GITHUB_ENV + echo "terminal_files=$(git diff --name-status origin/${{ github.base_ref }}...${{ github.head_ref }} | grep 'openbb_terminal/.*\.py$' | grep -v '^D' | cut -f 2- | xargs)" >> $GITHUB_ENV echo $terminal_files echo "platform_files=$(git diff --diff-filter=d --name-only origin/${{ github.base_ref }}...${{ github.head_ref }} | grep 'openbb_platform/.*\.py$' | grep -v 'openbb_platform/openbb/package' | grep -v 'integration' | grep -v 'tests' | xargs)" >> $GITHUB_ENV echo $platform_files diff --git a/build/pyinstaller/macOS_package_assets/OpenBB Terminal/OpenBB Terminal b/build/pyinstaller/macOS_package_assets/OpenBB Terminal/OpenBB Terminal index 404d8fa83b8..efe4bff9aec 100755 --- a/build/pyinstaller/macOS_package_assets/OpenBB Terminal/OpenBB Terminal +++ b/build/pyinstaller/macOS_package_assets/OpenBB Terminal/OpenBB Terminal @@ -16,8 +16,10 @@ echo " |___/ " BOOTUP_MESSAGE=" The boot up process might take a while. Please be patient... -Have you checked out Community Routines yet? You can access them through the OpenBB Hub! -Get started in less than 3 minutes: https://www.youtube.com/watch?v=UFh_Ku0fdtY +We will be deprecating the OpenBBTerminal in the future and this will be the final release. + + We have been working hard to build the OpenBB platform and we will be releasing a CLI version of the platform in the coming + months where you can customize your experience and explore all of the data available on the OpenBB platform. " echo "$BOOTUP_MESSAGE" diff --git a/openbb_terminal/alternative/alt_controller.py b/openbb_terminal/alternative/alt_controller.py index 019113dc053..27fcc4a02fe 100644 --- a/openbb_terminal/alternative/alt_controller.py +++ b/openbb_terminal/alternative/alt_controller.py @@ -23,7 +23,7 @@ class AlternativeDataController(BaseController): """Alternative Controller class""" CHOICES_COMMANDS: List[str] = ["hn"] - CHOICES_MENUS = ["covid", "oss", "realestate", "companieshouse"] + CHOICES_MENUS = ["oss", "realestate", "companieshouse"] PATH = "/alternative/" CHOICES_GENERATION = True @@ -39,7 +39,6 @@ class AlternativeDataController(BaseController): def print_help(self): """Print help""" mt = MenuText("alternative/") - mt.add_menu("covid") mt.add_menu("oss") mt.add_menu("realestate") mt.add_menu("companieshouse") @@ -47,13 +46,6 @@ class AlternativeDataController(BaseController): mt.add_cmd("hn") console.print(text=mt.menu_text, menu="Alternative") - @log_start_end(log=logger) - def call_covid(self, _): - """Process covid command""" - from openbb_terminal.alternative.covid.covid_controller import CovidController - - self.queue = self.load_class(CovidController, self.queue) - @log_start_end(log=logger) def call_oss(self, _): """Process oss command.""" diff --git a/openbb_terminal/alternative/covid/__init__.py b/openbb_terminal/alternative/covid/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openbb_terminal/alternative/covid/countries.txt b/openbb_terminal/alternative/covid/countries.txt deleted file mode 100644 index 770d1bdee47..00000000000 --- a/openbb_terminal/alternative/covid/countries.txt +++ /dev/null @@ -1,198 +0,0 @@ -Countries -Suriname -Qatar -Romania -Cabo Verde -Malawi -Micronesia -Belarus -Denmark -Fiji -Cambodia -Madagascar -Saint Vincent and the Grenadines -Paraguay -Eritrea -Barbados -Ethiopia -Laos -Zimbabwe -Lithuania -Comoros -Namibia -Spain -Papua New Guinea -Central African Republic -Honduras -Equatorial Guinea -Botswana -Canada -Italy -Jordan -Chad -Czechia -Uzbekistan -Azerbaijan -Poland -US -El Salvador -Ghana -Kiribati -Kuwait -Senegal -Holy See -Mauritius -Oman -Sierra Leone -Zambia -Nigeria -Kosovo -Brunei -China -Gabon -Seychelles -Guatemala -Somalia -Peru -Sao Tome and Principe -Sweden -Pakistan -Moldova -Togo -Niger -Ukraine -Sudan -Trinidad and Tobago -Netherlands -Philippines -Australia -Burkina Faso -Tajikistan -United Arab Emirates -Kyrgyzstan -Costa Rica -Portugal -Mozambique -Mauritania -Timor-Leste -Vietnam -Montenegro -Guyana -Mongolia -Bahrain -Cameroon -Palau -Jamaica -New Zealand -Iraq -Mali -Saint Kitts and Nevis -Brazil -Belgium -Maldives -Egypt -Panama -Rwanda -Israel -Russia -Nicaragua -Switzerland -Kazakhstan -Guinea-Bissau -Albania -Thailand -Cuba -Uruguay -Liechtenstein -Bosnia and Herzegovina -Haiti -Andorra -Taiwan* -Dominica -Congo (Kinshasa) -Ecuador -Malta -Vanuatu -Dominican Republic -Lesotho -Morocco -West Bank and Gaza -Lebanon -South Sudan -Armenia -Afghanistan -Grenada -Indonesia -Sri Lanka -Iran -Bolivia -Luxembourg -North Macedonia -Cyprus -Iceland -Serbia -Greece -United Kingdom -Georgia -Saint Lucia -Venezuela -Austria -Tonga -Saudi Arabia -Argentina -Summer Olympics 2020 -Angola -Algeria -Latvia -Yemen -Singapore -Turkey -Belize -Burundi -Ireland -South Africa -Bulgaria -India -Diamond Princess -Cote d'Ivoire -Libya -Slovenia -Benin -Djibouti -Congo (Brazzaville) -Tunisia -Nepal -Bahamas -Bhutan -Colombia -Hungary -Malaysia -Germany -Tanzania -Samoa -San Marino -Syria -Liberia -Korea, South -Uganda -Gambia -Marshall Islands -Kenya -Finland -Bangladesh -Eswatini -Slovakia -Burma -MS Zaandam -Japan -Guinea -Monaco -Mexico -Solomon Islands -Estonia -Chile -Croatia -Antigua and Barbuda -Norway -France -All \ No newline at end of file diff --git a/openbb_terminal/alternative/covid/covid_controller.py b/openbb_terminal/alternative/covid/covid_controller.py deleted file mode 100644 index e2d28ae6e98..00000000000 --- a/openbb_terminal/alternative/covid/covid_controller.py +++ /dev/null @@ -1,271 +0,0 @@ -"""COVID Controller Module""" - -__docformat__ = "numpy" - -import argparse -import logging -import pathlib -from typing import List, Optional - -import pandas as pd - -from openbb_terminal.alternative.covid import covid_view -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import ( - EXPORT_BOTH_RAW_DATA_AND_FIGURES, - 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 MenuText, console - -logger = logging.getLogger(__name__) - -country_file = pathlib.Path(__file__).parent.joinpath("countries.txt") - - -class CovidController(BaseController): - """Covid Controller class""" - - CHOICES_COMMANDS = ["country", "ov", "deaths", "cases", "rates", "slopes"] - PATH = "/alternative/covid/" - CHOICES_GENERATION = True - - def __init__(self, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.country = "US" - countries_df = pd.read_csv(country_file, sep="|", index_col=None) - countries_list = countries_df["Countries"].to_list() - self.COUNTRY_LIST = [x.lower().replace(" ", "_") for x in countries_list] - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - - self.completer = NestedCompleter.from_nested_dict(choices) - - def print_help(self): - """Print help""" - mt = MenuText("alternative/covid/") - mt.add_cmd("slopes") - mt.add_cmd("country") - mt.add_raw("\n") - mt.add_param("_country", self.country) - mt.add_raw("\n") - mt.add_cmd("ov") - mt.add_cmd("deaths") - mt.add_cmd("cases") - mt.add_cmd("rates") - console.print(text=mt.menu_text, menu="Alternative - COVID") - - def custom_reset(self): - """Class specific component of reset command""" - return ["alternative", "covid", f"country {self.country}"] - - @log_start_end(log=logger) - def call_country(self, other_args: List[str]): - """Process country command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="country", - description="Select a country to look at data for.", - ) - parser.add_argument( - "-c", - "--country", - type=str.lower, - choices=self.COUNTRY_LIST, - metavar="country_name", - dest="country", - help="Country to get data for.", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-c") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if ns_parser.country: - if ns_parser.country not in self.COUNTRY_LIST: - logger.error("%s not a valid selection", ns_parser.country) - console.print( - f"[red]{ns_parser.country} not a valid selection.[/red]\n" - ) - return - country = ns_parser.country.title().replace("_", " ") - if country == "Us": - country = "US" - self.country = country - console.print(f"[cyan]{country}[/cyan] loaded\n") - else: - logger.error("No country provided") - console.print("[red]Please input a country.[/red]\n") - - @log_start_end(log=logger) - def call_ov(self, other_args: List[str]): - """Process ov command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ov", - description="Show historical cases and deaths by country.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser: - covid_view.display_covid_ov( - self.country, - raw=ns_parser.raw, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_rates(self, other_args: List[str]): - """Process hist command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="rates", - description="Show historical death/cases rates for a country.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser: - covid_view.display_covid_stat( - self.country, - stat="rates", - raw=ns_parser.raw, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_deaths(self, other_args: List[str]): - """Process deaths command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="deaths", - description="Show historical deaths by country.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser: - covid_view.display_covid_stat( - self.country, - stat="deaths", - raw=ns_parser.raw, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_cases(self, other_args: List[str]): - """Process cases command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="cases", - description="Show historical cases for country.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES, - raw=True, - limit=10, - ) - if ns_parser: - covid_view.display_covid_stat( - self.country, - stat="cases", - raw=ns_parser.raw, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_slopes(self, other_args: List[str]): - """Process cases command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="slopes", - description="Show countries with highest slopes.", - ) - parser.add_argument( - "-d", - "--days", - type=check_positive, - help="Number of days back to look", - dest="days", - default=30, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - parser.add_argument( - "-t", - "--threshold", - default=10000, - dest="threshold", - help="Threshold for total cases over period", - type=check_positive, - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED, limit=10 - ) - if ns_parser: - if ns_parser.days < 2: - console.print("[red]Days must be greater than 1[/red]") - return - covid_view.display_case_slopes( - days_back=ns_parser.days, - limit=ns_parser.limit, - ascend=ns_parser.reverse, - threshold=ns_parser.threshold, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/alternative/covid/covid_model.py b/openbb_terminal/alternative/covid/covid_model.py deleted file mode 100644 index f87e87ea464..00000000000 --- a/openbb_terminal/alternative/covid/covid_model.py +++ /dev/null @@ -1,199 +0,0 @@ -"""Covid Model""" - -__docformat__ = "numpy" - -import logging -import warnings - -import numpy as np -import pandas as pd - -from openbb_terminal.decorators import log_start_end -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - -global_cases_time_series = ( - "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_" - "covid_19_time_series/time_series_covid19_confirmed_global.csv" -) -global_deaths_time_series = ( - "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_" - "covid_19_time_series/time_series_covid19_deaths_global.csv" -) - - -@log_start_end(log=logger) -def get_global_cases(country: str) -> pd.DataFrame: - """Get historical cases for given country. - - Parameters - ---------- - country: str - Country to search for - - Returns - ------- - pd.DataFrame - Dataframe of historical cases - """ - cases = pd.read_csv(global_cases_time_series) - cases = cases.rename(columns={"Country/Region": "Country"}) - cases = ( - cases.drop(columns=["Province/State", "Lat", "Long"]) - .groupby("Country") - .agg("sum") - .T - ) - cases.index = pd.to_datetime(cases.index) - if country not in cases: - console.print("[red]The selection `{country}` is not a valid option.[/red]\n") - return pd.DataFrame() - cases = pd.DataFrame(cases[country]).diff().dropna() - if cases.shape[1] > 1: - return pd.DataFrame(cases.sum(axis=1)) - return cases - - -@log_start_end(log=logger) -def get_global_deaths(country: str) -> pd.DataFrame: - """Get historical deaths for given country. - - Parameters - ---------- - country: str - Country to search for - - Returns - ------- - pd.DataFrame - Dataframe of historical deaths - """ - deaths = pd.read_csv(global_deaths_time_series) - deaths = deaths.rename(columns={"Country/Region": "Country"}) - deaths = ( - deaths.drop(columns=["Province/State", "Lat", "Long"]) - .groupby("Country") - .agg("sum") - .T - ) - deaths.index = pd.to_datetime(deaths.index) - if country not in deaths: - console.print("[red]The selection `{country}` is not a valid option.[/red]\n") - return pd.DataFrame() - deaths = pd.DataFrame(deaths[country]).diff().dropna() - if deaths.shape[1] > 1: - return pd.DataFrame(deaths.sum(axis=1)) - return deaths - - -@log_start_end(log=logger) -def get_covid_ov( - country: str, -) -> pd.DataFrame: - """Get historical cases and deaths by country. - - Parameters - ---------- - country: str - Country to get data for - - Returns - ------- - pd.DataFrame - Dataframe of historical cases and deaths - """ - if country.lower() == "us": - country = "US" - cases = get_global_cases(country) - deaths = get_global_deaths(country) - if cases.empty or deaths.empty: - return pd.DataFrame() - data = pd.concat([cases, deaths], axis=1) - data.columns = ["Cases", "Deaths"] - data.index = pd.to_datetime(data.index, format="%Y-%m-%d") - return data - - -@log_start_end(log=logger) -def get_covid_stat( - country: str, - stat: str = "cases", -) -> pd.DataFrame: - """Show historical cases and deaths by country. - - Parameters - ---------- - country: str - Country to get data for - stat: str - Statistic to get. Either "cases", "deaths" or "rates" - - Returns - ------- - pd.DataFrame - Dataframe of data for given country and statistic - """ - if stat == "cases": - data = get_global_cases(country) - elif stat == "deaths": - data = get_global_deaths(country) - elif stat == "rates": - cases = get_global_cases(country) - deaths = get_global_deaths(country) - data = (deaths / cases).fillna(0) * 100 - else: - console.print("Invalid stat selected.\n") - return pd.DataFrame() - data.index = [x.strftime("%Y-%m-%d") for x in data.index] - return data - - -@log_start_end(log=logger) -def get_case_slopes( - days_back: int = 30, - threshold: int = 10000, - ascend: bool = False, -) -> pd.DataFrame: - """Load cases and find slope over period. - - Parameters - ---------- - days_back: int - Number of historical days to consider - threshold: int - Threshold for total number of cases - ascend: bool - Flag to sort in ascending order - - Returns - ------- - pd.DataFrame - Dataframe containing slopes - """ - # Ignore the pandas warning for setting a slace with a value - warnings.filterwarnings("ignore") - data = pd.read_csv(global_cases_time_series) - data = data.rename(columns={"Country/Region": "Country"}) - data = ( - ( - data.drop(columns=["Province/State", "Lat", "Long"]) - .groupby("Country") - .agg("sum") - ) - .diff() - .dropna() - ) - hist = data.iloc[:, -days_back:] - hist["Sum"] = hist.sum(axis=1) - hist = hist[hist.Sum > threshold].drop(columns="Sum") - hist["Slope"] = hist.apply( - lambda x: np.polyfit(np.arange(days_back), x, 1)[0], axis=1 - ) - hist_slope = pd.DataFrame(hist["Slope"]) - - if ascend: - hist_slope.sort_values(by="Slope", ascending=ascend, inplace=True) - else: - hist_slope.sort_values(by="Slope", ascending=ascend, inplace=True) - return hist_slope diff --git a/openbb_terminal/alternative/covid/covid_view.py b/openbb_terminal/alternative/covid/covid_view.py deleted file mode 100644 index bb07dde7bda..00000000000 --- a/openbb_terminal/alternative/covid/covid_view.py +++ /dev/null @@ -1,322 +0,0 @@ -"""Covid View""" - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.alternative.covid import covid_model -from openbb_terminal.decorators import log_start_end -from openbb_terminal.helper_funcs import export_data, print_rich_table -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def plot_covid_ov( - country: str, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots historical cases and deaths by country. - - Parameters - ---------- - country: str - Country to plot - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - cases = covid_model.get_global_cases(country) / 1_000 - deaths = covid_model.get_global_deaths(country) - if cases.empty or deaths.empty: - return None - ov = pd.concat([cases, deaths], axis=1) - ov.columns = ["Cases", "Deaths"] - - fig = OpenBBFigure.create_subplots( - specs=[[{"secondary_y": True}]], horizontal_spacing=0.0 - ) - - fig.add_scatter( - x=cases.index, - y=cases[country].values, - name="Cases", - opacity=0.2, - line_color=theme.up_color, - showlegend=False, - secondary_y=False, - ) - fig.add_scatter( - x=cases.index, - y=cases[country].rolling(7).mean().values, - name="Cases (7d avg)", - line_color=theme.up_color, - hovertemplate="%{y:.2f}", - secondary_y=False, - ) - fig.add_scatter( - x=deaths.index, - y=deaths[country].values, - name="Deaths", - opacity=0.2, - yaxis="y2", - line_color=theme.down_color, - showlegend=False, - secondary_y=True, - ) - fig.add_scatter( - x=deaths.index, - y=deaths[country].rolling(7).mean().values, - name="Deaths (7d avg)", - yaxis="y2", - line_color=theme.down_color, - hovertemplate="%{y:.2f}", - secondary_y=True, - ) - fig.update_layout( - margin=dict(t=20), - title=f"Overview for {country.upper()}", - xaxis_title="Date", - yaxis=dict( - title="Cases [1k]", - side="left", - ), - yaxis2=dict( - title="Deaths", - side="right", - overlaying="y", - showgrid=False, - ), - hovermode="x unified", - ) - - return fig.show(external=external_axes, cmd_xshift=-10) - - -def plot_covid_stat( - country: str, - stat: str = "cases", - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Plots historical stat by country. - - Parameters - ---------- - country: str - Country to plot - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - - fig = OpenBBFigure(title=f"{country} COVID {stat}", xaxis_title="Date") - - if stat == "cases": - data = covid_model.get_global_cases(country) / 1_000 - fig.set_yaxis_title(f"{stat.title()} [1k]") - color = theme.up_color - elif stat == "deaths": - data = covid_model.get_global_deaths(country) - fig.set_yaxis_title(stat.title()) - color = theme.down_color - elif stat == "rates": - cases = covid_model.get_global_cases(country) - deaths = covid_model.get_global_deaths(country) - data = (deaths / cases).fillna(0) * 100 - color = theme.get_colors(reverse=True)[0] - fig.set_yaxis_title(f"{stat.title()} (Deaths/Cases)") - else: - return console.print("Invalid stat selected.\n") - - fig.add_scatter( - x=data.index, - y=data[country].values, - name=stat.title(), - opacity=0.2, - line_color=color, - showlegend=False, - ) - fig.add_scatter( - x=data.index, - y=data[country].rolling(7).mean().values, - name=f"{stat.title()} (7d avg)", - line_color=color, - hovertemplate="%{y:.2f}", - ) - - fig.update_layout(hovermode="x unified") - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -def display_covid_ov( - country: str, - raw: bool = False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - plot: bool = True, - external_axes: bool = False, -) -> Union[OpenBBFigure, None]: - """Prints table showing historical cases and deaths by country. - - Parameters - ---------- - country: str - Country to get data for - raw: bool - Flag to display raw data - limit: int - Number of raw data to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - plot: bool - Flag to display historical plot - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - fig = OpenBBFigure() - - if country.lower() == "us": - country = "US" - if plot or fig.is_image_export(export): - fig = plot_covid_ov(country, external_axes=True) - if raw: - data = covid_model.get_covid_ov(country) - print_rich_table( - data, - headers=[x.title() for x in data.columns], - show_index=True, - index_name="Date", - title=f"[bold]{country} COVID Numbers[/bold]", - export=bool(export), - limit=limit, - ) - - if export: - data = covid_model.get_covid_ov(country) - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "ov", - data, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) - - -@log_start_end(log=logger) -def display_covid_stat( - country: str, - stat: str = "cases", - raw: bool = False, - limit: int = 10, - export: str = "", - sheet_name: Optional[str] = None, - plot: bool = True, -) -> Union[OpenBBFigure, None]: - """Prints table showing historical cases and deaths by country. - - Parameters - ---------- - country: str - Country to get data for - stat: str - Statistic to get. Either "cases", "deaths" or "rates" - raw: bool - Flag to display raw data - limit: int - Number of raw data to show - sheet_name: str - Optionally specify the name of the sheet the data is exported to. - export: str - Format to export data - plot : bool - Flag to plot data - """ - fig = OpenBBFigure() - data = covid_model.get_covid_stat(country, stat) - - if plot or fig.is_image_export(export): - fig = plot_covid_stat(country, stat, external_axes=True) # type: ignore - - if raw: - print_rich_table( - data, - headers=[stat.title()], - show_index=True, - index_name="Date", - title=f"[bold]{country} COVID {stat}[/bold]", - export=bool(export), - limit=limit, - ) - if export: - data["date"] = data.index - data = data.reset_index(drop=True) - # make sure date is first column in export - cols = data.columns.tolist() - cols = cols[-1:] + cols[:-1] - data = data[cols] - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - stat, - data, - sheet_name, - fig, - ) - - return fig.show(external=raw) - - -@log_start_end(log=logger) -def display_case_slopes( - days_back: int = 30, - limit: int = 10, - threshold: int = 10000, - ascend: bool = False, - export: str = "", - sheet_name: Optional[str] = None, -) -> None: - """Prints table showing countries with the highest case slopes. - - Parameters - ---------- - days_back: int - Number of historical days to get slope for - limit: int - Number to show in table - ascend: bool - Flag to sort in ascending order - threshold: int - Threshold for total cases over period - export : str - Format to export data - """ - data = covid_model.get_case_slopes(days_back, threshold, ascend) - - print_rich_table( - data, - show_index=True, - index_name="Country", - title=f"[bold]{('Highest','Lowest')[ascend]} Sloping Cases[/bold] (Cases/Day)", - export=bool(export), - limit=limit, - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - f"slopes_{days_back}day", - data, - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/defi/defi_controller.py b/openbb_terminal/cryptocurrency/defi/defi_controller.py index 7889212d548..160b7599db5 100644 --- a/openbb_terminal/cryptocurrency/defi/defi_controller.py +++ b/openbb_terminal/cryptocurrency/defi/defi_controller.py @@ -12,13 +12,9 @@ from openbb_terminal.core.session.current_user import get_current_user from openbb_terminal.cryptocurrency.defi import ( coindix_model, coindix_view, - cryptosaurio_view, llama_model, llama_view, - smartstake_view, substack_view, - terramoney_fcd_model, - terramoney_fcd_view, ) from openbb_terminal.custom_prompt_toolkit import NestedCompleter from openbb_terminal.decorators import log_start_end @@ -26,7 +22,6 @@ from openbb_terminal.helper_funcs import ( EXPORT_BOTH_RAW_DATA_AND_FIGURES, EXPORT_ONLY_RAW_DATA_ALLOWED, check_positive, - check_terra_address_format, ) from openbb_terminal.menu import session from openbb_terminal.parent_classes import BaseController @@ -45,12 +40,6 @@ class DefiController(BaseController): "dtvl", "newsletter", "vaults", - "sinfo", - "validators", - "gacc", - "sreturn", - "lcsc", - "anchor", ] PATH = "/crypto/defi/" @@ -74,274 +63,8 @@ class DefiController(BaseController): mt.add_cmd("gdapps") mt.add_cmd("stvl") mt.add_cmd("dtvl") - mt.add_cmd("sinfo") - mt.add_cmd("validators") - mt.add_cmd("gacc") - mt.add_cmd("sreturn") - mt.add_cmd("lcsc") - mt.add_cmd("anchor") console.print(text=mt.menu_text, menu="Cryptocurrency - Decentralized Finance") - @log_start_end(log=logger) - def call_anchor(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="anchor", - description=""" - Displays anchor protocol earnings data of a certain terra address - --transactions flag can be passed to show history of previous transactions - [Source: https://cryptosaurio.com/] - """, - ) - parser.add_argument( - "--address", - dest="address", - type=check_terra_address_format, - help="Terra address. Valid terra addresses start with 'terra'", - required="-h" not in other_args, - ) - parser.add_argument( - "--transactions", - action="store_true", - help="Flag to show transactions history in anchor earn", - dest="transactions", - default=False, - ) - - if other_args and other_args[0][0] != "-": - other_args.insert(0, "--address") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - cryptosaurio_view.display_anchor_data( - show_transactions=ns_parser.transactions, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - address=ns_parser.address, - ) - - @log_start_end(log=logger) - def call_sinfo(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sinfo", - description=""" - Displays staking info of a certain terra address. - [Source: https://fcd.terra.dev/swagger] - """, - ) - parser.add_argument( - "-a", - "--address", - dest="address", - type=check_terra_address_format, - help="Terra address. Valid terra addresses start with 'terra'", - required="-h" not in other_args, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of delegations", - default=10, - ) - - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-a") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - terramoney_fcd_view.display_account_staking_info( - address=ns_parser.address, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_validators(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="validators", - description=""" - Displays information about terra validators. - [Source: https://fcd.terra.dev/swagger] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of validators to show", - default=10, - ) - parser.add_argument( - "-s", - "--sort", - dest="sortby", - type=str, - help="Sort by given column. Default: votingPower", - default="votingPower", - choices=terramoney_fcd_model.VALIDATORS_COLUMNS, - ) - parser.add_argument( - "-r", - "--reverse", - action="store_true", - dest="reverse", - default=False, - help=( - "Data is sorted in descending order by default. " - "Reverse flag will sort it in an ascending way. " - "Only works when raw data is displayed." - ), - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - terramoney_fcd_view.display_validators( - sortby=ns_parser.sortby, - ascend=ns_parser.reverse, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gacc(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="gacc", - description=""" - Displays terra blockchain account growth history. - [Source: https://fcd.terra.dev/swagger] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of days to show", - default=90, - ) - parser.add_argument( - "--cumulative", - action="store_false", - help="Show cumulative or discrete values. For active accounts only discrete value are available", - dest="cumulative", - default=True, - ) - parser.add_argument( - "-k", - "--kind", - dest="kind", - type=str, - help="Total account count or active account count. Default: total", - default="total", - choices=["active", "total"], - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - terramoney_fcd_view.display_account_growth( - kind=ns_parser.kind, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - cumulative=ns_parser.cumulative, - limit=ns_parser.limit, - ) - - @log_start_end(log=logger) - def call_sratio(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sratio", - description=""" - Displays terra blockchain staking ratio history. - [Source: https://fcd.terra.dev/swagger] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of days to show", - default=90, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - terramoney_fcd_view.display_staking_ratio_history( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - - @log_start_end(log=logger) - def call_sreturn(self, other_args: List[str]): - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sreturn", - description=""" - Displays terra blockchain staking returns history. - [Source: https://fcd.terra.dev/swagger] - """, - ) - parser.add_argument( - "-l", - "--limit", - dest="limit", - type=check_positive, - help="Number of days to show", - default=90, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - terramoney_fcd_view.display_staking_returns_history( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - limit=ns_parser.limit, - ) - @log_start_end(log=logger) def call_gdapps(self, other_args: List[str]): """Process gdapps command""" @@ -639,49 +362,3 @@ class DefiController(BaseController): " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None ), ) - - def call_lcsc(self, other_args: List[str]): - """Process lcsc command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="lcsc", - description=""" - Display Luna circulating supply changes stats. - [Source: Smartstake.io] - - Follow these steps to get the key token: - 1. Head to https://terra.smartstake.io/ - 2. Right click on your browser and choose Inspect - 3. Select Network tab (by clicking on the expand button next to Source tab) - 4. Go to Fetch/XHR tab, and refresh the page - 5. Get the option looks similar to the following: `listData?type=history&dayCount=30` - 6. Extract the key and token out of the URL - - """, - ) - - parser.add_argument( - "-d", - "--days", - dest="days", - type=check_positive, - help="Number of days to display. Default: 30 days", - default=30, - choices=range(1, 1000), - metavar="DAYS", - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, limit=5 - ) - - if ns_parser: - smartstake_view.display_luna_circ_supply_change( - days=ns_parser.days, - limit=ns_parser.limit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py b/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py index 147dfdc708a..133acc467d3 100644 --- a/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py +++ b/openbb_terminal/cryptocurrency/due_diligence/dd_controller.py @@ -17,8 +17,6 @@ from openbb_terminal.cryptocurrency.due_diligence import ( ccxt_view, coinbase_model, coinbase_view, - coinglass_model, - coinglass_view, coinpaprika_view, cryptopanic_view, glassnode_model, @@ -27,8 +25,6 @@ from openbb_terminal.cryptocurrency.due_diligence import ( messari_view, pycoingecko_view, santiment_view, - tokenterminal_model, - tokenterminal_view, ) from openbb_terminal.cryptocurrency.overview import cryptopanic_model from openbb_terminal.custom_prompt_toolkit import NestedCompleter @@ -36,7 +32,6 @@ from openbb_terminal.decorators import log_start_end from openbb_terminal.helper_funcs import ( EXPORT_BOTH_RAW_DATA_AND_FIGURES, EXPORT_ONLY_RAW_DATA_ALLOWED, - check_non_negative, check_positive, valid_date, ) @@ -60,17 +55,7 @@ def check_cg_id(symbol: str): class DueDiligenceController(CryptoBaseController): """Due Diligence Controller class""" - CHOICES_COMMANDS = [ - "load", - "fundrate", - "oi", - "liquidations", - "active", - "change", - "nonzero", - "eb", - "desc", - ] + CHOICES_COMMANDS = ["load", "active", "change", "nonzero", "eb"] SPECIFIC_CHOICES = { "cp": [ @@ -148,9 +133,6 @@ class DueDiligenceController(CryptoBaseController): {c: {} for c in glassnode_model.GLASSNODE_SUPPORTED_EXCHANGES} ) choices["mt"].update({c: None for c in self.messari_timeseries}) - choices["desc"].update( - {c: None for c in tokenterminal_model.get_project_ids()} - ) self.completer = NestedCompleter.from_nested_dict(choices) @@ -180,9 +162,6 @@ class DueDiligenceController(CryptoBaseController): mt.add_cmd("mkt") mt.add_cmd("ex") mt.add_cmd("balance") - mt.add_cmd("oi") - mt.add_cmd("fundrate") - mt.add_cmd("liquidations") mt.add_cmd("eb") mt.add_cmd("trades") mt.add_cmd("ob") @@ -504,98 +483,6 @@ class DueDiligenceController(CryptoBaseController): else: console.print(f"[red]{self.symbol} not supported on Glassnode.[/red]") - @log_start_end(log=logger) - def call_oi(self, other_args): - """Process oi command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="oi", - description=""" - Displays open interest by exchange for a certain asset - [Source: https://coinglass.github.io/API-Reference/] - """, - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=check_non_negative, - help="Frequency interval. Default: 0", - default=0, - choices=coinglass_model.INTERVALS, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - assert isinstance(self.symbol, str) # noqa: S101 - coinglass_view.display_open_interest( - symbol=self.symbol.upper(), - interval=ns_parser.interval, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_liquidations(self, other_args): - """Process liquidations command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="liquidations", - description=""" - Displays liquidations data for the loaded crypto asset - [Source: https://coinglass.github.io/API-Reference/#liquidation-chart] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - assert isinstance(self.symbol, str) # noqa: S101 - coinglass_view.display_liquidations( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_fundrate(self, other_args): - """Process fundrate command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="fundrate", - description=""" - Displays funding rate by exchange for a certain asset - [Source: https://coinglass.github.io/API-Reference/] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - assert isinstance(self.symbol, str) # noqa: S101 - coinglass_view.display_funding_rate( - symbol=self.symbol.upper(), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - @log_start_end(log=logger) def call_info(self, other_args): """Process info command""" @@ -1855,36 +1742,3 @@ class DueDiligenceController(CryptoBaseController): filter_=ns_parser.filter, region=ns_parser.region, ) - - @log_start_end(log=logger) - def call_desc(self, other_args): - """Process desc command""" - parser = argparse.ArgumentParser( - prog="desc", - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - description="""Display project description [Source: Token Terminal]""", - ) - parser.add_argument( - "-p", - "--project", - choices=tokenterminal_model.get_project_ids(), - required="-h" not in other_args, - dest="project", - help="Choose project of interest", - ) - - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser and ns_parser.project in tokenterminal_model.get_project_ids(): - tokenterminal_view.display_description( - project=ns_parser.project, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) diff --git a/openbb_terminal/cryptocurrency/due_diligence/tokenterminal_model.py b/openbb_terminal/cryptocurrency/due_diligence/tokenterminal_model.py deleted file mode 100644 index 5d6d5c7ed80..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/tokenterminal_model.py +++ /dev/null @@ -1,257 +0,0 @@ -"""Token Terminal Model""" - -import logging -from typing import Dict, List - -import pandas as pd -from tokenterminal import TokenTerminal - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import log_start_end - -logger = logging.getLogger(__name__) - -token_terminal = TokenTerminal( - key=get_current_user().credentials.API_TOKEN_TERMINAL_KEY -) - - -# Fetch all data for projects' -try: - PROJECTS_DATA = token_terminal.get_all_projects() - - # This is to catch the invalid header and raise Exception to load the autocomplete tickers - if PROJECTS_DATA["message"] == "Invalid authorization header": - raise Exception -except Exception: - PROJECTS_DATA = [ - "0x", - "1inch", - "88mph", - "aave", - "abracadabra-money", - "alchemist", - "alchemix-finance", - "algorand", - "alpha-finance", - "arweave", - "autofarm", - "avalanche", - "axie-infinity", - "balancer", - "bancor", - "barnbridge", - "basket-dao", - "benqi", - "binance-smart-chain", - "bitcoin", - "cap", - "cardano", - "centrifuge", - "clipper", - "compound", - "convex-finance", - "cosmos", - "cryptex", - "curve", - "decentral-games", - "decred", - "dforce", - "dhedge", - "dodo", - "dogecoin", - "dydx", - "ellipsis-finance", - "elrond", - "enzyme-finance", - "erasure-protocol", - "ethereum", - "ethereum-name-service", - "euler", - "fantom", - "fei-protocol", - "filecoin", - "futureswap", - "gmx", - "goldfinch", - "harvest-finance", - "helium", - "hurricaneswap", - "idle-finance", - "index-cooperative", - "instadapp", - "integral-protocol", - "karura", - "keeperdao", - "keep-network", - "kusama", - "kyber", - "lido-finance", - "liquity", - "litecoin", - "livepeer", - "looksrare", - "loopring", - "maiar", - "makerdao", - "maple-finance", - "mcdex", - "metamask", - "mstable", - "near-protocol", - "nexus-mutual", - "nftx", - "notional-finance", - "opensea", - "optimism", - "osmosis", - "pancakeswap", - "pangolin", - "perpetual-protocol", - "piedao", - "pocket-network", - "polkadot", - "polygon", - "polymarket", - "pooltogether", - "powerpool", - "quickswap", - "rarible", - "rari-capital", - "reflexer", - "ren", - "ribbon-finance", - "rocket-pool", - "saddle-finance", - "set-protocol", - "solana", - "solend", - "spookyswap", - "stake-dao", - "stellar", - "sushiswap", - "synthetix", - "terra", - "tezos", - "the-graph", - "thorchain", - "tokemak", - "tokenlon", - "tornado-cash", - "trader-joe", - "uma", - "uniswap", - "unit-protocol", - "venus", - "vesper-finance", - "volmex", - "wakaswap", - "yearn-finance", - "yield-guild-games", - "yield-yak", - "zcash", - "zora", - ] - -METRICS = [ - "twitter_followers", - "gmv_annualized", - "market_cap", - "take_rate", - "revenue", - "revenue_protocol", - "tvl", - "pe", - "pe_circulating", - "ps", - "ps_circulating", -] - - -@log_start_end(log=logger) -def get_possible_metrics() -> List[str]: - """This function returns the available metrics. - - Returns - ------- - List[str] - A list with the available metrics values. - """ - return METRICS - - -@log_start_end(log=logger) -def get_project_ids() -> List[str]: - """This function returns the available project ids. - - Returns - ------- - List[str] - A list with the all the project IDs - """ - - # check if its a dict - which would be a successful api call - - # might need to add error checking here later if they messed up the API key though. - return ["none"] - - -@log_start_end(log=logger) -def get_fundamental_metric_from_project( - metric: str, - project: str, -) -> pd.Series: - """Get fundamental metrics from a single project [Source: Token Terminal] - - Parameters - ---------- - metric : str - The metric of interest. See `get_possible_metrics()` for available metrics. - project : str - The project of interest. See `get_possible_projects()` for available categories. - - Returns - ------- - pandas.Series: - Date, Metric value - """ - project_metrics = token_terminal.get_historical_metrics(project) - - metric_date = list() - metric_value = list() - for proj in project_metrics: - if metric in proj: - val = proj[metric] - if isinstance(val, (float, int)): - metric_value.append(val) - metric_date.append(proj["datetime"]) - else: - return pd.Series(dtype="float64") - - if metric_value: - return pd.Series(index=pd.to_datetime(metric_date), data=metric_value)[::-1] - - return pd.Series(dtype="float64") - - -@log_start_end(log=logger) -def get_description( - project: str, -) -> Dict: - """Get description from a single project [Source: Token Terminal] - - Parameters - ---------- - project : str - The project of interest. See `get_possible_projects()` for available categories. - - Returns - ------- - Dict[str, Any] - Description of the project with fields: 'how', 'who', 'what', 'funding', - 'competition', 'business_model', 'github_contributors' - """ - for p in PROJECTS_DATA: - if p["project_id"] == project: - return p["description"] - - return Dict() diff --git a/openbb_terminal/cryptocurrency/due_diligence/tokenterminal_view.py b/openbb_terminal/cryptocurrency/due_diligence/tokenterminal_view.py deleted file mode 100644 index 5a826dd0611..00000000000 --- a/openbb_terminal/cryptocurrency/due_diligence/tokenterminal_view.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Token Terminal View""" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure -from openbb_terminal.cryptocurrency.due_diligence.tokenterminal_model import ( - METRICS, - get_description, - get_fundamental_metric_from_project, - get_project_ids, -) -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import export_data -from openbb_terminal.rich_config import console - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -@check_api_key(["API_TOKEN_TERMINAL_KEY"]) -def display_fundamental_metric_from_project_over_time( - metric: str, - project: str, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plots fundamental metric from a project over time [Source: Token Terminal] - - Parameters - ---------- - metric : str - The metric of interest. See `get_possible_metrics()` for available metrics. - project : str - The project of interest. See `get_project_ids()` for available categories. - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if project not in get_project_ids(): - return console.print( - f"[red]'{project}' project selected is invalid. See available projects with def get_project_ids()[/red]\n" - ) - - if metric not in METRICS: - return console.print( - f"[red]'{metric}' metric selected is invalid. See available metrics with get_possible_metrics()[/red]\n" - ) - - metric_over_time = get_fundamental_metric_from_project(metric, project) - - if metric_over_time.empty: - return console.print("[red]No data found.[/red]\n") - - fig = OpenBBFigure( - xaxis_title="Date", yaxis_title=f"{metric.replace('_', ' ').capitalize()} [USD]" - ) - fig.set_title( - f"{project.replace('_', ' ').capitalize()} {metric.replace('_', ' ').capitalize()}" - ) - - fig.add_scatter( - x=metric_over_time.index, - y=metric_over_time.values, - name=f"{project.replace('_', ' ').title()}", - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "funot", - metric_over_time, - sheet_name, - fig, - ) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_TOKEN_TERMINAL_KEY"]) -def display_description( - project: str, export: str = "", sheet_name: Optional[str] = None -): - """Prints description from a project [Source: Token Terminal] - - Parameters - ---------- - project : str - The project of interest. See `get_project_ids()` for available categories. - export : str - Export dataframe data to csv,json,xlsx file - """ - if project not in get_project_ids(): - console.print( - f"[red]'{project}' project selected is invalid. See available projects with def get_project_ids()[/red]\n" - ) - return - - description = get_description(project) - - for k in description: - console.print(f"{k.replace('_', ' ').upper()}\n {description[k]}\n") - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "desc", - pd.DataFrame(description.values(), index=description.keys()), - sheet_name, - ) diff --git a/openbb_terminal/cryptocurrency/onchain/onchain_controller.py b/openbb_terminal/cryptocurrency/onchain/onchain_controller.py index 4dadd83156e..f9eb6b635f3 100644 --- a/openbb_terminal/cryptocurrency/onchain/onchain_controller.py +++ b/openbb_terminal/cryptocurrency/onchain/onchain_controller.py @@ -7,20 +7,14 @@ __docformat__ = "numpy" import argparse import difflib import logging -from datetime import datetime, timedelta +from datetime import datetime from typing import List, Optional from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.cryptocurrency.due_diligence.glassnode_model import ( - GLASSNODE_SUPPORTED_HASHRATE_ASSETS, - INTERVALS_HASHRATE, -) -from openbb_terminal.cryptocurrency.due_diligence.glassnode_view import display_hashrate from openbb_terminal.cryptocurrency.onchain import ( bitquery_model, bitquery_view, blockchain_view, - ethgasstation_view, ethplorer_model, ethplorer_view, topledger_model, @@ -62,8 +56,6 @@ class OnchainController(BaseController): } CHOICES_COMMANDS = [ - "hr", - "gwei", "whales", "balance", "top", @@ -99,18 +91,14 @@ class OnchainController(BaseController): if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: choices: dict = self.choices_default - choices["hr"].update({c: {} for c in GLASSNODE_SUPPORTED_HASHRATE_ASSETS}) - self.completer = NestedCompleter.from_nested_dict(choices) def print_help(self): """Print help""" mt = MenuText("crypto/onchain/") - mt.add_cmd("hr") mt.add_cmd("btccp") mt.add_cmd("btcct") mt.add_cmd("btcblockdata") - mt.add_cmd("gwei") mt.add_cmd("whales") mt.add_cmd("topledger") mt.add_cmd("lt") @@ -223,101 +211,6 @@ class OnchainController(BaseController): ), ) - @log_start_end(log=logger) - def call_hr(self, other_args: List[str]): - """Process hr command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="hr", - description=""" - Display mean hashrate for a certain blockchain (ETH or BTC) - [Source: https://glassnode.org] - """, - ) - - parser.add_argument( - "-c", - "--coin", - dest="coin", - type=str, - help="Coin to check hashrate (BTC or ETH)", - default="BTC", - choices=GLASSNODE_SUPPORTED_HASHRATE_ASSETS, - ) - - parser.add_argument( - "-i", - "--interval", - dest="interval", - type=str, - help="Frequency interval. Default: 24h", - default="24h", - choices=INTERVALS_HASHRATE, - ) - - parser.add_argument( - "-s", - "--since", - dest="since", - type=valid_date, - help=f"Initial date. Default: {(datetime.now() - timedelta(days=365)).strftime('%Y-%m-%d')}", - default=(datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d"), - ) - - parser.add_argument( - "-u", - "--until", - dest="until", - type=valid_date, - help=f"Final date. Default: {(datetime.now()).strftime('%Y-%m-%d')}", - default=(datetime.now()).strftime("%Y-%m-%d"), - ) - - if other_args and other_args[0][0] != "-": - other_args.insert(0, "-c") - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES - ) - - if ns_parser: - display_hashrate( - symbol=ns_parser.coin, - interval=ns_parser.interval, - start_date=ns_parser.since.strftime("%Y-%m-%d"), - end_date=ns_parser.until.strftime("%Y-%m-%d"), - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_gwei(self, other_args: List[str]): - """Process gwei command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="onchain", - description=""" - Display ETH gas fees - [Source: https://ethgasstation.info] - """, - ) - - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - - if ns_parser: - ethgasstation_view.display_gwei_fees( - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - @log_start_end(log=logger) def call_whales(self, other_args: List[str]): """Process whales command""" diff --git a/openbb_terminal/miscellaneous/i18n/en.yml b/openbb_terminal/miscellaneous/i18n/en.yml index 16f148b90e2..d54e39cb108 100644 --- a/openbb_terminal/miscellaneous/i18n/en.yml +++ b/openbb_terminal/miscellaneous/i18n/en.yml @@ -26,8 +26,6 @@ en: account/show: Shows the current Personal Access Token account/revoke: Revoke the Personal Access Token account/_info_: Cloud storage of keys - account/sync: Turn the keys synchronization on/off - account/pull: Pull keys from cloud account/clear: Clear keys from cloud account/_routines_: Cloud storage of routines account/list: List available routines @@ -117,12 +115,11 @@ en: futures/search: search for a future based on category or exchange futures/historical: get historical timeseries futures/curve: get futures curves - alternative: alternative datasets, such as COVID and open source metrics + alternative: alternative datasets, such as open source metrics econometrics: statistical and quantitative methods for relationships between datasets forecast: timeseries forecasting with machine learning portfolio: perform portfolio optimization and look at portfolio performance and attribution - dashboards: interactive dashboards using voila and jupyter notebooks - askobb: BETA FEATURE - Ask OpenBB LLM for the data you are interested in + dashboards: interactive dashboards using streamlit stocks/filings: the most-recent form submissions to the SEC stocks/search: search a specific stock ticker for analysis stocks/load: load a specific stock ticker and additional info for analysis @@ -149,6 +146,7 @@ en: stocks/forecast: forecast techniques rnn, nbeats, transformer, block rnn stocks/options/unu: show unusual options activity stocks/options/calc: basic call/put PnL calculator + stocks/options/screen: screens tickers based on preset stocks/options/load: load new ticker stocks/options/exp: see and set expiration dates stocks/options/_ticker: Ticker @@ -167,6 +165,12 @@ en: stocks/options/eodchain: gets option chain at a specific date stocks/options/pricing: shows options pricing and risk neutral valuation stocks/options/hedge: shows portfolio weights in order to neutralise delta + stocks/options/screen/view: view available presets (or one in particular) + stocks/options/screen/set: set one of the available presets + stocks/options/screen/_preset: Preset + stocks/options/screen/scr: screen data from this preset + stocks/options/screen/_screened_tickers: Last screened tickers + stocks/options/screen/ca: take these to comparison analysis menu stocks/options/hedge/_ticker: Ticker stocks/options/hedge/_expiry: Expiry stocks/options/hedge/pick: pick the underlying asset position @@ -298,7 +302,7 @@ en: stocks/gov/lobbying: corporate lobbying details stocks/ca/ticker: set ticker to get similar companies from stocks/ca/_ticker: Ticker to get similar companies from - stocks/ca/tsne: run TSNE on all SP500 stocks + stocks/ca/tsne: run TSNE on all SP500 stocks and returns closest tickers stocks/ca/get: get similar stocks stocks/ca/set: reset and set similar companies stocks/ca/add: add more similar companies @@ -343,7 +347,6 @@ en: stocks/ba/hist: plot historical RHI and AHI data by hour stocks/ba/jcdr: Jim Cramer's daily recommendations stocks/ba/jctr: Jim Cramer's recommendations by ticker - stocks/ba/ns: Top news articles and sentiment provided by Onclusive stocks/fa/load: load a specific stock ticker for analysis stocks/fa/_ticker: Ticker stocks/fa/_company_overview: Company Overview @@ -420,6 +423,7 @@ en: stocks/ta/cci: commodity channel index stocks/ta/macd: moving average convergence/divergence stocks/ta/rsi: relative strength index + stocks/ta/rsp: relative strength percentile stocks/ta/stoch: stochastic oscillator stocks/ta/fisher: fisher transform stocks/ta/cg: centre of gravity @@ -441,7 +445,6 @@ en: stocks/ta/_custom_: Custom stocks/ta/fib: fibonacci retracement stocks/ta/multi: plot multiple indicators on the same chart - stocks/ta/ichimoku: Ichimoku Kinko Hyo indicator stocks/qa/load: load new ticker stocks/qa/pick: pick target column for analysis stocks/qa/_ticker: Ticker @@ -508,7 +511,6 @@ en: crypto/disc/dapp_metrics: show dapp metrics crypto/disc/defi_chains: show available defi chains crypto/disc/tokens: show chains that support tokens - crypto/disc/fees: cryptos where users pay most fees on crypto/ov/global: global crypto market statistics crypto/ov/defi: global DeFi market info crypto/ov/stables: stablecoins @@ -539,7 +541,6 @@ en: crypto/onchain/btcblockdata: displays BTC single block data crypto/onchain/gwei: check current eth gas fees crypto/onchain/whales: check crypto wales transactions - crypto/onchain/topledger: get on-chain data from topledger crypto/onchain/lt: last trades by dex or month crypto/onchain/dvcp: daily volume for crypto pair crypto/onchain/tv: token volume on DEXes @@ -583,7 +584,7 @@ en: crypto/defi/anchor: anchor earnings data crypto/tools/aprtoapy: convert apr to apy crypto/tools/il: calculate impermanent loss - crypto/nft/fp: collection floor price + crypto/nft/fp: collection sales/floor price crypto/nft/collections: top ethereum collections crypto/nft/stats: collection stats crypto/dd/_overview_: Overview @@ -702,7 +703,6 @@ en: etf/scr: screener ETFs overview/performance, using preset filters etf/overview: get overview etf/holdings: top company holdings - etf/holding_perf: Performance of holdings in ETF. etf/weights: sector weights allocation etf/candle: view a candle chart for ETF etf/news: latest news of the company @@ -769,8 +769,8 @@ en: economy/spread: show bond spread matrix economy/events: show economic calendar economy/edebt: show external debt statistics for various countries - economy/usdli: Compare the USD Liquidity Index against a choice of indices economy/plot: plot data from the above commands together + economy/rtps: real-time performance sectors economy/valuation: valuation of sectors, industry, country economy/performance: performance of sectors, industry, country economy/eval: create new series by performing operations on loaded data @@ -922,10 +922,9 @@ en: funds/exclusion: exclusion policy funds/_country_ms: Current Country MorningStar alternative/hn: Hacker News most popular stories - alternative/covid: COVID menu, cases, deaths, rates - alternative/oss: Open Source menu, star history, repos information - alternative/realestate: Real Estate menu, sold prices, HPIs - alternative/companieshouse: Companies House menu, UK company information + alternative/covid: COVID menu cases, deaths, rates + alternative/oss: Open Source menu star history, repos information + alternative/realestate: Real Estate menu sold prices, HPIs alternative/covid/slopes: get countries with highest slope in cases alternative/covid/country: select country for data alternative/covid/_country: Country @@ -945,14 +944,6 @@ en: alternative/realestate/_enddate: End Date alternative/realestate/regionstats: retrieve UK sold house price stats by region alternative/realestate/_region: Region - alternative/companieshouse/_company: Current Company - alternative/companieshouse/search: search for companies by name - alternative/companieshouse/load: retrieve basic company details by company registration number - alternative/companieshouse/officers: retrieve company officies - alternative/companieshouse/signifcontrol: retrieve those with significant control over the company - alternative/companieshouse/filings: retrieve list of documents filed with Companies House (Default 100) - alternative/companieshouse/filingdocument: download filed document - alternative/companieshouse/charges: registered charges against the company econometrics/_data_loc: Looking for data in econometrics/load: load a dataset (also works with StatsModels datasets) econometrics/export: export a processed dataset @@ -990,8 +981,6 @@ en: econometrics/coint: co-integration test on a multitude of columns econometrics/garch: estimate future volatility with GARCH econometrics/ret: calculate returns for the given time series - econometrics/vif: perform collinearity test (VIF) - econometrics/_assumption_testing_: Assumption Testing portfolio/bro: brokers holdings supports robinhood, ally, degiro, coinbase portfolio/po: portfolio optimization optimize your portfolio weights efficiently portfolio/load: load transactions into the portfolio (use load --example for an example) @@ -1098,13 +1087,11 @@ en: dashboards/chains: options chain analysis dashboards/shortdata: finra shortdata analysis dashboards/futures: historical futures performance - dashboards/options: cboe options analysis reports: customizable research reports through jupyter notebooks reports/_reports_: Build and run your custom reports or try one of our templates reports/_OpenBB_reports_: OpenBB reports reports/_Custom_reports_: Custom reports reports/run: Run a Jupyter notebook from OpenBBUserData/reports/custom reports - reports/exe: Execute a Jupyter notebook through a hyperlink using papermill forecast/_disclaimer_: DISCLAIMER forecast/_data_loc: Looking for data in forecast/load: load a dataset from csv @@ -1122,7 +1109,6 @@ en: forecast/rename: Rename columns from dataset forecast/export: Export a processed dataset forecast/_feateng_: Feature Engineering - forecast/timegpt: TimeGPT-1 (Beta) by Nixtla forecast/ema: Add Exponentially Weighted Moving Average forecast/sto: Add Stochastic Oscillator %K and %D forecast/rsi: Add Relative Strength Index diff --git a/openbb_terminal/parent_classes.py b/openbb_terminal/parent_classes.py index e94d49d54e4..9d8a45506fd 100644 --- a/openbb_terminal/parent_classes.py +++ b/openbb_terminal/parent_classes.py @@ -17,7 +17,6 @@ from typing import Any, Dict, List, Optional, Union # IMPORTS THIRDPARTY import numpy as np -import openai import pandas as pd from prompt_toolkit.formatted_text import HTML from prompt_toolkit.styles import Style @@ -42,8 +41,6 @@ from openbb_terminal.helper_funcs import ( get_flair, parse_and_split_input, prefill_form, - query_LLM_local, - query_LLM_remote, screenshot, search_wikipedia, set_command_location, @@ -115,7 +112,6 @@ class BaseController(metaclass=ABCMeta): "wiki", "stop", "screenshot", - "askobb", "hold", ] @@ -376,134 +372,6 @@ class BaseController(metaclass=ABCMeta): config_terminal.set_current_figure(None) config_terminal.reset_legend() - def call_askobb(self, other_args: List[str]) -> None: - """Accept user input as a string and return the most appropriate Terminal command""" - self.save_class() - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="askobb", - description="Accept input as a string and return the most appropriate Terminal command", - ) - parser.add_argument( - "--prompt", - "-p", - action="store", - type=str, - nargs="+", - dest="question", - required="-h" not in other_args and "--help" not in other_args, - default="", - help="Question for Askobb LLM", - ) - - parser.add_argument( - "--model", - "-m", - action="store", - type=str, - dest="gpt_model", - required=False, - default="gpt-3.5-turbo", - choices=["gpt-3.5-turbo", "gpt-4"], - help="GPT Model to use for Askobb LLM (default: gpt-3.5-turbo) or gpt-4 (beta)", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - - ns_parser = self.parse_known_args_and_warn( - parser, - other_args, - ) - - if ns_parser: - # check if user has passed a question with 2 or more words - if len(ns_parser.question) < 2: - console.print("[red]Please enter a prompt with more than 1 word[/red]") - else: - api_key = get_current_user().credentials.API_OPENAI_KEY - if ns_parser.gpt_model == "gpt-4" and api_key == "REPLACE_ME": - console.print( - "[red]GPT-4 only available with local OPENAI Key.\n[/]" - ) - return - - if api_key == "REPLACE_ME": - response, source_nodes = query_LLM_remote( - " ".join(ns_parser.question) - ) - - else: - if ns_parser.gpt_model != "gpt-4": - console.print( - "[yellow]Using local OpenAI Key" - ". Please remove from OpenBB Hub to query askobb remotely.[/]\n" - ) - # This is needed to avoid authentication error - openai.api_key = api_key - response, source_nodes = query_LLM_local( - " ".join(ns_parser.question), ns_parser.gpt_model - ) - - feedback = "" - if response is not None: - # check that "I don't know" and "Sorry" is not the response - if all( - phrase not in response - for phrase in [ - "I don't know", - "Sorry", - "I am not sure", - "no terminal command provided", - "no available", - "no command provided", - "no information", - "does not contain", - "I cannot provide", - ] - ): - console.print( - f"[green]Suggested Command:[/green] /{response}\n" - ) - - console.print( - "[yellow]Would you like to run this command?(y/n/fb)[/yellow]" - ) - user_response = input() - if user_response == "y": - self.queue.append("home/" + response) - elif user_response == "n": - console.print("Please refine your question and try again.") - elif user_response == "fb": - console.print( - "\n[yellow]Please enter your feedback on askobb:[/] " - ) - feedback = input() - if feedback: - console.print( - "\n[green]Thank you for your feedback![/]" - ) - - else: - console.print( - "[red]askobb could not respond with an appropriate answer.[/red]" - ) - console.print("Please refine your question and try again.") - - logger.info( - "ASKOBB: %s ", - json.dumps( - { - "Question": " ".join(ns_parser.question), - "Model": ns_parser.gpt_model, - "Response": response, - "Nodes": str(source_nodes), - "Feedback": feedback, - } - ), - ) - def save_class(self) -> None: """Save the current instance of the class to be loaded later.""" if get_current_user().preferences.REMEMBER_CONTEXTS: diff --git a/openbb_terminal/stocks/behavioural_analysis/ba_controller.py b/openbb_terminal/stocks/behavioural_analysis/ba_controller.py index eeb98d51bbb..09bcd28bd95 100644 --- a/openbb_terminal/stocks/behavioural_analysis/ba_controller.py +++ b/openbb_terminal/stocks/behavioural_analysis/ba_controller.py @@ -45,8 +45,6 @@ class BehaviouralAnalysisController(StockBaseController): CHOICES_COMMANDS = [ "load", "wsb", - "popular", - "getdd", "redditsent", "bullbear", "messages", @@ -92,8 +90,6 @@ class BehaviouralAnalysisController(StockBaseController): mt.add_cmd("headlines", self.ticker) mt.add_cmd("snews", self.ticker) mt.add_cmd("wsb") - mt.add_cmd("popular") - mt.add_cmd("getdd") mt.add_cmd("redditsent", self.ticker) mt.add_cmd("trending") mt.add_cmd("stalker") @@ -163,109 +159,6 @@ class BehaviouralAnalysisController(StockBaseController): limit=ns_parser.n_limit, new=ns_parser.b_new ) - @log_start_end(log=logger) - def call_popular(self, other_args: List[str]): - """Process popular command.""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="popular", - description="""Print latest popular tickers. [Source: Reddit]""", - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=10, - help="limit of top tickers to retrieve", - ) - parser.add_argument( - "-n", - "--num", - action="store", - dest="num", - type=check_positive, - default=10, - help="number of posts retrieved per sub reddit.", - ) - parser.add_argument( - "-s", - "--sub", - action="store", - dest="s_subreddit", - type=str, - help=""" - Subreddits to look for tickers, e.g. pennystocks,stocks. - Default: pennystocks, RobinHoodPennyStocks, Daytrading, StockMarket, stocks, investing, - wallstreetbets - """, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - reddit_view.display_popular_tickers( - limit=ns_parser.limit, - post_limit=ns_parser.num, - subreddits=ns_parser.s_subreddit, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - - @log_start_end(log=logger) - def call_getdd(self, other_args: List[str]): - """Process getdd command.""" - parser = argparse.ArgumentParser( - add_help=False, - prog="getdd", - description=""" - Print top stock's due diligence from other users. [Source: Reddit] - """, - ) - parser.add_argument( - "-l", - "--limit", - action="store", - dest="limit", - type=check_positive, - default=5, - help="limit of posts to retrieve.", - ) - parser.add_argument( - "-d", - "--days", - action="store", - dest="days", - type=check_positive, - default=3, - help="number of prior days to look for.", - ) - parser.add_argument( - "-a", - "--all", - action="store_true", - dest="all", - default=False, - help=""" - search through all flairs (apart from Yolo and Meme), otherwise we focus on - specific flairs: DD, technical analysis, Catalyst, News, Advice, Chart""", - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-l") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - reddit_view.display_due_diligence( - limit=ns_parser.limit, - n_days=ns_parser.days, - show_all_flairs=ns_parser.all, - ) - @log_start_end(log=logger) def call_redditsent(self, other_args: List[str]): """Process redditsent command.""" diff --git a/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py b/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py index 6ecb22f83d5..156049117f0 100644 --- a/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py +++ b/openbb_terminal/stocks/dark_pool_shorts/dps_controller.py @@ -24,12 +24,10 @@ from openbb_terminal.parent_classes import StockBaseController from openbb_terminal.rich_config import MenuText, console from openbb_terminal.stocks.dark_pool_shorts import ( finra_view, - ibkr_view, quandl_view, sec_view, shortinterest_view, stockgrid_view, - stocksera_view, yahoofinance_view, ) @@ -42,7 +40,6 @@ class DarkPoolShortsController(StockBaseController): CHOICES_COMMANDS = [ "load", "shorted", - "ctb", "hsi", "prom", "pos", @@ -88,7 +85,6 @@ class DarkPoolShortsController(StockBaseController): mt.add_cmd("load") mt.add_raw("\n") mt.add_cmd("shorted") - mt.add_cmd("ctb") mt.add_cmd("hsi") mt.add_cmd("prom") mt.add_cmd("pos") @@ -134,48 +130,6 @@ class DarkPoolShortsController(StockBaseController): ), ) - @log_start_end(log=logger) - def call_ctb(self, other_args: List[str]): - """Process CTB command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="ctb", - description="Show cost to borrow of stocks. [Source: Stocksera/Interactive Broker]", - ) - parser.add_argument( - "-n", - "--number", - action="store", - dest="number", - type=check_int_range(1, 10000), - default=20, - help="Number of records to retrieve.", - ) - parser.add_argument( - "--raw", - action="store_true", - default=False, - dest="raw", - help="Print raw data.", - ) - ns_parser = self.parse_known_args_and_warn( - parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED - ) - if ns_parser: - if ns_parser.source == "InteractiveBrokers": - ibkr_view.display_cost_to_borrow( - limit=ns_parser.number, - export=ns_parser.export, - sheet_name=( - " ".join(ns_parser.sheet_name) if ns_parser.sheet_name else None - ), - ) - elif ns_parser.source == "Stocksera": - stocksera_view.cost_to_borrow( - self.ticker, limit=ns_parser.number, raw=ns_parser.raw - ) - @log_start_end(log=logger) def call_hsi(self, other_args: List[str]): """Process hsi command""" diff --git a/openbb_terminal/stocks/dark_pool_shorts/stocksera_model.py b/openbb_terminal/stocks/dark_pool_shorts/stocksera_model.py deleted file mode 100644 index 4d10aad0059..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/stocksera_model.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Stocksera model""" - -__docformat__ = "numpy" - -import logging - -import pandas as pd -import stocksera -from stocksera.exceptions import StockseraRequestException - -from openbb_terminal.core.session.current_user import get_current_user -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_cost_to_borrow(symbol: str) -> pd.DataFrame: - """Get cost to borrow of stocks [Source: Stocksera] - - Parameters - ---------- - symbol : str - ticker to get cost to borrow from - - Returns - ------- - pd.DataFrame - Cost to borrow - """ - - df = pd.DataFrame() - - try: - client = stocksera.Client( - api_key=get_current_user().credentials.API_STOCKSERA_KEY - ) - df = pd.DataFrame(client.borrowed_shares(ticker=symbol)) - df.columns = ["Ticker", "Fees", "Available", "Date"] - df.set_index("Date", inplace=True) - - except StockseraRequestException: - console.print("[red]Invalid API Key[/red]\n") - except Exception as e: - console.print(f"[red]{e}[/red]\n") - return df diff --git a/openbb_terminal/stocks/dark_pool_shorts/stocksera_view.py b/openbb_terminal/stocks/dark_pool_shorts/stocksera_view.py deleted file mode 100644 index 29dfc8ca564..00000000000 --- a/openbb_terminal/stocks/dark_pool_shorts/stocksera_view.py +++ /dev/null @@ -1,131 +0,0 @@ -""" Stocksera View """ - -__docformat__ = "numpy" - -import logging -import os -from typing import Optional, Union - -import pandas as pd - -from openbb_terminal import OpenBBFigure, theme -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.decorators import check_api_key, log_start_end -from openbb_terminal.helper_funcs import ( - export_data, - lambda_long_number_format, - print_rich_table, -) -from openbb_terminal.rich_config import console -from openbb_terminal.stocks.dark_pool_shorts import stocksera_model - -logger = logging.getLogger(__name__) - - -@log_start_end(log=logger) -def plot_cost_to_borrow( - symbol: str, - data: pd.DataFrame, - external_axes: bool = False, -): - """Plot the cost to borrow of a stock. [Source: Stocksera] - - Parameters - ---------- - symbol : str - ticker to get cost to borrow from - data: pd.DataFrame - Cost to borrow dataframe - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - if data.empty: - return None - - fig = OpenBBFigure.create_subplots(1, 1, specs=[[{"secondary_y": True}]]) - fig.set_title(f"Cost to Borrow of {symbol}") - - fig.add_bar( - x=data.index, - y=data["Available"], - name="Number Shares", - marker_color=theme.up_color, - secondary_y=False, - ) - - fig.add_scatter( - x=data.index, - y=data["Fees"].values, - name="Fees", - marker_color=theme.get_colors()[0], - secondary_y=True, - ) - fig.update_yaxes(title_text="Fees %", secondary_y=True) - fig.update_yaxes(secondary_y=False, side="left") - fig.update_xaxes(title_text="Date", type="category", nticks=6) - fig.update_layout(margin=dict(l=50)) - - return fig.show(external=external_axes) - - -@log_start_end(log=logger) -@check_api_key(["API_STOCKSERA_KEY"]) -def cost_to_borrow( - symbol: str, - limit: int = 100, - raw: bool = False, - export: str = "", - sheet_name: Optional[str] = None, - external_axes: bool = False, -) -> Union[None, OpenBBFigure]: - """Plot the short interest of a stock. This corresponds to the - number of shares that have been sold short but have not yet been - covered or closed out. Either NASDAQ or NYSE [Source: Quandl] - Parameters - ---------- - symbol : str - ticker to get cost to borrow from - limit: int - Number of historical cost to borrow data to show - raw : bool - Flag to print raw data instead - export : str - Export dataframe data to csv,json,xlsx file - external_axes : bool, optional - Whether to return the figure object or not, by default False - """ - # Note: if you send an empty string stocksera will search every ticker - if not symbol: - return console.print("[red]No symbol provided[/red]\n") - - df_cost_to_borrow = stocksera_model.get_cost_to_borrow(symbol) - - df_cost_to_borrow = df_cost_to_borrow.head(limit)[::-1] - - pd.options.mode.chained_assignment = None - - fig = plot_cost_to_borrow(symbol, df_cost_to_borrow, True) - - if raw: - if not get_current_user().preferences.USE_INTERACTIVE_DF: - df_cost_to_borrow["Available"] = df_cost_to_borrow["Available"].apply( - lambda x: lambda_long_number_format(x) - ) - print_rich_table( - df_cost_to_borrow, - headers=list(df_cost_to_borrow.columns), - show_index=True, - title=f"Cost to Borrow of {symbol}", - export=bool(export), - ) - - export_data( - export, - os.path.dirname(os.path.abspath(__file__)), - "stocksera", - df_cost_to_borrow, - sheet_name, - fig, - ) - - return fig.show(external=raw or external_axes) diff --git a/openbb_terminal/stocks/options/hedge/hedge_controller.py b/openbb_terminal/stocks/options/hedge/hedge_controller.py deleted file mode 100644 index 7eb716b9024..00000000000 --- a/openbb_terminal/stocks/options/hedge/hedge_controller.py +++ /dev/null @@ -1,585 +0,0 @@ -""" Hedge Controller Module """ - -__docformat__ = "numpy" - -import argparse -import logging -from datetime import datetime -from typing import Dict, List, Optional - -import pandas as pd - -from openbb_terminal.core.session.current_user import get_current_user -from openbb_terminal.custom_prompt_toolkit import NestedCompleter -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 MenuText, console -from openbb_terminal.stocks import stocks_helper -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_view import plot_payoff - -# pylint: disable=R0902 - - -logger = logging.getLogger(__name__) - - -class HedgeController(BaseController): - """Hedge Controller class""" - - CHOICES_COMMANDS = [ - "list", - "add", - "rmv", - "pick", - "sop", - "plot", - ] - - PATH = "/stocks/options/hedge/" - CHOICES_GENERATION = True - - def __init__(self, ticker: str, expiration: str, queue: Optional[List[str]] = None): - """Constructor""" - super().__init__(queue) - - self.underlying_asset_position: str = "" - self.chain = get_option_chain(ticker, expiration) - self.calls = ( - list( - zip( - self.chain.calls["strike"].tolist(), - self.chain.calls["impliedVolatility"].tolist(), - self.chain.calls["lastPrice"].tolist(), - self.chain.calls["currency"].tolist(), - ) - ) - if "calls" in self.chain.columns - else [] - ) - self.puts = ( - list( - zip( - self.chain.puts["strike"].tolist(), - self.chain.puts["impliedVolatility"].tolist(), - self.chain.puts["lastPrice"].tolist(), - self.chain.calls["currency"].tolist(), - ) - ) - if "puts" in self.chain.columns - else [] - ) - - start = self.calls[0] if self.calls else 0 - end = self.calls[-1] if self.calls else 0 - - start = int(start[0]) if isinstance(start, list) and start else 0 - end = int(end[0]) if isinstance(end, list) and end else 0 - - self.PICK_CHOICES = [ - f"{strike} {position} {side}" - for strike in range(start, end, 5) - for position in ["Long", "Short"] - for side in ["Call", "Put"] - ] - - self.ticker = ticker - self.current_price: float = get_price(ticker) - self.expiration = expiration - self.implied_volatility = ( - self.chain.calls.get("impliedVolatility", 0.0) - if "calls" in self.chain.columns - else 0.0 - ) - self.options: Dict = {"Portfolio": {}, "Option A": {}, "Option B": {}} - self.underlying = 0.0 - self.side: str = "" - self.amount = 0.0 - self.strike = 0.0 - self.call_index_choices = range(len(self.calls)) - self.put_index_choices = range(len(self.puts)) - self.greeks: Dict = {"Portfolio": {}, "Option A": {}, "Option B": {}} - - if session and get_current_user().preferences.USE_PROMPT_TOOLKIT: - choices: dict = self.choices_default - # This menu contains dynamic choices that may change during runtime - self.choices = choices - self.completer = NestedCompleter.from_nested_dict(choices) - - def update_runtime_choices(self): - """Update runtime choices""" - if ( - self.options - and session - and get_current_user().preferences.USE_PROMPT_TOOLKIT - ): - self.choices["rmv"] = {c: None for c in ["Option A", "Option B"]} - self.choices["rmv"]["--all"] = {} - self.choices["rmv"]["-a"] = "--all" - self.completer = NestedCompleter.from_nested_dict(self.choices) - - def print_help(self): - """Print help""" - mt = MenuText("stocks/options/hedge/") - mt.add_param("_ticker", self.ticker or "") - mt.add_param("_expiry", self.expiration or "") - mt.add_raw("\n") - mt.add_cmd("pick") - mt.add_raw("\n") - mt.add_param("_underlying", self.underlying_asset_position) - mt.add_raw("\n") - mt.add_cmd("list") - mt.add_cmd("add", "Delta" in self.greeks["Portfolio"]) - mt.add_cmd( - "rmv", - "Delta" in self.greeks["Option A"] or "Delta" in self.greeks["Option B"], - ) - mt.add_cmd( - "sop", - "Delta" in self.greeks["Option A"] or "Delta" in self.greeks["Option B"], - ) - mt.add_cmd( - "plot", - "Delta" in self.greeks["Option A"] or "Delta" in self.greeks["Option B"], - ) - console.print(text=mt.menu_text, menu="Stocks - Options - Hedge") - - def custom_reset(self): - """Class specific component of reset command""" - if self.ticker: - if self.expiration: - return [ - "stocks", - f"load {self.ticker}", - "options", - f"exp -d {self.expiration}", - "hedge", - ] - return ["stocks", f"load {self.ticker}", "options", "hedge"] - return [] - - @log_start_end(log=logger) - def call_list(self, other_args): - """Lists available calls and puts""" - parser = argparse.ArgumentParser( - add_help=False, - prog="list", - description="""Lists available calls and puts.""", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - calls = pd.DataFrame([call[0] for call in self.calls]) - puts = pd.DataFrame([put[0] for put in self.puts]) - - options = pd.concat([calls, puts], axis=1).fillna("-") - - print_rich_table( - options, - title="Available Calls and Puts", - headers=list(["Calls", "Puts"]), - show_index=True, - index_name="Identifier", - ) - - @log_start_end(log=logger) - def call_add(self, other_args: List[str]): - """Process add command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="add", - description="""Add options to the diagram.""", - ) - parser.add_argument( - "-p", - "--put", - dest="put", - action="store_true", - help="Buy a put instead of a call", - default=False, - ) - parser.add_argument( - "-s", - "--short", - dest="short", - action="store_true", - help="Short the option instead of buying it", - default=False, - ) - option_type = ( - self.put_index_choices if "-p" in other_args else self.call_index_choices - ) - parser.add_argument( - "-i", - "--identifier", - dest="identifier", - help="The identifier of the option as found in the list command", - required="-h" not in other_args and "-k" not in other_args, - type=int, - choices=option_type, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-i") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - - if ns_parser: - if not self.greeks["Portfolio"]: - console.print( - "Please set the Underlying Asset Position by using the 'pick' command.\n" - ) - else: - opt_type = "Put" if ns_parser.put else "Call" - sign = -1 if ns_parser.short else 1 - options_list = self.puts if ns_parser.put else self.calls - - if ns_parser.identifier < len(options_list): - strike = options_list[ns_parser.identifier][0] - implied_volatility = options_list[ns_parser.identifier][1] - cost = options_list[ns_parser.identifier][2] - currency = options_list[ns_parser.identifier][3] - - option = { - "type": opt_type, - "sign": sign, - "strike": strike, - "implied_volatility": implied_volatility, - "cost": cost, - "currency": currency, - } - - side = 1 if opt_type == "Call" else -1 - - date_obj = datetime.strptime(self.expiration, "%Y-%m-%d") - days = float((date_obj - datetime.now()).days + 1) - - if days == 0.0: - days = 0.01 - - if "Delta" not in self.greeks["Option A"]: - self.options["Option A"] = option - ( - self.greeks["Option A"]["Delta"], - self.greeks["Option A"]["Gamma"], - self.greeks["Option A"]["Vega"], - ) = hedge_view.add_and_show_greeks( - self.current_price, - implied_volatility, - strike, - days / 365, - side, - ) - elif "Delta" not in self.greeks["Option B"]: - self.options["Option B"] = option - ( - self.greeks["Option B"]["Delta"], - self.greeks["Option B"]["Gamma"], - self.greeks["Option B"]["Vega"], - ) = hedge_view.add_and_show_greeks( - self.current_price, - implied_volatility, - strike, - days / 365, - side, - ) - else: - console.print( - "[red]The functionality only accepts two options. Therefore, please remove an " - "option with 'rmv' before continuing.[/red]\n" - ) - return - - positions = pd.DataFrame() - - for _, values in self.options.items(): - # Loops over options in the dictionary. If a position is empty, skips the printing. - if values: - option_position: str = ( - "Long" if values["sign"] == 1 else "Short" - ) - positions = positions.append( - [ - [ - values["type"], - option_position, - values["strike"], - values["implied_volatility"], - values["cost"], - values["currency"], - ] - ] - ) - - positions.columns = [ - "Type", - "Hold", - "Strike", - "Implied Volatility", - "Cost", - "Currency", - ] - - console.print("") - - print_rich_table( - positions, - title="Current Option Positions", - headers=list(positions.columns), - show_index=False, - ) - - if ( - "Delta" in self.greeks["Option A"] - and "Delta" in self.greeks["Option B"] - ): - hedge_view.show_calculated_hedge( - self.amount, option["type"], self.greeks, sign - ) - - self.update_runtime_choices() - else: - console.print("Please use a valid index\n") - - @log_start_end(log=logger) - def call_rmv(self, other_args: List[str]): - """Process rmv command""" - parser = argparse.ArgumentParser( - add_help=False, - prog="rmv", - description="""Remove one of the options to be shown in the hedge.""", - ) - parser.add_argument( - "-o", - "--option", - dest="option", - help="index of the option to remove", - nargs="+", - ) - parser.add_argument( - "-a", - "--all", - dest="all", - action="store_true", - help="remove all of the options", - default=False, - ) - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-o") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.options["Option A"] and not self.options["Option B"]: - console.print("Please add Options by using the 'add' command.\n") - else: - if ns_parser.all: - self.options = {"Option A": {}, "Option B": {}} - else: - option_name = " ".join(ns_parser.option) - - if option_name in self.options: - self.options[option_name] = {} - self.greeks[option_name] = {} - - self.update_runtime_choices() - else: - console.print(f"{option_name} is not an option.") - - if self.options["Option A"] or self.options["Option B"]: - positions = pd.DataFrame() - - for _, value in self.options.items(): - if value: - option_side: str = "Long" if value["sign"] == 1 else "Short" - positions = positions.append( - [ - [ - value["type"], - option_side, - value["strike"], - value["implied_volatility"], - ] - ] - ) - - positions.columns = ["Type", "Hold", "Strike", "Implied Volatility"] - - print_rich_table( - positions, - title="Current Option Positions", - headers=list(positions.columns), - show_index=False, - ) - - else: - console.print( - "No options have been selected, removing them is not possible\n" - ) - - @log_start_end(log=logger) - def call_pick(self, other_args: List[str]): - """Process pick command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="pick", - description="This function plots option hedge diagrams", - ) - parser.add_argument( - "-p", - "--pick", - dest="pick", - type=str.lower, - choices=stocks_helper.format_parse_choices(self.PICK_CHOICES), - help="Choose what you would like to pick", - required="-h" not in other_args, - ) - - parser.add_argument( - "-a", - "--amount", - dest="amount", - default=1000, - help="Choose the amount invested", - ) - - if other_args and "-" not in other_args[0][0]: - other_args.insert(0, "-p") - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - pick = stocks_helper.map_parse_choices(self.PICK_CHOICES)[ns_parser.pick] - strike_type, underlying_type, side_type = pick.split(" ") - amount_type = ns_parser.amount - - self.underlying_asset_position = ( - f"{underlying_type} {side_type} {amount_type} @ {strike_type}" - ) - - if underlying_type == "Short": - self.underlying = -1 - else: - self.underlying = 1 - - if side_type == "Put": - self.side = "Put" - side = -1 - else: - self.side = "Call" - side = 1 - - self.amount = float(amount_type) - self.strike = float(strike_type) - - index = -1 - date_obj = datetime.strptime(self.expiration, "%Y-%m-%d") - days = float((date_obj - datetime.now()).days + 1) - - if days == 0.0: - days = 0.01 - - if side == -1: - for i in range(len(self.chain.puts["strike"])): - if self.chain.puts["strike"][i] == self.strike: - index = i - break - implied_volatility = self.chain.puts["impliedVolatility"].iloc[index] - else: - for i in range(len(self.chain.calls["strike"])): - if self.chain.calls["strike"][i] == self.strike: - index = i - break - implied_volatility = self.chain.calls["impliedVolatility"].iloc[index] - - ( - self.greeks["Portfolio"]["Delta"], - self.greeks["Portfolio"]["Gamma"], - self.greeks["Portfolio"]["Vega"], - ) = add_hedge_option( - self.current_price, - implied_volatility, - float(self.strike), - days / 365, - side, - ) - - @log_start_end(log=logger) - def call_sop(self, other_args): - """Process sop command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="sop", - description="Displays selected option", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.options["Option A"] and not self.options["Option B"]: - console.print("Please add Options by using the 'add' command.\n") - else: - positions = pd.DataFrame() - - for _, value in self.options.items(): - if value: - option_side: str = "Long" if value["sign"] == 1 else "Short" # type: ignore - positions = positions.append( - [ - [ - value["type"], - option_side, - value["strike"], - value["implied_volatility"], - value["cost"], - value["currency"], - ] - ] - ) - - positions.columns = [ - "Type", - "Hold", - "Strike", - "Implied Volatility", - "Cost", - "Currency", - ] - - print_rich_table( - positions, - title="Current Option Positions", - headers=list(positions.columns), - show_index=False, - ) - - if ( - "Delta" in self.greeks["Option A"] - and "Delta" in self.greeks["Option B"] - ): - hedge_view.show_calculated_hedge( - self.amount, - self.options["Option A"]["type"], - self.greeks, - self.options["Option A"]["sign"], - ) - - @log_start_end(log=logger) - def call_plot(self, other_args): - """Process plot command""" - parser = argparse.ArgumentParser( - add_help=False, - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - prog="plot", - description="This function plots option payoff diagrams", - ) - ns_parser = self.parse_known_args_and_warn(parser, other_args) - if ns_parser: - if not self.options["Option A"] and not self.options["Option B"]: - console.print("Please add Options by using the 'add' command.\n") - else: - plot_payoff( - self.current_price, - [self.options["Option A"], self.options["Option B"]], - self.underlying, - self.ticker, - self.expiration, - ) diff --git a/openbb_terminal/stocks/options/hedge/hedge_model.py b/openbb_terminal/stocks/options/hedge/hedge_model.py deleted file mode 100644 index 9d9fc8e54d0..00000000000 --- a/openbb_terminal/stocks/options/hedge/hedge_model.py +++ /dev/null @@ -1,318 +0,0 @@ -"""Hedge model""" - -__docformat__ = "numpy" - -import math -from typing import Optional - -import numpy as np -from scipy.stats import norm - -# Based on article of Roman Paolucci: https://towardsdatascience.com/algorithmic-portfolio-hedging-9e069aafff5a - - -def calc_hedge( - portfolio_option_amount: float = 100, - side: str = "Call", - greeks: Optional[dict] = None, - sign: int = 1, -): - """Determine the hedge position and the weights within each option and - underlying asset to hold a neutral portfolio - - Parameters - ---------- - portfolio_option_amount: float - Number to show - side: str - Whether you have a Call or Put instrument - greeks: dict - Dictionary containing delta, gamma and vega values for the portfolio and option A and B. Structure is - as follows: {'Portfolio': {'Delta': VALUE, 'Gamma': VALUE, 'Vega': VALUE}} etc - sign: int - Whether you have a long (1) or short (-1) position - - Returns - ------- - option A weight: float - option B weight: float - portfolio weight: float - is_singular: boolean - """ - if greeks is None: - greeks = { - "Portfolio": { - "Delta": 1, - "Gamma": 9.1268e-05, - "Vega": 5.4661, - }, - "Option A": { - "Delta": 1, - "Gamma": 9.1268e-05, - "Vega": 5.4661, - }, - "Option B": { - "Delta": 1, - "Gamma": 9.1268e-05, - "Vega": 5.4661, - }, - } - # Shortnames for delta, gamma and vega of portfolio - portfolio_option_delta = greeks["Portfolio"]["Delta"] - portfolio_option_gamma = greeks["Portfolio"]["Gamma"] - portfolio_option_vega = greeks["Portfolio"]["Vega"] - - # Shortnames for delta, gamma and vega of option A - option_a_delta = greeks["Option A"]["Delta"] - option_a_gamma = greeks["Option A"]["Gamma"] - option_a_vega = greeks["Option A"]["Vega"] - - # Shortnames for delta, gamma and vega of option B - option_b_delta = greeks["Option B"]["Delta"] - option_b_gamma = greeks["Option B"]["Gamma"] - option_b_vega = greeks["Option B"]["Vega"] - - # Delta will be positive for long call and short put positions, negative for short call and long put positions. - delta_multiplier = 1 - - # Gamma is always positive for long positions and negative for short positions. - gamma_multiplier = 1 - - # Vega will be positive for long positions and negative for short positions. - vega_multiplier = 1 - - # Initialize variable - short = False - - # Short position - if sign == -1: - short = True - gamma_multiplier = -1 - vega_multiplier = -1 - if side == "Call": - # Short call position - delta_multiplier = -1 - elif side == "Put" and sign == 1: - # Long put position - delta_multiplier = -1 - - options_array = np.array( - [[option_a_gamma, option_b_gamma], [option_a_vega, option_b_vega]] - ) - - portfolio_greeks = [ - [portfolio_option_gamma * portfolio_option_amount], - [portfolio_option_vega * portfolio_option_amount], - ] - - singular = False - try: - inv = np.linalg.inv(np.round(options_array, 2)) - except np.linalg.LinAlgError: - options_array = np.round(options_array, 2) - a = options_array.shape[0] - i = np.eye(a, a) - inv = np.linalg.lstsq(options_array, i)[0] - singular = True - - weights = np.dot(inv, portfolio_greeks) - - portfolio_greeks = [ - [portfolio_option_delta * delta_multiplier * portfolio_option_amount], - [portfolio_option_gamma * gamma_multiplier * portfolio_option_amount], - [portfolio_option_vega * vega_multiplier * portfolio_option_amount], - ] - - options_array = np.array( - [ - [option_a_delta, option_b_delta], - [option_a_gamma, option_b_gamma], - [option_a_vega, option_b_vega], - ] - ) - - if not short: - neutral = np.round( - np.dot(np.round(options_array, 2), weights) - portfolio_greeks - ) - else: - neutral = np.round( - np.dot(np.round(options_array, 2), weights) + portfolio_greeks - ) - - return weights[0][0], weights[1][0], neutral[0][0], singular - - -def add_hedge_option( - price: float = 100, - implied_volatility: float = 20, - strike: float = 120, - days: float = 30, - sign: int = 1, -) -> tuple: - """Determine the delta, gamma and vega value of the portfolio and/or options. - - Parameters - ---------- - price: float - The price. - implied_volatility: float - The implied volatility. - strike: float - The strike price. - days: float - The amount of days until expiration. Use annual notation thus a month would be 30 / 360. - sign: int - Whether you have a long (1) or short (-1) position - - Returns - ------- - delta: float - gamma: float - portfolio: float - """ - # Determine delta position given the option - delta = calc_delta(price, implied_volatility, strike, days, 0, sign) - - # Determine gamma position given the option - gamma = calc_gamma(price, implied_volatility, strike, days, 0) - - # Determine vega position given the option - vega = calc_vega(price, implied_volatility, strike, days, 0) - - return delta, gamma, vega - - -def calc_delta( - asset_price: float = 100, - asset_volatility: float = 20, - strike_price: float = 120, - time_to_expiration: float = 30, - risk_free_rate: float = 0, - sign: int = 1, -): - """The first-order partial-derivative with respect to the underlying asset of the Black-Scholes - equation is known as delta. Delta refers to how the option value changes when there is a change in - the underlying asset price. Multiplying delta by a +-$1 change in the underlying asset, holding all other - parameters constant, will give you the new value of the option. Delta will be positive for long call and - short put positions, negative for short call and long put positions. - - Parameters - ---------- - asset_price: int - The price. - asset_volatility: float - The implied volatility. - strike_price: float - The strike price. - time_to_expiration: float - The amount of days until expiration. Use annual notation thus a month would be 30 / 360. - risk_free_rate: float - The risk free rate. - sign: int - Whether you have a long (1) or short (-1) position - - Returns - ------- - delta: float - Returns the value for the delta. - """ - b = math.exp(-risk_free_rate * time_to_expiration) - x1 = ( - math.log(asset_price / (b * strike_price)) - + 0.5 * (asset_volatility * asset_volatility) * time_to_expiration - ) - x1 = x1 / (asset_volatility * (time_to_expiration**0.5)) - delta = norm.cdf(x1) - - if sign == 1: - return delta - - return delta - 1 - - -def calc_gamma( - asset_price: float = 100, - asset_volatility: float = 20, - strike_price: float = 120, - time_to_expiration: float = 30, - risk_free_rate: float = 0, -): - """The second-order partial-derivative with respect to the underlying asset of the Black-Scholes equation - is known as gamma. Gamma refers to how the option’s delta changes when there is a change in the underlying - asset price. Multiplying gamma by a +-$1 change in the underlying asset, holding all other parameters constant, - will give you the new value of the option’s delta. Essentially, gamma is telling us the rate of change of delta - given a +-1 change in the underlying asset price. Gamma is always positive for long positions and - negative for short positions. - - Parameters - ---------- - asset_price: int - The price. - asset_volatility: float - The implied volatility. - strike_price: float - The strike price. - time_to_expiration: float - The amount of days until expiration. Use annual notation thus a month would be 30 / 360. - risk_free_rate: float - The risk free rate. - - Returns - ------- - gamma: float - Returns the value for the gamma. - """ - b = math.exp(-risk_free_rate * time_to_expiration) - x1 = ( - math.log(asset_price / (b * strike_price)) - + 0.5 * (asset_volatility * asset_volatility) * time_to_expiration - ) - x1 = x1 / (asset_volatility * (time_to_expiration**0.5)) - z1 = norm.cdf(x1) - gamma = z1 / (asset_price * asset_volatility * math.sqrt(time_to_expiration)) - - return gamma - - -def calc_vega( - asset_price: float = 100, - asset_volatility: float = 20, - strike_price: float = 120, - time_to_expiration: float = 30, - risk_free_rate: float = 0, -): - """The first-order partial-derivative with respect to the underlying asset volatility of - the Black-Scholes equation is known as vega. Vega refers to how the option value - changes when there is a change in the underlying asset volatility. Multiplying vega by - a +-1% change in the underlying asset volatility, holding all other parameters constant, will give - you the new value of the option. Vega will be positive for long positions and negative for short positions. - - Parameters - ---------- - asset_price: int - The price. - asset_volatility: float - The implied volatility. - strike_price: float - The strike price. - time_to_expiration: float - The amount of days until expiration. Use annual notation thus a month would be 30 / 360. - risk_free_rate: float - The risk free rate. - - Returns - ------- - vega: float - Returns the value for the gamma. - """ - b = math.exp(-risk_free_rate * time_to_expiration) - x1 = ( - math.log(asset_price / (b * strike_price)) - + 0.5 * (asset_volatility * asset_volatility) * time_to_expiration - ) - x1 = x1 / (asset_volatility * (time_to_expiration**0.5)) - z1 = norm.cdf(x1) - vega = asset_price * z1 * math.sqrt(time_to_expiration) - - return vega / 100 diff --git a/openbb_terminal/stocks/options/hedge/hedge_view.py b/openbb_terminal/stocks/options/hedge/hedge_view.py deleted file mode 100644 index 69c6586805f..00000000000 --- a/openbb_terminal/stocks/options/hedge/hedge_view.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Hedge view""" - -__docformat__ = "numpy" - -import logging -from typing import Optional - -import pandas as pd - -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 - -logger = logging.getLogger(__name__) - - -def add_and_show_greeks( - price: float = 100, - implied_volatility: float = 20, - strike: float = 120, - days: float = 30, - sign: int = 1, -): - """Determine the delta, gamma and vega value of the portfolio and/or options and show them. - - Parameters - ---------- - price: float - The price. - implied_volatility: float - The implied volatility. - strike: float - The strike price. - days: float - The amount of days until expiration. Use annual notation thus a month would be 30 / 360. - sign: int - Whether you have a long (1) or short (-1) position - - Returns - ------- - delta: float - gamma: float - vega: float - """ - # Add in hedge option - delta, gamma, vega = hedge_model.add_hedge_option( - price, implied_volatility, strike, days, sign - ) - - # Show the added delta, gamma and vega positions. Next to that, also show the inputted - # implied volatility and strike - positions = pd.DataFrame( - [delta, gamma, vega, implied_volatility, strike], - index=["Delta", "Gamma", "Vega", "Implied Volatility", "Strike Price"], - columns=["Positions"], - ) - - # Show table - print_rich_table(positions, show_index=True, headers=list(positions.columns)) - - return delta, gamma, vega - - -def show_calculated_hedge( - portfolio_option_amount: float = 100, - side: str = "Call", - greeks: Optional[dict] = None, - sign: int = 1, -): - """Determine the hedge position and the weights within each option and - underlying asset to hold a neutral portfolio and show them - - Parameters - ---------- - portfolio_option_amount: float - Number to show - side: str - Whether you have a Call or Put instrument - greeks: dict - Dictionary containing delta, gamma and vega values for the portfolio and option A and B. Structure is - as follows: {'Portfolio': {'Delta': VALUE, 'Gamma': VALUE, 'Vega': VALUE}} etc - sign: int - Whether you have a long (1) or short (-1) position - - Returns - ------- - A table with the neutral portfolio weights. - """ - if greeks is None: - greeks = { - "Portfolio": { - "Delta": 1, - "Gamma": 9.1268e-05, - "Vega": 5.4661, - }, - "Option A": { - "Delta": 1, - "Gamma": 9.1268e-05, - "Vega": 5.4661, - }, - "Option B": { - "Delta": 1, - "Gamma": 9.1268e-05, - "Vega": 5.4661, - }, - } - # Calculate hedge position - ( - weight_option_a, - weight_option_b, - weight_shares, - is_singular, - ) = hedge_model.calc_hedge(portfolio_option_amount, side, greeks, sign) - - if sum([weight_option_a, weight_option_b, weight_shares]): - # Show the weights that would create a neutral portfolio - positions = pd.DataFrame( - [weight_option_a, weight_option_b, weight_shares], - index=["Weight Option A", "Weight Option B", "Weight Shares"], - columns=["Positions"], - ) - - print_rich_table( - positions, - title="Neutral Portfolio Weights", - headers=list(positions.columns), - show_index=True, - ) - - if is_singular: - console.print( - "[red]Warning\n[/red]" - "The selected combination of options yields multiple solutions.\n" - "This is the first feasible solution, possibly not the best one." - ) - else: - console.print( - "[red]Due to there being multiple solutions (Singular Matrix) the current options " - "combination can not be solved. Please input different options.[/red]" - ) diff --git a/openbb_terminal/stocks/options/options_controller.py b/openbb_terminal/stocks/options/options_controller.py index 5631635a1a1..d0e136a96ce 100644 --- a/openbb_terminal/stocks/options/options_controller.py +++ b/openbb_terminal/stocks/options/options_controller.py @@ -36,7 +36,6 @@ from openbb_terminal.stocks.options import ( yfinance_model, yfinance_view, ) -from openbb_terminal.stocks.options.hedge import hedge_controller from openbb_terminal.stocks.options.options_view import ( display_chains, display_expiry_dates, @@ -81,7 +80,6 @@ class OptionsController(BaseController): ] CHOICES_MENUS = [ "pricing", - "hedge", ] grhist_greeks_choices = [ @@ -276,7 +274,6 @@ class OptionsController(BaseController): mt.add_cmd("greeks", self.ticker and self.selected_date) mt.add_cmd("eodchain", self.ticker and self.selected_date) mt.add_raw("\n") - mt.add_menu("hedge", self.ticker and self.selected_date) console.print(text=mt.menu_text, menu="Stocks - Options") def custom_reset(self): @@ -1405,23 +1402,6 @@ class OptionsController(BaseController): show_all=ns_parser.all, ) - @log_start_end(log=logger) - def call_hedge(self, _): - """Process hedge command""" - if self.ticker: - if self.selected_date: - self.queue = self.load_class( - hedge_controller.HedgeController, - self.ticker, - self.selected_date, - self.queue, - ) - else: - console.print("No expiry loaded. First use `exp {expiry date}`") - - else: - console.print("No ticker loaded. First use `load `") - @log_start_end(log=logger) def call_eodchain(self, other_args: List[str]): """Process greeks command""" diff --git a/openbb_terminal/terminal_controller.py b/openbb_terminal/terminal_controller.py index abda862b6bd..d4d67a1bd70 100644 --- a/openbb_terminal/terminal_controller.py +++ b/openbb_terminal/terminal_controller.py @@ -89,7 +89,6 @@ class TerminalController(BaseController): "guess", "news", "intro", - "askobb", "record", ] CHOICES_MENUS = [ @@ -232,8 +231,6 @@ class TerminalController(BaseController): mt.add_cmd("stop") mt.add_cmd("exe") mt.add_raw("\n") - mt.add_cmd("askobb") - mt.add_raw("\n") mt.add_info("_main_menu_") mt.add_menu("stocks") mt.add_menu("crypto") diff --git a/terminal.py b/terminal.py index f9d8d8e5248..d7f8265415b 100644 --- a/terminal.py +++ b/terminal.py @@ -1,11 +1,14 @@ import sys +import warnings from multiprocessing import freeze_support -import openbb_terminal.config_terminal as cfg +# pylint:disable=wrong-import-position +warnings.filterwarnings("ignore", category=FutureWarning, module="yfinance.*") +import openbb_terminal.config_terminal as cfg # noqa: E402 # pylint:disable=unused-import,import-outside-toplevel -import openbb_terminal.core.session.current_system as syst # noqa: F401 -from openbb_terminal.terminal_helper import ( +import openbb_terminal.core.session.current_system as syst # noqa: F401,E402 +from openbb_terminal.terminal_helper import ( # noqa: E402 hide_splashscreen, is_auth_enabled, is_installer,