diff --git a/ROADMAP.md b/ROADMAP.md
index 96116af982a..2e5c86b6dd2 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -181,6 +181,7 @@
* [x] Add Screener menu with overview, valuation, financial, ownership, performance, technical commands based on filter presets from Finviz (@didier) - [PR #314](https://github.com/DidierRLopes/GamestonkTerminal/pull/314)
* [x] Add README with explanation of how presets are stored and can be added by experienced users. (@didier) - [PR #314](https://github.com/DidierRLopes/GamestonkTerminal/pull/314)
* [x] Add screener signals (e.g. top gainers, new highs, most volatile, oversold, major news, ...) from Finviz (@didier) - [PR #314](https://github.com/DidierRLopes/GamestonkTerminal/pull/314)
+* [x] Plot screener historical using Yahoo Finance data (@didier) - [PR #319](https://github.com/DidierRLopes/GamestonkTerminal/pull/319)
**NEXT**
diff --git a/gamestonk_terminal/screener/README.md b/gamestonk_terminal/screener/README.md
index 5011b355582..d805ade7fba 100644
--- a/gamestonk_terminal/screener/README.md
+++ b/gamestonk_terminal/screener/README.md
@@ -6,6 +6,8 @@ This menu aims to filter stocks based on pre-specified preset filters, and the u
* view available presets
* [set](#set)
* set one of the available presets
+* [historical](#historical)
+ * view historical price [Yahoo Finance]
* [overview](#overview)
* overview information [Finviz]
* [valuation](#valuation)
@@ -45,6 +47,23 @@ Set preset from under presets folder.
* -p : Filter presets
+## historical
+
+```text
+usage: historical [-t {o,h,l,c,a}] [-s {top_gainers,top_losers,...}] [--start START]
+```
+
+View historical price of stocks. [Source: Yahoo Finance]
+
+* -t : Type of candle: Default 'a' for adjusted close.
+* -s : Signal type. Default: None. When specified (see list available in [signals](#signals)), the preset is disregarded.
+* --start : Start time with format "%Y-%m-%d". Default 6 months earlier.
+
+
+
+
+
+
## overview
```text
diff --git a/gamestonk_terminal/screener/screener_controller.py b/gamestonk_terminal/screener/screener_controller.py
index 6d71576f95b..40640cc6d8b 100644
--- a/gamestonk_terminal/screener/screener_controller.py
+++ b/gamestonk_terminal/screener/screener_controller.py
@@ -4,6 +4,7 @@ __docformat__ = "numpy"
import os
import argparse
from typing import List
+import matplotlib.pyplot as plt
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import get_flair
from gamestonk_terminal.menu import session
@@ -12,6 +13,7 @@ from gamestonk_terminal.helper_funcs import (
parse_known_args_and_warn,
)
from gamestonk_terminal.screener import finviz_view
+from gamestonk_terminal.screener import yahoo_finance_view
class ScreenerController:
@@ -24,6 +26,7 @@ class ScreenerController:
"quit",
"view",
"set",
+ "historical",
"overview",
"valuation",
"financial",
@@ -56,6 +59,7 @@ class ScreenerController:
print("")
print(f"PRESET: {self.preset}")
print("")
+ print(" historical view historical price")
print(" overview overview information")
print(" valuation valuation information")
print(" financial financial information")
@@ -181,6 +185,10 @@ class ScreenerController:
"""Process overview command"""
self.set_preset(self, other_args)
+ def call_historical(self, other_args: List[str]):
+ """Process historical command"""
+ yahoo_finance_view.historical(other_args, self.preset)
+
def call_overview(self, other_args: List[str]):
"""Process overview command"""
finviz_view.screener(other_args, self.preset, "overview")
@@ -230,6 +238,8 @@ def menu():
an_input = input(f"{get_flair()} (scr)> ")
try:
+ plt.close("all")
+
process_input = scr_controller.switch(an_input)
if process_input is not None:
diff --git a/gamestonk_terminal/screener/yahoo_finance_view.py b/gamestonk_terminal/screener/yahoo_finance_view.py
new file mode 100644
index 00000000000..6ad1c6e93bb
--- /dev/null
+++ b/gamestonk_terminal/screener/yahoo_finance_view.py
@@ -0,0 +1,186 @@
+import argparse
+from typing import List
+from pandas.plotting import register_matplotlib_converters
+import matplotlib.pyplot as plt
+import datetime
+import configparser
+import yfinance as yf
+from finvizfinance.screener import ticker
+from gamestonk_terminal.screener import finviz_view
+from gamestonk_terminal import feature_flags as gtff
+from gamestonk_terminal.helper_funcs import (
+ parse_known_args_and_warn,
+ plot_autoscale,
+ valid_date,
+)
+from gamestonk_terminal.config_plot import PLOT_DPI
+
+register_matplotlib_converters()
+
+d_candle_types = {
+ "o": "Open",
+ "h": "High",
+ "l": "Low",
+ "c": "Close",
+ "a": "Adj Close",
+}
+
+
+def check_one_of_ohlca(type_candles: str) -> str:
+ if (
+ type_candles == "o"
+ or type_candles == "h"
+ or type_candles == "l"
+ or type_candles == "c"
+ or type_candles == "a"
+ ):
+ return type_candles
+ raise argparse.ArgumentTypeError("The type of candles specified is not recognized")
+
+
+def historical(other_args: List[str], preset_loaded: str):
+ """View historical price of stocks that meet preset
+
+ Parameters
+ ----------
+ other_args : List[str]
+ Command line arguments to be processed with argparse
+ ticker : str
+ Loaded preset filter
+ """
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ prog="historical",
+ description="""Historical price comparison between similar companies [Source: Yahoo Finance]
+ """,
+ )
+ parser.add_argument(
+ "--start",
+ type=valid_date,
+ default=datetime.datetime.now() - datetime.timedelta(days=6 * 30),
+ dest="start",
+ help="The starting date (format YYYY-MM-DD) of the historical price to plot",
+ )
+ parser.add_argument(
+ "-t",
+ "--type",
+ action="store",
+ dest="type_candle",
+ type=check_one_of_ohlca,
+ default="a", # in case it's adjusted close
+ help=("type of candles: o-open, h-high, l-low, c-close, a-adjusted close."),
+ )
+ parser.add_argument(
+ "-s",
+ "--signal",
+ action="store",
+ dest="signal",
+ type=str,
+ default=None,
+ help="Signal",
+ choices=list(finviz_view.d_signals.keys()),
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ preset_filter = configparser.RawConfigParser()
+ preset_filter.optionxform = str # type: ignore
+ preset_filter.read(
+ "gamestonk_terminal/screener/presets/" + preset_loaded + ".ini"
+ )
+
+ d_general = preset_filter["General"]
+ d_filters = {
+ **preset_filter["Descriptive"],
+ **preset_filter["Fundamental"],
+ **preset_filter["Technical"],
+ }
+
+ d_filters = {k: v for k, v in d_filters.items() if v}
+
+ screen = ticker.Ticker()
+
+ if ns_parser.signal:
+ screen.set_filter(signal=finviz_view.d_signals[ns_parser.signal])
+ else:
+ if d_general["Signal"]:
+ screen.set_filter(filters_dict=d_filters, signal=d_general["Signal"])
+ else:
+ screen.set_filter(filters_dict=d_filters)
+
+ l_min = list()
+ l_leg = list()
+ plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ l_stocks = screen.ScreenerView(verbose=0)
+
+ if len(l_stocks) > 10:
+ print(
+ "We limit stocks shown to 10, as after this the plot becomes too noisy. "
+ "Also, we ran out of different colors."
+ )
+ l_stocks = l_stocks[:10]
+
+ while l_stocks:
+ l_parsed_stocks = list()
+ for symbol in l_stocks:
+ try:
+ df_similar_stock = yf.download(
+ symbol,
+ start=datetime.datetime.strftime(ns_parser.start, "%Y-%m-%d"),
+ progress=False,
+ threads=False,
+ )
+ if not df_similar_stock.empty:
+ plt.plot(
+ df_similar_stock.index,
+ df_similar_stock[
+ d_candle_types[ns_parser.type_candle]
+ ].values,
+ )
+ l_min.append(df_similar_stock.index[0])
+ l_leg.append(symbol)
+
+ l_parsed_stocks.append(symbol)
+ except Exception as e:
+ print("")
+ print(e)
+ print(
+ "Disregard previous error, which is due to API Rate limits from Yahoo Finance."
+ )
+ print(
+ f"Because we like '{symbol}', and we won't leave without getting data from it."
+ )
+
+ for parsed_stock in l_parsed_stocks:
+ l_stocks.remove(parsed_stock)
+
+ if ns_parser.signal:
+ plt.title(
+ f"Screener Historical Price using {finviz_view.d_signals[ns_parser.signal]} signal"
+ )
+ else:
+ plt.title(f"Screener Historical Price using {preset_loaded} preset")
+
+ plt.xlabel("Time")
+ plt.ylabel("Share Price ($)")
+ plt.legend(l_leg)
+ plt.grid(b=True, which="major", color="#666666", linestyle="-")
+ plt.minorticks_on()
+ plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
+ # ensures that the historical data starts from same datapoint
+ plt.xlim([max(l_min), df_similar_stock.index[-1]])
+
+ if gtff.USE_ION:
+ plt.ion()
+
+ plt.show()
+ print("")
+
+ except SystemExit:
+ print("Similar companies need to be provided", "\n")
+ except Exception as e:
+ print(e, "\n")