Files
OpenBB/openbb_terminal/common/behavioural_analysis/reddit_view.py
montezdesousa 02901f1473 Trim command output spaces (#3424)
* first round of removing spaces

* remove more print statemnts

* remove more spaces

* bullbear color

* spacc color

* improve stalker readability

* delete more spaces

* reddit colors

* rever hood

* flake8

* forgot this one

* fix econometrics tests

* fix econometrics tests

* fix more tests

Co-authored-by: James Maslek <jmaslek11@gmail.com>
2022-11-16 11:47:24 +00:00

473 lines
14 KiB
Python

"""Reddit View"""
__docformat__ = "numpy"
import logging
import os
import warnings
from datetime import datetime
from typing import Dict, List, Optional
import finviz
import matplotlib.pyplot as plt
import pandas as pd
import praw
import seaborn as sns
from openbb_terminal.common.behavioural_analysis import reddit_model
from openbb_terminal.config_terminal import theme
from openbb_terminal.config_plot import PLOT_DPI
from openbb_terminal.decorators import check_api_key
from openbb_terminal.decorators import log_start_end
from openbb_terminal.helper_funcs import (
export_data,
plot_autoscale,
print_rich_table,
is_valid_axes_count,
)
from openbb_terminal.rich_config import console
# pylint: disable=R0913
logger = logging.getLogger(__name__)
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def print_and_record_reddit_post(
submissions_dict: Dict, submission: praw.models.reddit.submission.Submission
):
"""Prints reddit submission.
Parameters
----------
submissions_dict : Dict
Dictionary for storing reddit post information
submission : praw.models.reddit.submission.Submission
Submission to show
"""
# Refactor data
s_datetime = datetime.utcfromtimestamp(submission.created_utc).strftime(
"%Y-%m-%d %H:%M:%S"
)
s_link = f"https://old.reddit.com{submission.permalink}"
s_all_awards = "".join(
f"{award['count']} {award['name']}\n" for award in submission.all_awardings
)
s_all_awards = s_all_awards[:-2]
# Create dictionary with data to construct dataframe allows to save data
submissions_dict[submission.id] = {
"created_utc": s_datetime,
"subreddit": submission.subreddit,
"link_flair_text": submission.link_flair_text,
"title": submission.title,
"score": submission.score,
"link": s_link,
"num_comments": submission.num_comments,
"upvote_ratio": submission.upvote_ratio,
"awards": s_all_awards,
}
# Print post data collected so far
console.print(f"[yellow]{s_datetime}[/yellow] - {submission.title}")
console.print(f"[blue]{s_link}[/blue]\n")
columns = ["Subreddit", "Flair", "Score", "# Comments", "Upvote %", "Awards"]
data = [
submission.subreddit,
submission.link_flair_text,
submission.score,
submission.num_comments,
f"{round(100 * submission.upvote_ratio)}%",
s_all_awards,
]
df = pd.DataFrame([data], columns=columns)
print_rich_table(
df, headers=list(df.columns), show_index=False, title="Reddit Submission"
)
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def print_reddit_post(sub: tuple):
"""Prints reddit submission.
Parameters
----------
sub : tuple
Row from submissions dataframe
"""
sub_list = list(sub[1])
date = sub_list[0]
title = sub_list[3]
link = sub_list[-1]
console.print(f"[yellow]{date}[/yellow] - {title}")
console.print(f"[blue]{link}[/blue]\n")
columns = [
"Subreddit",
"Flair",
"Score",
"# Comments",
"Upvote %",
"Awards",
]
print_rich_table(
pd.DataFrame(sub[1][columns]).T,
headers=columns,
show_index=False,
title="Reddit Submission",
)
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_watchlist(limit: int = 5):
"""Prints other users watchlist. [Source: Reddit].
Parameters
----------
limit: int
Maximum number of submissions to look at
"""
subs, d_watchlist_tickers, n_flair_posts_found = reddit_model.get_watchlists(limit)
if subs:
for sub in subs:
print_and_record_reddit_post({}, sub)
console.print("")
if n_flair_posts_found > 0:
lt_watchlist_sorted = sorted(
d_watchlist_tickers.items(), key=lambda item: item[1], reverse=True
)
s_watchlist_tickers = ""
n_tickers = 0
for t_ticker in lt_watchlist_sorted:
try:
# If try doesn't trigger exception, it means that this stock exists on finviz
# thus we can print it.
finviz.get_stock(t_ticker[0])
if int(t_ticker[1]) > 1:
s_watchlist_tickers += f"{t_ticker[1]} {t_ticker[0]}, "
n_tickers += 1
except Exception:
# console.print(e, "\n")
pass
if n_tickers:
console.print(
"The following stock tickers have been mentioned more than once across the previous watchlists:"
)
console.print(s_watchlist_tickers[:-2] + "\n")
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_popular_tickers(
limit: int = 10, post_limit: int = 50, subreddits: str = "", export: str = ""
):
"""Prints table showing latest popular tickers. [Source: Reddit].
Parameters
----------
limit : int
Number of top tickers to get
post_limit : int
How many posts to analyze in each subreddit
subreddits : str, optional
String of comma separated subreddits.
export : str
Format to export dataframe
"""
popular_tickers_df = reddit_model.get_popular_tickers(limit, post_limit, subreddits)
if not popular_tickers_df.empty:
print_rich_table(
popular_tickers_df,
headers=list(popular_tickers_df.columns),
show_index=False,
title=f"The following TOP {limit} tickers have been mentioned",
)
else:
console.print("No tickers found")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"popular",
popular_tickers_df,
)
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_spac_community(limit: int = 10, popular: bool = False):
"""Prints tickers mentioned in r/SPACs [Source: Reddit].
Parameters
----------
limit: int
Number of posts to look through
popular: bool
Search by popular instead of new
"""
subs, d_watchlist_tickers = reddit_model.get_spac_community(limit, popular)
if not subs.empty:
for sub in subs.iterrows():
print_reddit_post(sub)
console.print("")
if d_watchlist_tickers:
lt_watchlist_sorted = sorted(
d_watchlist_tickers.items(), key=lambda item: item[1], reverse=True
)
s_watchlist_tickers = ""
n_tickers = 0
for t_ticker in lt_watchlist_sorted:
try:
# If try doesn't trigger exception, it means that this stock exists on finviz
# thus we can print it.
finviz.get_stock(t_ticker[0])
if int(t_ticker[1]) > 1:
s_watchlist_tickers += f"{t_ticker[1]} {t_ticker[0]}, "
n_tickers += 1
except Exception:
# console.print(e, "\n")
pass
if n_tickers:
console.print(
"The following stock tickers have been mentioned more than once across the previous SPACs:"
)
console.print(s_watchlist_tickers[:-2])
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_spac(limit: int = 5):
"""Prints posts containing 'spac' in top communities.
Parameters
----------
limit: int
Number of posts to get from each subreddit
"""
warnings.filterwarnings("ignore") # To avoid printing the warning
subs, d_watchlist_tickers, n_flair_posts_found = reddit_model.get_spac(limit)
if not subs.empty:
for sub in subs.iterrows():
print_reddit_post(sub)
console.print("")
if n_flair_posts_found > 0:
lt_watchlist_sorted = sorted(
d_watchlist_tickers.items(), key=lambda item: item[1], reverse=True
)
s_watchlist_tickers = ""
n_tickers = 0
for t_ticker in lt_watchlist_sorted:
try:
# If try doesn't trigger exception, it means that this stock exists on finviz
# thus we can print it.
finviz.get_stock(t_ticker[0])
if int(t_ticker[1]) > 1:
s_watchlist_tickers += f"{t_ticker[1]} {t_ticker[0]}, "
n_tickers += 1
except Exception:
pass
if n_tickers:
console.print(
"The following stock tickers have been mentioned more than once across the previous SPACs:"
)
console.print(s_watchlist_tickers[:-2])
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_wsb_community(limit: int = 10, new: bool = False):
"""Prints WSB posts.
Parameters
----------
limit : int, optional
Number of posts to look at, by default 10
new : bool, optional
Flag to sort by new instead of hot, by default False
"""
subs = reddit_model.get_wsb_community(limit, new)
if not subs.empty:
for sub in subs.iterrows():
print_reddit_post(sub)
console.print("")
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_due_diligence(
symbol: str, limit: int = 10, n_days: int = 3, show_all_flairs: bool = False
):
"""Prints Reddit due diligence data for a given ticker.
Parameters
----------
symbol: str
Stock ticker symbol
limit: int
Number of posts to get
n_days: int
Number of days back to get posts
show_all_flairs: bool
Search through all flairs (apart from Yolo and Meme)
"""
subs = reddit_model.get_due_dilligence(symbol, limit, n_days, show_all_flairs)
if not subs.empty:
for sub in subs.iterrows():
print_reddit_post(sub)
console.print("")
else:
console.print(f"No DD posts found for {symbol}\n")
@log_start_end(log=logger)
@check_api_key(
[
"API_REDDIT_CLIENT_ID",
"API_REDDIT_CLIENT_SECRET",
"API_REDDIT_USERNAME",
"API_REDDIT_USER_AGENT",
"API_REDDIT_PASSWORD",
]
)
def display_redditsent(
symbol: str,
sortby: str = "relevance",
limit: int = 100,
graphic: bool = False,
time_frame: str = "week",
full_search: bool = True,
subreddits: str = "all",
display: bool = False,
export: str = "",
external_axes: Optional[List[plt.Axes]] = None,
):
"""Plots Reddit sentiment about a search term. Prints table showing if display is True.
Parameters
----------
symbol: str
The ticker symbol being search for in Reddit
sortby: str
Type of search
limit: str
Number of posts to get at most
graphic: bool
Displays box and whisker plot
time_frame: str
Time frame for search
full_search: bool
Enable comprehensive search for ticker
subreddits: str
Comma-separated list of subreddits
display: bool
Enable printing of raw sentiment values for each post
export: str
Format to export data
external_axes: Optional[List[plt.Axes]]
If supplied, expect 1 external axis
"""
df, polarity_scores, avg_polarity = reddit_model.get_posts_about(
symbol, limit, sortby, time_frame, full_search, subreddits
)
if df.empty:
console.print(f"No posts for {symbol} found")
return
if display:
print_rich_table(df=df)
if graphic:
if not external_axes:
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
elif is_valid_axes_count(external_axes, 1):
(ax,) = external_axes
else:
return
sns.boxplot(x=polarity_scores, ax=ax)
ax.set_title(f"Sentiment Score of {symbol}")
ax.set_xlabel("Sentiment Score")
if not external_axes:
theme.visualize_output()
console.print(f"Sentiment Analysis for {symbol} is {avg_polarity}\n")
export_data(
export,
os.path.dirname(os.path.abspath(__file__)),
"polarity_scores",
df,
)