Add discovery of stocks menu

This commit is contained in:
didier
2021-01-16 17:22:58 +00:00
parent 79be82c90c
commit a0fb0280f6
4 changed files with 389 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import argparse
from alpha_vantage.sectorperformance import SectorPerformances
import config_bot as cfg
import pandas as pd
import matplotlib.pyplot as plt
# ---------------------------------------------------- SECTORS ----------------------------------------------------
def sectors(l_args):
parser = argparse.ArgumentParser(prog='sectors', description='Plot sectors performance')
try:
(ns_parser, l_unknown_args) = parser.parse_known_args(l_args)
except SystemExit:
print("")
return
if l_unknown_args:
print(f"The following args couldn't be interpreted: {l_unknown_args}")
sp = SectorPerformances(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas')
df_sectors, d_meta_data = sp.get_sector()
df_sectors['Rank A: Real-Time Performance'].plot(kind='bar')
plt.title('Real Time Performance (%) per Sector')
plt.tight_layout()
plt.grid()
plt.show()
print("")

75
discovery/disc_menu.py Normal file
View File

@@ -0,0 +1,75 @@
import config_bot as cfg
import argparse
from discovery import alpha_vantage_api
from discovery import yahoo_finance_api
from discovery import reddit_api
# -----------------------------------------------------------------------------------------------------------------------
def print_discovery():
""" Print help """
print(" help show this fundamental analysis menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print("")
print(" sectors show sectors performance [Alpha Vantage]")
print(" gainers show latest top gainers [Yahoo Finance]")
print(" watchlist show other users watchlist [Reddit]")
print(" popular show popular tickers [Reddit]")
print("")
return
# ---------------------------------------------------- MENU ----------------------------------------------------
def disc_menu():
# Add list of arguments that the discovery parser accepts
disc_parser = argparse.ArgumentParser(prog='discovery', add_help=False)
disc_parser.add_argument('cmd', choices=['help', 'q', 'quit',
'sectors', 'gainers', 'watchlist', 'popular'])
print_discovery()
# Loop forever and ever
while True:
# Get input command from user
as_input = input('> ')
# Parse fundamental analysis command of the list of possible commands
try:
(ns_known_args, l_args) = disc_parser.parse_known_args(as_input.split())
except SystemExit:
print("The command selected doesn't exist\n")
continue
if ns_known_args.cmd == 'help':
print_discovery()
elif ns_known_args.cmd == 'q':
# Just leave the DISC menu
return False
elif ns_known_args.cmd == 'quit':
# Abandon the program
return True
# ------------------------------------------------ ALPHA VANTAGE -----------------------------------------------
elif ns_known_args.cmd == 'sectors':
alpha_vantage_api.sectors(l_args)
# ------------------------------------------------ YAHOO FINANCE ------------------------------------------------
elif ns_known_args.cmd == 'gainers':
yahoo_finance_api.gainers(l_args)
# ---------------------------------------------------- REDDIT ---------------------------------------------------
elif ns_known_args.cmd == 'watchlist':
reddit_api.watchlist(l_args)
elif ns_known_args.cmd == 'popular':
reddit_api.popular_tickers(l_args)
# ------------------------------------------------------------------------------------------------------------
else:
print("Command not recognized!")

260
discovery/reddit_api.py Normal file
View File

@@ -0,0 +1,260 @@
import argparse
from prettytable import PrettyTable
from datetime import datetime, timedelta
import pandas as pd
from psaw import PushshiftAPI
import praw
from datetime import datetime
from pytz import timezone
from holidays import US as holidaysUS
from stock_market_helper_funcs import *
import re
import finviz
import config_bot as cfg
# -------------------------------------------------------------------------------------------------------------------
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',
description=""" Show other users watchlist [Reddit] """)
parser.add_argument('-l', "--limit", action="store", dest="n_limit", type=check_positive, default=5, help='Limit of posts with watchlists retrieved')
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
l_sub_reddits = ['pennystocks', 'RobinHoodPennyStocks', 'Daytrading', 'StockMarket', 'stocks', 'investing', 'wallstreetbets']
d_submission = {}
d_watchlist_tickers = {}
l_watchlist_links = list()
l_watchlist_author = list()
praw_api = praw.Reddit(client_id=cfg.API_REDDIT_CLIENT_ID,
client_secret=cfg.API_REDDIT_CLIENT_SECRET,
username=cfg.API_REDDIT_USERNAME,
user_agent=cfg.API_REDDIT_USER_AGENT,
password=cfg.API_REDDIT_PASSWORD)
dt_last_time_market_close = get_last_time_market_was_open(datetime.now()-timedelta(hours=24))
n_ts_after = int(dt_last_time_market_close.timestamp())
psaw_api = PushshiftAPI()
submissions = psaw_api.search_submissions(after=n_ts_after,
subreddit=l_sub_reddits,
q='WATCHLIST|Watchlist|watchlist',
filter=['id'])
n_flair_posts_found = 0
while True:
submission = next(submissions, None)
if submission:
# Get more information about post using PRAW api
submission = praw_api.submission(id=submission.id)
# Ensure that the post hasn't been removed by moderator in the meanwhile,
#that there is a description and it's not just an image, that the flair is
#meaningful, and that we aren't re-considering same author's watchlist
if not submission.removed_by_category and submission.selftext \
and submission.link_flair_text not in ['Yolo', 'Meme'] \
and submission.author.name not in l_watchlist_author:
# Make sure there is at least 1 ticker found on the post text
l_tickers_found = set(re.findall("[A-Z]{3,5}", submission.selftext))
if l_tickers_found:
# Add another author's name to the parsed watchlists
l_watchlist_author.append(submission.author.name)
# Lookup stock tickers within a watchlist
for key in l_tickers_found:
if key in d_watchlist_tickers:
# Increment stock ticker found
d_watchlist_tickers[key] += 1
else:
# Initialize stock ticker found
d_watchlist_tickers[key] = 1
l_watchlist_links.append(f"https://www.reddit.com{submission.permalink}")
# delte 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"\n{s_datetime} - {submission.title}")
print(f"{s_link}")
# Increment count of valid posts found
n_flair_posts_found += 1
# Check if number of wanted posts found has been reached
if n_flair_posts_found > ns_parser.n_limit-1:
break
# Check if search_submissions didn't get anymore posts
else:
break
if n_flair_posts_found:
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])
s_watchlist_tickers += f"{t_ticker[1]} {t_ticker[0]}, "
n_tickers += 1
except:
pass
if n_tickers:
print("The following stock tickers have been mentioned across the previous watchlists:")
print(s_watchlist_tickers[:-2]+'\n')
except:
print("")
# -------------------------------------------------------------------------------------------------------------------
def popular_tickers(l_args):
parser = argparse.ArgumentParser(prog='popular',
description=""" Latest popular tickers [Reddit] """)
parser.add_argument('-l', "--limit", action="store", dest="n_limit", type=check_positive, default=30,
help='Limit of posts retrieved per sub reddit')
parser.add_argument('-s', "--sub", action="store", dest="s_subreddit", type=str,
help="""Subreddit to look for tickers. Default: pennystocks, RobinHoodPennyStocks, Daytrading,
StockMarket, stocks, investing, wallstreetbets""")
parser.add_argument('-d', "--days", action="store", dest="n_days", type=check_positive, default=1,
help="Look for the tickers from those n past days")
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
n_ts_after = int((datetime.today() - timedelta(days=ns_parser.n_days)).timestamp())
if ns_parser.s_subreddit:
l_sub_reddits = [ns_parser.s_subreddit]
else:
l_sub_reddits = ['pennystocks', 'RobinHoodPennyStocks', 'Daytrading', 'StockMarket', 'stocks', 'investing', 'wallstreetbets']
d_submission = {}
d_watchlist_tickers = {}
l_watchlist_links = list()
l_watchlist_author = list()
praw_api = praw.Reddit(client_id=cfg.API_REDDIT_CLIENT_ID,
client_secret=cfg.API_REDDIT_CLIENT_SECRET,
username=cfg.API_REDDIT_USERNAME,
user_agent=cfg.API_REDDIT_USER_AGENT,
password=cfg.API_REDDIT_PASSWORD)
psaw_api = PushshiftAPI()
for s_sub_reddit in l_sub_reddits:
print(f"Search for latest tickers under {ns_parser.n_limit} '{s_sub_reddit}' posts")
submissions = psaw_api.search_submissions(after=int(n_ts_after),
subreddit=s_sub_reddit,
limit=ns_parser.n_limit,
filter=['id'])
n_tickers = 0
while True:
submission = next(submissions, None)
if submission:
# Get more information about post using PRAW api
submission = praw_api.submission(id=submission.id)
# Ensure that the post hasn't been removed by moderator in the meanwhile,
#that there is a description and it's not just an image, that the flair is
#meaningful, and that we aren't re-considering same author's content
if not submission.removed_by_category and (submission.selftext or submission.title) \
and submission.link_flair_text not in ['Yolo', 'Meme'] \
and submission.author.name not in l_watchlist_author:
# Make sure there is at least 1 ticker found on the post text
l_tickers_found = set(re.findall("[A-Z]{3,5}", submission.selftext))
if not l_tickers_found:
l_tickers_found = set(re.findall("[A-Z]{3,5}", submission.title))
if l_tickers_found:
n_tickers += len(l_tickers_found)
# Add another author's name to the parsed watchlists
l_watchlist_author.append(submission.author.name)
# Lookup stock tickers within a watchlist
for key in l_tickers_found:
if key in d_watchlist_tickers:
# Increment stock ticker found
d_watchlist_tickers[key] += 1
else:
# Initialize stock ticker found
d_watchlist_tickers[key] = 1
# Check if search_submissions didn't get anymore posts
else:
break
print(f" {n_tickers} tickers found.")
lt_watchlist_sorted = sorted(d_watchlist_tickers.items(), key=lambda item: item[1], reverse=True)
print("\nThe following TOP10 tickers have been mentioned in the last 24 hours:")
n_top_stocks = 0
for t_ticker in lt_watchlist_sorted:
if n_top_stocks > 9:
break
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])
print(f"{t_ticker[1]} {t_ticker[0]}")
n_top_stocks += 1
except:
pass
except:
print("")

View File

@@ -0,0 +1,27 @@
import argparse
from alpha_vantage.sectorperformance import SectorPerformances
from alpha_vantage.timeseries import TimeSeries
import config_bot as cfg
import pandas as pd
#from stock_market_helper_funcs import *
# ---------------------------------------------------- GAINERS ----------------------------------------------------
def gainers(l_args):
parser = argparse.ArgumentParser(prog='gainers',
description='Show top ticker gainers from Yahoo Finance')
parser.add_argument('-n', "--num", action="store", dest="n_gainers", type=int, default=5, choices=range(1, 25),
help='Number of the top gainers stocks to retrieve (default 5)')
try:
(ns_parser, l_unknown_args) = parser.parse_known_args(l_args)
except SystemExit:
print("")
return
if l_unknown_args:
print(f"The following args couldn't be interpreted: {l_unknown_args}")
df_gainers = pd.read_html('https://finance.yahoo.com/screener/predefined/day_gainers')[0]
print(df_gainers.head(ns_parser.n_gainers).to_string(index=False))
print("")