mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-13 04:41:24 +00:00
Add discovery of stocks menu
This commit is contained in:
27
discovery/alpha_vantage_api.py
Normal file
27
discovery/alpha_vantage_api.py
Normal 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
75
discovery/disc_menu.py
Normal 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
260
discovery/reddit_api.py
Normal 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("")
|
||||
|
||||
27
discovery/yahoo_finance_api.py
Normal file
27
discovery/yahoo_finance_api.py
Normal 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("")
|
||||
Reference in New Issue
Block a user