diff --git a/docs/code/gamestonk_terminal.common.technical_analysis.rst b/docs/code/gamestonk_terminal.common.technical_analysis.rst index e1351a5b92e..e40291b86ad 100644 --- a/docs/code/gamestonk_terminal.common.technical_analysis.rst +++ b/docs/code/gamestonk_terminal.common.technical_analysis.rst @@ -68,14 +68,6 @@ gamestonk\_terminal.common.technical\_analysis.trend\_indicators\_view module :undoc-members: :show-inheritance: -gamestonk\_terminal.common.technical\_analysis.trendline\_api module --------------------------------------------------------------------- - -.. automodule:: gamestonk_terminal.common.technical_analysis.trendline_api - :members: - :undoc-members: - :show-inheritance: - gamestonk\_terminal.common.technical\_analysis.volatility\_model module ----------------------------------------------------------------------- diff --git a/gamestonk_terminal/common/technical_analysis/trendline_api.py b/gamestonk_terminal/common/technical_analysis/trendline_api.py deleted file mode 100644 index 2331f1ac750..00000000000 --- a/gamestonk_terminal/common/technical_analysis/trendline_api.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Technical Analysis Trendline Module""" -__docformat__ = "numpy" - -from typing import Union -from datetime import datetime - -import yfinance as yf -from pandas.core.frame import DataFrame -from scipy.stats import linregress - - -def load_ticker(ticker: str, start_date: Union[str, datetime]) -> DataFrame: - """Loads a ticker data from Yahoo Finance, adds a data index column data_id and Open-Close High/Low columns. - - Parameters - ---------- - ticker : str - The stock ticker. - start_date : Union[str,datetime] - Start date to load stock ticker data formatted YYYY-MM-DD. - - Returns - ------- - DataFrame - A Panda's data frame with columns Open, High, Low, Close, Adj Close, Volume, date_id, OC-High, OC-Low. - """ - # print(f"Start date: {start_date}") - df_data = yf.download(ticker, start=start_date, progress=False) - - df_data["date_id"] = (df_data.index.date - df_data.index.date.min()).astype( - "timedelta64[D]" - ) - df_data["date_id"] = df_data["date_id"].dt.days + 1 - - df_data["OC_High"] = df_data[["Open", "Close"]].max(axis=1) - df_data["OC_Low"] = df_data[["Open", "Close"]].min(axis=1) - - return df_data - - -def find_trendline(df_data: DataFrame, y_key: str, high_low: str = "high") -> DataFrame: - """Attempts to find a trend line based on y_key column from a given stock ticker data frame. - - Parameters - ---------- - df_data : DataFrame - The stock ticker data frame with at least date_id, y_key columns. - - y_key : str - Column name to base the trend line on. - - high_low: str, optional - Either "high" or "low". High is the default. - - Returns - ------- - DataFrame - If a trend is successfully found, - An updated Panda's data frame with a trend data {y_key}_trend column. - If no trend was found, - An original Panda's data frame - """ - - for iteration in [3, 4, 5, 6, 7]: - df_temp = df_data.copy() - while len(df_temp) > iteration: - reg = linregress( - x=df_temp["date_id"], - y=df_temp[y_key], - ) - - if high_low == "high": - df_temp = df_temp.loc[ - df_temp[y_key] > reg[0] * df_temp["date_id"] + reg[1] - ] - else: - df_temp = df_temp.loc[ - df_temp[y_key] < reg[0] * df_temp["date_id"] + reg[1] - ] - - if len(df_temp) > 1: - break - - if len(df_temp) == 1: - return df_data - - reg = linregress( - x=df_temp["date_id"], - y=df_temp[y_key], - ) - - df_data[f"{y_key}_trend"] = reg[0] * df_data["date_id"] + reg[1] - - return df_data diff --git a/gamestonk_terminal/economy/econ_data_helper.py b/gamestonk_terminal/economy/econ_data_helper.py index 7789b6aec79..810aae2af75 100644 --- a/gamestonk_terminal/economy/econ_data_helper.py +++ b/gamestonk_terminal/economy/econ_data_helper.py @@ -7,7 +7,7 @@ from datetime import datetime, timedelta import pandas as pd import mplfinance as mpf -from gamestonk_terminal.common.technical_analysis import trendline_api as trend +from gamestonk_terminal.stocks import stocks_helper # pylint: disable=too-many-arguments @@ -48,20 +48,20 @@ def draw_graph( if os.path.isfile(df_stock_cache): df_stock = pd.read_pickle(df_stock_cache) else: - df_stock = trend.load_ticker( + df_stock = stocks_helper.load_ticker( ticker, (datetime.now() - timedelta(days=time_delta)).strftime("%Y-%m-%d"), ) - df_stock = trend.find_trendline(df_stock, "OC_High", "high") - df_stock = trend.find_trendline(df_stock, "OC_Low", "how") + df_stock = stocks_helper.find_trendline(df_stock, "OC_High", "high") + df_stock = stocks_helper.find_trendline(df_stock, "OC_Low", "how") df_stock.to_pickle(df_stock_cache) else: - df_stock = trend.load_ticker( + df_stock = stocks_helper.load_ticker( ticker, (datetime.now() - timedelta(days=time_delta)).strftime("%Y-%m-%d") ) - df_stock = trend.find_trendline(df_stock, "OC_High", "high") - df_stock = trend.find_trendline(df_stock, "OC_Low", "how") + df_stock = stocks_helper.find_trendline(df_stock, "OC_High", "high") + df_stock = stocks_helper.find_trendline(df_stock, "OC_Low", "how") mc = mpf.make_marketcolors( up="green", down="red", edge="black", wick="black", volume="in", ohlc="i" diff --git a/gamestonk_terminal/stocks/behavioural_analysis/README.md b/gamestonk_terminal/stocks/behavioural_analysis/README.md index 846331922b2..1fe7c524c63 100644 --- a/gamestonk_terminal/stocks/behavioural_analysis/README.md +++ b/gamestonk_terminal/stocks/behavioural_analysis/README.md @@ -1,6 +1,4 @@ -# BEHAVIOURAL ANALYSIS - -Check all features [HERE](https://gamestonkterminal.github.io/GamestonkTerminal/common/behavioural_analysis/). +# Behavioural Analysis [>>](https://gamestonkterminal.github.io/GamestonkTerminal/common/behavioural_analysis/) Command|Description|Source ----|----|---- @@ -11,6 +9,7 @@ Command|Description|Source `popular` |show popular tickers|[Reddit](https://reddit.com) `spac_c` |show other users spacs announcements from subreddit SPACs community|[Reddit](https://reddit.com) `spac` |show other users spacs announcements from other subs|[Reddit](https://reddit.com) +`getdd` |gets due diligence from another user's post|[Reddit](https://reddit.com) `bullbear` |estimate quick sentiment from last 30 messages on board|[Stocktwits](https://stocktwits.com/) `messages` |output up to the 30 last messages on the board|[Stocktwits](https://stocktwits.com/) `trending` |trending stocks|[Stocktwits](https://stocktwits.com/) @@ -21,6 +20,8 @@ Command|Description|Source `regions` |regions that show highest interest in stock|[Google](https://google.com/) `queries` |top related queries with this stock|[Google](https://google.com/) `rise` |top rising related queries with stock|[Google](https://google.com/) +`popularsi` |show most popular stocks on social media right now|[Sentiment Investor](https://sentimentinvestor.com) +`emerging` |show stocks that are being talked about more than usual|[Sentiment Investor](https://sentimentinvestor.com) `metrics` |core social sentiment metrics for this stock|[Sentiment Investor](https://sentimentinvestor.com) `social` |social media figures for stock popularity|[Sentiment Investor](https://sentimentinvestor.com) `historical` |plot the past week of data for a selected metric|[Sentiment Investor](https://sentimentinvestor.com) diff --git a/gamestonk_terminal/stocks/dark_pool_shorts/README.md b/gamestonk_terminal/stocks/dark_pool_shorts/README.md index 1c0b7545bd7..a53b02eae78 100644 --- a/gamestonk_terminal/stocks/dark_pool_shorts/README.md +++ b/gamestonk_terminal/stocks/dark_pool_shorts/README.md @@ -1,5 +1,13 @@ -# DARK POOL SHORTS +# Dark Pool and Short Data [>>](https://gamestonkterminal.github.io/GamestonkTerminal/stocks/dark_pool_shorts) -Check all features [HERE](https://gamestonkterminal.github.io/GamestonkTerminal/stocks/dark_pool_shorts). - -**TODO** +Command|Description|Source +---|---|--- +`shorted` |show most shorted stocks |[Yahoo Finance](https://finance.yahoo.com/) +`hsi` |show top high short interest stocks of over 20% ratio |[High Short Interest](https://www.highshortinterest.com/) +`prom` |promising tickers based on dark pool shares regression |[FINRA](https://www.finra.org) +`pos` |dark pool short position|[Stockgrid](https://stockgrid.io) +`sidtc` |short interest and days to cover |[Stockgrid](https://stockgrid.io) +`dpotc` |dark pools (ATS) vs OTC data |[FINRA](https://www.finra.org) +`ftd` |fails-to-deliver data|[SEC](https://sec.report) +`spos` |net short vs position |[Stockgrid](https://stockgrid.io) +`psi` |price vs short interest volume |[Stockgrid](https://stockgrid.io) or [Quandl](https://data.nasdaq.com) diff --git a/gamestonk_terminal/stocks/discovery/README.md b/gamestonk_terminal/stocks/discovery/README.md index 021e7f00da2..f9df0f30561 100644 --- a/gamestonk_terminal/stocks/discovery/README.md +++ b/gamestonk_terminal/stocks/discovery/README.md @@ -1,29 +1,22 @@ -# DISCOVER STOCKS - -Check all features [HERE](https://gamestonkterminal.github.io/GamestonkTerminal/stocks/discovery/). +# Discovery [>>](https://gamestonkterminal.github.io/GamestonkTerminal/stocks/discovery/) Command|Description|Source ---|---|--- -`ipo` |past and future IPOs |[Finnhub](https://finnhub.io) -`map` |S&P500 index stocks map |[Finviz](https://finviz.com) -`rtp_sectors` |real-time performance sectors |[Alpha Vantage](www.alphavantage.co) +`rtearn` | realtime earnings from and expected moves | [Geek of Wall St](https://thegeekofwallstreet.com) +`pipo` |past IPOs dates |[Finnhub](https://finnhub.io) +`fipo` |future IPOs dates |[Finnhub](https://finnhub.io) `gainers` |show latest top gainers |[Yahoo Finance](https://finance.yahoo.com/) `losers` |show latest top losers |[Yahoo Finance](https://finance.yahoo.com/) -`orders` |orders by Fidelity Customers |[Fidelity](https://www.fidelity.com/) -`ark_orders` |orders by ARK Investment Management LLC | [Cathiesark](https://www.cathiesark.com) -`up_earnings` |upcoming earnings release dates |[Seeking Alpha](https://seekingalpha.com/) -`high_short` |show top high short interest stocks of over 20% ratio |[High Short Interest](https://www.highshortinterest.com/) -`low_float` |show low float stocks under 10M shares float |[Low Float](https://www.lowfloat.com/) -`simply_wallst` |Simply Wall St. research data |[Simply Wall St.](https://simplywall.st/about) -`spachero` |great website for SPACs research |[SpacHero](https://www.spachero.com/) -`uwhales` |good website for SPACs research |[UnusualWhales](https://unusualwhales.com/) -`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) +`ugs` |undervalued stocks with revenue and earnings growth in excess of 25% |[Yahoo Finance](https://finance.yahoo.com/) +`gtech` |tech stocks with revenue and earnings growth more than 25% |[Yahoo Finance](https://finance.yahoo.com/) +`active` |most active stocks by intraday trade volume |[Yahoo Finance](https://finance.yahoo.com/) +`ulc` |potentially undervalued large cap stocks |[Yahoo Finance](https://finance.yahoo.com/) +`asc` |small cap stocks with earnings growth rates better than 25% |[Yahoo Finance](https://finance.yahoo.com/) +`ford` |orders by Fidelity Customers |[Fidelity](https://www.fidelity.com/) +`arkord` |orders by ARK Investment Management LLC | [Cathiesark](https://www.cathiesark.com) +`upcoming` |upcoming earnings release dates |[Seeking Alpha](https://seekingalpha.com/) `latest` |latest news |[Seeking Alpha](https://seekingalpha.com/) `trending` |trending news |[Seeking Alpha](https://seekingalpha.com/) -`darkpool` |dark pool tickers with growing activity |[FINRA](https://www.finra.org) -`darkshort` |dark pool short position|[Stockgrid](https://stockgrid.io) -`shortvol` |short interest and days to cover |[Stockgrid](https://stockgrid.io) -`popular` |show most popular stocks on social media right now|[Sentiment Investor](https://sentimentinvestor.com) -`emerging` |show stocks that are being talked about more than usual|[Sentiment Investor](https://sentimentinvestor.com) +`low_float` |show low float stocks under 10M shares float |[Low Float](https://www.lowfloat.com/) +`hotpenny` |today's hot penny stocks |[hotpenny](https://pennystockflow.com) +`fds` |advanced Equities search based on country, sector, industry, name and/or description |[FinanceDatabase](https://github.com/JerBouma/FinanceDatabase) diff --git a/gamestonk_terminal/stocks/stocks_controller.py b/gamestonk_terminal/stocks/stocks_controller.py index abc23d4eee5..f57ddf198c1 100644 --- a/gamestonk_terminal/stocks/stocks_controller.py +++ b/gamestonk_terminal/stocks/stocks_controller.py @@ -16,7 +16,12 @@ from gamestonk_terminal.helper_funcs import ( parse_known_args_and_warn, ) from gamestonk_terminal.menu import session -from gamestonk_terminal.stocks.stocks_helper import display_candle, load, quote +from gamestonk_terminal.stocks.stocks_helper import ( + display_candle, + load, + quote, + process_candle, +) from gamestonk_terminal.helper_funcs import ( valid_date, @@ -214,14 +219,22 @@ Market {('CLOSED', 'OPEN')[b_is_stock_market_open()]} ) parser.add_argument( "-s", - "--start_date", + "--start", dest="s_start", type=valid_date, - default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"), + default=self.stock.index[0], help="Start date for candle data", ) parser.add_argument( - "--plotly", + "-e", + "--end", + dest="s_end", + type=valid_date, + default=self.stock.index[-1], + help="End date for candle data", + ) + parser.add_argument( + "--it", dest="plotly", action="store_true", default=False, @@ -280,7 +293,7 @@ Market {('CLOSED', 'OPEN')[b_is_stock_market_open()]} if not ns_parser: return if not self.ticker: - print("No ticker loaded. First use `load {ticker}`\n") + print("No ticker loaded. First use `load {ticker}`\n") return if ns_parser.raw: @@ -293,11 +306,11 @@ Market {('CLOSED', 'OPEN')[b_is_stock_market_open()]} ) else: + df_stock = process_candle(self.stock) + display_candle( - s_ticker=self.ticker + "." + self.suffix - if self.suffix - else self.ticker, - s_start=ns_parser.s_start, + s_ticker=self.ticker, + df_stock=df_stock, plotly=ns_parser.plotly, ) diff --git a/gamestonk_terminal/stocks/stocks_helper.py b/gamestonk_terminal/stocks/stocks_helper.py index 44482866628..64c29eb4263 100644 --- a/gamestonk_terminal/stocks/stocks_helper.py +++ b/gamestonk_terminal/stocks/stocks_helper.py @@ -2,8 +2,9 @@ __docformat__ = "numpy" import argparse from datetime import datetime, timedelta -from typing import List - +from typing import List, Union +import warnings +from scipy import stats import matplotlib.pyplot as plt import mplfinance as mpf import pandas as pd @@ -19,13 +20,13 @@ from tabulate import tabulate from gamestonk_terminal import config_plot as cfgPlot from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal import feature_flags as gtff -from gamestonk_terminal.common.technical_analysis import trendline_api as trend from gamestonk_terminal.helper_funcs import ( parse_known_args_and_warn, plot_autoscale, valid_date, ) +warnings.simplefilter("ignore") # pylint: disable=no-member,too-many-branches,C0302 @@ -139,6 +140,14 @@ def load( dest="s_start_date", help="The starting date (format YYYY-MM-DD) of the stock", ) + parser.add_argument( + "-e", + "--end", + type=valid_date, + default=datetime.now().strftime("%Y-%m-%d"), + dest="s_end_date", + help="The ending date (format YYYY-MM-DD) of the stock", + ) parser.add_argument( "-i", "--interval", @@ -209,12 +218,17 @@ def load( df_stock_candidate.sort_index(ascending=True, inplace=True) # Slice dataframe from the starting date YYYY-MM-DD selected - df_stock_candidate = df_stock_candidate[ns_parser.s_start_date :] + df_stock_candidate = df_stock_candidate[ + ns_parser.s_start_date : ns_parser.s_end_date + ] # Yahoo Finance Source elif ns_parser.source == "yf": df_stock_candidate = yf.download( - ns_parser.s_ticker, start=ns_parser.s_start_date, progress=False + ns_parser.s_ticker, + start=ns_parser.s_start_date, + end=ns_parser.s_end_date, + progress=False, ) # Check that loading a stock was not successful @@ -252,7 +266,9 @@ def load( df_stock_candidate.sort_index(ascending=True, inplace=True) # Slice dataframe from the starting date YYYY-MM-DD selected - df_stock_candidate = df_stock_candidate[ns_parser.s_start_date :] + df_stock_candidate = df_stock_candidate[ + ns_parser.s_start_date : ns_parser.s_end_date + ] # Check if start time from dataframe is more recent than specified if df_stock_candidate.index[0] > pd.to_datetime(ns_parser.s_start_date): @@ -290,7 +306,9 @@ def load( df_stock_candidate.sort_index(ascending=True, inplace=True) # Slice dataframe from the starting date YYYY-MM-DD selected - df_stock_candidate = df_stock_candidate[ns_parser.s_start_date :] + df_stock_candidate = df_stock_candidate[ + ns_parser.s_start_date : ns_parser.s_end_date + ] # Check if start time from dataframe is more recent than specified if df_stock_candidate.index[0] > pd.to_datetime(ns_parser.s_start_date): @@ -376,7 +394,9 @@ def load( df_stock_candidate.index.name = "date" # Slice dataframe from the starting date YYYY-MM-DD selected - df_stock_candidate = df_stock_candidate[ns_parser.s_start_date :] + df_stock_candidate = df_stock_candidate[ + ns_parser.s_start_date : ns_parser.s_end_date + ] # Check if start time from dataframe is more recent than specified if df_stock_candidate.index[0] > pd.to_datetime(ns_parser.s_start_date): @@ -407,27 +427,23 @@ def load( return [s_ticker, s_start, s_interval, df_stock] -def display_candle(s_ticker: str, s_start: datetime, plotly: bool): - """Shows candle plot of loaded ticker +def display_candle(s_ticker: str, df_stock: pd.DataFrame, plotly: bool): + """Shows candle plot of loaded ticker. [Source: Yahoo Finance, IEX Cloud or Alpha Vantage] Parameters ---------- + df_stock: pd.DataFrame + Stock dataframe s_ticker: str - Ticker to display - s_start: datetime - Starting date for candle + Ticker name plotly: bool Flag to show interactive plotly chart - """ - # TODO: Add option to customize plot even further - print(type(s_start)) - df_stock = trend.load_ticker(s_ticker, s_start) df_stock["ma20"] = df_stock["Close"].rolling(20).mean().fillna(method="bfill") df_stock["ma50"] = df_stock["Close"].rolling(50).mean().fillna(method="bfill") - df_stock = trend.find_trendline(df_stock, "OC_High", "high") - df_stock = trend.find_trendline(df_stock, "OC_Low", "low") + df_stock = find_trendline(df_stock, "OC_High", "high") + df_stock = find_trendline(df_stock, "OC_Low", "low") if not plotly: mc = mpf.make_marketcolors( up="green", @@ -460,7 +476,7 @@ def display_candle(s_ticker: str, s_start: datetime, plotly: bool): type="candle", mav=(20, 50), volume=True, - title=f"\n{s_ticker} - Starting {s_start.strftime('%Y-%m-%d')}", + title=f"\nStock {s_ticker}", addplot=ap0, xrotation=10, style=s, @@ -812,3 +828,120 @@ def plot_view_stock(df: pd.DataFrame, symbol: str, interval: str): plt.show() print("") + + +def load_ticker( + ticker: str, start_date: Union[str, datetime], end_date: Union[str, datetime] = "" +) -> pd.DataFrame: + """Loads a ticker data from Yahoo Finance, adds a data index column data_id and Open-Close High/Low columns. + + Parameters + ---------- + ticker : str + The stock ticker. + start_date : Union[str,datetime] + Start date to load stock ticker data formatted YYYY-MM-DD. + end_date : Union[str,datetime] + End date to load stock ticker data formatted YYYY-MM-DD. + + Returns + ------- + DataFrame + A Panda's data frame with columns Open, High, Low, Close, Adj Close, Volume, date_id, OC-High, OC-Low. + """ + if end_date: + df_data = yf.download(ticker, start=start_date, end=end_date, progress=False) + else: + df_data = yf.download(ticker, start=start_date, progress=False) + + df_data["date_id"] = (df_data.index.date - df_data.index.date.min()).astype( + "timedelta64[D]" + ) + df_data["date_id"] = df_data["date_id"].dt.days + 1 + + df_data["OC_High"] = df_data[["Open", "Close"]].max(axis=1) + df_data["OC_Low"] = df_data[["Open", "Close"]].min(axis=1) + + return df_data + + +def process_candle(df_data: pd.DataFrame) -> pd.DataFrame: + """Process DataFrame into candle style plot + + Parameters + ---------- + df_data : DataFrame + Stock dataframe. + + Returns + ------- + DataFrame + A Panda's data frame with columns Open, High, Low, Close, Adj Close, Volume, date_id, OC-High, OC-Low. + """ + df_data["date_id"] = (df_data.index.date - df_data.index.date.min()).astype( + "timedelta64[D]" + ) + df_data["date_id"] = df_data["date_id"].dt.days + 1 + + df_data["OC_High"] = df_data[["Open", "Close"]].max(axis=1) + df_data["OC_Low"] = df_data[["Open", "Close"]].min(axis=1) + + return df_data + + +def find_trendline( + df_data: pd.DataFrame, y_key: str, high_low: str = "high" +) -> pd.DataFrame: + """Attempts to find a trend line based on y_key column from a given stock ticker data frame. + + Parameters + ---------- + df_data : DataFrame + The stock ticker data frame with at least date_id, y_key columns. + + y_key : str + Column name to base the trend line on. + + high_low: str, optional + Either "high" or "low". High is the default. + + Returns + ------- + DataFrame + If a trend is successfully found, + An updated Panda's data frame with a trend data {y_key}_trend column. + If no trend was found, + An original Panda's data frame + """ + + for iteration in [3, 4, 5, 6, 7]: + df_temp = df_data.copy() + while len(df_temp) > iteration: + reg = stats.linregress( + x=df_temp["date_id"], + y=df_temp[y_key], + ) + + if high_low == "high": + df_temp = df_temp.loc[ + df_temp[y_key] > reg[0] * df_temp["date_id"] + reg[1] + ] + else: + df_temp = df_temp.loc[ + df_temp[y_key] < reg[0] * df_temp["date_id"] + reg[1] + ] + + if len(df_temp) > 1: + break + + if len(df_temp) == 1: + return df_data + + reg = stats.linregress( + x=df_temp["date_id"], + y=df_temp[y_key], + ) + + df_data[f"{y_key}_trend"] = reg[0] * df_data["date_id"] + reg[1] + + return df_data