Fixing various bugs (#3474)

* Fixed warning message

* Refactored screener menu

* Pylint fix

* Fixed bugs

* With bug

* Choices : update error message

* Fixed issues

* Fixed more bugs

* Fixed more bugs

Co-authored-by: Chavithra PARANA <chavithra@gmail.com>
This commit is contained in:
Colin Delahunty
2022-11-18 15:08:29 +01:00
committed by GitHub
parent 2273a705fd
commit cca41d4f08
10 changed files with 158 additions and 221 deletions

View File

@@ -152,7 +152,7 @@ def _get_argument_parser(
else:
raise AssertionError(
"One of these functions should be called once:\n"
" - parse_known_args_and_warn\n"
" - patch_parse_simple_args\n"
" - parse_known_args_and_warn\n"
)
@@ -201,7 +201,7 @@ def build_controller_choice_map(controller) -> dict:
argument_parser=argument_parser
)
except Exception as exception:
if environ["DEBUG_MODE"] == "true":
if environ.get("DEBUG_MODE", "false") == "true":
raise exception
return controller_choice_map

View File

@@ -769,9 +769,7 @@ class BehaviouralAnalysisController(StockBaseController):
"[red]Ticker provided doesn't exist, load another one.\n[/red]"
)
else:
console.print(
"[red]Words or sentences to be correlated against with, need to be provided.\n[/red]"
)
console.print("[red]Please provide a phrase for analysis.\n[/red]")
else:
console.print(
"[red]No ticker loaded. Please load using 'load <ticker>'.\n[/red]"

View File

@@ -26,7 +26,7 @@ def check_premium_key(json_response: Dict) -> bool:
"any of the premium plans at https://www.alphavantage.co/premium/ to instantly unlock all premium endpoints"
}:
console.print(
"This is a premium endpoint for AlphaVantage. Please use a premium key\n"
"This is a premium endpoint for AlphaVantage. Please use a premium key.\n"
)
return True
return False
@@ -614,8 +614,11 @@ def get_fraud_ratios(symbol: str, detail: bool = False) -> pd.DataFrame:
df_bs, _ = fd.get_balance_sheet_annual(symbol=symbol)
df_is, _ = fd.get_income_statement_annual(symbol=symbol)
except Exception as e:
console.print(e)
except ValueError as e:
if "premium endpoint" in str(e):
console.print(
"This is a premium endpoint for AlphaVantage. Please use a premium key.\n"
)
return pd.DataFrame()
# pylint: disable=no-member
@@ -729,8 +732,11 @@ def get_dupont(symbol: str) -> pd.DataFrame:
df_bs, _ = fd.get_balance_sheet_annual(symbol=symbol)
df_is, _ = fd.get_income_statement_annual(symbol=symbol)
except Exception as e:
console.print(e)
except ValueError as e:
if "premium endpoint" in str(e):
console.print(
"This is a premium endpoint for AlphaVantage. Please use a premium key.\n"
)
return pd.DataFrame()
# pylint: disable=no-member

View File

@@ -436,7 +436,6 @@ def display_dupont(
"""
df = av_model.get_dupont(symbol)
if df.empty:
console.print("[red]Invalid response from AlphaVantage[/red]\n")
return
if raw:
print_rich_table(

View File

@@ -12,7 +12,6 @@ from openbb_terminal.custom_prompt_toolkit import NestedCompleter
from openbb_terminal import feature_flags as obbff
from openbb_terminal.core.config.paths import USER_PRESETS_DIRECTORY
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_classes import AllowArgsWithWhiteSpace
from openbb_terminal.helper_funcs import (
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
EXPORT_ONLY_RAW_DATA_ALLOWED,
@@ -29,20 +28,21 @@ from openbb_terminal.stocks.screener import (
finviz_view,
yahoofinance_view,
screener_view,
screener_helper,
)
logger = logging.getLogger(__name__)
# pylint: disable=E1121
# TODO: HELP WANTED! This menu required some refactoring. Things that can be addressed:
# - better preset management (MVC style).
PRESETS_PATH = USER_PRESETS_DIRECTORY / "stocks" / "screener"
class ScreenerController(BaseController):
"""Screener Controller class"""
CHOICES_MENUS = ["ca"]
CHOICES_COMMANDS = [
"view",
"set",
@@ -53,7 +53,6 @@ class ScreenerController(BaseController):
"ownership",
"performance",
"technical",
"ca",
]
PRESETS_PATH_DEFAULT = Path(__file__).parent / "presets"
@@ -73,6 +72,7 @@ class ScreenerController(BaseController):
historical_candle_choices = ["o", "h", "l", "c", "a"]
PATH = "/stocks/scr/"
CHOICES_GENERATION = True
def __init__(self, queue: List[str] = None):
"""Constructor"""
@@ -82,36 +82,7 @@ class ScreenerController(BaseController):
self.screen_tickers: List = list()
if session and obbff.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
choices["view"] = {c: {} for c in self.preset_choices}
choices["set"] = {c: {} for c in self.preset_choices}
choices["historical"] = {
"--start": None,
"-s": "--start",
"--type": {c: {} for c in self.historical_candle_choices},
"--no-scale": {},
"-n": "--no-scale",
"--limit": None,
"-l": "--limit",
}
screener_standard = {
"--preset": {c: {} for c in self.preset_choices},
"-p": "--preset",
"--sort": {c: {} for c in finviz_view.d_cols_to_sort["overview"]},
"-s": "--sort",
"--limit": None,
"-l": "--limit",
"--reverse": {},
"-r": "--reverse",
}
choices["overview"] = screener_standard
choices["valuation"] = screener_standard
choices["financial"] = screener_standard
choices["ownership"] = screener_standard
choices["performance"] = screener_standard
choices["technical"] = screener_standard
choices: dict = self.choices_default
self.completer = NestedCompleter.from_nested_dict(choices)
def parse_input(self, an_input: str) -> List:
@@ -311,10 +282,11 @@ class ScreenerController(BaseController):
parser.add_argument(
"-s",
"--sort",
action=AllowArgsWithWhiteSpace,
choices=screener_helper.finviz_choices("overview"),
type=str.lower,
dest="sort",
default="",
nargs="+",
metavar="SORT",
default="Ticker",
help="Sort elements of the table.",
)
if other_args and "-" not in other_args[0][0]:
@@ -329,29 +301,15 @@ class ScreenerController(BaseController):
else:
preset = self.preset
if ns_parser.sort:
if ns_parser.sort not in finviz_view.d_cols_to_sort["overview"]:
console.print(f"{ns_parser.sort} not a valid sort choice.\n")
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=preset,
data_type="overview",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=preset,
data_type="overview",
limit=ns_parser.limit,
ascend=ns_parser.ascend,
sortby=ns_parser.sort,
export=ns_parser.export,
)
sort_map = screener_helper.finviz_map("overview")
self.screen_tickers = finviz_view.screener(
loaded_preset=preset,
data_type="overview",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=sort_map[ns_parser.sort],
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_valuation(self, other_args: List[str]):
@@ -399,9 +357,10 @@ class ScreenerController(BaseController):
"-s",
"--sort",
dest="sort",
default="",
nargs="+",
action=AllowArgsWithWhiteSpace,
default="Ticker",
choices=screener_helper.finviz_choices("valuation"),
type=str.lower,
metavar="SORT",
help="Sort elements of the table.",
)
if other_args and "-" not in other_args[0][0]:
@@ -411,29 +370,15 @@ class ScreenerController(BaseController):
)
if ns_parser:
if ns_parser.sort:
if ns_parser.sort not in finviz_view.d_cols_to_sort["valuation"]:
console.print(f"{ns_parser.sort} not a valid sort choice.\n")
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="valuation",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="valuation",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
sort_map = screener_helper.finviz_map("valuation")
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="valuation",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=sort_map[ns_parser.sort],
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_financial(self, other_args: List[str]):
@@ -480,10 +425,11 @@ class ScreenerController(BaseController):
parser.add_argument(
"-s",
"--sort",
action=AllowArgsWithWhiteSpace,
choices=screener_helper.finviz_choices("financial"),
type=str.lower,
dest="sort",
default="",
nargs="+",
metavar="SORT",
default="Ticker",
help="Sort elements of the table.",
)
if other_args and "-" not in other_args[0][0]:
@@ -493,29 +439,15 @@ class ScreenerController(BaseController):
)
if ns_parser:
if ns_parser.sort:
if ns_parser.sort not in finviz_view.d_cols_to_sort["financial"]:
console.print(f"{ns_parser.sort} not a valid sort choice.\n")
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="financial",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="financial",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
sort_map = screener_helper.finviz_map("financial")
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="financial",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=sort_map[ns_parser.sort],
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_ownership(self, other_args: List[str]):
@@ -563,9 +495,10 @@ class ScreenerController(BaseController):
"-s",
"--sort",
dest="sort",
default="",
nargs="+",
action=AllowArgsWithWhiteSpace,
metavar="SORT",
default="Ticker",
choices=screener_helper.finviz_choices("ownership"),
type=str.lower,
help="Sort elements of the table.",
)
if other_args and "-" not in other_args[0][0]:
@@ -576,29 +509,15 @@ class ScreenerController(BaseController):
if ns_parser:
if ns_parser.sort:
if ns_parser.sort not in finviz_view.d_cols_to_sort["ownership"]:
console.print(f"{ns_parser.sort} not a valid sort choice.\n")
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="ownership",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="ownership",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
sort_map = screener_helper.finviz_map("ownership")
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="ownership",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=sort_map[ns_parser.sort],
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_performance(self, other_args: List[str]):
@@ -645,10 +564,11 @@ class ScreenerController(BaseController):
parser.add_argument(
"-s",
"--sort",
action=AllowArgsWithWhiteSpace,
choices=screener_helper.finviz_choices("performance"),
type=str.lower,
dest="sort",
default="",
nargs="+",
default="Ticker",
metavar="SORTBY",
help="Sort elements of the table.",
)
if other_args and "-" not in other_args[0][0]:
@@ -657,31 +577,16 @@ class ScreenerController(BaseController):
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
)
sort_map = screener_helper.finviz_map("performance")
if ns_parser:
if ns_parser.sort:
if ns_parser.sort not in finviz_view.d_cols_to_sort["performance"]:
console.print(f"{ns_parser.sort} not a valid sort choice.\n")
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="performance",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="performance",
limit=ns_parser.limit,
ascend=ns_parser.store_true,
sortby=ns_parser.sort,
export=ns_parser.export,
)
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="performance",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=sort_map[ns_parser.sort],
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_technical(self, other_args: List[str]):
@@ -728,10 +633,10 @@ class ScreenerController(BaseController):
parser.add_argument(
"-s",
"--sort",
action=AllowArgsWithWhiteSpace,
choices=screener_helper.finviz_choices("technical"),
type=str.lower,
dest="sort",
default="",
nargs="+",
default="Ticker",
help="Sort elements of the table.",
)
if other_args and "-" not in other_args[0][0]:
@@ -741,39 +646,27 @@ class ScreenerController(BaseController):
)
if ns_parser:
if ns_parser.sort:
if ns_parser.sort not in finviz_view.d_cols_to_sort["technical"]:
console.print(f"{ns_parser.sort} not a valid sort choice.\n")
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="technical",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
else:
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="technical",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=ns_parser.sort,
export=ns_parser.export,
)
sort_map = screener_helper.finviz_map("technical")
self.screen_tickers = finviz_view.screener(
loaded_preset=self.preset,
data_type="technical",
limit=ns_parser.limit,
ascend=ns_parser.reverse,
sortby=sort_map[ns_parser.sort],
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_ca(self, _):
"""Call the comparison analysis menu with selected tickers"""
if self.screen_tickers:
self.queue = ca_controller.ComparisonAnalysisController(
self.screen_tickers, self.queue
).menu(custom_path_menu_above="/stocks/")
self.queue = self.load_class(
ca_controller.ComparisonAnalysisController,
self.screen_tickers,
self.queue,
)
else:
console.print(
"Some tickers must be screened first through one of the presets!\n"
"Please select a screener using 'set' and then run 'historical' "
"before going to the CA menu.\n"
)

View File

@@ -0,0 +1,34 @@
from typing import List, Dict
from openbb_terminal.stocks.screener import finviz_view
def finviz_choices(section: str) -> List[str]:
"""Generates the available choices for the given section. Formatted in an argparse friendly way
Parameters
----------
section: str
The section to generate the choices for
Returns
-------
List[str]
The argparse friendly strings
"""
return [x.replace(" ", "").lower() for x in finviz_view.d_cols_to_sort[section]]
def finviz_map(section: str) -> Dict[str, str]:
"""Generates a mapping of our custom formatted choices to the actual choices
Parameters
----------
section: str
The section to generate the choices for
Returns
-------
Dict[str]
Keys are the arpgarse friendly version, values are the original version
"""
return {x.replace(" ", "").lower(): x for x in finviz_view.d_cols_to_sort[section]}

View File

@@ -111,9 +111,12 @@ def historical(
l_stocks = screen.screener_view(verbose=0)
limit_random_stocks = False
df_screener = pd.DataFrame()
final_screener = pd.DataFrame()
if l_stocks:
if len(l_stocks) < 2:
console.print("Please select two or more tickers.\n")
return pd.DataFrame(), [], False
if len(l_stocks) > limit:
random.shuffle(l_stocks)
l_stocks = sorted(l_stocks[:limit])
@@ -123,25 +126,29 @@ def historical(
)
limit_random_stocks = True
selector = d_candle_types[type_candle]
df_screener = yf.download(
l_stocks, start=start_date, progress=False, threads=False
)[d_candle_types[type_candle]][l_stocks]
df_screener = df_screener[l_stocks]
)[selector]
clean_screener = df_screener[l_stocks]
final_screener = clean_screener[l_stocks]
if np.any(df_screener.isna()):
nan_tickers = df_screener.columns[df_screener.isna().sum() >= 1].to_list()
if np.any(final_screener.isna()):
nan_tickers = final_screener.columns[
final_screener.isna().sum() >= 1
].to_list()
console.print(
f"NaN values found in: {', '.join(nan_tickers)}. Replacing with zeros."
)
df_screener = df_screener.fillna(0)
final_screener = final_screener.fillna(0)
# This puts everything on 0-1 scale for visualizing
if normalize:
mm_scale = MinMaxScaler()
df_screener = pd.DataFrame(
mm_scale.fit_transform(df_screener),
columns=df_screener.columns,
index=df_screener.index,
final_screener = pd.DataFrame(
mm_scale.fit_transform(final_screener),
columns=final_screener.columns,
index=final_screener.index,
)
return df_screener, l_stocks, limit_random_stocks
return final_screener, l_stocks, limit_random_stocks

View File

@@ -64,6 +64,9 @@ def historical(
preset_loaded, limit, start_date, type_candle, normalize
)
if df_screener.empty:
return []
if l_stocks:
# This plot has 1 axis
if not external_axes:

View File

@@ -1,2 +0,0 @@
[red]Invalid response from AlphaVantage[/red]