Merge pull request #3 from DidierRLopes/marketbeat

Improve user experience of disc/ratings
This commit is contained in:
hinxx
2021-04-17 12:21:30 +02:00
committed by GitHub
6 changed files with 236 additions and 1 deletions

View File

@@ -33,6 +33,7 @@
* [x] Top Losers (@didier) - [PR #171](https://github.com/DidierRLopes/GamestonkTerminal/pull/171)
* [x] ARK orders (@aia) - [PR #140](https://github.com/DidierRLopes/GamestonkTerminal/pull/140)
* [x] Analyse sectors/industry performance, overview and valuation from Finviz (@didier) - [PR #315](https://github.com/DidierRLopes/GamestonkTerminal/pull/315)
* [x] Add top ratings updates (@hinxx) - [PR #350](https://github.com/DidierRLopes/GamestonkTerminal/pull/350)
**NEXT**

View File

@@ -79,6 +79,7 @@ Command|Description|Source
`valuation` |valuation of sectors, industry, country |[Finviz](https://finviz.com)
`performance` |performance of sectors, industry, country |[Finviz](https://finviz.com)
`spectrum` |spectrum of sectors, industry, country |[Finviz](https://finviz.com)
`ratings ` |top ratings updates |[MarketBeat](https://marketbeat.com)
 
@@ -351,4 +352,4 @@ technical |technical (e.g. Beta, SMA50, 52W Low, RSI, Change)
signals |view filter signals (e.g. -s top_gainers)
 
 

View File

@@ -32,6 +32,8 @@ This menu aims to discover new stocks, and the usage of the following commands a
* performance of sectors, industry, country [Finviz]
* [spectrum](#spectrum)
* spectrum of sectors, industry, country [Finviz]
* [ratings](#ratings)
* top ratings updates [MarketBeat]
## map <a name="map"></a>
@@ -218,3 +220,18 @@ Spectrum of sectors, industry, country. [Source: Finviz]
* -g : Data group (sector, industry or country). Default: Sector.
![cntry_spec](https://user-images.githubusercontent.com/25267873/113639067-48fd5e80-9670-11eb-95cf-0931845ddd12.png)
## ratings <a name="ratings"></a>
```
usage: ratings [-t N_THRESHOLD]
```
Top ratings updates. [Source: MarketBeat]
* -t : Minimum threshold in percentage change between current and target price to show ratings. Default: 100.
<img width="963" alt="ratings" src="https://user-images.githubusercontent.com/25267873/115095983-4544c400-9f1b-11eb-8869-8ec8a0f8eae0.png">

View File

@@ -20,6 +20,7 @@ from gamestonk_terminal.discovery import (
spachero_view,
unusual_whales_view,
yahoo_finance_view,
marketbeat_view,
)
@@ -46,6 +47,7 @@ class DiscoveryController:
"valuation",
"performance",
"spectrum",
"ratings",
]
def __init__(self):
@@ -87,6 +89,7 @@ class DiscoveryController:
print(" valuation valuation of sectors, industry, country [Finviz]")
print(" performance performance of sectors, industry, country [Finviz]")
print(" spectrum spectrum of sectors, industry, country [Finviz]")
print(" ratings top ratings updates [MarketBeat]")
print("")
def switch(self, an_input: str):
@@ -187,6 +190,10 @@ class DiscoveryController:
other_args, "spectrum"
)
def call_ratings(self, other_args: List[str]):
"""Process ratings command"""
marketbeat_view.ratings_view(other_args)
def menu():
"""Discovery Menu"""

View File

@@ -0,0 +1,93 @@
""" MarketBeat Model """
__docformat__ = "numpy"
from typing import List
import requests
from bs4 import BeautifulSoup
from gamestonk_terminal.helper_funcs import get_user_agent
def get_ratings_html(url_ratings: str) -> str:
"""Wraps HTTP requests.get for testibility
Parameters
----------
url_ratings : str
Ratings URL
Returns
-------
str
HTML page of ratings
"""
ratings_html = requests.get(
url_ratings, headers={"User-Agent": get_user_agent()}
).text
return ratings_html
def get_ratings() -> List[dict]:
"""Returns a list of ratings
Parameters
----------
None
Returns
-------
list
Ratings list
"""
ratings = list()
url_ratings = "https://www.marketbeat.com/ratings/"
text_soup_ratings = BeautifulSoup(
get_ratings_html(url_ratings),
"lxml",
)
for stock_rows in text_soup_ratings.findAll("tr"):
tds = stock_rows.findAll("td")
if len(tds) == 8:
rating = {}
# company
td = tds[0]
company = td.find("div", {"class": "title-area"}).text
ticker = td.find("div", {"class": "ticker-area"}).text
rating["company"] = company
rating["ticker"] = ticker
# action
td = tds[1]
action = td.text
rating["action"] = action
# brokerage
td = tds[2]
brokerage = td.find("a").text
rating["brokerage"] = brokerage
# current price
td = tds[3]
if td.find("span"):
td.span.extract()
current_price = td.text
rating["current_price"] = current_price
# target price
td = tds[4]
target_price = td.text
rating["target_price"] = target_price
# new rating
td = tds[5]
new_rating = td.text
rating["rating"] = new_rating
# impact on price
td = tds[6]
if td.find("a"):
impact = td.find("a").text
else:
impact = td.text
rating["impact"] = impact
ratings.append(rating)
return ratings

View File

@@ -0,0 +1,116 @@
""" MarketBeat View """
__docformat__ = "numpy"
import argparse
import numpy as np
import pandas as pd
from typing import List
from gamestonk_terminal.helper_funcs import (
check_positive,
parse_known_args_and_warn,
)
from gamestonk_terminal.discovery import marketbeat_model
def ratings_view(other_args: List[str]):
"""Prints top ratings updates
Parameters
----------
other_args : List[str]
argparse other args - ["-t", "100"]
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="ratings",
description="""Top ratings updates. [Source: MarketBeat]""",
)
parser.add_argument(
"-t",
"--threshold",
action="store",
dest="n_threshold",
type=check_positive,
default=100,
help="Minimum threshold in percentage change between current and target price to show ratings",
)
if other_args:
if "-" not in other_args[0]:
other_args.insert(0, "-t")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
try:
ratings = marketbeat_model.get_ratings()
df_ratings = pd.DataFrame(ratings)
df_ratings = df_ratings[df_ratings["target_price"] != ""]
df_ratings["old_target"] = df_ratings["target_price"].apply(
lambda x: x.split(" ➝ ")[0] if "➝" in x else ""
)
df_ratings["new_target"] = df_ratings["target_price"].apply(
lambda x: x.split(" ➝ ")[1] if "➝" in x else x
)
df_ratings["clean_current_price"] = df_ratings["current_price"].apply(
lambda x: "".join([c if c.isdigit() or c == "." else "" for c in str(x)])
)
df_ratings["clean_current_price"] = df_ratings["clean_current_price"].astype(
float
)
df_ratings["clean_target_price"] = df_ratings["new_target"].apply(
lambda x: "".join([c if c.isdigit() or c == "." else "" for c in str(x)])
)
df_ratings["clean_target_price"] = df_ratings["clean_target_price"].astype(
float
)
df_ratings["pct_increase"] = (
100
* (df_ratings["clean_target_price"] - df_ratings["clean_current_price"])
/ df_ratings["clean_current_price"]
)
df_ratings["pct_increase"] = df_ratings["pct_increase"].round(2)
df_ratings["pct_abs"] = np.abs(df_ratings["pct_increase"])
df_ratings["pct_abs"] = df_ratings["pct_abs"].round(2)
df_ratings = df_ratings.sort_values("pct_abs", ascending=False)
df_ratings_top = df_ratings[df_ratings["pct_abs"] > ns_parser.n_threshold][
[
"ticker",
"brokerage",
"rating",
"current_price",
"old_target",
"new_target",
"pct_increase",
]
]
df_ratings_top["pct_increase"] = df_ratings_top["pct_increase"].apply(
lambda x: str(x) + " %"
)
print(
df_ratings_top.rename(
columns={
"ticker": "Ticker",
"brokerage": "Brokerage",
"rating": "Rating",
"current_price": "Current Price",
"old_target": "Old Target",
"new_target": "New Target",
"pct_increase": "Percentage Increase",
}
).to_string(index=False)
)
print("")
except Exception as e:
print(e, "\n")