Merge pull request #6 from DidierRLopes/main

updating
This commit is contained in:
DJ
2021-04-10 03:18:25 -06:00
committed by GitHub
12 changed files with 435 additions and 56 deletions

View File

@@ -108,10 +108,12 @@
* [x] Add multi-ticker historical data comparison (@didier) - [PR #141](https://github.com/DidierRLopes/GamestonkTerminal/pull/141)
* [x] Add multi-ticker financials comparison (@didier) - [PR #237](https://github.com/DidierRLopes/GamestonkTerminal/pull/237)
* [x] Add multi-ticker sentiment comparison (@didier) - [PR #250](https://github.com/DidierRLopes/GamestonkTerminal/pull/250)
* [x] Get similar companies from Finviz based on Industry and Sector (and possibly country) from Finviz (@didier) - [PR #323](https://github.com/DidierRLopes/GamestonkTerminal/pull/323)
* [x] Output brief screen (overview, performance, financial, valuation) across similar companies (@didier) - [PR #323](https://github.com/DidierRLopes/GamestonkTerminal/pull/323)
**NEXT**
* [ ] Get similar companies from Finviz based on Industry and Sector (and possibly country) from Finviz (@didier)
* [ ] Perform quick screening across similar companies based on key metrics (overview, performance, financial, valuation) from Finviz (@didier)
---

View File

@@ -3,7 +3,7 @@
This menu aims to compare a pre-loaded stock with similar companies, and the usage of the following commands along with an example will be exploited below.
* [get](#get)
* get similar companies [Polygon]
* get similar companies [Finviz or Polygon]
* [select](#select)
* select similar companies
* [historical](#historical)
@@ -18,18 +18,32 @@ This menu aims to compare a pre-loaded stock with similar companies, and the usa
* cashflow financials comparison [Market Watch]
* [sentiment](#sentiment)
* sentiment analysis comparison [FinBrain]
* [scorr](#corr)
* [scorr](#scorr)
* sentiment correlation between similar companies [FinBrain]
* [overview](#overview)
* brief overview (e.g. Sector, Industry, Market Cap, Volume) [Finviz]
* [valuation](#valuation)
* brief valuation (e.g. P/E, PEG, P/S, P/B, EPS this Y) [Finviz]
* [financial](#financial)
* brief financial (e.g. Dividend, ROA, ROE, ROI, Earnings) [Finviz]
* [ownership](#ownership)
* brief ownership (e.g. Float, Insider Own, Short Ratio) [Finviz]
* [performance](#performance)
* brief performance (e.g. Perf Week, Perf YTD, Volatility M) [Finviz]
* [technical](#technical)
* brief technical (e.g. Beta, SMA50, 52W Low, RSI, Change) [Finviz]
## get <a name="get"></a>
```text
get
get [-p] [--nocountry]
```
Get similar companies to compare with. [Source: Polygon]
Get similar companies to compare with. [Source: Finviz by default]
* -p : get similar companies using polygon source. Default False, i.e. using Finviz source.
* --nocountry : when getting similar companies from Finviz, we filter by same Industry, Sector and Country. However, if we don't want to filter by same country we can set this flag. For this flag to work, the `-p` flag can't be selected
## select <a name="select"></a>
@@ -146,3 +160,85 @@ Sentiment correlation between similar companies [Source: FinBrain]
<img width="282" alt="Captura de ecrã 2021-03-20, às 15 57 01" src="https://user-images.githubusercontent.com/25267873/111920499-4521e780-8a87-11eb-9533-6844ba92f8f0.png">
## overview <a name="overview"></a>
```text
usage: overview [-s L_SIMILAR] [-a L_ALSO]
```
Prints screener data of similar companies. Some of the fields shown are: Sector, Industry, Market Cap, Volume. [Source: Finviz]
* -s : similar companies to compare with. Default pre-loaded ones.
* -a : apart from loaded similar companies also compare with.
<img width="1060" alt="overview" src="https://user-images.githubusercontent.com/25267873/114103686-7a209d80-98c1-11eb-9d6a-592f42dccbf1.png">
## valuation <a name="valuation"></a>
```text
usage: valuation [-s L_SIMILAR] [-a L_ALSO]
```
Prints screener data of similar companies. Some of the fields shown are: P/E, PEG, P/S, P/B, EPS this Y. [Source: Finviz]
* -s : similar companies to compare with. Default pre-loaded ones.
* -a : apart from loaded similar companies also compare with.
<img width="1067" alt="valuation" src="https://user-images.githubusercontent.com/25267873/114103689-7ab93400-98c1-11eb-95ed-8ceab347a8b4.png">
## financial <a name="financial"></a>
```text
usage: financial [-s L_SIMILAR] [-a L_ALSO]
```
Prints screener data of similar companies. Some of the fields shown are: Dividend, ROA, ROE, ROI, Earnings. [Source: Finviz]
* -s : similar companies to compare with. Default pre-loaded ones.
* -a : apart from loaded similar companies also compare with.
<img width="1049" alt="financial" src="https://user-images.githubusercontent.com/25267873/114103690-7ab93400-98c1-11eb-8f20-767ee23e1b37.png">
## ownership <a name="ownership"></a>
```text
usage: ownership [-s L_SIMILAR] [-a L_ALSO]
```
Prints screener data of similar companies. Some of the fields shown are: Float, Insider Own, Short Ratio. [Source: Finviz]
* -s : similar companies to compare with. Default pre-loaded ones.
* -a : apart from loaded similar companies also compare with.
<img width="1064" alt="ownership" src="https://user-images.githubusercontent.com/25267873/114103694-7bea6100-98c1-11eb-8261-49cca9b8a34c.png">
## performance <a name="performance"></a>
```text
usage: performance [-s L_SIMILAR] [-a L_ALSO]
```
Prints screener data of similar companies. Some of the fields shown are: Perf Week, Perf YTD, Volatility M. [Source: Finviz]
* -s : similar companies to compare with. Default pre-loaded ones.
* -a : apart from loaded similar companies also compare with.
<img width="1064" alt="performance" src="https://user-images.githubusercontent.com/25267873/114103691-7b51ca80-98c1-11eb-87d1-c816cda569bd.png">
## technical <a name="technical"></a>
```text
usage: technical [-s L_SIMILAR] [-a L_ALSO]
```
Prints screener data of similar companies. Some of the fields shown are: Beta, SMA50, 52W Low, RSI, Change. [Source: Finviz]
* -s : similar companies to compare with. Default pre-loaded ones.
* -a : apart from loaded similar companies also compare with.
<img width="1058" alt="technical" src="https://user-images.githubusercontent.com/25267873/114103696-7bea6100-98c1-11eb-93d9-66c732559c75.png">

View File

@@ -9,10 +9,12 @@ import pandas as pd
from matplotlib import pyplot as plt
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal import config_terminal as cfg
from finvizfinance.screener.overview import Overview
from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn
from gamestonk_terminal.comparison_analysis import yahoo_finance_api as yf_api
from gamestonk_terminal.comparison_analysis import market_watch_api as mw_api
from gamestonk_terminal.comparison_analysis import finbrain_api as f_api
from gamestonk_terminal.comparison_analysis import finviz_compare_view
from gamestonk_terminal.menu import session
from prompt_toolkit.completion import NestedCompleter
@@ -34,6 +36,12 @@ class ComparisonAnalysisController:
"cashflow",
"sentiment",
"scorr",
"overview",
"valuation",
"financial",
"ownership",
"performance",
"technical",
]
def __init__(
@@ -47,7 +55,7 @@ class ComparisonAnalysisController:
):
"""Constructor"""
self.similar = similar
self.user = user
self.user = ""
self.stock = stock
self.ticker = ticker
self.start = start
@@ -71,31 +79,34 @@ class ComparisonAnalysisController:
else:
print(f"\n{s_intraday} Stock: {self.ticker}")
s_similar_source = ("Polygon API", "User")[self.user]
if self.similar:
print(f"[{s_similar_source}] Similar Companies: {', '.join(self.similar)}")
else:
print(f"No similar companies [{s_similar_source}]")
print(f"[{self.user}] Similar Companies: {', '.join(self.similar)}")
# else:
# print(f"No similar companies [{self.user}]")
print("\nComparison Analysis Mode:")
print(" help show this comparison analysis 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 [Polygon API]")
print(" get get similar companies")
print(" select select similar companies")
print("")
print(" historical historical price data comparison")
print(" hcorr historical price correlation")
print("")
print(" income income financials comparison")
print(" balance balance financials comparison")
print(" cashflow cashflow comparison")
print("")
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("")
return
@staticmethod
@@ -106,22 +117,65 @@ class ComparisonAnalysisController:
prog="get",
description="""Get similar companies to compare with.""",
)
parser.add_argument(
"-p",
"--polygon",
action="store_true",
default=False,
dest="b_polygon",
help="Polygon data source flag.",
)
# If polygon source not selected, 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 "-p" not in other_args and "--polygon" not in 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.",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
result = requests.get(
f"https://api.polygon.io/v1/meta/symbols/{self.ticker.upper()}/company?&apiKey={cfg.API_POLYGON_KEY}"
)
if ns_parser.b_polygon:
result = requests.get(
f"https://api.polygon.io/v1/meta/symbols/{self.ticker.upper()}/company?&apiKey={cfg.API_POLYGON_KEY}"
)
if result.status_code == 200:
self.similar = result.json()["similar"]
self.user = "Polygon"
else:
print(result.json()["error"])
if result.status_code == 200:
self.similar = result.json()["similar"]
print(f"[Polygon API] Similar Companies: {', '.join(self.similar)}")
self.user = False
else:
print(result.json()["error"])
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()
)
self.user = "Finviz"
if self.similar:
print(f"\n[{self.user}] Similar Companies: {', '.join(self.similar)}")
if len(self.similar) > 10:
print(
"\nThe limit of stocks to compare with are 10. Hence, the similar stocks list will be:"
)
self.similar = self.similar[:10]
print(", ".join(self.similar))
except Exception as e:
print(e)
@@ -157,7 +211,7 @@ class ComparisonAnalysisController:
return
self.similar = ns_parser.l_similar
self.user = True
self.user = "User"
except Exception as e:
print(e)
@@ -232,6 +286,32 @@ class ComparisonAnalysisController:
"""Process sentiment correlation command"""
f_api.sentiment_correlation(other_args, self.ticker, self.similar)
def call_overview(self, other_args: List[str]):
"""Process overview command"""
finviz_compare_view.screener(other_args, "overview", self.ticker, self.similar)
def call_valuation(self, other_args: List[str]):
"""Process valuation command"""
finviz_compare_view.screener(other_args, "valuation", self.ticker, self.similar)
def call_financial(self, other_args: List[str]):
"""Process financial command"""
finviz_compare_view.screener(other_args, "financial", self.ticker, self.similar)
def call_ownership(self, other_args: List[str]):
"""Process ownership command"""
finviz_compare_view.screener(other_args, "ownership", self.ticker, self.similar)
def call_performance(self, other_args: List[str]):
"""Process performance command"""
finviz_compare_view.screener(
other_args, "performance", self.ticker, self.similar
)
def call_technical(self, other_args: List[str]):
"""Process technical command"""
finviz_compare_view.screener(other_args, "technical", self.ticker, self.similar)
def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str):
"""Comparison Analysis Menu"""

View File

@@ -0,0 +1,112 @@
""" Finviz View """
__docformat__ = "numpy"
import argparse
from typing import List
import pandas as pd
from finvizfinance.screener import (
technical,
overview,
valuation,
financial,
ownership,
performance,
)
from gamestonk_terminal.helper_funcs import (
parse_known_args_and_warn,
)
def get_comparison_data(data_type: str, similar: List[str]):
"""Screener Overview
Parameters
----------
data_type : str
Data type between: overview, valuation, financial, ownership, performance, technical
Returns
----------
pd.DataFrame
Dataframe with overview, valuation, financial, ownership, performance or technical
"""
if data_type == "overview":
screen = overview.Overview()
elif data_type == "valuation":
screen = valuation.Valuation()
elif data_type == "financial":
screen = financial.Financial()
elif data_type == "ownership":
screen = ownership.Ownership()
elif data_type == "performance":
screen = performance.Performance()
elif data_type == "technical":
screen = technical.Technical()
else:
print("Invalid selected screener type")
return pd.DataFrame()
screen.set_filter(ticker=",".join(similar))
df_screen = screen.ScreenerView(verbose=0)
return df_screen
def screener(other_args: List[str], data_type: str, ticker: str, similar: List[str]):
"""Screener
Parameters
----------
other_args : List[str]
Command line arguments to be processed with argparse
data_type : str
Data type string between: overview, valuation, financial, ownership, performance, technical
ticker : str
Main ticker to compare income
similar : List[str]
Similar companies to compare income with
"""
parser = argparse.ArgumentParser(
add_help=False,
prog=data_type,
description="""
Prints screener data of similar companies. [Source: Finviz]
""",
)
parser.add_argument(
"-s",
"--similar",
dest="l_similar",
type=lambda s: [str(item).upper() for item in s.split(",")],
default=similar,
help="similar companies to compare with.",
)
parser.add_argument(
"-a",
"--also",
dest="l_also",
type=lambda s: [str(item).upper() for item in s.split(",")],
default=[],
help="apart from loaded similar companies also compare with.",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
l_similar = ns_parser.l_similar
l_similar += ns_parser.l_also
# Add main ticker to similar list of companies
l_similar = [ticker] + l_similar
df_screen = get_comparison_data(data_type, l_similar)
if not df_screen.empty:
print(df_screen.to_string())
print("")
except Exception as e:
print(e, "\n")
return

View File

@@ -81,22 +81,49 @@ def historical(l_args, df_stock, s_ticker, s_start, s_interval, similar):
plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
plt.title(f"Similar companies to {s_ticker}")
df_stock = yf.download(s_ticker, start=s_start, progress=False)
df_stock = yf.download(
s_ticker, start=s_start, progress=False, threads=False
)
plt.plot(
df_stock.index, df_stock[d_candle_types[ns_parser.type_candle]].values
)
# plt.plot(df_stock.index, df_stock["5. adjusted close"].values, lw=2)
l_min = [df_stock.index[0]]
l_leg = [s_ticker]
for symbol in similar:
df_similar_stock = yf.download(symbol, start=s_start, progress=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_stocks = similar[:]
while l_stocks:
l_parsed_stocks = list()
for symbol in l_stocks:
try:
df_similar_stock = yf.download(
symbol, start=s_start, 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)
plt.xlabel("Time")
plt.ylabel("Share Price ($)")
plt.legend(l_leg)

View File

@@ -1,4 +1,8 @@
""" pycoingecko_api """
__docformat__ = "numpy"
import argparse
from typing import List
import pandas as pd
from pandas.plotting import register_matplotlib_converters
import matplotlib.pyplot as plt
@@ -11,8 +15,15 @@ register_matplotlib_converters()
# pylint: disable=inconsistent-return-statements
def load(l_args):
def load(other_args: List[str]):
"""Load selected Cryptocurrency
Parameters
----------
other_args : List[str]
argparse arguments
"""
cg = CoinGeckoAPI()
parser = argparse.ArgumentParser(
add_help=False,
@@ -40,11 +51,11 @@ def load(l_args):
)
try:
if l_args:
if "-" not in l_args[0]:
l_args.insert(0, "-c")
if other_args:
if "-" not in other_args[0]:
other_args.insert(0, "-c")
ns_parser = parse_known_args_and_warn(parser, l_args)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
@@ -57,7 +68,7 @@ def load(l_args):
except KeyError:
print(f"Could not find coin with the id: {ns_parser.coin}")
print("")
return [None, pd.DataFrame]
return [None, pd.DataFrame()]
prices = cg.get_coin_market_chart_by_id(
coin, vs_currency=ns_parser.vs, days=ns_parser.days
@@ -81,8 +92,19 @@ def load(l_args):
return [None, pd.DataFrame()]
def view(coin, prices, l_args):
def view(coin: str, prices: pd.DataFrame, other_args: List[str]):
"""Plots loaded cryptocurrency
Parameters
----------
coin : str
Cryptocurrency
prices : pandas.DataFrame
Dataframe containing prices and dates for selected coin
other_args : List[str]
argparse arguments
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="Crypto",
@@ -91,7 +113,7 @@ def view(coin, prices, l_args):
""",
)
try:
ns_parser = parse_known_args_and_warn(parser, l_args)
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return

View File

@@ -1,5 +1,9 @@
from pycoingecko import CoinGeckoAPI
"""
Helper script that generates a dictionary to convert between symbols and IDs.
This allows the user to input (for example) "btc" instead of the expected "bitcoin"
"""
from pycoingecko import CoinGeckoAPI
cg = CoinGeckoAPI()
coins = cg.get_coins()

View File

@@ -125,7 +125,7 @@ def shareholders(l_args, s_ticker):
].apply(lambda x: long_number_format(x))
df_institutional_shareholders["Stake"] = df_institutional_shareholders[
"Stake"
].apply(lambda x: str("{:.2f}".format(100 * x)) + " %")
].apply(lambda x: str(f"{100 * x:.2f}") + " %")
print(df_institutional_shareholders.to_string(index=False))
print("")
@@ -142,7 +142,7 @@ def shareholders(l_args, s_ticker):
lambda x: long_number_format(x)
)
df_mutualfund_shareholders["Stake"] = df_mutualfund_shareholders["Stake"].apply(
lambda x: str("{:.2f}".format(100 * x)) + " %"
lambda x: str(f"{100 * x:.2f}") + " %"
)
print(df_mutualfund_shareholders.to_string(index=False))

View File

@@ -559,11 +559,11 @@ def independence(
result = bds(residuals, max_dim=6)
print("BDS Test")
print("Dim 2: z-static {:.4f} Prob {:.4f}".format(result[0][0], result[1][0]))
print("Dim 3: z-static {:.4f} Prob {:.4f}".format(result[0][1], result[1][1]))
print("Dim 4: z-static {:.4f} Prob {:.4f}".format(result[0][2], result[1][2]))
print("Dim 5: z-static {:.4f} Prob {:.4f}".format(result[0][3], result[1][3]))
print("Dim 6: z-static {:.4f} Prob {:.4f}".format(result[0][4], result[1][4]))
print(f"Dim 2: z-static {result[0][0]:.4f} Prob {result[1][0]:.4f}")
print(f"Dim 3: z-static {result[0][1]:.4f} Prob {result[1][1]:.4f}")
print(f"Dim 4: z-static {result[0][2]:.4f} Prob {result[1][2]:.4f}")
print(f"Dim 5: z-static {result[0][3]:.4f} Prob {result[1][3]:.4f}")
print(f"Dim 6: z-static {result[0][4]:.4f} Prob {result[1][4]:.4f}")
print("")

View File

@@ -9,17 +9,17 @@ This menu aims to filter stocks based on pre-specified preset filters, and the u
* [historical](#historical)
* view historical price [Yahoo Finance]
* [overview](#overview)
* overview (e.g. Sector, Industry, Market Cap, Volume) [Finviz]
* overview data (e.g. Sector, Industry, Market Cap, Volume) [Finviz]
* [valuation](#valuation)
* valuation (e.g. P/E, PEG, P/S, P/B, EPS this Y) [Finviz]
* valuation data (e.g. P/E, PEG, P/S, P/B, EPS this Y) [Finviz]
* [financial](#financial)
* financial (e.g. Dividend, ROA, ROE, ROI, Earnings) [Finviz]
* financial data (e.g. Dividend, ROA, ROE, ROI, Earnings) [Finviz]
* [ownership](#ownership)
* ownership (e.g. Float, Insider Own, Short Ratio) [Finviz]
* ownership data (e.g. Float, Insider Own, Short Ratio) [Finviz]
* [performance](#performance)
* performance (e.g. Perf Week, Perf YTD, Volatility M) [Finviz]
* performance data (e.g. Perf Week, Perf YTD, Volatility M) [Finviz]
* [technical](#technical)
* technical (e.g. Beta, SMA50, 52W Low, RSI, Change) [Finviz]
* technical data (e.g. Beta, SMA50, 52W Low, RSI, Change) [Finviz]
* contains [Oversold (-s) signal example](#signal-oversold)
* [signals](#signals)
* view filter signals (e.g. -s top_gainers) [Finviz]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
from unittest import mock, TestCase
import json
from pycoingecko import CoinGeckoAPI
from gamestonk_terminal.cryptocurrency.coin_api import load
# pylint: disable=unused-import
class TestCoinGeckoAPI(TestCase):
@mock.patch(
"gamestonk_terminal.cryptocurrency.coin_api.CoinGeckoAPI.get_coin_market_chart_by_id"
)
def test_coin_api_load(self, mock_load):
"""
Mock load function through get_coin_market_chart_by_id.
Mock returns a dict saved as .json
"""
with open("tests/data/btc_usd_test_data.json") as f:
sample_return = json.load(f)
mock_load.return_value = sample_return
coin, mock_return = load(["-c", "bitcoin"])
self.assertEqual(coin, "bitcoin")
self.assertTrue(mock_load.called)
self.assertTrue(mock_return.shape == (722, 2))
def test_coin(self):
"""Test that pycoingecko retrieves the major coins"""
coins = CoinGeckoAPI().get_coins()
coin_list = [coin["id"] for coin in coins]
test_coins = ["bitcoin", "ethereum", "dogecoin"]
for test in test_coins:
self.assertIn(test, coin_list)