mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-28 13:01:38 +00:00
Merge pull request #3 from DidierRLopes/marketbeat
Improve user experience of disc/ratings
This commit is contained in:
@@ -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**
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
|
||||
## 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">
|
||||
|
||||
|
||||
|
||||
@@ -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"""
|
||||
|
||||
93
gamestonk_terminal/discovery/marketbeat_model.py
Executable file
93
gamestonk_terminal/discovery/marketbeat_model.py
Executable 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
|
||||
116
gamestonk_terminal/discovery/marketbeat_view.py
Normal file
116
gamestonk_terminal/discovery/marketbeat_view.py
Normal 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")
|
||||
Reference in New Issue
Block a user