summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjmaslek <jmaslek11@gmail.com>2021-09-02 20:30:33 -0400
committerGitHub <noreply@github.com>2021-09-02 20:30:33 -0400
commitffcaa19e793351875592c6f0fb5a9f818efa713f (patch)
treea0a08ca4a300bfbb86cc5d76964441f8cfff734e
parent6d675a02cec675f1308809031b7779bc88d76d66 (diff)
Refactor ca menu (#726)
* First wave of refactoring. yfinance and marketwatch menus * Finbrain commands refactored * Refactor finviz commands * Add custom ML model for running get * Bug fixes + dim ca commands when no similar selected * Add pylint disable * move ml command into yfinance stuff. fix typos * Change historical/corr to have interested ticker first * Pylint error * Comment out fa test
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/ca_controller.py902
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/finbrain_model.py46
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/finbrain_view.py384
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/finnhub_model.py22
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/finviz_compare_model.py78
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/finviz_compare_view.py124
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/market_watch_view.py539
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/marketwatch_model.py256
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/marketwatch_view.py181
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/polygon_model.py36
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/yahoo_finance_model.py129
-rw-r--r--gamestonk_terminal/stocks/comparison_analysis/yahoo_finance_view.py350
-rw-r--r--gamestonk_terminal/stocks/stocks_helper.py219
13 files changed, 1849 insertions, 1417 deletions
diff --git a/gamestonk_terminal/stocks/comparison_analysis/ca_controller.py b/gamestonk_terminal/stocks/comparison_analysis/ca_controller.py
index d2a64f04806..4b2d58199ae 100644
--- a/gamestonk_terminal/stocks/comparison_analysis/ca_controller.py
+++ b/gamestonk_terminal/stocks/comparison_analysis/ca_controller.py
@@ -1,26 +1,35 @@
"""Comparison Analysis Controller Module"""
__docformat__ = "numpy"
-
+# pylint:disable=too-many-lines
import argparse
import os
import random
from typing import List
-from datetime import datetime
-import requests
+
+from colorama import Style
import pandas as pd
from matplotlib import pyplot as plt
-from finvizfinance.screener.overview import Overview
from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal import feature_flags as gtff
-from gamestonk_terminal import config_terminal as cfg
-from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn
-from gamestonk_terminal.stocks.comparison_analysis import yahoo_finance_view
-from gamestonk_terminal.stocks.comparison_analysis import market_watch_view
-from gamestonk_terminal.stocks.comparison_analysis import finbrain_view
-from gamestonk_terminal.stocks.comparison_analysis import finviz_compare_view
-from gamestonk_terminal.portfolio.portfolio_optimization import po_controller
+from gamestonk_terminal.helper_funcs import (
+ check_non_negative,
+ get_flair,
+ parse_known_args_and_warn,
+)
from gamestonk_terminal.menu import session
+from gamestonk_terminal.portfolio.portfolio_optimization import po_controller
+from gamestonk_terminal.stocks.comparison_analysis import (
+ finbrain_view,
+ finnhub_model,
+ finviz_compare_model,
+ finviz_compare_view,
+ marketwatch_view,
+ polygon_model,
+ yahoo_finance_view,
+ yahoo_finance_model,
+)
+from gamestonk_terminal.stocks.stocks_helper import load
# pylint: disable=E1121
@@ -29,14 +38,15 @@ class ComparisonAnalysisController:
"""Comparison Analysis Controller class"""
# Command choices
- CHOICES = [
- "?",
- "cls",
- "help",
- "q",
- "quit",
- "get",
+ CHOICES = ["?", "cls", "help", "q", "quit"]
+
+ CHOICES_COMMANDS = [
+ "load",
+ "getpoly",
+ "getfinnhub",
+ "getfinviz",
"select",
+ "add",
"historical",
"hcorr",
"income",
@@ -50,13 +60,15 @@ class ComparisonAnalysisController:
"ownership",
"performance",
"technical",
- "po",
+ "tsne",
]
+ CHOICES_MENUS = ["po"]
+ CHOICES += CHOICES_COMMANDS + CHOICES_MENUS
def __init__(
self,
ticker: str,
- start: datetime,
+ start: str,
interval: str,
stock: pd.DataFrame,
):
@@ -66,7 +78,7 @@ class ComparisonAnalysisController:
----------
ticker : str
Stock ticker
- start : datetime
+ start : str
Start time
interval : str
Time interval
@@ -89,135 +101,186 @@ class ComparisonAnalysisController:
def print_help(self):
"""Print help"""
- print(
- "https://github.com/GamestonkTerminal/GamestonkTerminal/tree/main/gamestonk_terminal"
- "/stocks/comparison_analysis"
- )
-
+ all_loaded = bool(not self.ticker or not self.similar)
s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"]
-
if self.start:
- print(
- f"\n{s_intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
- )
+ stock_str = f"{s_intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
else:
- print(f"\n{s_intraday} Stock: {self.ticker}")
-
- if self.similar:
- print(f"[{self.user}] Similar Companies: {', '.join(self.similar)}")
-
- print("\nComparison Analysis Mode:")
- print(" cls clear screen")
- print(" ?/help show this menu again")
- print(" q quit this menu, and shows back to main menu")
- print(" quit quit to abandon program")
- print("")
- print(" get get similar companies")
- print(" select select similar companies")
- print("")
- print(" historical historical price data comparison")
- print(" hcorr historical price correlation")
- print(" income income financials comparison")
- print(" balance balance financials comparison")
- print(" cashflow cashflow comparison")
- print(" sentiment sentiment analysis comparison")
- print(" scorr sentiment correlation")
- print("")
- print(" overview brief overview comparison")
- print(" valuation brief valuation comparison")
- print(" financial brief financial comparison")
- print(" ownership brief ownership comparison")
- print(" performance brief performance comparison")
- print(" technical brief technical comparison")
- print("")
-
- if self.similar:
- print("> po portfolio optimization for selected tickers")
- print("")
- return
+ stock_str = f"{s_intraday} Stock: {self.ticker}"
+ help_str = f"""
+https://github.com/GamestonkTerminal/GamestonkTerminal/tree/main/gamestonk_terminal/stocks/comparison_analysis
+
+{stock_str}
+Similar Companies: {', '.join(self.similar) or None}
+
+Comparison Analysis:
+ cls clear screen
+ ?/help show this menu again
+ q quit this menu, and shows back to main menu
+ quit quit to abandon program
+
+ load load new base ticker
+ add add more companies to current selected (max 10 total)
+ select reset and select similar companies
+
+Get Similar:
+ tsne run TSNE on all SP500 stocks and returns 10 closest tickers
+ getpoly get similar stocks from polygon API
+ getfinnhub get similar stocks from finnhub API
+ getfinviz get similar stocks from finviz API
+{Style.DIM if all_loaded else Style.NORMAL}Yahoo Finance:
+ historical historical price data comparison
+ hcorr historical price correlation {Style.RESET_ALL}
+Market Watch:
+ income income financials comparison
+ balance balance financials comparison
+ cashflow cashflow comparison
+Finbrain:
+ sentiment sentiment analysis comparison {Style.DIM if all_loaded else Style.NORMAL}
+ scorr sentiment correlation{Style.RESET_ALL}
+Finviz:
+ overview brief overview comparison
+ valuation brief valuation comparison
+ financial brief financial comparison
+ ownership brief ownership comparison
+ performance brief performance comparison
+ technical brief technical comparison
+
+> po portfolio optimization for selected tickers
+ """
+ print(help_str)
- def get_similar_companies(self, other_args: List[str]):
- """Get similar companies. [Source: Polygon API]
+ def call_load(self, other_args: List[str]):
+ """Process load command"""
+ self.ticker, self.start, self.interval, self.stock = load(
+ other_args, self.ticker, self.start, self.interval, self.stock
+ )
+ if "." in self.ticker:
+ self.ticker = self.ticker.split(".")[0]
- Parameters
- ----------
- other_args : List[str]
- argparse other args
- """
+ def call_tsne(self, other_args: List[str]):
+ """Process tsne command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
- prog="get",
- description="""Get similar companies to compare with.""",
+ prog="tsne",
+ description="""Get similar companies to compare with using sklearn TSNE.""",
)
parser.add_argument(
- "-s",
- "--source",
- action="store",
- default="finviz",
- dest="source",
- choices=["polygon", "finnhub", "finviz"],
- help="source that provides similar companies",
- )
-
- # If source is finviz the user may want to get
- # similar companies based on Industry and Sector only, and not
- # on the fact that they are based on the same country
- if "finviz" in other_args or not other_args:
- parser.add_argument(
- "--nocountry",
- action="store_true",
- default=False,
- dest="b_no_country",
- help="Similar stocks from finviz using only Industry and Sector.",
- )
+ "-l",
+ "--learnrate",
+ default=200,
+ dest="lr",
+ type=check_non_negative,
+ help="TSNE Learning rate. Typical values are between 50 and 200",
+ )
+ parser.add_argument(
+ "-p", "--no_plot", action="store_true", default=False, dest="no_plot"
+ )
try:
- if other_args:
- if "-" not in other_args[0]:
- other_args.insert(0, "-s")
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+ self.similar = yahoo_finance_model.get_sp500_comps_tsne(
+ self.ticker, lr=ns_parser.lr, no_plot=ns_parser.no_plot
+ )
+ print(f"[ML] Similar Companies: {', '.join(self.similar)}", "\n")
+ except Exception as e:
+ print(e, "\n")
+ def call_getfinviz(self, other_args: List[str]):
+ """Process getfinviz command"""
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="getfinviz",
+ description="""Get similar companies from finviz to compare with.""",
+ )
+ parser.add_argument(
+ "--nocountry",
+ action="store_true",
+ default=False,
+ dest="b_no_country",
+ help="Similar stocks from finviz using only Industry and Sector.",
+ )
+ try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
+ if ns_parser.b_no_country:
+ compare_list = ["Sector", "Industry"]
+ else:
+ compare_list = ["Sector", "Industry", "Country"]
- if ns_parser.source == "polygon":
- result = requests.get(
- f"https://api.polygon.io/v1/meta/symbols/{self.ticker.upper()}/company?&apiKey={cfg.API_POLYGON_KEY}"
- )
+ self.similar, self.user = finviz_compare_model.get_similar_companies(
+ self.ticker, compare_list
+ )
- if result.status_code == 200:
- self.similar = result.json()["similar"]
- self.user = "Polygon"
- else:
- print(result.json()["error"])
+ if self.ticker.upper() in self.similar:
+ self.similar.remove(self.ticker.upper())
- elif ns_parser.source == "finnhub":
- result = requests.get(
- f"https://finnhub.io/api/v1/stock/peers?symbol={self.ticker}&token={cfg.API_FINNHUB_KEY}"
+ if len(self.similar) > 10:
+ random.shuffle(self.similar)
+ self.similar = sorted(self.similar[:10])
+ print(
+ "The limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed.\n",
)
- if result.status_code == 200:
- d_peers = result.json()
+ if self.similar:
+ print(
+ f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n"
+ )
+ except Exception as e:
+ print(e, "\n")
- if d_peers:
- self.similar = d_peers
- self.user = "Finnhub"
- else:
- print("Similar companies not found.")
+ def call_getpoly(self, other_args: List[str]):
+ """Process get command"""
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="getpoly",
+ description="""Get similar companies from polygon to compare with.""",
+ )
- else:
- if ns_parser.b_no_country:
- compare_list = ["Sector", "Industry"]
- else:
- compare_list = ["Sector", "Industry", "Country"]
-
- self.similar = (
- Overview()
- .compare(self.ticker, compare_list, verbose=0)["Ticker"]
- .to_list()
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+ self.similar, self.user = polygon_model.get_similar_companies(self.ticker)
+
+ if self.ticker.upper() in self.similar:
+ self.similar.remove(self.ticker.upper())
+
+ if len(self.similar) > 10:
+ random.shuffle(self.similar)
+ self.similar = sorted(self.similar[:10])
+ print(
+ "The limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed.\n",
+ )
+
+ if self.similar:
+ print(
+ f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n"
)
- self.user = "Finviz"
+
+ except Exception as e:
+ print(e, "\n")
+
+ def call_getfinnhub(self, other_args: List[str]):
+ """Process get command"""
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="getfinnhub",
+ description="""Get similar companies from finnhubto compare with.""",
+ )
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ self.similar, self.user = finnhub_model.get_similar_companies(self.ticker)
if self.ticker.upper() in self.similar:
self.similar.remove(self.ticker.upper())
@@ -230,20 +293,49 @@ class ComparisonAnalysisController:
)
if self.similar:
- print(f"[{self.user}] Similar Companies: {', '.join(self.similar)}")
- print("")
+ print(
+ f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n"
+ )
except Exception as e:
print(e, "\n")
- def select_similar_companies(self, other_args: List[str]):
- """Select similar companies, e.g. NIO,XPEV,LI
+ def call_add(self, other_args: List[str]):
+ """Process add command"""
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="add",
+ description="""Add similar companies to compare with.""",
+ )
+ parser.add_argument(
+ "-s",
+ "--similar",
+ dest="l_similar",
+ type=lambda s: [str(item).upper() for item in s.split(",")],
+ default=[],
+ help="similar companies to compare with.",
+ )
+
+ try:
+ # For the case where a user uses: 'add NIO,XPEV,LI'
+ if other_args and "-" not in other_args[0]:
+ other_args.insert(0, "-s")
- Parameters
- ----------
- other_args : List[str]
- argparse other args
- """
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+ # Add sets to avoid duplicates
+ self.similar = list(set(self.similar + ns_parser.l_similar))
+ if len(self.similar) > 10:
+ self.similar = list(random.sample(set(self.similar), 10))
+ self.user = "Custom"
+ print(f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n")
+ except Exception as e:
+ print(e, "\n")
+
+ def call_select(self, other_args: List[str]):
+ """Process select command"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
@@ -261,17 +353,16 @@ class ComparisonAnalysisController:
try:
# For the case where a user uses: 'select NIO,XPEV,LI'
- if other_args:
- if "-" not in other_args[0]:
- other_args.insert(0, "-s")
+ if other_args and "-" not in other_args[0]:
+ other_args.insert(0, "-s")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
self.similar = ns_parser.l_similar
- self.user = "User"
- print("")
+ self.user = "Custom"
+ print(f"[{self.user}] Similar Companies: {', '.join(self.similar)}", "\n")
except Exception as e:
print(e, "\n")
@@ -320,85 +411,564 @@ class ComparisonAnalysisController:
"""Process Quit command - quit the program"""
return True
- def call_get(self, other_args: List[str]):
- """Process get command"""
- self.get_similar_companies(other_args)
-
- def call_select(self, other_args: List[str]):
- """Process select command"""
- self.select_similar_companies(other_args)
-
def call_historical(self, other_args: List[str]):
"""Process historical command"""
- yahoo_finance_view.historical(
- other_args, self.stock, self.ticker, self.start, self.interval, self.similar
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="historical",
+ description="""Historical price comparison between similar companies.
+ """,
+ )
+ parser.add_argument(
+ "-t",
+ "--type",
+ action="store",
+ dest="type_candle",
+ type=str,
+ choices=["o", "h", "l", "c", "a"],
+ default="a", # in case it's adjusted close
+ help="Candle data to use: o-open, h-high, l-low, c-close, a-adjusted close.",
+ )
+ parser.add_argument(
+ "-s",
+ "--no-scale",
+ action="store_false",
+ dest="no_scale",
+ default=True,
+ help="Flag to not put all prices on same 0-1 scale",
+ )
+ parser.add_argument(
+ "--export",
+ choices=["csv", "json", "xlsx"],
+ default="",
+ type=str,
+ dest="export",
+ help="Export dataframe data to csv,json,xlsx file",
)
+ try:
+ if self.interval != "1440min":
+ print(
+ "Intraday historical data analysis comparison is not yet available."
+ )
+ # Alpha Vantage only supports 5 calls per minute, we need another API to get intraday data
+ else:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ if not self.similar or not self.ticker:
+ print(
+ "Please make sure there are both a loaded ticker and similar tickers selected. \n"
+ )
+ return
+
+ yahoo_finance_view.display_historical(
+ ticker=self.ticker,
+ similar_tickers=self.similar,
+ start=self.start,
+ candle_type=ns_parser.type_candle,
+ normalize=ns_parser.no_scale,
+ export=ns_parser.export,
+ )
+
+ except Exception as e:
+ print(e, "\n")
+
def call_hcorr(self, other_args: List[str]):
"""Process historical correlation command"""
- yahoo_finance_view.correlation(
- other_args, self.stock, self.ticker, self.start, self.interval, self.similar
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="corr",
+ description=""" Correlation heatmap based on historical price comparison between similar
+ companies.
+ """,
+ )
+ parser.add_argument(
+ "-t",
+ "--type",
+ action="store",
+ dest="type_candle",
+ type=str,
+ choices=["o", "h", "l", "c", "a"],
+ default="a", # in case it's adjusted close
+ help="Candle data to use: o-open, h-high, l-low, c-close, a-adjusted close.",
)
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ if not self.similar or not self.ticker:
+ print(
+ "Please make sure there are both a loaded ticker and similar tickers selected. \n"
+ )
+ return
+
+ yahoo_finance_view.display_correlation(
+ ticker=self.ticker,
+ similar_tickers=self.similar,
+ start=self.start,
+ candle_type=ns_parser.type_candle,
+ )
+ except Exception as e:
+ print(e, "\n")
+
def call_income(self, other_args: List[str]):
"""Process income command"""
- market_watch_view.compare_income(other_args, self.ticker, self.similar)
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="income",
+ description="""
+ Prints either yearly or quarterly income statement the company, and compares
+ it against similar companies.
+ """,
+ )
+ parser.add_argument(
+ "-q",
+ "--quarter",
+ action="store_true",
+ default=False,
+ dest="b_quarter",
+ help="Quarter financial data flag.",
+ )
+ parser.add_argument(
+ "-t",
+ "--timeframe",
+ dest="s_timeframe",
+ type=str,
+ default=None,
+ help="Specify yearly/quarterly timeframe. Default is last.",
+ )
+ parser.add_argument(
+ "--export",
+ choices=["csv", "json", "xlsx"],
+ default="",
+ type=str,
+ dest="export",
+ help="Export dataframe data to csv,json,xlsx file",
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ marketwatch_view.display_income_comparison(
+ ticker=self.ticker,
+ similar=self.similar,
+ timeframe=ns_parser.s_timeframe,
+ quarter=ns_parser.b_quarter,
+ )
+ except Exception as e:
+ print(e, "\n")
def call_balance(self, other_args: List[str]):
"""Process balance command"""
- market_watch_view.compare_balance(other_args, self.ticker, self.similar)
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ prog="balance",
+ description="""
+ Prints either yearly or quarterly balance statement the company, and compares
+ it against similar companies..
+ """,
+ )
+ parser.add_argument(
+ "-q",
+ "--quarter",
+ action="store_true",
+ default=False,
+ dest="b_quarter",
+ help="Quarter financial data flag.",
+ )
+
+ parser.add_argument(
+ "-t",
+ "--timeframe",
+ dest="s_timeframe",
+ type=str,
+ default=None,
+ help="Specify yearly/quarterly timeframe. Default is last.",
+ )
+ parser.add_argument(
+ "--export",
+ choices=["csv", "json", "xlsx"],
+ default="",
+ type=str,
+ dest="export",
+ help="Export dataframe data to csv,json,xlsx file",
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ marketwatch_view.display_balance_comparison(
+ ticker=self.ticker,
+ similar=self.similar,
+ timeframe=ns_parser.s_timeframe,
+ quarter=ns_parser.b_quarter,
+ )
+
+ except Exception as e:
+ print(e, "\n")
def call_cashflow(self, other_args: List[str]):
"""Process cashflow command"""
- market_watch_view.compare_cashflow(other_args, self.ticker, self.similar)
+ parser = argparse.ArgumentParser(
+ add_help=False,