From fa951486965760470207f4f820fde024100e19fb Mon Sep 17 00:00:00 2001 From: didier Date: Sat, 6 Mar 2021 21:24:26 +0000 Subject: [PATCH] Hopefully last big refactoring --- .../fundamental_analysis/yahoo_finance_api.py | 87 ++--- gamestonk_terminal/reddit_helpers.py | 37 +- gamestonk_terminal/sentiment/google_api.py | 34 +- gamestonk_terminal/sentiment/reddit_api.py | 319 ++---------------- .../sentiment/stocktwits_api.py | 34 +- gamestonk_terminal/sentiment/twitter_api.py | 51 ++- terminal.py | 116 ++----- 7 files changed, 148 insertions(+), 530 deletions(-) diff --git a/gamestonk_terminal/fundamental_analysis/yahoo_finance_api.py b/gamestonk_terminal/fundamental_analysis/yahoo_finance_api.py index 71e11b381d1..a41dec2007b 100644 --- a/gamestonk_terminal/fundamental_analysis/yahoo_finance_api.py +++ b/gamestonk_terminal/fundamental_analysis/yahoo_finance_api.py @@ -3,10 +3,14 @@ from datetime import datetime import yfinance as yf import pandas as pd -from gamestonk_terminal.helper_funcs import long_number_format +from gamestonk_terminal.dataframe_helpers import clean_df_index + +from gamestonk_terminal.helper_funcs import ( + long_number_format, + parse_known_args_and_warn, +) -# ---------------------------------------------------- INFO ---------------------------------------------------- def info(l_args, s_ticker): parser = argparse.ArgumentParser( prog="info", @@ -20,37 +24,26 @@ def info(l_args, s_ticker): Market cap, Average volume, Price to sales trailing 12 months, Day low, Ask, Ask size, Volume, Fifty two week high, Forward PE, Fifty two week low, Bid, Tradeable, Bid size, Day high, Exchange, Short name, Long name, Exchange timezone name, Exchange timezone - short name, Is esg populated, Gmt off set milliseconds, Quote type, Symbol, Message board id, - Market, Enterprise to revenue, Profit margins, Enterprise to ebitda, 52 week change, - Forward EPS, Shares outstanding, Book value, Shares short, Shares percent shares out, - Last fiscal year end, Held percent institutions, Net income to common, Trailing EPS, - Sand p52 week change, Price to book, Held percent insiders, Next fiscal year end, - Most recent quarter, Short ratio, Shares short previous month date, Float shares, - Enterprise value, Last split date, Last split factor, Earnings quarterly growth, + short name, Is esg populated, Gmt off set milliseconds, Quote type, Symbol, Message + board id, Market, Enterprise to revenue, Profit margins, Enterprise to ebitda, 52 week + change, Forward EPS, Shares outstanding, Book value, Shares short, Shares percent + shares out, Last fiscal year end, Held percent institutions, Net income to common, + Trailing EPS, Sand p52 week change, Price to book, Held percent insiders, Next fiscal + year end, Most recent quarter, Short ratio, Shares short previous month date, Float + shares, Enterprise value, Last split date, Last split factor, Earnings quarterly growth, Date short interest, PEG ratio, Short percent of float, Shares short prior month, Regular market price, Logo_url. [Source: Yahoo Finance] """, ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + parse_known_args_and_warn(parser, l_args) stock = yf.Ticker(s_ticker) df_info = pd.DataFrame(stock.info.items(), columns=["Metric", "Value"]) df_info = df_info.set_index("Metric") - # Remove white spaces - df_info.index = [ - "".join( - " " + char if char.isupper() else char.strip() for char in idx - ).strip() - for idx in df_info.index.tolist() - ] - df_info.index = [s_val.capitalize() for s_val in df_info.index] + clean_df_index(df_info) if ( "Last split date" in df_info.index @@ -92,19 +85,15 @@ def info(l_args, s_ticker): return -# ---------------------------------------------------- SHAREHOLDERS ---------------------------------------------------- def shareholders(l_args, s_ticker): parser = argparse.ArgumentParser( prog="shrs", - description="""Print Major, institutional and mutualfunds shareholders. [Source: Yahoo Finance]""", + description="""Print Major, institutional and mutualfunds shareholders. + [Source: Yahoo Finance]""", ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + parse_known_args_and_warn(parser, l_args) stock = yf.Ticker(s_ticker) pd.set_option("display.max_colwidth", -1) @@ -121,8 +110,8 @@ def shareholders(l_args, s_ticker): # Institutional holders print("Institutional holders") df_institutional_shareholders = stock.institutional_holders - df_institutional_shareholders.columns = ( - df_institutional_shareholders.columns.str.replace("% Out", "Stake") + df_institutional_shareholders.columns = df_institutional_shareholders.columns.str.replace( + "% Out", "Stake" ) df_institutional_shareholders["Shares"] = df_institutional_shareholders[ "Shares" @@ -139,8 +128,8 @@ def shareholders(l_args, s_ticker): # Mutualfunds holders print("Mutualfunds holders") df_mutualfund_shareholders = stock.mutualfund_holders - df_mutualfund_shareholders.columns = ( - df_mutualfund_shareholders.columns.str.replace("% Out", "Stake") + df_mutualfund_shareholders.columns = df_mutualfund_shareholders.columns.str.replace( + "% Out", "Stake" ) df_mutualfund_shareholders["Shares"] = df_mutualfund_shareholders[ "Shares" @@ -161,25 +150,17 @@ def shareholders(l_args, s_ticker): return -# ---------------------------------------------------- SUSTAINABILITY ---------------------------------------------------- def sustainability(l_args, s_ticker): parser = argparse.ArgumentParser( prog="sust", description=""" Print sustainability values of the company. The following fields are expected: - Palmoil, Controversialweapons, Gambling, Socialscore, Nuclear, Furleather, Alcoholic, Gmo, - Catholic, Socialpercentile, Peercount, Governancescore, Environmentpercentile, Animaltesting, - Tobacco, Totalesg, Highestcontroversy, Esgperformance, Coal, Pesticides, Adult, Percentile, - Peergroup, Smallarms, Environmentscore, Governancepercentile, Militarycontract. [Source: Yahoo Finance] + Palmoil, Controversialweapons, Gambling, Socialscore, Nuclear, Furleather, Alcoholic, Gmo, Catholic, Socialpercentile, Peercount, Governancescore, Environmentpercentile, Animaltesting, Tobacco, Totalesg, Highestcontroversy, Esgperformance, Coal, Pesticides, Adult, Percentile, Peergroup, Smallarms, Environmentscore, Governancepercentile, Militarycontract. [Source: Yahoo Finance] """, ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + parse_known_args_and_warn(parser, l_args) stock = yf.Ticker(s_ticker) pd.set_option("display.max_colwidth", -1) @@ -191,15 +172,7 @@ def sustainability(l_args, s_ticker): print("") return - df_sustainability.index = [ - "".join( - " " + char if char.isupper() else char.strip() for char in idx - ).strip() - for idx in df_sustainability.index.tolist() - ] - df_sustainability.index = [ - s_val.capitalize() for s_val in df_sustainability.index - ] + clean_df_index(df_sustainability) df_sustainability = df_sustainability.rename( index={ @@ -225,21 +198,17 @@ def sustainability(l_args, s_ticker): return -# ---------------------------------------------------- CALENDAR_EARNINGS ---------------------------------------------------- def calendar_earnings(l_args, s_ticker): parser = argparse.ArgumentParser( prog="calendar_earnings", description=""" - Calendar earnings of the company. Including revenue and earnings estimates. [Source: Yahoo Finance] + Calendar earnings of the company. Including revenue and earnings estimates. + [Source: Yahoo Finance] """, ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + parse_known_args_and_warn(parser, l_args) stock = yf.Ticker(s_ticker) df_calendar = stock.calendar diff --git a/gamestonk_terminal/reddit_helpers.py b/gamestonk_terminal/reddit_helpers.py index 2e5b3b06f12..8c0a0de1c8f 100644 --- a/gamestonk_terminal/reddit_helpers.py +++ b/gamestonk_terminal/reddit_helpers.py @@ -1,5 +1,6 @@ -from datetime import datetime - +from datetime import datetime, timedelta +import re +from holidays import US as holidaysUS from prettytable import PrettyTable @@ -43,3 +44,35 @@ def print_and_record_reddit_post(submissions_dict, submission): ) print(t_post) print("\n") + + +def get_last_time_market_was_open(dt): + # Check if it is a weekend + if dt.date().weekday() > 4: + dt = get_last_time_market_was_open(dt - timedelta(hours=24)) + + # Check if it is a holiday + if dt.strftime("%Y-%m-%d") in holidaysUS(): + dt = get_last_time_market_was_open(dt - timedelta(hours=24)) + + dt = dt.replace(hour=21, minute=0, second=0) + + return dt + + +def find_tickers(submission): + ls_text = list() + ls_text.append(submission.selftext) + ls_text.append(submission.title) + + submission.comments.replace_more(limit=0) + for comment in submission.comments.list(): + ls_text.append(comment.body) + + l_tickers_found = list() + for s_text in ls_text: + for s_ticker in set(re.findall(r"([A-Z]{3,5} )", s_text)): + l_tickers_found.append(s_ticker.strip()) + + return l_tickers_found + diff --git a/gamestonk_terminal/sentiment/google_api.py b/gamestonk_terminal/sentiment/google_api.py index 3670af91446..587011926c1 100644 --- a/gamestonk_terminal/sentiment/google_api.py +++ b/gamestonk_terminal/sentiment/google_api.py @@ -1,10 +1,13 @@ import argparse from pytrends.request import TrendReq import matplotlib.pyplot as plt -from gamestonk_terminal.helper_funcs import check_positive, valid_date +from gamestonk_terminal.helper_funcs import ( + check_positive, + valid_date, + parse_known_args_and_warn, +) -# ------------------------------------------------------------------------------------------------------------------- def mentions(l_args, s_ticker, s_start): parser = argparse.ArgumentParser( prog="mentions", @@ -23,11 +26,7 @@ def mentions(l_args, s_ticker, s_start): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) pytrend = TrendReq() pytrend.build_payload(kw_list=[s_ticker]) @@ -63,7 +62,6 @@ def mentions(l_args, s_ticker, s_start): print("") -# ------------------------------------------------------------------------------------------------------------------- def regions(l_args, s_ticker): parser = argparse.ArgumentParser( prog="regions", @@ -81,11 +79,7 @@ def regions(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) pytrend = TrendReq() pytrend.build_payload(kw_list=[s_ticker]) @@ -108,7 +102,6 @@ def regions(l_args, s_ticker): print("") -# ------------------------------------------------------------------------------------------------------------------- def queries(l_args, s_ticker): parser = argparse.ArgumentParser( prog="queries", @@ -126,11 +119,7 @@ def queries(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) pytrend = TrendReq() pytrend.build_payload(kw_list=[s_ticker]) @@ -148,7 +137,6 @@ def queries(l_args, s_ticker): print("") -# ------------------------------------------------------------------------------------------------------------------- def rise(l_args, s_ticker): parser = argparse.ArgumentParser( prog="rise", @@ -166,11 +154,7 @@ def rise(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) pytrend = TrendReq() pytrend.build_payload(kw_list=[s_ticker]) diff --git a/gamestonk_terminal/sentiment/reddit_api.py b/gamestonk_terminal/sentiment/reddit_api.py index b92bc2bab1a..c275882f460 100644 --- a/gamestonk_terminal/sentiment/reddit_api.py +++ b/gamestonk_terminal/sentiment/reddit_api.py @@ -1,35 +1,22 @@ import argparse -import re -from datetime import datetime, timedelta import warnings +from datetime import datetime, timedelta import pandas as pd from prawcore.exceptions import ResponseException from requests import HTTPError from prettytable import PrettyTable from psaw import PushshiftAPI import praw -from holidays import US as holidaysUS import finviz -from gamestonk_terminal.helper_funcs import check_positive +from gamestonk_terminal.helper_funcs import check_positive, parse_known_args_and_warn from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.reddit_helpers import ( + get_last_time_market_was_open, + print_and_record_reddit_post, + find_tickers, +) -# ------------------------------------------------------------------------------------------------------------------- -def get_last_time_market_was_open(dt): - # Check if it is a weekend - if dt.date().weekday() > 4: - dt = get_last_time_market_was_open(dt - timedelta(hours=24)) - - # Check if it is a holiday - if dt.strftime("%Y-%m-%d") in holidaysUS(): - dt = get_last_time_market_was_open(dt - timedelta(hours=24)) - - dt = dt.replace(hour=21, minute=0, second=0) - - return dt - - -# ------------------------------------------------------------------------------------------------------------------- def watchlist(l_args): parser = argparse.ArgumentParser( prog="watchlist", @@ -46,11 +33,7 @@ def watchlist(l_args): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) l_sub_reddits = [ "pennystocks", @@ -66,7 +49,6 @@ def watchlist(l_args): d_watchlist_tickers = {} l_watchlist_links = list() l_watchlist_author = list() - ls_text = list() praw_api = praw.Reddit( client_id=cfg.API_REDDIT_CLIENT_ID, @@ -108,18 +90,7 @@ def watchlist(l_args): and submission.link_flair_text not in ["Yolo", "Meme"] and submission.author.name not in l_watchlist_author ): - ls_text = list() - ls_text.append(submission.selftext) - ls_text.append(submission.title) - - submission.comments.replace_more(limit=0) - for comment in submission.comments.list(): - ls_text.append(comment.body) - - l_tickers_found = list() - for s_text in ls_text: - for s_ticker in set(re.findall(r"([A-Z]{3,5} )", s_text)): - l_tickers_found.append(s_ticker.strip()) + l_tickers_found = find_tickers(submission) if l_tickers_found: # Add another author's name to the parsed watchlists @@ -137,56 +108,8 @@ def watchlist(l_args): l_watchlist_links.append( f"https://old.reddit.com{submission.permalink}" ) - # delete below, not necessary I reckon. Probably just link? - # Refactor data - s_datetime = datetime.utcfromtimestamp( - submission.created_utc - ).strftime("%d/%m/%Y %H:%M:%S") - s_link = f"https://old.reddit.com{submission.permalink}" - s_all_awards = "" - for award in submission.all_awardings: - s_all_awards += f"{award['count']} {award['name']}\n" - s_all_awards = s_all_awards[:-2] - - # Create dictionary with data to construct dataframe allows to save data - d_submission[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 - print(f"\n{s_datetime} - {submission.title}") - print(f"{s_link}") - t_post = PrettyTable( - [ - "Subreddit", - "Flair", - "Score", - "# Comments", - "Upvote %", - "Awards", - ] - ) - t_post.add_row( - [ - submission.subreddit, - submission.link_flair_text, - submission.score, - submission.num_comments, - f"{round(100*submission.upvote_ratio)}%", - s_all_awards, - ] - ) - print(t_post) - print("") + print_and_record_reddit_post(d_submission, submission) # Increment count of valid posts found n_flair_posts_found += 1 @@ -224,7 +147,6 @@ def watchlist(l_args): print("") -# ------------------------------------------------------------------------------------------------------------------- def popular_tickers(l_args): parser = argparse.ArgumentParser( prog="popular", @@ -256,7 +178,8 @@ def popular_tickers(l_args): type=str, help=""" subreddits to look for tickers, e.g. pennystocks,stocks. - Default: pennystocks, RobinHoodPennyStocks, Daytrading, StockMarket, stocks, investing, wallstreetbets + Default: pennystocks, RobinHoodPennyStocks, Daytrading, StockMarket, stocks, investing, + wallstreetbets """, ) parser.add_argument( @@ -270,12 +193,7 @@ def popular_tickers(l_args): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - print("popular -s -l -d ") - return + ns_parser = parse_known_args_and_warn(parser, l_args) n_ts_after = int( (datetime.today() - timedelta(days=ns_parser.n_days)).timestamp() @@ -338,18 +256,7 @@ def popular_tickers(l_args): and (submission.selftext or submission.title) and submission.author.name not in l_watchlist_author ): - ls_text = list() - ls_text.append(submission.selftext) - ls_text.append(submission.title) - - submission.comments.replace_more(limit=0) - for comment in submission.comments.list(): - ls_text.append(comment.body) - - l_tickers_found = list() - for s_text in ls_text: - for s_ticker in set(re.findall(r"([A-Z]{3,5} )", s_text)): - l_tickers_found.append(s_ticker.strip()) + l_tickers_found = find_tickers(submission) if l_tickers_found: n_tickers += len(l_tickers_found) @@ -443,7 +350,6 @@ def popular_tickers(l_args): print("") -# ------------------------------------------------------------------------------------------------------------------- def spac_community(l_args): parser = argparse.ArgumentParser( prog="spac_c", @@ -468,11 +374,7 @@ def spac_community(l_args): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) praw_api = praw.Reddit( client_id=cfg.API_REDDIT_CLIENT_ID, @@ -509,18 +411,7 @@ def spac_community(l_args): and submission.link_flair_text not in ["Yolo", "Meme"] and submission.author.name not in l_watchlist_author ): - ls_text = list() - ls_text.append(submission.selftext) - ls_text.append(submission.title) - - submission.comments.replace_more(limit=0) - for comment in submission.comments.list(): - ls_text.append(comment.body) - - l_tickers_found = list() - for s_text in ls_text: - for s_ticker in set(re.findall(r"([A-Z]{3,5} )", s_text)): - l_tickers_found.append(s_ticker.strip()) + l_tickers_found = find_tickers(submission) if l_tickers_found: # Add another author's name to the parsed watchlists @@ -536,58 +427,10 @@ def spac_community(l_args): d_watchlist_tickers[key] = 1 l_watchlist_links.append( - f"https://www.reddit.com{submission.permalink}" + f"https://old.reddit.com{submission.permalink}" ) - # delete below, not necessary I reckon. Probably just link? - # Refactor data - s_datetime = datetime.utcfromtimestamp( - submission.created_utc - ).strftime("%d/%m/%Y %H:%M:%S") - s_link = f"https://www.reddit.com{submission.permalink}" - s_all_awards = "" - for award in submission.all_awardings: - s_all_awards += f"{award['count']} {award['name']}\n" - s_all_awards = s_all_awards[:-2] - - # Create dictionary with data to construct dataframe allows to save data - d_submission[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 - print(f"{s_datetime} - {submission.title}") - print(f"{s_link}") - t_post = PrettyTable( - [ - "Subreddit", - "Flair", - "Score", - "# Comments", - "Upvote %", - "Awards", - ] - ) - t_post.add_row( - [ - submission.subreddit, - submission.link_flair_text, - submission.score, - submission.num_comments, - f"{round(100*submission.upvote_ratio)}%", - s_all_awards, - ] - ) - print(t_post) - print("\n") + print_and_record_reddit_post(d_submission, submission) # Check if search_submissions didn't get anymore posts else: @@ -622,7 +465,6 @@ def spac_community(l_args): print("") -# ------------------------------------------------------------------------------------------------------------------- def spac(l_args): parser = argparse.ArgumentParser( prog="spac", description=""" Show other users SPACs announcement [Reddit] """ @@ -647,11 +489,7 @@ def spac(l_args): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) praw_api = praw.Reddit( client_id=cfg.API_REDDIT_CLIENT_ID, @@ -703,19 +541,7 @@ def spac(l_args): and submission.link_flair_text not in ["Yolo", "Meme"] and submission.author.name not in l_watchlist_author ): - - ls_text = list() - ls_text.append(submission.selftext) - ls_text.append(submission.title) - - submission.comments.replace_more(limit=0) - for comment in submission.comments.list(): - ls_text.append(comment.body) - - l_tickers_found = list() - for s_text in ls_text: - for s_ticker in set(re.findall(r"([A-Z]{3,5} )", s_text)): - l_tickers_found.append(s_ticker.strip()) + l_tickers_found = find_tickers(submission) if l_tickers_found: # Add another author's name to the parsed watchlists @@ -733,56 +559,8 @@ def spac(l_args): l_watchlist_links.append( f"https://old.reddit.com{submission.permalink}" ) - # delete below, not necessary I reckon. Probably just link? - # Refactor data - s_datetime = datetime.utcfromtimestamp( - submission.created_utc - ).strftime("%d/%m/%Y %H:%M:%S") - s_link = f"https://old.reddit.com{submission.permalink}" - s_all_awards = "" - for award in submission.all_awardings: - s_all_awards += f"{award['count']} {award['name']}\n" - s_all_awards = s_all_awards[:-2] - - # Create dictionary with data to construct dataframe allows to save data - d_submission[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 - print(f"{s_datetime} - {submission.title}") - print(f"{s_link}") - t_post = PrettyTable( - [ - "Subreddit", - "Flair", - "Score", - "# Comments", - "Upvote %", - "Awards", - ] - ) - t_post.add_row( - [ - submission.subreddit, - submission.link_flair_text, - submission.score, - submission.num_comments, - f"{round(100*submission.upvote_ratio)}%", - s_all_awards, - ] - ) - print(t_post) - print("\n") + print_and_record_reddit_post(d_submission, submission) # Increment count of valid posts found n_flair_posts_found += 1 @@ -824,7 +602,6 @@ def spac(l_args): print("") -# ------------------------------------------------------------------------------------------------------------------- def wsb_community(l_args): parser = argparse.ArgumentParser( prog="wsb", @@ -849,11 +626,7 @@ def wsb_community(l_args): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) praw_api = praw.Reddit( client_id=cfg.API_REDDIT_CLIENT_ID, @@ -892,54 +665,8 @@ def wsb_community(l_args): f"https://old.reddit.com{submission.permalink}" ) - # Refactor data - s_datetime = datetime.utcfromtimestamp( - submission.created_utc - ).strftime("%d/%m/%Y %H:%M:%S") - s_link = f"https://old.reddit.com{submission.permalink}" - s_all_awards = "" - for award in submission.all_awardings: - s_all_awards += f"{award['count']} {award['name']}\n" - s_all_awards = s_all_awards[:-2] + print_and_record_reddit_post(d_submission, submission) - # Create dictionary with data to construct dataframe allows to save data - d_submission[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 - print(f"{s_datetime} - {submission.title}") - print(f"{s_link}") - t_post = PrettyTable( - [ - "Subreddit", - "Flair", - "Score", - "# Comments", - "Upvote %", - "Awards", - ] - ) - t_post.add_row( - [ - submission.subreddit, - submission.link_flair_text, - submission.score, - submission.num_comments, - f"{round(100*submission.upvote_ratio)}%", - s_all_awards, - ] - ) - print(t_post) - print("") # Check if search_submissions didn't get anymore posts else: break diff --git a/gamestonk_terminal/sentiment/stocktwits_api.py b/gamestonk_terminal/sentiment/stocktwits_api.py index ad29ebb6a1d..e74bed1494d 100644 --- a/gamestonk_terminal/sentiment/stocktwits_api.py +++ b/gamestonk_terminal/sentiment/stocktwits_api.py @@ -1,10 +1,9 @@ import argparse import requests import pandas as pd -from gamestonk_terminal.helper_funcs import check_positive +from gamestonk_terminal.helper_funcs import check_positive, parse_known_args_and_warn -# ------------------------------------------------------------------------------------------------------------------- def bullbear(l_args, s_ticker): parser = argparse.ArgumentParser( prog="bullbear", @@ -25,11 +24,7 @@ def bullbear(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) result = requests.get( f"https://api.stocktwits.com/api/2/streams/symbol/{ns_parser.s_ticker}.json" @@ -57,7 +52,6 @@ def bullbear(l_args, s_ticker): print("") -# ------------------------------------------------------------------------------------------------------------------- def messages(l_args, s_ticker): parser = argparse.ArgumentParser( prog="messages", @@ -84,11 +78,7 @@ def messages(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) result = requests.get( f"https://api.stocktwits.com/api/2/streams/symbol/{ns_parser.s_ticker}.json" @@ -96,7 +86,7 @@ def messages(l_args, s_ticker): if result.status_code == 200: for idx, message in enumerate(result.json()["messages"]): print( - "------------------------------------------------------------------------------------------" + "------------------------------------------------------------------------------" ) print(message["body"]) if idx > ns_parser.n_lim - 1: @@ -110,18 +100,13 @@ def messages(l_args, s_ticker): print("") -# ------------------------------------------------------------------------------------------------------------------- def trending(l_args): parser = argparse.ArgumentParser( prog="trending", description="""Stocks trending. [Source: Stocktwits]""" ) try: - (_, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) result = requests.get("https://api.stocktwits.com/api/2/trending/symbols.json") if result.status_code == 200: @@ -145,7 +130,6 @@ def trending(l_args): print("") -# ------------------------------------------------------------------------------------------------------------------- def stalker(l_args): parser = argparse.ArgumentParser( prog="stalker", @@ -172,11 +156,7 @@ def stalker(l_args): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) result = requests.get( f"https://api.stocktwits.com/api/2/streams/user/{ns_parser.s_user}.json" @@ -185,7 +165,7 @@ def stalker(l_args): if result.status_code == 200: for idx, message in enumerate(result.json()["messages"]): print( - "------------------------------------------------------------------------------------------" + "------------------------------------------------------------------------------" ) print(message["created_at"].replace("T", " ").replace("Z", "")) print(message["body"]) diff --git a/gamestonk_terminal/sentiment/twitter_api.py b/gamestonk_terminal/sentiment/twitter_api.py index aeed3cb78e8..9ab815059d1 100644 --- a/gamestonk_terminal/sentiment/twitter_api.py +++ b/gamestonk_terminal/sentiment/twitter_api.py @@ -8,7 +8,11 @@ import numpy as np import flair import matplotlib.pyplot as plt from gamestonk_terminal import config_terminal as cfg -from gamestonk_terminal.helper_funcs import get_data, clean_tweet +from gamestonk_terminal.helper_funcs import ( + get_data, + clean_tweet, + parse_known_args_and_warn, +) def load_tweets(s_ticker: str, count: int) -> DataFrame: @@ -58,21 +62,19 @@ def load_tweets(s_ticker: str, count: int) -> DataFrame: df_tweets["probability"] = probs df_tweets["sentiment"] = sentiments - # Add sentiment estimation (probability positive for POSITIVE sentiment, and negative for NEGATIVE sentiment) + # Add sentiment estimation (probability positive for POSITIVE sentiment, + # and negative for NEGATIVE sentiment) df_tweets["sentiment_estimation"] = df_tweets.apply( - lambda row: row["probability"] * (-1, 1)[row["sentiment"] == "POSITIVE"], - axis=1, + lambda row: row["probability"] * (-1, 1)[row["sentiment"] == "POSITIVE"], axis=1 ).cumsum() # Cumulative sentiment_estimation df_tweets["prob_sen"] = df_tweets.apply( - lambda row: row["probability"] * (-1, 1)[row["sentiment"] == "POSITIVE"], - axis=1, + lambda row: row["probability"] * (-1, 1)[row["sentiment"] == "POSITIVE"], axis=1 ) return df_tweets -# ------------------------------------------------- INFERENCE ------------------------------------------------- def inference(l_args, s_ticker): parser = argparse.ArgumentParser( prog="infer", @@ -80,7 +82,8 @@ def inference(l_args, s_ticker): Print quick sentiment inference from last tweets that contain the ticker. This model splits the text into character-level tokens and uses the DistilBERT model to make predictions. DistilBERT is a distilled version of the powerful - BERT transformer model. Not only time period of these, but also frequency. [Source: Twitter] + BERT transformer model. Not only time period of these, but also frequency. + [Source: Twitter] """, ) @@ -96,11 +99,7 @@ def inference(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) # Get tweets using Twitter API params = { @@ -148,7 +147,8 @@ def inference(l_args, s_ticker): df_tweets["probability"] = probs df_tweets["sentiment"] = sentiments - # Add sentiment estimation (probability positive for POSITIVE sentiment, and negative for NEGATIVE sentiment) + # Add sentiment estimation (probability positive for POSITIVE sentiment, + # and negative for NEGATIVE sentiment) df_tweets["sentiment_estimation"] = df_tweets.apply( lambda row: row["probability"] * (-1, 1)[row["sentiment"] == "POSITIVE"], axis=1, @@ -174,20 +174,13 @@ def inference(l_args, s_ticker): print(f"From: {dt_from.strftime('%Y-%m-%d %H:%M:%S')}") print(f"To: {dt_to.strftime('%Y-%m-%d %H:%M:%S')}") - # pd.set_option("display.max_rows", None) - # pd.set_option("display.max_columns", None) - # pd.set_option("display.width", None) - # pd.set_option("display.max_colwidth", None) - # print(df_tweets) - print(f"{len(df_tweets)} tweets were analyzed.") dt_delta = dt_to - dt_from n_freq = dt_delta.total_seconds() / len(df_tweets) print(f"Frequency of approx 1 tweet every {round(n_freq)} seconds.") s_sen = f"{('NEGATIVE', 'POSITIVE')[int(df_tweets['sentiment_estimation'].values[-1] > 0)]}" - s_conf = f"{round(100*df_tweets['sentiment_estimation'].values[-1]/df_tweets['probability'].sum())}%" - print(f"The sentiment of {s_ticker} is: {s_sen} ({s_conf})") + print(f"The sentiment of {s_ticker} is: {s_sen} ({n_pct} %)") print("") except Exception as e: @@ -195,7 +188,6 @@ def inference(l_args, s_ticker): print("") -# ------------------------------------------------- SENTIMENT ------------------------------------------------- def sentiment(l_args, s_ticker): parser = argparse.ArgumentParser( prog="sen", @@ -203,13 +195,14 @@ def sentiment(l_args, s_ticker): Plot in-depth sentiment predicted from tweets from last days that contain pre-defined ticker. This model splits the text into character-level tokens and uses the DistilBERT model to make predictions. DistilBERT is a distilled - version of the powerful BERT transformer model. Note that a big num of tweets extracted per - hour in conjunction with a high number of days in the past, will make the + version of the powerful BERT transformer model. Note that a big num of tweets + extracted per hour in conjunction with a high number of days in the past, will make the algorithm take a long period of time to estimate sentiment. [Source: Twitter] """, ) - # in reality this argument could be 100, but after testing it takes too long to compute which may not be acceptable + # in reality this argument could be 100, but after testing it takes too long + # to compute which may not be acceptable parser.add_argument( "-n", "--num", @@ -232,11 +225,7 @@ def sentiment(l_args, s_ticker): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) - - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}\n") - return + ns_parser = parse_known_args_and_warn(parser, l_args) # Setup API request params and headers headers = {"authorization": f"Bearer {cfg.API_TWITTER_BEARER_TOKEN}"} diff --git a/terminal.py b/terminal.py index bf6e8c3cf1b..f24f514c2f0 100644 --- a/terminal.py +++ b/terminal.py @@ -9,6 +9,7 @@ from gamestonk_terminal.helper_funcs import ( check_positive, b_is_stock_market_open, plot_view_stock, + parse_known_args_and_warn, ) from gamestonk_terminal.fundamental_analysis import fa_menu as fam from gamestonk_terminal.technical_analysis import ta_menu as tam @@ -24,7 +25,6 @@ from gamestonk_terminal import config_terminal as cfg # warnings.simplefilter("always") -# ----------------------------------------------------- LOAD ----------------------------------------------------- def load(l_args, s_ticker, s_start, s_interval, df_stock): parser = argparse.ArgumentParser( prog="load", description=""" Load a stock in order to perform analysis""" @@ -56,14 +56,12 @@ def load(l_args, s_ticker, s_start, s_interval, df_stock): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + ns_parser = parse_known_args_and_warn(parser, l_args) + except SystemExit: print("") return [s_ticker, s_start, s_interval, df_stock] - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - # Update values: s_ticker = ns_parser.s_ticker s_start = ns_parser.s_start_date @@ -106,7 +104,6 @@ def load(l_args, s_ticker, s_start, s_interval, df_stock): return [s_ticker, s_start, s_interval, df_stock] -# ----------------------------------------------------- VIEW ----------------------------------------------------- def view(l_args, s_ticker, s_start, s_interval, df_stock): parser = argparse.ArgumentParser( prog="view", @@ -158,14 +155,12 @@ def view(l_args, s_ticker, s_start, s_interval, df_stock): ) try: - (ns_parser, l_unknown_args) = parser.parse_known_args(l_args) + ns_parser = parse_known_args_and_warn(parser, l_args) + except SystemExit: print("") return - if l_unknown_args: - print(f"The following args couldn't be interpreted: {l_unknown_args}") - # Update values: s_ticker = ns_parser.s_ticker @@ -275,7 +270,6 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): print("") -# ----------------------------------------------------------------------------------------------------------------------- # pylint: disable=too-many-branches def main(): """ @@ -320,10 +314,14 @@ def main(): # Print first welcome message and help print("\nWelcome to Didier's Gamestonk Terminal\n") - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) + should_print_help = True # Loop forever and ever while True: + if should_print_help: + print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) + should_print_help = False + # Get input command from user as_input = input("> ") @@ -340,12 +338,12 @@ def main(): print("The command selected doesn't exist\n") continue + b_quit = False if ns_known_args.opt == "help": - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) + should_print_help = True elif (ns_known_args.opt == "quit") or (ns_known_args.opt == "q"): - print("Hope you made money today. Good bye my lover, good bye my friend.\n") - return + break elif ns_known_args.opt == "clear": print("Clearing stock ticker to be used for analysis") @@ -353,103 +351,40 @@ def main(): s_start = "" elif ns_known_args.opt == "load": - [s_ticker, s_start, s_interval, df_stock] = load( + s_ticker, s_start, s_interval, df_stock = load( l_args, s_ticker, s_start, s_interval, df_stock ) elif ns_known_args.opt == "view": view(l_args, s_ticker, s_start, s_interval, df_stock) - # DISCOVERY MENU elif ns_known_args.opt == "disc": b_quit = dm.disc_menu() - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # PAPERMILL MENU elif ns_known_args.opt == "mill": b_quit = mill.papermill_menu() - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # SENTIMENT MARKET elif ns_known_args.opt == "sen": b_quit = sm.sen_menu(s_ticker, s_start) - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # RESEARCH MENU elif ns_known_args.opt == "res": b_quit = rm.res_menu(s_ticker) - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # FUNDAMENTAL ANALYSIS MENU elif ns_known_args.opt == "fa": b_quit = fam.fa_menu(s_ticker, s_start, s_interval) - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # TECHNICAL ANALYSIS MENU elif ns_known_args.opt == "ta": b_quit = tam.ta_menu(df_stock, s_ticker, s_start, s_interval) - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # DUE DILIGENCE MENU elif ns_known_args.opt == "dd": b_quit = ddm.dd_menu(df_stock, s_ticker, s_start, s_interval) - if b_quit: - print( - "Hope you made money today. Good bye my lover, good bye my friend.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - - # PREDICTION TECHNIQUES elif ns_known_args.opt == "pred": if s_interval == "1440min": b_quit = pm.pred_menu(df_stock, s_ticker, s_start, s_interval) - # If stock data is intradaily, we need to get data again as prediction techniques work on daily adjusted data + # If stock data is intradaily, we need to get data again as prediction + # techniques work on daily adjusted data else: try: ts = TimeSeries( @@ -468,20 +403,21 @@ def main(): except Exception as e: print(e) print("Either the ticker or the API_KEY are invalids. Try again!") - b_quit = False return - if b_quit: - print( - "Hope you enjoyed the terminal. Remember that stonks only go up. Diamond hands.\n" - ) - return - - print_help(s_ticker, s_start, s_interval, b_is_stock_market_open()) - else: print("Shouldn't see this command!") + continue + + if b_quit: + break + else: + should_print_help = True + + print( + "Hope you enjoyed the terminal. Remember that stonks only go up. Diamond hands.\n" + ) if __name__ == "__main__":